diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
commit | e308faf24f68e262d92d294a01ddca7a17e76762 (patch) | |
tree | 22c47cb315811834861f013067878ff664e95abd /drivers | |
parent | 30c6397ce63178fcb3e7963ac247f0a03132aca9 (diff) |
Sync with Linux 2.1.46.
Diffstat (limited to 'drivers')
87 files changed, 10311 insertions, 2479 deletions
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 3f90981fd..d46336e34 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -2897,6 +2897,8 @@ static void raw_cmd_done(int flag) raw_cmd->flags |= FD_RAW_HARDFAILURE; } else { raw_cmd->reply_count = inr; + if(raw_cmd->reply_count > MAX_REPLIES) + raw_cmd->reply_count=0; for (i=0; i< raw_cmd->reply_count; i++) raw_cmd->reply[i] = reply_buffer[i]; diff --git a/drivers/block/ide.c b/drivers/block/ide.c index 1b293c539..61b792f74 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -2602,6 +2602,7 @@ __initfunc(static void probe_for_hwifs (void)) ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1); ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0); ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0); + ide_probe_pci (PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, &ide_init_triton, 0); #endif /* CONFIG_BLK_DEV_TRITON */ #ifdef CONFIG_BLK_DEV_OPTI621 ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0); diff --git a/drivers/block/ide.h b/drivers/block/ide.h index 95724e6e9..ecfdc12ea 100644 --- a/drivers/block/ide.h +++ b/drivers/block/ide.h @@ -302,7 +302,7 @@ typedef void (ide_selectproc_t) (ide_drive_t *); typedef enum { ide_unknown, ide_generic, ide_triton, ide_cmd640, ide_dtc2278, ide_ali14xx, ide_qd6580, ide_umc8672, ide_ht6560b, - ide_promise } + ide_promise, ide_via } hwif_chipset_t; typedef struct hwif_s { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 53fef9b74..9bb78d63e 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -292,7 +292,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) return -EBADF; if (lo->lo_inode) return -EBUSY; - inode = file->f_inode; + inode = file->f_dentry->d_inode; if (!inode) { printk("loop_set_fd: NULL inode?!?\n"); return -EINVAL; @@ -318,7 +318,7 @@ static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg) } lo->lo_inode = inode; - atomic_inc(&lo->lo_inode->i_count); + lo->lo_inode->i_count++; lo->transfer = NULL; figure_loop_size(lo); MOD_INC_USE_COUNT; diff --git a/drivers/block/md.c b/drivers/block/md.c index 12cb6dcf0..8f8ba19d5 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -202,10 +202,10 @@ static int do_md_stop (int minor, struct inode *inode) { int i; - if (atomic_read(&inode->i_count)>1 || md_dev[minor].busy>1) /* ioctl : one open channel */ + if (inode->i_count > 1 || md_dev[minor].busy>1) /* ioctl : one open channel */ { printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, - atomic_read(&inode->i_count), md_dev[minor].busy); + inode->i_count, md_dev[minor].busy); return -EBUSY; } diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 4ac6c0c41..7d7a3931c 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -360,10 +360,10 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) * Read block 0 to test for gzipped kernel */ if (fp->f_op->llseek) - fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0); + fp->f_op->llseek(fp->f_dentry->d_inode, fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; - fp->f_op->read(fp->f_inode, fp, buf, size); + fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size); /* * If it matches the gzip magic numbers, return -1 @@ -390,11 +390,11 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) * Read block 1 to test for minix and ext2 superblock */ if (fp->f_op->llseek) - fp->f_op->llseek(fp->f_inode, fp, + fp->f_op->llseek(fp->f_dentry->d_inode, fp, (start_block+1) * BLOCK_SIZE, 0); fp->f_pos = (start_block+1) * BLOCK_SIZE; - fp->f_op->read(fp->f_inode, fp, buf, size); + fp->f_op->read(fp->f_dentry->d_inode, fp, buf, size); /* Try minix */ if (minixsb->s_magic == MINIX_SUPER_MAGIC || @@ -421,7 +421,7 @@ identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)) done: if (fp->f_op->llseek) - fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0); + fp->f_op->llseek(fp->f_dentry->d_inode, fp, start_block * BLOCK_SIZE, 0); fp->f_pos = start_block * BLOCK_SIZE; if ((nblocks > 0) && blk_size[MAJOR(device)]) { @@ -444,8 +444,9 @@ done: */ __initfunc(static void rd_load_image(kdev_t device,int offset)) { - struct inode inode, out_inode; + struct inode inode, out_inode; struct file infile, outfile; + struct dentry in_dentry, out_dentry; unsigned short fs; kdev_t ram_device; int nblocks, i; @@ -457,15 +458,19 @@ __initfunc(static void rd_load_image(kdev_t device,int offset)) memset(&infile, 0, sizeof(infile)); memset(&inode, 0, sizeof(inode)); + memset(&in_dentry, 0, sizeof(in_dentry)); inode.i_rdev = device; infile.f_mode = 1; /* read only */ - infile.f_inode = &inode; + infile.f_dentry = &in_dentry; + in_dentry.d_inode = &inode; memset(&outfile, 0, sizeof(outfile)); memset(&out_inode, 0, sizeof(out_inode)); + memset(&out_dentry, 0, sizeof(out_dentry)); out_inode.i_rdev = ram_device; outfile.f_mode = 3; /* read/write */ - outfile.f_inode = &out_inode; + outfile.f_dentry = &out_dentry; + out_dentry.d_inode = &out_inode; if (blkdev_open(&inode, &infile) != 0) return; if (blkdev_open(&out_inode, &outfile) != 0) return; @@ -506,9 +511,9 @@ __initfunc(static void rd_load_image(kdev_t device,int offset)) printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks); for (i=0; i < nblocks; i++) { - infile.f_op->read(infile.f_inode, &infile, buf, + infile.f_op->read(infile.f_dentry->d_inode, &infile, buf, BLOCK_SIZE); - outfile.f_op->write(outfile.f_inode, &outfile, buf, + outfile.f_op->write(outfile.f_dentry->d_inode, &outfile, buf, BLOCK_SIZE); if (!(i % 16)) { printk("%c\b", rotator[rotate & 0x3]); @@ -637,7 +642,7 @@ __initfunc(static int fill_inbuf(void)) { if (exit_code) return -1; - insize = crd_infp->f_op->read(crd_infp->f_inode, crd_infp, + insize = crd_infp->f_op->read(crd_infp->f_dentry->d_inode, crd_infp, inbuf, INBUFSIZ); if (insize == 0) return -1; @@ -656,7 +661,7 @@ __initfunc(static void flush_window(void)) unsigned n; uch *in, ch; - crd_outfp->f_op->write(crd_outfp->f_inode, crd_outfp, window, + crd_outfp->f_op->write(crd_outfp->f_dentry->d_inode, crd_outfp, window, outcnt); in = window; for (n = 0; n < outcnt; n++) { diff --git a/drivers/block/triton.c b/drivers/block/triton.c index 7956307c8..006051af6 100644 --- a/drivers/block/triton.c +++ b/drivers/block/triton.c @@ -122,15 +122,25 @@ static unsigned int piix_key; #define PIIX_FLAGS_PREFETCH 4 #define PIIX_FLAGS_FAST_DMA 8 -typedef struct { - unsigned d0_flags :4; - unsigned d1_flags :4; - unsigned recovery :2; - unsigned reserved :2; - unsigned sample :2; - unsigned sidetim_enabled:1; - unsigned ports_enabled :1; -} piix_timing_t; + +union chip_en_reg_u { + struct { + unsigned d0_flags :4; + unsigned d1_flags :4; + unsigned recovery :2; + unsigned reserved :2; + unsigned sample :2; + unsigned sidetim_enabled:1; + unsigned ports_enabled :1; + } piix_s; + struct { + unsigned sec_en :1; + unsigned pri_en :1; + unsigned reserved :14; + } via_s; +}; + +typedef union chip_en_reg_u piix_timing_t; typedef struct { unsigned pri_recovery :2; @@ -269,16 +279,16 @@ static int piix_dmaproc (ide_dma_action_t func, ide_drive_t *drive) printk("%s: pcibios read failed\n", HWIF(drive)->name); return 1; } - dflags = drive->select.b.unit ? timing.d1_flags : timing.d0_flags; + dflags = drive->select.b.unit ? timing.piix_s.d1_flags : timing.piix_s.d0_flags; if (dflags & PIIX_FLAGS_FAST_PIO) { if (func == ide_dma_on && drive->media == ide_disk) dflags |= PIIX_FLAGS_FAST_DMA; else dflags &= ~PIIX_FLAGS_FAST_DMA; if (drive->select.b.unit == 0) - timing.d0_flags = dflags; + timing.piix_s.d0_flags = dflags; else - timing.d1_flags = dflags; + timing.piix_s.d1_flags = dflags; if (pcibios_write_config_word(piix_pci_bus, piix_pci_fn, reg, *(short *)&timing)) { printk("%s: pcibios write failed\n", HWIF(drive)->name); return 1; @@ -456,8 +466,14 @@ void ide_init_triton (byte bus, byte fn) chipset = "PIIX4"; else if (devid == PCI_DEVICE_ID_INTEL_82371SB_1) chipset = "PIIX3"; - else + else if (devid == PCI_DEVICE_ID_INTEL_82371_1) chipset = "PIIX"; + else if (devid == PCI_DEVICE_ID_VIA_82C586_1) + chipset = "VP1"; + else { + printk("Unknown PCI IDE interface 0x%x\n", devid); + goto quit; + } printk("%s: bus-master IDE device on PCI bus %d function %d\n", chipset, bus, fn); @@ -470,13 +486,24 @@ void ide_init_triton (byte bus, byte fn) printk("%s: IDE ports are not enabled (BIOS)\n", chipset); goto quit; } - if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) - goto quit; - if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1]))) - goto quit; - if ((!timings[0].ports_enabled) && (!timings[1].ports_enabled)) { - printk("%s: neither IDE port is enabled\n", chipset); - goto quit; + if (devid == PCI_DEVICE_ID_VIA_82C586_1) { + /* pri and sec channel enables are in port 0x40 */ + if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) + goto quit; + if ((!timings[0].via_s.pri_en && (!timings[0].via_s.sec_en))) { + printk("%s: neither IDE port is enabled\n", chipset); + goto quit; + } + } + else { /* INTEL piix */ + if ((rc = pcibios_read_config_word(bus, fn, 0x40, (short *)&timings[0]))) + goto quit; + if ((rc = pcibios_read_config_word(bus, fn, 0x42, (short *)&timings[1]))) + goto quit; + if ((!timings[0].piix_s.ports_enabled) && (!timings[1].piix_s.ports_enabled)) { + printk("%s: neither IDE port is enabled\n", chipset); + goto quit; + } } /* @@ -526,10 +553,30 @@ void ide_init_triton (byte bus, byte fn) case 0x170: pri_sec = 1; break; default: continue; } + + if (devid == PCI_DEVICE_ID_VIA_82C586_1) { + timing = timings[0]; + switch (h) { + case 0: + if (!timing.piix_s.ports_enabled) { + printk("port 0 DMA not enabled\n"); + continue; + } + case 1: + if (!timing.piix_s.sidetim_enabled) { + printk("port 1 DMA not enabled\n"); + continue; + } + } + hwif->chipset = ide_via; + } + else { /* PIIX */ + timing = timings[pri_sec]; - if (!timing.ports_enabled) /* interface disabled? */ + if (!timing.piix_s.ports_enabled) /* interface disabled? */ continue; hwif->chipset = ide_triton; + } if (dma_enabled) init_piix_dma(hwif, bmiba + (pri_sec ? 8 : 0)); #ifdef DISPLAY_PIIX_TIMINGS @@ -539,17 +586,32 @@ void ide_init_triton (byte bus, byte fn) { const char *slave; piix_sidetim_t sidetim; - byte sample = 5 - timing.sample; - byte recovery = 4 - timing.recovery; + byte sample = 5 - timing.piix_s.sample; + byte recovery = 4 - timing.piix_s.recovery; + unsigned int drvtim; + + if (devid == PCI_DEVICE_ID_VIA_82C586_1) { + pcibios_read_config_dword(bus, fn, 0x48, &drvtim); + if (pri_sec == 0) { + printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+(drvtim>>28), 1+((drvtim & 0x0f000000)>>24)); + printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf00000)>>20), 1+((drvtim & 0x0f0000)>>16)); + continue; + } else { + printk(" %s master: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf000)>>12), 1+((drvtim & 0x0f00)>>8)); + printk(" %s slave: active_pulse_CLKs=%d, recovery_CLKs=%d\n", hwif->name, 1+((drvtim & 0xf0)>>4), 1+(drvtim & 0x0f)); + continue; + } + } + if ((devid == PCI_DEVICE_ID_INTEL_82371SB_1 || devid == PCI_DEVICE_ID_INTEL_82371AB) - && timing.sidetim_enabled + && timing.piix_s.sidetim_enabled && !pcibios_read_config_byte(bus, fn, 0x44, (byte *) &sidetim)) slave = ""; /* PIIX3 and later */ else slave = "/slave"; /* PIIX, or PIIX3 in compatibility mode */ printk(" %s master%s: sample_CLKs=%d, recovery_CLKs=%d\n", hwif->name, slave, sample, recovery); - print_piix_drive_flags ("master:", timing.d0_flags); + print_piix_drive_flags ("master:", timing.piix_s.d0_flags); if (!*slave) { if (pri_sec == 0) { sample = 5 - sidetim.pri_sample; @@ -560,7 +622,7 @@ void ide_init_triton (byte bus, byte fn) } printk(" slave : sample_CLKs=%d, recovery_CLKs=%d\n", sample, recovery); } - print_piix_drive_flags ("slave :", timing.d1_flags); + print_piix_drive_flags ("slave :", timing.piix_s.d1_flags); } #endif /* DISPLAY_PIIX_TIMINGS */ } diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog index 6a0009511..3083c0c54 100644 --- a/drivers/char/ChangeLog +++ b/drivers/char/ChangeLog @@ -1,3 +1,27 @@ +Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * serial.c (begin_break, end_break, rs_ioctl): Applied patch + to support BSD ioctls to set and clear the break + condition explicitly. + + * console.c (scrup, scrdown, insert_line, delete_line): Applied + fix suggested by Aaron Tiensivu to speed up block scrolls + up and down. + + * n_tty.c (opost_block, write_chan): Added a modified "fast + console" patch which processes a block of text via + "cooking" efficiently. + +Wed Jun 18 15:25:50 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> + + * tty_io.c (init_dev, release_dev): Applied fix suggested by Bill + Hawes to prevent race conditions in the tty code. + + * n_tty.c (n_tty_chars_in_buffer): Applied fix suggested by Bill + Hawes so that n_tty_chars_in_buffer returns the correct + value in the case when the tty is in cannonical mode. (To + avoid a pty deadlock with telnetd.) + Thu Feb 27 01:53:08 1997 Theodore Ts'o <tytso@rsts-11.mit.edu> * serial.c (change_speed): Add support for the termios flag diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 4216b3154..c02b376e8 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -52,6 +52,7 @@ if [ "$CONFIG_MOUSE" = "y" ]; then if [ "$CONFIG_PSMOUSE" != "n" ]; then bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE fi + tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD fi if [ "$CONFIG_MODULES" = "y" ]; then @@ -97,4 +98,5 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG fi bool 'Enhanced Real Time Clock Support' CONFIG_RTC +tristate 'PC joystick support' CONFIG_JOYSTICK endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 14efcf554..73c1ac599 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -145,6 +145,14 @@ else endif endif +ifeq ($(CONFIG_JOYSTICK),y) +L_OBJS += joystick.o +else + ifeq ($(CONFIG_JOYSTICK),m) + M_OBJS += joystick.o + endif +endif + ifeq ($(CONFIG_MS_BUSMOUSE),y) M = y L_OBJS += msbusmouse.o @@ -218,6 +226,16 @@ ifdef CONFIG_SUN_MOUSE M = y endif +ifeq ($(CONFIG_PC110_PAD),y) +M = y +L_OBJS += pc110pad.o +else + ifeq ($(CONFIG_PC110_PAD),m) + M_OBJS += pc110pad.o + MM = m + endif +endif + ifeq ($(CONFIG_SUN_OPENPROMIO),y) M = y else diff --git a/drivers/char/console.c b/drivers/char/console.c index 61ab64aac..b24def4a2 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -514,13 +514,15 @@ static void set_origin(int currcons) __set_origin(__real_origin); } -static void scrup(int currcons, unsigned int t, unsigned int b) +static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr) { int hardscroll = hardscroll_enabled; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - if (t || b != video_num_lines) + if (t || b != video_num_lines || nr > 1) hardscroll = 0; if (hardscroll) { origin += video_size_row; @@ -575,31 +577,35 @@ static void scrup(int currcons, unsigned int t, unsigned int b) set_origin(currcons); } else { unsigned short * d = (unsigned short *) (origin+video_size_row*t); - unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1)); + unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr)); - memcpyw(d, s, (b-t-1) * video_size_row); - memsetw(d + (b-t-1) * video_num_columns, video_erase_char, video_size_row); + memcpyw(d, s, (b-t-nr) * video_size_row); + memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr); } } static void -scrdown(int currcons, unsigned int t, unsigned int b) +scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr) { unsigned short *s; unsigned int count; + unsigned int step; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - s = (unsigned short *) (origin+video_size_row*(b-2)); - if (b >= t + 1) { - count = b - t - 1; - while (count) { - count--; - memcpyw(s + video_num_columns, s, video_size_row); - s -= video_num_columns; - } + s = (unsigned short *) (origin+video_size_row*(b-nr-1)); + step = video_num_columns * nr; + count = b - t - nr; + while (count--) { + memcpyw(s + step, s, video_size_row); + s -= video_num_columns; + } + while (nr--) { + s += video_num_columns; + memsetw(s, video_erase_char, video_size_row); } - memsetw(s + video_num_columns, video_erase_char, video_size_row); has_scrolled = 1; } @@ -609,7 +615,7 @@ static void lf(int currcons) * if below scrolling region */ if (y+1 == bottom) - scrup(currcons,top,bottom); + scrup(currcons,top,bottom, 1); else if (y < video_num_lines-1) { y++; pos += video_size_row; @@ -623,7 +629,7 @@ static void ri(int currcons) * if above scrolling region */ if (y == top) - scrdown(currcons,top,bottom); + scrdown(currcons,top,bottom,1); else if (y > 0) { y--; pos -= video_size_row; @@ -1148,9 +1154,9 @@ static void insert_char(int currcons) need_wrap = 0; } -static void insert_line(int currcons) +static void insert_line(int currcons, unsigned int nr) { - scrdown(currcons,y,bottom); + scrdown(currcons,y,bottom,nr); need_wrap = 0; } @@ -1167,9 +1173,9 @@ static void delete_char(int currcons) need_wrap = 0; } -static void delete_line(int currcons) +static void delete_line(int currcons, unsigned int nr) { - scrup(currcons,y,bottom); + scrup(currcons,y,bottom,nr); need_wrap = 0; } @@ -1189,8 +1195,7 @@ static void csi_L(int currcons, unsigned int nr) nr = video_num_lines; else if (!nr) nr = 1; - while (nr--) - insert_line(currcons); + insert_line(currcons, nr); } static void csi_P(int currcons, unsigned int nr) @@ -1209,8 +1214,7 @@ static void csi_M(int currcons, unsigned int nr) nr = video_num_lines; else if (!nr) nr=1; - while (nr--) - delete_line(currcons); + delete_line(currcons, nr); } static void save_cur(int currcons) diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index efa895a69..94969bf28 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,6 +1,6 @@ #define BLOCKMOVE static char rcsid[] = -"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $"; +"$Revision: 1.1.1.1 $$Date: 1997/06/01 03:17:29 $"; /* * linux/drivers/char/cyclades.c @@ -29,6 +29,9 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 1.1.1.1 1997/06/01 03:17:29 ralf + * Initial import of Linux/MIPS pre-2.1.40. + * * Revision 1.36.4.27 1997/03/26 10:30:00 daniel * Changed for suport linux versions 2.1.X. * Backward compatible with linux versions 2.0.X. @@ -98,7 +101,7 @@ static char rcsid[] = * after -Y stuff (to make changes clearer) * * Revision 1.36.4.12 1996/07/11 15:40:55 bentson - * Add code to poll Cyclom-Z. Add code to get & set RS-232 control. + * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. * Add code to send break. Clear firmware ID word at startup (so * that other code won't talk to inactive board). * @@ -391,7 +394,13 @@ static char rcsid[] = constant in the definition below. No other change is necessary to support more boards/ports. */ -#define NR_PORTS 64 +//#define NR_PORTS 64 +#define NR_PORTS 128 + +#define ZE_V1_NPORTS 64 +#define ZO_V1 0 +#define ZO_V2 1 +#define ZE_V1 2 #define SERIAL_PARANOIA_CHECK #undef SERIAL_DEBUG_OPEN @@ -453,35 +462,51 @@ static char rcsid[] = #include <linux/kernel.h> #include <linux/bios32.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/version.h> -#if LINUX_VERSION_CODE >= 131328 +#if (LINUX_VERSION_CODE >= 0x020100) #include <asm/uaccess.h> +#include <linux/init.h> -#define memcpy_fromfs copy_from_user -#define memcpy_tofs copy_to_user -#define put_fs_long put_user -#define vremap ioremap +#define cy_put_user put_user -static unsigned long get_fs_long(unsigned long *addr) +static unsigned long cy_get_user(unsigned long *addr) { unsigned long result = 0; int error = get_user (result, addr); if (error) - printk ("cyclades: get_fs_long: error == %d\n", error); + printk ("cyclades: cy_get_user: error == %d\n", error); return result; } +#else + +#define __initfunc(__arginit) __arginit +#define copy_from_user memcpy_fromfs +#define copy_to_user memcpy_tofs +#define cy_get_user get_fs_long +#define cy_put_user put_fs_long +#define ioremap vremap + #endif #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif + #define IS_CYC_Z(card) ((card).num_chips == 1) +#define Z_FPGA_CHECK(card) \ + ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0) + +#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->mail_box_0) || \ + Z_FPGA_CHECK(card)) && \ + (ZFIRM_ID==((struct FIRM_ID *) \ + ((card).base_addr+ID_ADDRESS))->signature)) + #define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) @@ -548,7 +573,7 @@ static struct cyclades_card *IRQ_cards[16]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the memcpy_fromfs blocks while swapping in a page, + * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one @@ -698,10 +723,12 @@ static void CP4(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ static void CP8(int data) { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ +#if 0 static void CP16(int data) { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ static void CP32(long data) { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ +#endif /* @@ -758,6 +785,7 @@ do_softint(void *private_) if (!tty) return; +#if (LINUX_VERSION_CODE >= 0x020125) if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); @@ -774,6 +802,24 @@ do_softint(void *private_) } wake_up_interruptible(&tty->write_wait); } +#else + if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } +#endif } /* do_softint */ @@ -1351,7 +1397,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ -/******** Start of block of Cyclom-Z specific code *********/ +/******** Start of block of Cyclades-Z specific code *********/ /***********************************************************/ @@ -1365,7 +1411,7 @@ cyz_fetch_msg( struct cyclades_card *cinfo, unsigned long loc_doorbell; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) @@ -1397,7 +1443,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, int index; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) @@ -1408,7 +1454,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, pci_doorbell = &((struct RUNTIME_9060 *) (cinfo->ctl_addr))->pci_doorbell; while( (*pci_doorbell & 0xff) != 0){ - if (index++ == 100){ + if (index++ == 1000){ return(-1); } udelay(50L); @@ -1421,6 +1467,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo, } /* cyz_issue_cmd */ +#if 0 static int cyz_update_channel( struct cyclades_card *cinfo, u_long channel, u_char mode, u_char cmd) @@ -1430,7 +1477,7 @@ cyz_update_channel( struct cyclades_card *cinfo, struct ZFW_CTRL *zfw_ctrl; struct CH_CTRL *ch_ctrl; - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return (-1); } zfw_ctrl = @@ -1442,6 +1489,7 @@ cyz_update_channel( struct cyclades_card *cinfo, return cyz_issue_cmd(cinfo, channel, cmd, 0L); } /* cyz_update_channel */ +#endif static void @@ -1467,6 +1515,7 @@ cyz_poll(unsigned long arg) u_long channel; u_char cmd; u_long *param; + u_long hw_ver, fw_ver; cyz_timerlist.expires = jiffies + 100; @@ -1476,7 +1525,7 @@ cyz_poll(unsigned long arg) firm_id = (struct FIRM_ID *) (cinfo->base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ continue; } @@ -1484,6 +1533,8 @@ cyz_poll(unsigned long arg) (struct ZFW_CTRL *) (cinfo->base_addr + firm_id->zfwctrl_addr); board_ctrl = &zfw_ctrl->board_ctrl; + fw_ver = board_ctrl->fw_version; + hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0; while( cyz_fetch_msg( cinfo, &channel, &cmd, ¶m) == 1){ char_count = 0; @@ -1514,7 +1565,9 @@ cyz_poll(unsigned long arg) break; case C_CM_MDCD: if (info->flags & ASYNC_CHECK_CD){ - if( ch_ctrl[channel].rs_status & C_RS_DCD){ + if (((hw_ver != 0 || fw_ver > 241) + ? ((u_long)param) + : ch_ctrl[channel].rs_status) & C_RS_DCD) { /* SP("Open Wakeup\n"); */ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); @@ -1709,7 +1762,7 @@ cyz_poll(unsigned long arg) } /* cyz_poll */ -/********** End of block of Cyclom-Z specific code *********/ +/********** End of block of Cyclades-Z specific code *********/ /***********************************************************/ @@ -1794,7 +1847,7 @@ startup(struct cyclades_port * info) base_addr = (unsigned char*) (cy_card[card].base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])){ return -ENODEV; } @@ -1951,7 +2004,7 @@ shutdown(struct cyclades_port * info) #endif firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])) { return; } @@ -2143,7 +2196,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, base_addr = (char *)(cinfo->base_addr); firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(*cinfo)){ return -EINVAL; } @@ -2237,11 +2290,17 @@ cy_open(struct tty_struct *tty, struct file * filp) will make the user pay attention. */ if (IS_CYC_Z(cy_card[info->card])) { - struct FIRM_ID *firm_id = - (struct FIRM_ID *) - (cy_card[info->card].base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ - printk("Cyclom-Z firmware not yet loaded\n"); + if (!ISZLOADED(cy_card[info->card])) { + if (((ZE_V1 ==((struct RUNTIME_9060 *) + ((cy_card[info->card]).ctl_addr))->mail_box_0) && + Z_FPGA_CHECK(cy_card[info->card])) && + (ZFIRM_HLT==((struct FIRM_ID *) + ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)) + { + printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); + } else { + printk("Cyclades-Z firmware not yet loaded\n"); + } return -ENODEV; } } @@ -2448,7 +2507,7 @@ cy_write(struct tty_struct * tty, int from_user, } if (from_user) { - memcpy_fromfs(tmp_buf, buf, c); + copy_from_user(tmp_buf, buf, c); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); @@ -2816,7 +2875,7 @@ set_line_char(struct cyclades_port * info) firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature != ZFIRM_ID){ + if (!ISZLOADED(cy_card[card])) { return; } @@ -2956,7 +3015,7 @@ get_serial_info(struct cyclades_port * info, tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); + copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ @@ -2970,7 +3029,7 @@ set_serial_info(struct cyclades_port * info, if (!new_info) return -EFAULT; - memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); + copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { @@ -3051,7 +3110,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature == ZFIRM_ID){ + if (ISZLOADED(cy_card[card])) { zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + firm_id->zfwctrl_addr); @@ -3070,7 +3129,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) } } - put_fs_long(result,(unsigned long *) value); + cy_put_user(result,(unsigned long *) value); return 0; } /* get_modem_info */ @@ -3082,7 +3141,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, int card,chip,channel,index; unsigned char *base_addr; unsigned long flags; - unsigned int arg = get_fs_long((unsigned long *) value); + unsigned int arg = cy_get_user((unsigned long *) value); struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; struct BOARD_CTRL *board_ctrl; @@ -3180,7 +3239,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, firm_id = (struct FIRM_ID *) (cy_card[card].base_addr + ID_ADDRESS); - if (firm_id->signature == ZFIRM_ID){ + if (ISZLOADED(cy_card[card])) { zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + firm_id->zfwctrl_addr); @@ -3281,7 +3340,7 @@ static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); + copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); info->mon.int_count = 0; info->mon.char_count = 0; info->mon.char_max = 0; @@ -3335,7 +3394,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; - put_fs_long(tmp,value); + cy_put_user(tmp,value); } else { // Nothing to do! } @@ -3354,7 +3413,7 @@ set_default_threshold(struct cyclades_port * info, unsigned long value) static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_threshold,value); + cy_put_user(info->default_threshold,value); return 0; }/* get_default_threshold */ @@ -3401,7 +3460,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value) + (cy_chip_offset[chip]<<index)); tmp = base_addr[CyRTPR<<index]; - put_fs_long(tmp,value); + cy_put_user(tmp,value); } else { // Nothing to do! } @@ -3420,7 +3479,7 @@ set_default_timeout(struct cyclades_port * info, unsigned long value) static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_fs_long(info->default_timeout,value); + cy_put_user(info->default_timeout,value); return 0; }/* get_default_timeout */ @@ -3530,7 +3589,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - put_fs_long(C_CLOCAL(tty) ? 1 : 0, + cy_put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); break; case TIOCSSOFTCAR: @@ -3541,7 +3600,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, break; } - arg = get_fs_long((unsigned long *) arg); + arg = cy_get_user((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); @@ -3967,8 +4026,8 @@ cy_detect_isa(void)) } /* probe for CD1400... */ -#if LINUX_VERSION_CODE >= 131328 - cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000); +#if (LINUX_VERSION_CODE >= 0x020100) + cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000); #endif cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0); if (cy_isa_nchan == 0) { @@ -3988,6 +4047,7 @@ cy_detect_isa(void)) printk("Cyclom-Y/ISA found at 0x%x ", (unsigned int) cy_isa_address); printk("but no more channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(nboard); } /* fill the next cy_card structure available */ @@ -3998,6 +4058,7 @@ cy_detect_isa(void)) printk("Cyclom-Y/ISA found at 0x%x ", (unsigned int) cy_isa_address); printk("but no more cards can be used .\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(nboard); } @@ -4051,6 +4112,8 @@ cy_detect_pci(void)) unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; unsigned short i,j,cy_pci_nchan; unsigned short device_id,dev_index = 0,board_index = 0; + unsigned long mailbox; + unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; if(pcibios_present() == 0) { /* PCI bus not present */ return(0); @@ -4070,6 +4133,9 @@ cy_detect_pci(void)) } } + if (device_id == 0) + break; + /* read PCI configuration area */ pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_INTERRUPT_LINE, &cy_pci_irq); @@ -4081,9 +4147,7 @@ cy_detect_pci(void)) PCI_BASE_ADDRESS_2, &cy_pci_addr2); pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_REVISION_ID, &cyy_rev_id); - if (device_id == 0){ - break; - }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", @@ -4096,11 +4160,11 @@ cy_detect_pci(void)) cy_pci_addr1 &= 0xfffffffc; cy_pci_addr2 &= 0xfffffff0; -#if LINUX_VERSION_CODE < 131328 +#if (LINUX_VERSION_CODE < 0x020100) if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ #endif cy_pci_addr2 = - (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin); + (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin); #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n", @@ -4112,12 +4176,14 @@ cy_detect_pci(void)) printk("Cyclom-Y PCI host card with "); printk("no Serial-Modules at 0x%x.\n", (unsigned int) cy_pci_addr2); + i--; continue; } if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { printk("Cyclom-Y/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(i); } /* fill the next cy_card structure available */ @@ -4128,6 +4194,7 @@ cy_detect_pci(void)) printk("Cyclom-Y/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); } @@ -4167,65 +4234,101 @@ cy_detect_pci(void)) cy_next_channel += cy_pci_nchan; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ /* print message */ - printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); - printk("Cyclom-Z/PCI not supported for low addresses\n"); + printk("Cyclades-Z/PCI not supported for low addresses\n"); break; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ #ifdef CY_PCI_DEBUG - printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); #endif - cy_pci_addr2 &= 0xfffffff0; - cy_pci_addr2 = (unsigned int) vremap( - cy_pci_addr2 & PAGE_MASK, - PAGE_ALIGN(CyPCI_Zwin)) - + (cy_pci_addr2 & (PAGE_SIZE-1)); cy_pci_addr0 &= 0xfffffff0; - cy_pci_addr0 = (unsigned int) vremap( + cy_pci_addr0 = (unsigned int) ioremap( cy_pci_addr0 & PAGE_MASK, PAGE_ALIGN(CyPCI_Zctl)) + (cy_pci_addr0 & (PAGE_SIZE-1)); + + mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; + cy_pci_addr2 &= 0xfffffff0; + if (mailbox == ZE_V1) { + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Ze_win)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + if (ZeIndex == NR_CARDS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } else { + Ze_addr0[ZeIndex] = cy_pci_addr0; + Ze_addr2[ZeIndex] = cy_pci_addr2; + ZeIndex++; + } + i--; + continue; + } else { + cy_pci_addr2 = (unsigned int) ioremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + } + #ifdef CY_PCI_DEBUG - printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", cy_pci_addr2, cy_pci_addr0); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) + if (mailbox == ZO_V1) { + ((struct RUNTIME_9060 *)(cy_pci_addr0)) ->loc_addr_base = WIN_CREG; - PAUSE - printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n", - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) - ->loc_addr_base = WIN_RAM; + PAUSE + printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n", + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_RAM; + } else { + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); + } #endif /* The following clears the firmware id word. This ensures that the driver will not attempt to talk to the board until it has been properly initialized. */ PAUSE - *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; + if (mailbox == ZO_V1) + *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; - /* This must be a Cyclom-8Zo/PCI. The extendable + /* This must be a Cyclades-8Zo/PCI. The extendable version will have a different device_id and will be allocated its maximum number of ports. */ cy_pci_nchan = 8; + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + /* fill the next cy_card structure available */ for (j = 0 ; j < NR_CARDS ; j++) { if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Z/PCI found at 0x%x ", + printk("Cyclades-Z/PCI found at 0x%x ", (unsigned int) cy_pci_addr2); printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); } @@ -4235,9 +4338,9 @@ cy_detect_pci(void)) SA_INTERRUPT,"cyclomZ",NULL)) { printk("Could not allocate IRQ%d ", - (unsigned int) cy_pci_addr2); - printk("for Cyclom-Z/PCI at 0x%x.\n", cy_pci_irq); + printk("for Cyclades-Z/PCI at 0x%x.\n", + (unsigned int) cy_pci_addr2); return(i); } } @@ -4254,12 +4357,12 @@ cy_detect_pci(void)) /* print message */ /* don't report IRQ if board is no IRQ */ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { - printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", j+1,cy_pci_addr2, (cy_pci_addr2 + CyPCI_Zwin - 1), (int)cy_pci_irq); }else{ - printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ", + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", j+1,cy_pci_addr2, (cy_pci_addr2 + CyPCI_Zwin - 1)); } @@ -4268,6 +4371,93 @@ cy_detect_pci(void)) cy_next_channel += cy_pci_nchan; } } + + for (; ZeIndex != 0 && i < NR_CARDS; i++) { + cy_pci_addr0 = Ze_addr0[0]; + cy_pci_addr2 = Ze_addr2[0]; + for (j = 0 ; j < ZeIndex-1 ; j++) { + Ze_addr0[j] = Ze_addr0[j+1]; + Ze_addr2[j] = Ze_addr2[j+1]; + } + ZeIndex--; + mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; +#ifdef CY_PCI_DEBUG + printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + /* This must be the new Cyclades-Ze/PCI. */ + cy_pci_nchan = ZE_V1_NPORTS; + + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + cy_pci_irq); + printk("for Cyclades-Z/PCI at 0x%x.\n", + (unsigned int) cy_pci_addr2); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = 1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Ze_win - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Ze_win - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + if (ZeIndex != 0) { + printk("Cyclades-Z/PCI found at 0x%x ", + (unsigned int) Ze_addr2[0]); + printk("but no more cards can be used.\n"); + printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); + } return(i); #else return(0); @@ -4319,6 +4509,8 @@ cy_init(void)) struct cyclades_card *cinfo; int number_z_boards = 0; int board,port,i; + unsigned long mailbox; + int nports; show_version(); @@ -4416,10 +4608,13 @@ cy_init(void)) /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { cinfo = &cy_card[board]; - if (cinfo->num_chips == 1){ /* Cyclom-8Zo/PCI */ + if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */ number_z_boards++; + mailbox = ((struct RUNTIME_9060 *) + cy_card[board].ctl_addr)->mail_box_0; + nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; for (port = cinfo->first_line ; - port < cinfo->first_line + 8; + port < cinfo->first_line + nports; port++) { info = &cy_port[port]; @@ -4523,7 +4718,7 @@ cy_init(void)) cyz_timerlist.expires = jiffies + 1; add_timer(&cyz_timerlist); #ifdef CY_PCI_DEBUG - printk("Cyclom-Z polling initialized\n"); + printk("Cyclades-Z polling initialized\n"); #endif } diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c new file mode 100644 index 000000000..e85d36a98 --- /dev/null +++ b/drivers/char/joystick.c @@ -0,0 +1,376 @@ +/* + + linux/drivers/char/joystick.c + Copyright (C) 1992, 1993 Arthur C. Smith + Joystick driver for Linux running on an IBM compatible computer. + +VERSION INFO: +01/08/93 ACS 0.1: Works but needs multi-joystick support +01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1) + Added delay between measuring joystick axis + Added scaling ioctl +02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel + panics 8-) +02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read. + After looking at a schematic of a joystick card + it became apparent that any write to the joystick + port started ALL the joystick one shots. If the + one that we are reading is short enough and the + first one to be read, the second one will return + bad data if it's one shot has not expired when + the joystick port is written for the second time. + Thus solves the mystery delay problem in 0.2! +05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added + joystick support to the make config options, + updated the driver to return the buttons as + positive logic, and read both axis at once + (thanks Eyal!), and added some new ioctls. +02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15 + kernel (and hopefully 1.0). Also did some + cleanup: indented code, fixed some typos, wrote + man page, etc... +05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code +04/03/96 Matt Rhoten 0.8: many minor changes: + new read loop from Hal Maney <maney@norden.com> + cleaned up #includes to allow #include of + joystick.h with gcc -Wall and from g++ + made js_init fail if it finds zero joysticks + general source/comment cleanup + use of MOD_(INC|DEC)_USE_COUNT + changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE> + to compile correctly under 1.3 in kernel or as module +06/30/97 Alan Cox 0.9: Ported to 2.1.x + Reformatted to resemble Linux coding standard + Removed semaphore bug (we can dump the lot I think) + Fixed xntp timer adjust during joystick timer0 bug + Changed variable names to lower case. Kept binary + compatibility. + Better ioctl names. Kept binary compatibility. + Removed 'save_busy'. Just set busy to 1. +*/ + +#include <linux/module.h> +#include <linux/joystick.h> +#include <linux/mm.h> +#include <linux/major.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +static struct js_config js_data[JS_MAX]; /* misc data */ +static int js_exist; /* which joysticks' axis exist? */ +static int js_read_semaphore; /* to prevent two processes from trying + to read different joysticks at the + same time */ + +/* + * get_timer0(): + * returns the current value of timer 0. This is a 16 bit counter that starts + * at LATCH and counts down to 0 + */ + +extern inline int get_timer0(void) +{ + unsigned long flags; + int t0, t1; + save_flags(flags); + cli(); + outb (0, PIT_MODE); + t0 = (int) inb (PIT_COUNTER_0); + t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0; + restore_flags(flags); + return (t1); +} + +/* + * find_axes(): + * + * returns which axes are hooked up, in a bitfield. 2^n is set if + * axis n is hooked up, for 0 <= n < 4. + * + * REVIEW: should update this to handle eight-axis (four-stick) game port + * cards. anyone have one of these to test on? mattrh 3/23/96 + */ + +extern inline int find_axes(void) +{ + int j; + outb (0xff, JS_PORT); /* trigger oneshots */ + /* and see what happens */ + for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--); + /* do nothing; wait for the timeout */ + js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */ + js_exist = (~js_exist) & 0x0f; +/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/ + return js_exist; +} + +static int js_ioctl (struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned int minor = MINOR (inode->i_rdev); + if (minor >= JS_MAX) + return -ENODEV; + + if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/ + return -ENODEV; + switch (cmd) + { + + case JSIOCSCAL: /*from struct *arg to js_data[minor]*/ + if(copy_from_user(&js_data[minor].js_corr, + (void *)arg, sizeof(struct js_status))) + return -EFAULT; + break; + case JSIOCGCAL: /*to struct *arg from js_data[minor]*/ + if(copy_to_user((void *) arg, &js_data[minor].js_corr, + sizeof(struct js_status))) + return -EFAULT; + break; + case JSIOCSTIMEOUT: + if(copy_from_user(&js_data[minor].js_timeout, + (void *)arg, sizeof(js_data[0].js_timeout))) + return -EFAULT; + break; + case JSIOCGTIMEOUT: + if(copy_to_user((void *)arg, &js_data[minor].js_timeout, + sizeof(js_data[0].js_timeout))) + return -EFAULT; + break; + case JSIOCSTIMELIMIT: + if(copy_from_user(&js_data[minor].js_timelimit, + (void *)arg, sizeof(js_data[0].js_timelimit))) + return -EFAULT; + break; + case JSIOCGTIMELIMIT: + if(copy_to_user((void *)arg, &js_data[minor].js_timelimit, + sizeof(js_data[0].js_timelimit))) + return -EFAULT; + break; + case JSIOCGCONFIG: + if(copy_to_user((void *)arg, &js_data[minor], + sizeof(struct js_config))) + return -EFAULT; + break; + case JSIOCSCONFIG: + if(copy_from_user(&js_data[minor], (void *)arg, + sizeof(struct js_config))) + return -EFAULT; + /* Must be busy to do this ioctl! */ + js_data[minor].busy = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * js_open(): + * device open routine. increments module usage count, initializes + * data for that joystick. + * + * returns: 0 or + * -ENODEV: asked for joystick other than #0 or #1 + * -ENODEV: asked for joystick on axis where there is none + * -EBUSY: attempt to open joystick already open + */ + +static int js_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR (inode->i_rdev); + int j; + + if (minor >= JS_MAX) + return -ENODEV; /*check for joysticks*/ + + for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--); + cli(); /*block js_read while js_exist is being modified*/ + /*js minor exists?*/ + if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) { + js_exist = (~js_exist) & 0x0f; + sti(); + return -ENODEV; + } + js_exist = (~js_exist) & 0x0f; + sti(); + + if (js_data[minor].busy) + return -EBUSY; + js_data[minor].busy = JS_TRUE; + js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/ + js_data[minor].js_corr.y = JS_DEF_CORR; + js_data[minor].js_timeout = JS_DEF_TIMEOUT; + js_data[minor].js_timelimit = JS_DEF_TIMELIMIT; + js_data[minor].js_expiretime = jiffies; + + MOD_INC_USE_COUNT; + return 0; +} + +static int js_release (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR (inode->i_rdev); + inode->i_atime = CURRENT_TIME; + js_data[minor].busy = JS_FALSE; + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * js_read() reads the buttons x, and y axis from both joysticks if a + * given interval has expired since the last read or is equal to + * -1l. The buttons are in port 0x201 in the high nibble. The axis are + * read by writing to 0x201 and then measuring the time it takes the + * one shots to clear. + */ + +static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count) +{ + int j, chk, jsmask; + int t0, t_x0, t_y0, t_x1, t_y1; + unsigned int minor, minor2; + int buttons; + + if (count != JS_RETURN) + return -EINVAL; + minor = MINOR (inode->i_rdev); + inode->i_atime = CURRENT_TIME; + if (jiffies >= js_data[minor].js_expiretime) + { + minor2 = minor << 1; + j = js_data[minor].js_timeout; + for (; (js_exist & inb (JS_PORT)) && j; j--); + if (j == 0) + return -ENODEV; /*no joystick here*/ + /*Make sure no other proc is using port*/ + + cli(); + js_read_semaphore++; + sti(); + + buttons = ~(inb (JS_PORT) >> 4); + js_data[0].js_save.buttons = buttons & 0x03; + js_data[1].js_save.buttons = (buttons >> 2) & 0x03; + j = js_data[minor].js_timeout; + jsmask = 0; + + cli(); /*no interrupts!*/ + outb (0xff, JS_PORT); /*trigger one-shots*/ + /*get init timestamp*/ + t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 (); + /*wait for an axis' bit to clear or timeout*/ + while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask)) + { + if (!(chk & JS_X_0)) { + t_x0 = get_timer0(); + jsmask |= JS_X_0; + } + if (!(chk & JS_Y_0)) { + t_y0 = get_timer0(); + jsmask |= JS_Y_0; + } + if (!(chk & JS_X_1)) { + t_x1 = get_timer0(); + jsmask |= JS_X_1; + } + if (!(chk & JS_Y_1)) { + t_y1 = get_timer0(); + jsmask |= JS_Y_1; + } + } + sti(); /* allow interrupts */ + + js_read_semaphore = 0; /* allow other reads to progress */ + if (j == 0) + return -ENODEV; /*read timed out*/ + js_data[0].js_expiretime = jiffies + + js_data[0].js_timelimit; /*update data*/ + js_data[1].js_expiretime = jiffies + + js_data[1].js_timelimit; + js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >> + js_data[0].js_corr.x; + js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >> + js_data[0].js_corr.y; + js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >> + js_data[1].js_corr.x; + js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >> + js_data[1].js_corr.y; + } + + if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN)) + return -EFAULT; + return JS_RETURN; +} + + +static struct file_operations js_fops = +{ + NULL, /* js_lseek*/ + js_read, /* js_read */ + NULL, /* js_write*/ + NULL, /* js_readaddr*/ + NULL, /* js_select */ + js_ioctl, /* js_ioctl*/ + NULL, /* js_mmap */ + js_open, /* js_open*/ + js_release, /* js_release*/ + NULL /* js_fsync */ +}; + +#ifdef MODULE + +#define joystick_init init_module + +void cleanup_module (void) +{ + if (unregister_chrdev (JOYSTICK_MAJOR, "joystick")) + printk ("joystick: cleanup_module failed\n"); + release_region(JS_PORT, 1); +} + +#endif /* MODULE */ + +int joystick_init(void) +{ + int js_num; + int js_count; + + if (check_region(JS_PORT, 1)) { + printk("js_init: port already in use\n"); + return -EBUSY; + } + + js_num = find_axes(); + js_count = !!(js_num & 0x3) + !!(js_num & 0xC); + + + if (js_count == 0) + { + printk("No joysticks found.\n"); + return -ENODEV; + /* if the user boots the machine, which runs insmod, and THEN + decides to hook up the joystick, well, then we do the wrong + thing. But it's a good idea to avoid giving out a false sense + of security by letting the module load otherwise. */ + } + + if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) { + printk ("Unable to get major=%d for joystick\n", + JOYSTICK_MAJOR); + return -EBUSY; + } + request_region(JS_PORT, 1, "joystick"); + + for (js_num = 0; js_num < JS_MAX; js_num++) + js_data[js_num].busy = JS_FALSE; + js_read_semaphore = 0; + + printk (KERN_INFO "Found %d joystick%c.\n", + js_count, + (js_num == 1) ? ' ' : 's'); + return 0; +} + diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 5eb52752d..9f9e5a992 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -724,7 +724,11 @@ __initfunc(int lp_init(void)) return 0; printk(KERN_INFO "lp: driver loaded but no devices found\n"); +#ifdef MODULE + return 0; +#else return 1; +#endif } #ifdef MODULE diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 499132bf8..063503595 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -20,6 +20,7 @@ #include <linux/mm.h> #include <linux/random.h> #include <linux/init.h> +#include <linux/joystick.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -134,8 +135,7 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str #endif if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -532,6 +532,13 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_SOUND soundcard_init(); #endif +#ifdef CONFIG_JOYSTICK + /* + * Some joysticks only appear when the soundcard they are + * connected too is confgured. Keep the sound/joystick ordering. + */ + joystick_init(); +#endif #if CONFIG_QIC02_TAPE qic02_tape_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 90ff2026f..6262792b6 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -217,6 +217,9 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_SUN_MOUSE sun_mouse_init(); #endif +#ifdef CONFIG_PC110_PAD + pc110pad_init(); +#endif /* * Only one watchdog can succeed. We probe the pcwatchdog first, * then the wdt cards and finally the software watchdog which always diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 8db4e1443..32dc3a51d 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -88,9 +88,17 @@ void n_tty_flush_buffer(struct tty_struct * tty) /* * Return number of characters buffered to be delivered to user + * */ int n_tty_chars_in_buffer(struct tty_struct *tty) { + if (tty->icanon) { + if (!tty->canon_data) return 0; + + return (tty->canon_head > tty->read_tail) ? + tty->canon_head - tty->read_tail : + tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + } return tty->read_cnt; } @@ -157,6 +165,72 @@ static int opost(unsigned char c, struct tty_struct *tty) return 0; } +/* + * opost_block --- to speed up block console writes, among other + * things. + */ +static int opost_block(struct tty_struct * tty, + const unsigned char * inbuf, unsigned int nr) +{ + char buf[80]; + int space; + int i; + char *cp; + + space = tty->driver.write_room(tty); + if (!space) + return 0; + if (nr > space) + nr = space; + if (nr > sizeof(buf)) + nr = sizeof(buf); + nr -= copy_from_user(buf, inbuf, nr); + if (!nr) + return 0; + + for (i = 0, cp = buf; i < nr; i++, cp++) { + switch (*cp) { + case '\n': + if (O_ONLRET(tty)) + tty->column = 0; + if (O_ONLCR(tty)) + goto break_out; + tty->canon_column = tty->column; + break; + case '\r': + if (O_ONOCR(tty) && tty->column == 0) + goto break_out; + if (O_OCRNL(tty)) { + *cp = '\n'; + if (O_ONLRET(tty)) + tty->canon_column = tty->column = 0; + break; + } + tty->canon_column = tty->column = 0; + break; + case '\t': + goto break_out; + case '\b': + if (tty->column > 0) + tty->column--; + break; + default: + if (O_OLCUC(tty)) + *cp = toupper(*cp); + if (!iscntrl(*cp)) + tty->column++; + break; + } + } +break_out: + if (tty->driver.flush_chars) + tty->driver.flush_chars(tty); + i = tty->driver.write(tty, 0, buf, i); + return i; +} + + + static inline void put_char(unsigned char c, struct tty_struct *tty) { tty->driver.put_char(tty, c); @@ -632,7 +706,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old) return; tty->icanon = (L_ICANON(tty) != 0); - if (tty->flags & (1<<TTY_HW_COOK_IN)) { + if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { tty->raw = 1; tty->real_raw = 1; return; @@ -780,7 +854,7 @@ do_it_again: /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_inode->i_rdev != CONSOLE_DEV && + if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV && current->tty == tty) { if (tty->pgrp <= 0) printk("read_chan: tty->pgrp <= 0!\n"); @@ -838,7 +912,7 @@ do_it_again: tty->minimum_to_wake = (minimum - (b - buf)); if (!input_available_p(tty, 0)) { - if (tty->flags & (1 << TTY_OTHER_CLOSED)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { retval = -EIO; break; } @@ -934,12 +1008,12 @@ static int write_chan(struct tty_struct * tty, struct file * file, const unsigned char * buf, unsigned int nr) { struct wait_queue wait = { current, NULL }; - int c; + int c, num; const unsigned char *b = buf; int retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) { + if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) { retval = tty_check_change(tty); if (retval) return retval; @@ -956,8 +1030,13 @@ static int write_chan(struct tty_struct * tty, struct file * file, retval = -EIO; break; } - if (O_OPOST(tty) && !(tty->flags & (1<<TTY_HW_COOK_OUT))) { + if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { while (nr > 0) { + num = opost_block(tty, b, nr); + b += num; + nr -= num; + if (nr == 0) + break; get_user(c, b); if (opost(c, tty) < 0) break; @@ -993,7 +1072,7 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; - if (tty->flags & (1 << TTY_OTHER_CLOSED)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c new file mode 100644 index 000000000..7a6b8cde0 --- /dev/null +++ b/drivers/char/pc110pad.c @@ -0,0 +1,690 @@ +/* + * Linux driver for the PC110 pad + * + * The pad provides triples of data. The first byte has + * 0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down + * The second byte is bits 0-6 X + * The third is bits 0-6 Y + * + * This is read internally and used to synthesize a stream of + * triples in the form expected from a PS/2 device. + * + * 0.0 1997-05-16 Alan Cox <alan@cymru.net> - Pad reader + * 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation + * 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture + * 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/busmouse.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/ptrace.h> +#include <linux/poll.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> + +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include "pc110pad.h" + + +static struct pc110pad_params default_params = { + PC110PAD_PS2, /* read mode */ + 50 MS, /* bounce interval */ + 200 MS, /* tap interval */ + 10, /* IRQ */ + 0x15E0, /* I/O port */ +}; + + +static struct pc110pad_params current_params; + + +/* driver/filesystem interface management */ +static struct wait_queue *queue; +static struct fasync_struct *asyncptr; +static int active=0; /* number of concurrent open()s */ + + +/* + * Utility to reset a timer to go off some time in the future. + */ + +static void set_timer_callback(struct timer_list *timer, int ticks) +{ + del_timer(timer); + timer->expires = jiffies+ticks; + add_timer(timer); +} + + +/* + * Take care of letting any waiting processes know that + * now would be a good time to do a read(). Called + * whenever a state transition occurs, real or synthetic. + */ + +static void wake_readers(void) +{ + wake_up_interruptible(&queue); + if(asyncptr) + kill_fasync(asyncptr, SIGIO); +} + + +/*****************************************************************************/ +/* + * Deal with the messy business of synthesizing button tap and drag + * events. + * + * Exports: + * notify_pad_up_down() + * Must be called whenever debounced pad up/down state changes. + * button_pending + * Flag is set whenever read_button() has new values + * to return. + * read_button() + * Obtains the current synthetic mouse button state. + */ + +/* + * These keep track of up/down transitions needed to generate the + * synthetic mouse button events. While recent_transition is set, + * up/down events cause transition_count to increment. tap_timer + * turns off the recent_transition flag and may cause some synthetic + * up/down mouse events to be created by incrementing synthesize_tap. + */ + +static int button_pending=0; +static int recent_transition=0; +static int transition_count=0; +static int synthesize_tap=0; +static void tap_timeout(unsigned long data); +static struct timer_list tap_timer = { NULL, NULL, 0, 0, tap_timeout }; + + +/* + * This callback goes off a short time after an up/down transition; + * before it goes off, transitions will be considered part of a + * single PS/2 event and counted in transition_count. Once the + * timeout occurs the recent_transition flag is cleared and + * any synthetic mouse up/down events are generated. + */ + +static void tap_timeout(unsigned long data) +{ + if(!recent_transition) + { + printk("pc110pad: tap_timeout but no recent transition!\n"); + } + if( transition_count==2 || transition_count==4 || transition_count==6 ) + { + synthesize_tap+=transition_count; + button_pending = 1; + wake_readers(); + } + recent_transition=0; +} + + +/* + * Called by the raw pad read routines when a (debounced) up/down + * transition is detected. + */ + +void notify_pad_up_down(void) +{ + if(recent_transition) + { + transition_count++; + } + else + { + transition_count=1; + recent_transition=1; + } + set_timer_callback(&tap_timer, current_params.tap_interval); + + /* changes to transition_count can cause reported button to change */ + button_pending = 1; + wake_readers(); +} + + +static void read_button(int *b) +{ + if(synthesize_tap) + { + *b=--synthesize_tap & 1; + } + else + { + *b=(!recent_transition && transition_count==3); /* drag */ + } + button_pending=(synthesize_tap>0); +} + + +/*****************************************************************************/ +/* + * Read pad absolute co-ordinates and debounced up/down state. + * + * Exports: + * pad_irq() + * Function to be called whenever the pad signals + * that it has new data available. + * read_raw_pad() + * Returns the most current pad state. + * xy_pending + * Flag is set whenever read_raw_pad() has new values + * to return. + * Imports: + * wake_readers() + * Called when movement occurs. + * notify_pad_up_down() + * Called when debounced up/down status changes. + */ + +/* + * These are up/down state and absolute co-ords read directly from pad + */ + +static int raw_data[3]; +static int raw_data_count=0; +static int raw_x=0, raw_y=0; /* most recent absolute co-ords read */ +static int raw_down=0; /* raw up/down state */ +static int debounced_down=0; /* up/down state after debounce processing */ +static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE; + /* set just after an up/down transition */ +static int xy_pending=0; /* set if new data have not yet been read */ + +/* + * Timer goes off a short while after an up/down transition and copies + * the value of raw_down to debounced_down. + */ + +static void bounce_timeout(unsigned long data); +static struct timer_list bounce_timer = { NULL, NULL, 0, 0, bounce_timeout }; + + +static void bounce_timeout(unsigned long data) +{ + /* + * No further up/down transitions happened within the + * bounce period, so treat this as a genuine transition. + */ + switch(bounce) + { + case NO_BOUNCE: + { + /* + * Strange; the timer callback should only go off if + * we were expecting to do bounce processing! + */ + printk("pc110pad, bounce_timeout: bounce flag not set!\n"); + break; + } + case JUST_GONE_UP: + { + /* + * The last up we spotted really was an up, so set + * debounced state the same as raw state. + */ + bounce=NO_BOUNCE; + if(debounced_down==raw_down) + { + printk("pc110pad, bounce_timeout: raw already debounced!\n"); + } + debounced_down=raw_down; + + notify_pad_up_down(); + break; + } + case JUST_GONE_DOWN: + { + /* + * We don't debounce down events, but we still time + * out soon after one occurs so we can avoid the (x,y) + * skittering that sometimes happens. + */ + bounce=NO_BOUNCE; + break; + } + } +} + + +/* + * Callback when pad's irq goes off; copies values in to raw_* globals; + * initiates debounce processing. + */ +static void pad_irq(int irq, void *ptr, struct pt_regs *regs) +{ + + /* Obtain byte from pad and prime for next byte */ + { + int value=inb_p(current_params.io); + int handshake=inb_p(current_params.io+2); + outb_p(handshake | 1, current_params.io+2); + outb_p(handshake &~1, current_params.io+2); + inb_p(0x64); + + raw_data[raw_data_count++]=value; + } + + if(raw_data_count==3) + { + int new_down=raw_data[0]&0x01; + int new_x=raw_data[1]; + int new_y=raw_data[2]; + if(raw_data[0]&0x10) new_x+=128; + if(raw_data[0]&0x80) new_x+=256; + if(raw_data[0]&0x08) new_y+=128; + + if( (raw_x!=new_x) || (raw_y!=new_y) ) + { + raw_x=new_x; + raw_y=new_y; + xy_pending=1; + } + + if(new_down != raw_down) + { + /* Down state has changed. raw_down always holds + * the most recently observed state. + */ + raw_down=new_down; + + /* Forget any earlier bounce processing */ + if(bounce) + { + del_timer(&bounce_timer); + bounce=NO_BOUNCE; + } + + if(new_down) + { + if(debounced_down) + { + /* pad gone down, but we were reporting + * it down anyway because we suspected + * (correctly) that the last up was just + * a bounce + */ + } + else + { + bounce=JUST_GONE_DOWN; + set_timer_callback(&bounce_timer, + current_params.bounce_interval); + /* start new stroke/tap */ + debounced_down=new_down; + notify_pad_up_down(); + } + } + else /* just gone up */ + { + if(recent_transition) + { + /* early bounces are probably part of + * a multi-tap gesture, so process + * immediately + */ + debounced_down=new_down; + notify_pad_up_down(); + } + else + { + /* don't trust it yet */ + bounce=JUST_GONE_UP; + set_timer_callback(&bounce_timer, + current_params.bounce_interval); + } + } + } + wake_readers(); + raw_data_count=0; + } +} + + +static void read_raw_pad(int *down, int *debounced, int *x, int *y) +{ + disable_irq(current_params.irq); + { + *down=raw_down; + *debounced=debounced_down; + *x=raw_x; + *y=raw_y; + xy_pending = 0; + } + enable_irq(current_params.irq); +} + +/*****************************************************************************/ +/* + * Filesystem interface + */ + +/* + * Read returns byte triples, so we need to keep track of + * how much of a triple has been read. This is shared across + * all processes which have this device open---not that anything + * will make much sense in that case. + */ +static int read_bytes[3]; +static int read_byte_count=0; + + + +static void sample_raw(int d[3]) +{ + d[0]=raw_data[0]; + d[1]=raw_data[1]; + d[2]=raw_data[2]; +} + + +static void sample_rare(int d[3]) +{ + int thisd, thisdd, thisx, thisy; + + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + + d[0]=(thisd?0x80:0) + | (thisx/256)<<4 + | (thisdd?0x08:0) + | (thisy/256) + ; + d[1]=thisx%256; + d[2]=thisy%256; +} + + +static void sample_debug(int d[3]) +{ + int thisd, thisdd, thisx, thisy; + int b; + cli(); + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce; + d[1]=(recent_transition?0x80:0)+transition_count; + read_button(&b); + d[2]=(synthesize_tap<<4) | (b?0x01:0); + sti(); +} + + +static void sample_ps2(int d[3]) +{ + static int lastx, lasty, lastd; + + int thisd, thisdd, thisx, thisy; + int dx, dy, b; + + /* + * Obtain the current mouse parameters and limit as appropriate for + * the return data format. Interrupts are only disabled while + * obtaining the parameters, NOT during the puts_fs_byte() calls, + * so paging in put_user() does not affect mouse tracking. + */ + read_raw_pad(&thisd, &thisdd, &thisx, &thisy); + read_button(&b); + + /* Now compare with previous readings. Note that we use the + * raw down flag rather than the debounced one. + */ + if( (thisd && !lastd) /* new stroke */ + || (bounce!=NO_BOUNCE) ) + { + dx=0; + dy=0; + } + else + { + dx = (thisx-lastx); + dy = -(thisy-lasty); + } + lastx=thisx; + lasty=thisy; + lastd=thisd; + +/* + d[0]= ((dy<0)?0x20:0) + | ((dx<0)?0x10:0) + | 0x08 + | (b? 0x01:0x00) + ; +*/ + d[0]= ((dy<0)?0x20:0) + | ((dx<0)?0x10:0) + | (b? 0x00:0x08) + ; + d[1]=dx; + d[2]=dy; +} + + + +static int fasync_pad(struct inode *inode, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(inode, filp, on, &asyncptr); + if (retval < 0) + return retval; + return 0; +} + + +/* + * close access to the pad + */ +static int close_pad(struct inode * inode, struct file * file) +{ + fasync_pad(inode, file, 0); + if (--active) + return; + outb(0x30, current_params.io+2); /* switch off digitiser */ + MOD_DEC_USE_COUNT; + return 0; +} + + +/* + * open access to the pad + */ +static int open_pad(struct inode * inode, struct file * file) +{ + if (active++) + return 0; + MOD_INC_USE_COUNT; + + cli(); + outb(0x30, current_params.io+2); /* switch off digitiser */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + pad_irq(0,0,0); /* read to flush any pending bytes */ + outb(0x38, current_params.io+2); /* switch on digitiser */ + current_params = default_params; + raw_data_count=0; /* re-sync input byte counter */ + read_byte_count=0; /* re-sync output byte counter */ + button_pending=0; + recent_transition=0; + transition_count=0; + synthesize_tap=0; + del_timer(&bounce_timer); + del_timer(&tap_timer); + sti(); + + return 0; +} + + +/* + * writes are disallowed + */ +static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count) +{ + return -EINVAL; +} + + +void new_sample(int d[3]) +{ + switch(current_params.mode) + { + case PC110PAD_RAW: sample_raw(d); break; + case PC110PAD_RARE: sample_rare(d); break; + case PC110PAD_DEBUG: sample_debug(d); break; + case PC110PAD_PS2: sample_ps2(d); break; + } +} + + +/* + * Read pad data. Currently never blocks. + */ +static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count) +{ + int r; + + for(r=0; r<count; r++) + { + if(!read_byte_count) + new_sample(read_bytes); + if(put_user(read_bytes[read_byte_count], buffer+r)) + return -EFAULT; + read_byte_count = (read_byte_count+1)%3; + } + return r; +} + + +/* + * select for pad input + */ + +static unsigned int pad_poll(struct file *file, poll_table * wait) +{ + poll_wait(&queue, wait); + if(button_pending || xy_pending) + return POLLIN | POLLRDNORM; + return 0; +} + + +static int pad_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct pc110pad_params new; + + if (!inode) + return -EINVAL; + + switch (cmd) { + case PC110PADIOCGETP: + new = current_params; + if(copy_to_user((void *)arg, &new, sizeof(new))) + return -EFAULT; + return 0; + + case PC110PADIOCSETP: + if(copy_from_user(&new, (void *)arg, sizeof(new))) + return -EFAULT; + + if( (new.mode<PC110PAD_RAW) + || (new.mode>PC110PAD_PS2) + || (new.bounce_interval<0) + || (new.tap_interval<0) + ) + return -EINVAL; + + current_params.mode = new.mode; + current_params.bounce_interval = new.bounce_interval; + current_params.tap_interval = new.tap_interval; + return 0; + } + return -ENOIOCTLCMD; +} + + +static struct file_operations pad_fops = { + NULL, /* pad_seek */ + read_pad, + write_pad, + NULL, /* pad_readdir */ + pad_poll, + pad_ioctl, + NULL, /* pad_mmap */ + open_pad, + close_pad, + NULL, /* fsync */ + fasync_pad, + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + + +static struct miscdevice pc110_pad = { + PC110PAD_MINOR, "pc110 pad", &pad_fops +}; + + +static int pc110pad_init(void) +{ + current_params = default_params; + + if(request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0)) + { + printk("pc110pad: Unable to get IRQ.\n"); + return -EBUSY; + } + if(check_region(current_params.io, 4)) + { + printk("pc110pad: I/O area in use.\n"); + free_irq(current_params.irq,0); + return -EBUSY; + } + request_region(current_params.io, 4, "pc110pad"); + printk("PC110 digitizer pad at 0x%X, irq %d.\n", + current_params.io,current_params.irq); + misc_register(&pc110_pad); + outb(0x30, current_params.io+2); /* switch off digitiser */ + + return 0; +} + +#ifdef MODULE + +static void pc110pad_unload(void) +{ + outb(0x30, current_params.io+2); /* switch off digitiser */ + if(current_params.irq) + free_irq(current_params.irq, 0); + current_params.irq=0; + release_region(current_params.io, 4); + misc_deregister(&pc110_pad); +} + + + +int init_module(void) +{ + return pc110pad_init(); +} + +void cleanup_module(void) +{ + pc110pad_unload(); +} +#endif diff --git a/drivers/char/pc110pad.h b/drivers/char/pc110pad.h new file mode 100644 index 000000000..56d8d82e0 --- /dev/null +++ b/drivers/char/pc110pad.h @@ -0,0 +1,31 @@ +#ifndef _PC110PAD_H +#define _PC110PAD_H + +#include <linux/ioctl.h> + +enum pc110pad_mode { + PC110PAD_RAW, /* bytes as they come out of the hardware */ + PC110PAD_RARE, /* debounced up/down and absolute x,y */ + PC110PAD_DEBUG, /* up/down, debounced, transitions, button */ + PC110PAD_PS2, /* ps2 relative (default) */ +}; + + +struct pc110pad_params { + enum pc110pad_mode mode; + int bounce_interval; + int tap_interval; + int irq; + int io; +}; + +#define MS *HZ/1000 + +/* Appears as device major=10 (MISC), minor=PC110_PAD */ + +#define PC110PAD_IOCTL_TYPE 0x9a + +#define PC110PADIOCGETP _IOR(PC110PAD_IOCTL_TYPE, 0, struct pc110pad_params) +#define PC110PADIOCSETP _IOR(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params) + +#endif /* _PC110PAD_H */ diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c index 006c60ddf..eea857b8a 100644 --- a/drivers/char/psaux.c +++ b/drivers/char/psaux.c @@ -53,6 +53,8 @@ #include <asm/uaccess.h> #include <asm/system.h> +#include "pc_keyb.h" + #ifdef CONFIG_SGI #include <asm/segment.h> #include <asm/sgihpc.h> @@ -80,17 +82,6 @@ #define AUX_DISABLE 0xa7 /* disable aux */ #define AUX_ENABLE 0xa8 /* enable aux */ -/* aux device commands */ -#define AUX_SET_RES 0xe8 /* set resolution */ -#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */ -#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */ -#define AUX_GET_SCALE 0xe9 /* get scaling factor */ -#define AUX_SET_STREAM 0xea /* set stream mode */ -#define AUX_SET_SAMPLE 0xf3 /* set sample rate */ -#define AUX_ENABLE_DEV 0xf4 /* enable aux device */ -#define AUX_DISABLE_DEV 0xf5 /* disable aux device */ -#define AUX_RESET 0xff /* reset aux device */ - #define MAX_RETRIES 60 /* some aux operations take long time*/ #if defined(__alpha__) && !defined(CONFIG_PCI) # define AUX_IRQ 9 /* Jensen is odd indeed */ @@ -212,7 +203,16 @@ static void aux_write_dev(int val) /* * Write to device & handle returned ack */ -#if defined INITIALIZE_DEVICE + +#ifdef INITIALIZE_DEVICE +__initfunc(static void aux_write_dev_nosleep(int val)) +{ + poll_aux_status_nosleep(); + ps2_outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG); + poll_aux_status_nosleep(); + ps2_outb_p(val, KBD_DATA_REG); +} + static int aux_write_ack(int val) { int retries = 0; @@ -663,11 +663,11 @@ __initfunc(int psaux_init(void)) aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ poll_aux_status_nosleep(); #endif /* INITIALIZE_DEVICE */ - ps2_outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */ + ps2_outb_p(KBD_CCMD_MOUSE_DISABLE, AUX_COMMAND); /* Disable Aux device */ + poll_aux_status_nosleep(); + ps2_outb_p(KBD_CCMD_WRITE_MODE, AUX_COMMAND); poll_aux_status_nosleep(); - ps2_outb_p(AUX_CMD_WRITE,AUX_COMMAND); - poll_aux_status_nosleep(); /* Disable interrupts */ - ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */ + ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); } return 0; } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 8f1015397..929bb2f85 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -26,20 +26,6 @@ struct pty_struct { #define PTY_MAGIC 0x5001 -#define PTY_BUF_SIZE PAGE_SIZE/2 - -/* - * tmp_buf is used as a temporary buffer by pty_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a pty write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the PTY's, since it significantly saves memory if - * large numbers of PTY's are open. - */ -static unsigned char *tmp_buf; -static struct semaphore tmp_buf_sem = MUTEX; - static struct tty_driver pty_driver, pty_slave_driver; static struct tty_driver old_pty_driver, old_pty_slave_driver; static int pty_refcount; @@ -104,37 +90,51 @@ static void pty_unthrottle(struct tty_struct * tty) set_bit(TTY_THROTTLED, &tty->flags); } +/* + * WSH 05/24/97: modified to + * (1) use space in tty->flip instead of a shared temp buffer + * The flip buffers aren't being used for a pty, so there's lots + * of space available. The buffer is protected by a per-pty + * semaphore that should almost never come under contention. + * (2) avoid redundant copying for cases where count >> receive_room + * N.B. Calls from user space may now return an error code instead of + * a count. + */ static int pty_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct tty_struct *to = tty->link; - int c=0, n, r; + int c=0, n; char *temp_buffer; if (!to || tty->stopped) return 0; - + if (from_user) { - down(&tmp_buf_sem); - temp_buffer = tmp_buf + - ((tty->driver.subtype-1) * PTY_BUF_SIZE); + down(&tty->flip.pty_sem); + temp_buffer = &tty->flip.char_buf[0]; while (count > 0) { - n = MIN(count, PTY_BUF_SIZE); + /* check space so we don't copy needlessly */ + n = MIN(count, to->ldisc.receive_room(to)); + if (!n) break; + + n = MIN(n, PTY_BUF_SIZE); n -= copy_from_user(temp_buffer, buf, n); if (!n) { if (!c) c = -EFAULT; break; } - r = to->ldisc.receive_room(to); - if (r <= 0) - break; - n = MIN(n, r); - to->ldisc.receive_buf(to, temp_buffer, 0, n); - buf += n; c+= n; + + /* check again in case the buffer filled up */ + n = MIN(n, to->ldisc.receive_room(to)); + if (!n) break; + buf += n; + c += n; count -= n; + to->ldisc.receive_buf(to, temp_buffer, 0, n); } - up(&tmp_buf_sem); + up(&tty->flip.pty_sem); } else { c = MIN(count, to->ldisc.receive_room(to)); to->ldisc.receive_buf(to, buf, 0, c); @@ -153,14 +153,42 @@ static int pty_write_room(struct tty_struct *tty) return to->ldisc.receive_room(to); } +/* + * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior + * The chars_in_buffer() value is used by the ldisc select() function + * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). + * The pty driver chars_in_buffer() Master/Slave must behave differently: + * + * The Master side needs to allow typed-ahead commands to accumulate + * while being canonicalized, so we report "our buffer" as empty until + * some threshold is reached, and then report the count. (Any count > + * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock + * the count returned must be 0 if no canonical data is available to be + * read. (The N_TTY ldisc.chars_in_buffer now knows this.) + * + * The Slave side passes all characters in raw mode to the Master side's + * buffer where they can be read immediately, so in this case we can + * return the true count in the buffer. + */ static int pty_chars_in_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; + int count; if (!to || !to->ldisc.chars_in_buffer) return 0; - return to->ldisc.chars_in_buffer(to); + /* The ldisc must report 0 if no characters available to be read */ + count = to->ldisc.chars_in_buffer(to); + + if (tty->driver.subtype == PTY_TYPE_SLAVE) return count; + + /* Master side driver ... if the other side's read buffer is less than + * half full, return 0 to allow writers to proceed; otherwise return + * the count. This leaves a comfortable margin to avoid overflow, + * and still allows half a buffer's worth of typed-ahead commands. + */ + return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); } static void pty_flush_buffer(struct tty_struct *tty) @@ -194,17 +222,6 @@ static int pty_open(struct tty_struct *tty, struct file * filp) pty = pty_state + line; tty->driver_data = pty; - if (!tmp_buf) { - unsigned long page = __get_free_page(GFP_KERNEL); - if (!tmp_buf) { - retval = -ENOMEM; - if (!page) - goto out; - tmp_buf = (unsigned char *) page; - memset((void *) page, 0, PAGE_SIZE); - } else - free_page(page); - } retval = -EIO; if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) goto out; @@ -288,8 +305,6 @@ __initfunc(int pty_init(void)) old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS; old_pty_slave_driver.other = &old_pty_driver; - tmp_buf = 0; - if (tty_register_driver(&pty_driver)) panic("Couldn't register pty driver"); if (tty_register_driver(&pty_slave_driver)) diff --git a/drivers/char/random.c b/drivers/char/random.c index 5f7619391..527ac8609 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,7 +1,7 @@ /* * random.c -- A strong random number generator * - * Version 1.02, last modified 15-Apr-97 + * Version 1.03, last modified 26-Apr-97 * * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved. * @@ -227,6 +227,7 @@ */ #include <linux/utsname.h> +#include <linux/config.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/string.h> @@ -1124,8 +1125,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long * update the access time. */ if (inode && count != 0) { - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + UPDATE_ATIME(inode); } return (count ? count : retval); @@ -1181,7 +1181,7 @@ random_write(struct inode * inode, struct file * file, } if ((ret > 0) && inode) { inode->i_mtime = CURRENT_TIME; - inode->i_dirt = 1; + mark_inode_dirty(inode); } return ret; } @@ -1335,11 +1335,15 @@ struct file_operations urandom_fops = { * starting point for each pair of TCP endpoints. This defeats * attacks which rely on guessing the initial TCP sequence number. * This algorithm was suggested by Steve Bellovin. + * + * Using a very strong hash was taking an appreciable amount of the total + * TCP connection establishment time, so this is a weaker hash, + * compensated for by changing the secret periodically. */ /* F, G and H are basic MD4 functions: selection, majority, parity */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) @@ -1357,9 +1361,9 @@ struct file_operations urandom_fops = { (a) = ROTL ((s), (a));} /* - * Basic cut-down MD4 transform + * Basic cut-down MD4 transform. Returns only 32 bits of result. */ -static void halfMD4Transform (__u32 buf[4], __u32 in[8]) +static __u32 halfMD4Transform (__u32 const buf[4], __u32 const in[8]) { __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; @@ -1376,77 +1380,141 @@ static void halfMD4Transform (__u32 buf[4], __u32 in[8]) /* Round 2 */ GG (a, b, c, d, in[ 0], 3); GG (d, a, b, c, in[ 4], 5); - GG (a, b, c, d, in[ 1], 9); - GG (d, a, b, c, in[ 5], 13); + GG (c, d, a, b, in[ 1], 9); + GG (b, c, d, a, in[ 5], 13); GG (a, b, c, d, in[ 2], 3); GG (d, a, b, c, in[ 6], 5); - GG (a, b, c, d, in[ 3], 9); - GG (d, a, b, c, in[ 7], 13); + GG (c, d, a, b, in[ 3], 9); + GG (b, c, d, a, in[ 7], 13); /* Round 3 */ HH (a, b, c, d, in[ 0], 3); - HH (c, d, a, b, in[ 4], 9); - HH (a, b, c, d, in[ 2], 11); - HH (c, d, a, b, in[ 6], 15); + HH (d, a, b, c, in[ 4], 9); + HH (c, d, a, b, in[ 2], 11); + HH (b, c, d, a, in[ 6], 15); HH (a, b, c, d, in[ 1], 3); - HH (c, d, a, b, in[ 5], 9); - HH (a, b, c, d, in[ 3], 11); - HH (c, d, a, b, in[ 7], 15); + HH (d, a, b, c, in[ 5], 9); + HH (c, d, a, b, in[ 3], 11); + HH (b, c, d, a, in[ 7], 15); - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; + return buf[1] + b; /* "most hashed" word */ + /* Alternative: return sum of all words? */ } +/* This should not be decreased so low that ISNs wrap too fast. */ #define REKEY_INTERVAL 300 +#define HASH_BITS 24 __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport) { static __u32 rekey_time = 0; + static __u32 count = 0; static __u32 secret[12]; - static char count = 0; struct timeval tv; - __u32 tmp[12]; __u32 seq; /* - * Pick a random secret every REKEY_INTERVAL seconds + * Pick a random secret every REKEY_INTERVAL seconds. */ - do_gettimeofday(&tv); + do_gettimeofday(&tv); /* We need the usecs below... */ + if (!rekey_time || (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { - get_random_bytes(&secret, sizeof(secret)); rekey_time = tv.tv_sec; - count++; + /* First three words are overwritten below. */ + get_random_bytes(&secret+3, sizeof(secret)-12); + count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS; } - memcpy(tmp, secret, sizeof(tmp)); /* - * Pick a unique starting offset for each - * TCP connection endpoints (saddr, daddr, sport, dport) + * Pick a unique starting offset for each TCP connection endpoints + * (saddr, daddr, sport, dport). + * Note that the words are placed into the first words to be + * mixed in with the halfMD4. This is because the starting + * vector is also a random secret (at secret+8), and further + * hashing fixed data into it isn't going to improve anything, + * so we should get started with the variable data. */ - tmp[8]=saddr; - tmp[9]=daddr; - tmp[10]=(sport << 16) + dport; - halfMD4Transform(tmp, tmp+4); - + secret[0]=saddr; + secret[1]=daddr; + secret[2]=(sport << 16) + dport; + + seq = (halfMD4Transform(secret+8, secret) & + ((1<<HASH_BITS)-1)) + (count << HASH_BITS); + /* * As close as possible to RFC 793, which * suggests using a 250kHz clock. - * Further reading shows this assumes 2MB/s networks. - * For 10MB/s ethernet, a 1MHz clock is appropriate. + * Further reading shows this assumes 2Mb/s networks. + * For 10Mb/s ethernet, a 1MHz clock is appropriate. * That's funny, Linux has one built in! Use it! + * (Networks are faster now - should this be increased?) */ - seq = (tmp[1]&0xFFFFFF) + (tv.tv_usec+tv.tv_sec*1000000) + - (count << 24); + seq += tv.tv_usec + tv.tv_sec*1000000; #if 0 printk("init_seq(%lx, %lx, %d, %d) = %d\n", saddr, daddr, sport, dport, seq); #endif - return (seq); + return seq; +} + +#ifdef CONFIG_SYN_COOKIES +/* + * Secure SYN cookie computation. This is the algorithm worked out by + * Dan Bernstein and Eric Schenk. + * + * For linux I implement the 1 minute counter by looking at the jiffies clock. + * The count is passed in as a parameter; + * + */ +__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr, + __u16 sport, __u16 dport, __u32 sseq, __u32 count) +{ + static int is_init = 0; + static __u32 secret[2][16]; + __u32 tmp[16]; + __u32 seq; + + /* + * Pick two random secret the first time we open a TCP connection. + */ + if (is_init == 0) { + get_random_bytes(&secret[0], sizeof(secret[0])); + get_random_bytes(&secret[1], sizeof(secret[1])); + is_init = 1; + } + + /* + * Compute the secure sequence number. + * The output should be: + * MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number + * + (count * 2^24) + * + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24). + * Where count increases every minute by 1. + */ + + memcpy(tmp, secret[0], sizeof(tmp)); + tmp[8]=saddr; + tmp[9]=daddr; + tmp[10]=(sport << 16) + dport; + HASH_TRANSFORM(tmp, tmp); + seq = tmp[1]; + + memcpy(tmp, secret[1], sizeof(tmp)); + tmp[8]=saddr; + tmp[9]=daddr; + tmp[10]=(sport << 16) + dport; + tmp[11]=count; /* minute counter */ + HASH_TRANSFORM(tmp, tmp); + + seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff); + + /* Zap lower 3 bits to leave room for the MSS representation */ + return (seq & 0xfffff8); } +#endif + #ifdef RANDOM_BENCHMARK /* diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index a8614999d..5f03f8887 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -152,7 +152,7 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, unsigned long count) { struct wait_queue wait = { current, NULL }; - int retval; + int retval = 0; if (count < sizeof(unsigned long)) return -EINVAL; @@ -180,7 +180,9 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf, data = rtc_irq_data; rtc_irq_data = 0; restore_flags(flags); - retval = put_user(data, (unsigned long *)buf)) ?: sizeof(unsigned long); + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); } current->state = TASK_RUNNING; @@ -262,7 +264,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, * "don't care" or "match all". Only the tm_hour, * tm_min and tm_sec are used. */ - int retval; unsigned char hrs, min, sec; struct rtc_time alm_tm; @@ -305,7 +306,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } case RTC_SET_TIME: /* Set the RTC */ { - int retval; struct rtc_time rtc_tm; unsigned char mon, day, hrs, min, sec, leap_yr; unsigned char save_control, save_freq_select; @@ -418,7 +418,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, default: return -EINVAL; } - return copy_to_user(arg, &wtime, sizeof wtime) ? -EFAULT : 0; + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; } /* diff --git a/drivers/char/serial.c b/drivers/char/serial.c index e2a93a0c3..ca561c4e9 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -1421,6 +1421,9 @@ static void shutdown(struct async_struct * info) info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; #endif + /* disable break condition */ + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); @@ -2089,6 +2092,30 @@ static void send_break( struct async_struct * info, int duration) } /* + * This routine sets the break condition on the serial port. + */ +static void begin_break(struct async_struct * info) +{ + if (!info->port) + return; + cli(); + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); + sti(); +} + +/* + * This routine clears the break condition on the serial port. + */ +static void end_break(struct async_struct * info) +{ + if (!info->port) + return; + cli(); + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + sti(); +} + +/* * This routine returns a bitfield of "wild interrupts". Basically, * any unclaimed interrupts which is flapping around. */ @@ -2292,6 +2319,19 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, if (current->signal & ~current->blocked) return -EINTR; return 0; + case TIOCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + begin_break(info); + return 0; + case TIOCCBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + end_break(info); + return 0; case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); case TIOCSSOFTCAR: diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index e2044c086..1e0e1a2a0 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -1,6 +1,6 @@ /* -*- linux-c -*- * - * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $ + * $Id: sysrq.c,v 1.1 1997/06/17 13:24:07 ralf Exp $ * * Linux Magic System Request Key Hacks * @@ -112,7 +112,7 @@ void handle_sysrq(int key, struct pt_regs *pt_regs, show_mem(); break; case 2 ... 11: /* 0-9 -- set console logging level */ - key -= 2; + key--; if (key == 10) key = 0; orig_log_level = key; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index c08e44a27..7a31e162d 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -45,9 +45,12 @@ * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 * - * Move console and virtual terminal code to more apropriate files, + * Move console and virtual terminal code to more appropriate files, * implement CONFIG_VT and generalize console device interface. * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 + * + * Rewrote init_dev and release_dev to eliminate races. + * -- Bill Hawes <whawes@star.net>, June 97 */ #include <linux/config.h> @@ -90,8 +93,8 @@ #undef TTY_DEBUG_HANGUP -#define TTY_PARANOIA_CHECK -#define CHECK_TTY_COUNT +#define TTY_PARANOIA_CHECK 1 +#define CHECK_TTY_COUNT 1 struct termios tty_std_termios; /* for the benefit of tty drivers */ struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */ @@ -370,13 +373,15 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) for (filp = inuse_filps; filp; filp = filp->f_next) { if (filp->private_data != tty) continue; - if (!filp->f_inode) + if (!filp->f_dentry) + continue; + if (!filp->f_dentry->d_inode) continue; - if (filp->f_inode->i_rdev == CONSOLE_DEV) + if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV) continue; if (filp->f_op != &tty_fops) continue; - tty_fasync(filp->f_inode, filp, 0); + tty_fasync(filp->f_dentry->d_inode, filp, 0); filp->f_op = fops; } @@ -384,7 +389,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); @@ -533,7 +538,7 @@ void start_tty(struct tty_struct *tty) } if (tty->driver.start) (tty->driver.start)(tty); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); @@ -548,7 +553,7 @@ static long tty_read(struct inode * inode, struct file * file, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_read")) return -EIO; - if (!tty || (tty->flags & (1 << TTY_IO_ERROR))) + if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; /* This check not only needs to be done before reading, but also @@ -630,7 +635,7 @@ static long tty_write(struct inode * inode, struct file * file, tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_write")) return -EIO; - if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR))) + if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; #if 0 if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) && @@ -651,18 +656,31 @@ static long tty_write(struct inode * inode, struct file * file, (unsigned int)count); } +/* Semaphore to protect creating and releasing a tty */ +static struct semaphore tty_sem = MUTEX; +static void down_tty_sem(int index) +{ + down(&tty_sem); +} +static void up_tty_sem(int index) +{ + up(&tty_sem); +} +static void release_mem(struct tty_struct *tty, int idx); + /* - * This is so ripe with races that you should *really* not touch this - * unless you know exactly what you are doing. All the changes have to be - * made atomically, or there may be incorrect pointers all over the place. + * WSH 06/09/97: Rewritten to remove races and properly clean up after a + * failed open. The new code protects the open with a semaphore, so it's + * really quite straightforward. The semaphore locking can probably be + * relaxed for the (most common) case of reopening a tty. */ static int init_dev(kdev_t device, struct tty_struct **ret_tty) { - struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc; + struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; struct tty_driver *driver; - int retval; + int retval=0; int idx; driver = get_tty_driver(device); @@ -670,189 +688,251 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty) return -ENODEV; idx = MINOR(device) - driver->minor_start; - tty = o_tty = NULL; + tty = driver->table[idx]; + + /* + * Check whether we need to acquire the tty semaphore to avoid + * race conditions. For now, play it safe. + */ + down_tty_sem(idx); + + /* check whether we're reopening an existing tty */ + if(tty) goto fast_track; + + /* + * First time open is complex, especially for PTY devices. + * This code guarantees that either everything succeeds and the + * TTY is ready for operation, or else the table slots are vacated + * and the allocated memory released. (Except that the termios + * and locked termios may be retained.) + */ + + o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; - o_tty_loc = NULL; - o_tp_loc = o_ltp_loc = NULL; - tty_loc = &driver->table[idx]; - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; + tty = (struct tty_struct*) get_free_page(GFP_KERNEL); + if(!tty) + goto fail_no_mem; + initialize_tty_struct(tty); + tty->device = device; + tty->driver = *driver; -repeat: - retval = -EIO; - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER && - *tty_loc && (*tty_loc)->count) - goto end_init; - retval = -ENOMEM; - if (!*tty_loc && !tty) { - if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL))) - goto end_init; - initialize_tty_struct(tty); - tty->device = device; - tty->driver = *driver; - goto repeat; - } - if (!*tp_loc && !tp) { + tp_loc = &driver->termios[idx]; + if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) - goto end_init; + goto free_mem_out; *tp = driver->init_termios; - goto repeat; } - if (!*ltp_loc && !ltp) { + + ltp_loc = &driver->termios_locked[idx]; + if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) - goto end_init; + goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); - goto repeat; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty_loc = &driver->other->table[idx]; - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; - if (!*o_tty_loc && !o_tty) { - kdev_t o_device; - - o_tty = (struct tty_struct *) - get_free_page(GFP_KERNEL); - if (!o_tty) - goto end_init; - o_device = MKDEV(driver->other->major, - driver->other->minor_start + idx); - initialize_tty_struct(o_tty); - o_tty->device = o_device; - o_tty->driver = *driver->other; - goto repeat; - } - if (!*o_tp_loc && !o_tp) { + if (driver->type == TTY_DRIVER_TYPE_PTY) { + o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); + if (!o_tty) + goto free_mem_out; + initialize_tty_struct(o_tty); + o_tty->device = (kdev_t) MKDEV(driver->other->major, + driver->other->minor_start + idx); + o_tty->driver = *driver->other; + + o_tp_loc = &driver->other->termios[idx]; + if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) - goto end_init; + goto free_mem_out; *o_tp = driver->other->init_termios; - goto repeat; } - if (!*o_ltp_loc && !o_ltp) { + + o_ltp_loc = &driver->other->termios_locked[idx]; + if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) - goto end_init; + goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); - goto repeat; } - + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->table[idx] = o_tty; + if (!*o_tp_loc) + *o_tp_loc = o_tp; + if (!*o_ltp_loc) + *o_ltp_loc = o_ltp; + o_tty->termios = *o_tp_loc; + o_tty->termios_locked = *o_ltp_loc; + (*driver->other->refcount)++; + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; } - /* Now we have allocated all the structures: update all the pointers.. */ - if (!*tp_loc) { + + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_mem to clean up, so + * there's no need to null out the local pointers. + */ + driver->table[idx] = tty; + if (!*tp_loc) *tp_loc = tp; - tp = NULL; - } - if (!*ltp_loc) { + if (!*ltp_loc) *ltp_loc = ltp; - ltp = NULL; + tty->termios = *tp_loc; + tty->termios_locked = *ltp_loc; + (*driver->refcount)++; + tty->count++; + + /* + * Structures all installed ... call the ldisc open routines. + * If we fail here just call release_mem to clean up. No need + * to decrement the use counts, as release_mem doesn't care. + */ + if (tty->ldisc.open) { + retval = (tty->ldisc.open)(tty); + if (retval) + goto release_mem_out; } - if (!*tty_loc) { - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - *tty_loc = tty; - (*driver->refcount)++; - (*tty_loc)->count++; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); - if (retval < 0) { - (*tty_loc)->count--; - tty = NULL; - goto end_init; - } - } - tty = NULL; - } else { - if ((*tty_loc)->flags & (1 << TTY_CLOSING)) { - printk("Attempt to open closing tty %s.\n", - tty_name(*tty_loc)); - printk("Ack!!!! This should never happen!!\n"); - return -EINVAL; + if (o_tty && o_tty->ldisc.open) { + retval = (o_tty->ldisc.open)(o_tty); + if (retval) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + goto release_mem_out; } - (*tty_loc)->count++; } - if (driver->type == TTY_DRIVER_TYPE_PTY) { - if (!*o_tp_loc) { - *o_tp_loc = o_tp; - o_tp = NULL; - } - if (!*o_ltp_loc) { - *o_ltp_loc = o_ltp; - o_ltp = NULL; - } - if (!*o_tty_loc) { - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - *o_tty_loc = o_tty; - (*driver->other->refcount)++; - if (o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); - if (retval < 0) { - (*tty_loc)->count--; - o_tty = NULL; - goto end_init; - } - } - o_tty = NULL; + goto success; + + /* + * This fast open can be used if the tty is already open. + * No memory is allocated, and the only failures are from + * attempting to open a closing tty or attempting multiple + * opens on a pty master. + */ +fast_track: + if (test_bit(TTY_CLOSING, &tty->flags)) { + retval = -EIO; + goto end_init; + } + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) { + retval = -EIO; + goto end_init; } - (*tty_loc)->link = *o_tty_loc; - (*o_tty_loc)->link = *tty_loc; - if (driver->subtype == PTY_TYPE_MASTER) - (*o_tty_loc)->count++; + tty->link->count++; } - (*tty_loc)->driver = *driver; - *ret_tty = *tty_loc; - retval = 0; + tty->count++; + tty->driver = *driver; /* N.B. why do this every time?? */ + +success: + *ret_tty = tty; + + /* All paths come through here to release the semaphore */ end_init: - if (tty) - free_page((unsigned long) tty); - if (o_tty) - free_page((unsigned long) o_tty); - if (tp) - kfree_s(tp, sizeof(struct termios)); + up_tty_sem(idx); + return retval; + + /* Release locally allocated memory ... nothing placed in slots */ +free_mem_out: if (o_tp) kfree_s(o_tp, sizeof(struct termios)); + if (o_tty) + free_page((unsigned long) o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); - if (o_ltp) - kfree_s(o_ltp, sizeof(struct termios)); - return retval; + if (tp) + kfree_s(tp, sizeof(struct termios)); + free_page((unsigned long) tty); + +fail_no_mem: + retval = -ENOMEM; + goto end_init; + + /* call the tty release_mem routine to clean out this slot */ +release_mem_out: + printk("init_dev: ldisc open failed, clearing slot %d\n", idx); + release_mem(tty, idx); + goto end_init; +} + +/* + * Releases memory associated with a tty structure, and clears out the + * driver table slots. + */ +static void release_mem(struct tty_struct *tty, int idx) +{ + struct tty_struct *o_tty; + struct termios *tp; + + if ((o_tty = tty->link) != NULL) { + o_tty->driver.table[idx] = NULL; + if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = o_tty->driver.termios[idx]; + o_tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + o_tty->magic = 0; + (*o_tty->driver.refcount)--; + free_page((unsigned long) o_tty); + } + + tty->driver.table[idx] = NULL; + if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { + tp = tty->driver.termios[idx]; + tty->driver.termios[idx] = NULL; + kfree_s(tp, sizeof(struct termios)); + } + tty->magic = 0; + (*tty->driver.refcount)--; + free_page((unsigned long) tty); } /* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. + * + * WSH 09/09/97: rewritten to avoid some nasty race conditions that could + * lead to double frees or releasing memory still in use. */ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; - struct termios *tp, *o_tp, *ltp, *o_ltp; - struct task_struct *p; + int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev")) + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev")) return; check_tty_count(tty, "release_dev"); - tty_fasync(filp->f_inode, filp, 0); - - tp = tty->termios; - ltp = tty->termios_locked; + tty_fasync(filp->f_dentry->d_inode, filp, 0); idx = MINOR(tty->device) - tty->driver.minor_start; + pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && + tty->driver.subtype == PTY_TYPE_MASTER); + o_tty = tty->link; + #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver.num) { printk("release_dev: bad idx when trying to free (%s)\n", @@ -864,15 +944,15 @@ static void release_dev(struct file * filp) idx, kdevname(tty->device)); return; } - if (tp != tty->driver.termios[idx]) { - printk("release_dev: driver.termios[%d] not termios for (" - "%s)\n", + if (tty->termios != tty->driver.termios[idx]) { + printk("release_dev: driver.termios[%d] not termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (ltp != tty->driver.termios_locked[idx]) { - printk("release_dev: driver.termios_locked[%d] not termios_locked for (" - "%s)\n", + if (tty->termios_locked != tty->driver.termios_locked[idx]) { + printk("release_dev: driver.termios_locked[%d] not " + "termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } @@ -883,10 +963,6 @@ static void release_dev(struct file * filp) tty->count); #endif - o_tty = tty->link; - o_tp = (o_tty) ? o_tty->termios : NULL; - o_ltp = (o_tty) ? o_tty->termios_locked : NULL; - #ifdef TTY_PARANOIA_CHECK if (tty->driver.other) { if (o_tty != tty->driver.other->table[idx]) { @@ -895,34 +971,90 @@ static void release_dev(struct file * filp) idx, kdevname(tty->device)); return; } - if (o_tp != tty->driver.other->termios[idx]) { - printk("release_dev: other->termios[%d] not o_termios for (" - "%s)\n", + if (o_tty->termios != tty->driver.other->termios[idx]) { + printk("release_dev: other->termios[%d] not o_termios " + "for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_ltp != tty->driver.other->termios_locked[idx]) { - printk("release_dev: other->termios_locked[%d] not o_termios_locked for (" - "%s)\n", + if (o_tty->termios_locked != + tty->driver.other->termios_locked[idx]) { + printk("release_dev: other->termios_locked[%d] not " + "o_termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } - if (o_tty->link != tty) { printk("release_dev: bad pty pointers\n"); return; } } #endif - + /* + * Sanity check: if tty->count is going to zero, there shouldn't be + * any waiters on tty->read_wait or tty->write_wait. We test the + * wait queues and kick everyone out _before_ actually starting to + * close. This ensures that we won't block while releasing the tty + * structure. + * + * The test for the o_tty closing is necessary, since the master and + * slave sides may close in any order. If the slave side closes out + * first, its count will be one, since the master side holds an open. + * Thus this test wouldn't be triggered at the time the slave closes, + * so we do it now. + * + * Note that it's possible for the tty to be opened again while we're + * flushing out waiters. By recalculating the closing flags before + * each iteration we avoid any problems. + */ + while (1) { + tty_closing = tty->count <= 1; + o_tty_closing = o_tty && + (o_tty->count <= (pty_master ? 1 : 0)); + do_sleep = 0; + + if (tty_closing) { + if (waitqueue_active(&tty->read_wait)) { + wake_up(&tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&tty->write_wait)) { + wake_up(&tty->write_wait); + do_sleep++; + } + } + if (o_tty_closing) { + if (waitqueue_active(&o_tty->read_wait)) { + wake_up(&o_tty->read_wait); + do_sleep++; + } + if (waitqueue_active(&o_tty->write_wait)) { + wake_up(&o_tty->write_wait); + do_sleep++; + } + } + if (!do_sleep) + break; + + printk("release_dev: %s: read/write wait queue active!\n", + tty_name(tty)); + schedule(); + } + + /* + * The closing flags are now consistent with the open counts on + * both sides, and we've completed the last operation that could + * block, so it's safe to proceed with closing. + */ + if (tty->driver.close) tty->driver.close(tty, filp); - if (tty->driver.type == TTY_DRIVER_TYPE_PTY && - tty->driver.subtype == PTY_TYPE_MASTER) { - if (--tty->link->count < 0) { + + if (pty_master) { + if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", - tty->count, tty_name(tty)); - tty->link->count = 0; + o_tty->count, tty_name(o_tty)); + o_tty->count = 0; } } if (--tty->count < 0) { @@ -930,60 +1062,50 @@ static void release_dev(struct file * filp) tty->count, tty_name(tty)); tty->count = 0; } - if (tty->count) - return; /* - * Sanity check --- if tty->count is zero, there shouldn't be - * any waiters on tty->read_wait or tty->write_wait. But just - * in case.... + * Perform some housekeeping before deciding whether to return. + * + * Set the TTY_CLOSING flag if this was the last open. In the + * case of a pty we may have to wait around for the other side + * to close, and TTY_CLOSING makes sure we can't be reopened. */ - while (1) { - if (waitqueue_active(&tty->read_wait)) { - printk("release_dev: %s: read_wait active?!?\n", - tty_name(tty)); - wake_up(&tty->read_wait); - } else if (waitqueue_active(&tty->write_wait)) { - printk("release_dev: %s: write_wait active?!?\n", - tty_name(tty)); - wake_up(&tty->write_wait); - } else - break; - schedule(); - } - + if(tty_closing) + set_bit(TTY_CLOSING, &tty->flags); + if(o_tty_closing) + set_bit(TTY_CLOSING, &o_tty->flags); + /* - * We're committed; at this point, we must not block! + * If _either_ side is closing, make sure there aren't any + * processes that still think tty or o_tty is their controlling + * tty. Also, clear redirect if it points to either tty. */ - if (o_tty) { - if (o_tty->count) - return; - tty->driver.other->table[idx] = NULL; - tty->driver.other->termios[idx] = NULL; - kfree_s(o_tp, sizeof(struct termios)); + if (tty_closing || o_tty_closing) { + struct task_struct *p; + + read_lock(&tasklist_lock); + for_each_task(p) { + if (p->tty == tty || (o_tty && p->tty == o_tty)) + p->tty = NULL; + } + read_unlock(&tasklist_lock); + + if (redirect == tty || (o_tty && redirect == o_tty)) + redirect = NULL; } + + /* check whether both sides are closing ... */ + if (!tty_closing || (o_tty && !o_tty_closing)) + return; + filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure..."); #endif - tty->flags |= (1 << TTY_CLOSING); - - /* - * Make sure there aren't any processes that still think this - * tty is their controlling tty. - */ - read_lock(&tasklist_lock); - for_each_task(p) { - if (p->tty == tty) - p->tty = NULL; - if (o_tty && p->tty == o_tty) - p->tty = NULL; - } - read_unlock(&tasklist_lock); /* - * Shutdown the current line discipline, and reset it to - * N_TTY. + * Shutdown the current line discipline, and reset it to N_TTY. + * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); @@ -995,41 +1117,34 @@ static void release_dev(struct file * filp) o_tty->ldisc = ldiscs[N_TTY]; } - tty->driver.table[idx] = NULL; - if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { - tty->driver.termios[idx] = NULL; - kfree_s(tp, sizeof(struct termios)); - } - if (tty == redirect || o_tty == redirect) - redirect = NULL; /* * Make sure that the tty's task queue isn't activated. If it - * is, take it out of the linked list. + * is, take it out of the linked list. The tqueue isn't used by + * pty's, so skip the test for them. */ - spin_lock_irq(&tqueue_lock); - if (tty->flip.tqueue.sync) { - struct tq_struct *tq, *prev; - - for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { - if (tq == &tty->flip.tqueue) { - if (prev) - prev->next = tq->next; - else - tq_timer = tq->next; - break; + if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { + spin_lock_irq(&tqueue_lock); + if (tty->flip.tqueue.sync) { + struct tq_struct *tq, *prev; + + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if (tq == &tty->flip.tqueue) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } } } + spin_unlock_irq(&tqueue_lock); } - spin_unlock_irq(&tqueue_lock); - tty->magic = 0; - (*tty->driver.refcount)--; - free_page((unsigned long) tty); - filp->private_data = 0; - if (o_tty) { - o_tty->magic = 0; - (*o_tty->driver.refcount)--; - free_page((unsigned long) o_tty); - } + + /* + * The release_mem function takes care of the details of clearing + * the slots and preserving the termios structure. + */ + release_mem(tty, idx); } /* @@ -1077,6 +1192,7 @@ retry_open: retval = init_dev(device, &tty); if (retval) return retval; + /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */ filp->private_data = tty; check_tty_count(tty, "tty_open"); if (tty->driver.type == TTY_DRIVER_TYPE_PTY && @@ -1123,11 +1239,6 @@ retry_open: return 0; } -/* - * Note that releasing a pty master also releases the child, so - * we have to make the redirection checks after that and on both - * sides of a pty. - */ static int tty_release(struct inode * inode, struct file * filp) { release_dev(filp); @@ -1139,7 +1250,7 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait) struct tty_struct * tty; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "tty_poll")) + if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll")) return 0; if (tty->ldisc.poll) @@ -1545,6 +1656,7 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->flip.flag_buf_ptr = tty->flip.flag_buf; tty->flip.tqueue.routine = flush_to_ldisc; tty->flip.tqueue.data = tty; + tty->flip.pty_sem = MUTEX; } /* diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 603250b81..c0d7440c3 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -237,6 +237,11 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long func_scr_writew((func_scr_readw(org) & 0xff00) | c, org); } } +#ifdef CONFIG_FB_CONSOLE + if (currcons == fg_console) + /* Horribly inefficient if count < screen size. */ + update_screen(currcons); +#endif written = buf - buf0; file->f_pos += written; RETURN( written ); diff --git a/drivers/isdn/avmb1/capiutil.c b/drivers/isdn/avmb1/capiutil.c index b3c25cd2a..51d57fe9c 100644 --- a/drivers/isdn/avmb1/capiutil.c +++ b/drivers/isdn/avmb1/capiutil.c @@ -1,5 +1,5 @@ /* - * $Id: capiutil.c,v 1.3 1997/05/18 09:24:18 calle Exp $ + * $Id: capiutil.c,v 1.1 1997/06/08 14:58:41 ralf Exp $ * * CAPI 2.0 convert capi message to capi message struct * @@ -7,6 +7,9 @@ * Rewritten for Linux 1996 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: capiutil.c,v $ + * Revision 1.1 1997/06/08 14:58:41 ralf + * These files were missing in the 2.1.42 merge. + * * Revision 1.3 1997/05/18 09:24:18 calle * added verbose disconnect reason reporting to avmb1. * some fixes in capi20 interface. @@ -26,6 +29,7 @@ * */ #include <linux/module.h> +#include <linux/config.h> #include <linux/string.h> #include <linux/ctype.h> #include <linux/stddef.h> diff --git a/drivers/net/8390.c b/drivers/net/8390.c index fbf67cb42..1150ddcf4 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -186,6 +186,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct device *dev) /* Mask interrupts from the ethercard. */ outb_p(0x00, e8390_base + EN0_IMR); + synchronize_irq(); if (dev->interrupt) { printk("%s: Tx request while isr active.\n",dev->name); outb_p(ENISR_ALL, e8390_base + EN0_IMR); diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 23befe03c..bba3e43b8 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -84,6 +84,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET fi @@ -113,7 +114,12 @@ fi # if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_ATALK" != "n" ]; then - tristate 'LocalTalk PC support' CONFIG_LTPC + tristate 'Apple/Farallon LocalTalk PC support' CONFIG_LTPC + tristate 'COPS LocalTalk PC support' CONFIG_COPS + if [ "$CONFIG_COPS" != "n" ]; then + bool 'Dayna firmware support' CONFIG_COPS_DAYNA + bool 'Tangent firmware support' CONFIG_COPS_TANGENT + fi fi fi @@ -146,12 +152,14 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8 bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800 bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 - if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then - bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 - fi - if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then - bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 - fi +# if [ -f doesn't work with xconfig. Enable it again when the drivers are really +# included. +# if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then +# bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666 +# fi +# if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then +# bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800 +# fi fi fi tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 3a8721933..b149eac58 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -391,6 +391,14 @@ else endif endif +ifeq ($(CONFIG_TLAN),y) +L_OBJS += tlan.o +else + ifeq ($(CONFIG_TLAN),m) + M_OBJS += tlan.o + endif +endif + ifeq ($(CONFIG_ZNET),y) L_OBJS += znet.o endif @@ -663,6 +671,14 @@ else endif endif +ifeq ($(CONFIG_COPS),y) +L_OBJS += cops.o +else + ifeq ($(CONFIG_COPS),m) + M_OBJS += cops.o + endif +endif + ifeq ($(CONFIG_BAYCOM),y) L_OBJS += baycom.o CONFIG_HDLCDRV_BUILTIN = y @@ -785,7 +801,7 @@ dlci.o: dlci.c CONFIG sdladrv.o: sdladrv.c CONFIG wanpipe.o: $(WANPIPE_OBJS) - ld -r -o $@ $^ + ld -r -o $@ $(WANPIPE_OBJS) sdlamain.o: sdlamain.c CONFIG diff --git a/drivers/net/README.wanpipe b/drivers/net/README.wanpipe index bcfed26fe..9650edb73 100644 --- a/drivers/net/README.wanpipe +++ b/drivers/net/README.wanpipe @@ -1,10 +1,10 @@ ------------------------------------------------------------------------------ WANPIPE(tm) Multiprotocol WAN Driver for Linux WAN Router ------------------------------------------------------------------------------ -Release 3.0.0 -December 31, 1996 +Release 3.1.0 +January 30, 1997 Author: Gene Kozin <genek@compuserve.com> -Copyright (c) 1995-1996 Sangoma Technologies Inc. +Copyright (c) 1995-1997 Sangoma Technologies Inc. ------------------------------------------------------------------------------ INTRODUCTION @@ -81,6 +81,12 @@ include/linux: REVISION HISTORY +3.1.0 January 30, 1997 + + o Implemented IOCTL for executing adapter commands. + o Fixed a bug in frame relay code causing driver configured as a FR + switch to be stuck in WAN_DISCONNECTED mode. + 3.0.0 December 31, 1996 o Uses Linux WAN Router interface diff --git a/drivers/net/Space.c b/drivers/net/Space.c index ce820df60..64bc62c41 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -89,6 +89,7 @@ extern int atarilance_probe(struct device *); extern int a2065_probe(struct device *); extern int ariadne_probe(struct device *); extern int hydra_probe(struct device *); +extern int tlan_probe(struct device *); extern int cs89x0_probe(struct device *dev); /* Detachable devices ("pocket adaptors") */ @@ -242,6 +243,9 @@ __initfunc(static int ethif_probe(struct device *dev)) #ifdef CONFIG_SUNLANCE && sparc_lance_probe(dev) #endif +#ifdef CONFIG_TLAN + && tlan_probe(dev) +#endif #ifdef CONFIG_HAPPYMEAL && happy_meal_probe(dev) #endif @@ -301,6 +305,17 @@ static struct device atp_dev = { # define NEXT_DEV (&dev_ltpc) #endif /* LTPC */ +#if defined(CONFIG_COPS) + extern int cops_probe(struct device *); + static struct device dev_cops = { + "lt0", + 0, 0, 0, 0, + 0x0, 0, + 0, 0, 0, NEXT_DEV, cops_probe }; +# undef NEXT_DEV +# define NEXT_DEV (&dev_cops) +#endif /* COPS */ + /* The first device defaults to I/O base '0', which means autoprobe. */ #ifndef ETH0_ADDR # define ETH0_ADDR 0 diff --git a/drivers/net/cops.c b/drivers/net/cops.c new file mode 100644 index 000000000..66e3c5fea --- /dev/null +++ b/drivers/net/cops.c @@ -0,0 +1,1018 @@ +/* cops.c: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + * + * With more than a little help from; + * - Alan Cox <Alan.Cox@linux.org> + * + * Derived from: + * - skeleton.c: A network driver outline for linux. + * Written 1993-94 by Donald Becker. + * - ltpc.c: A driver for the LocalTalk PC card. + * Written by Bradford W. Johnson. + * + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Changes: + * 19970608 Alan Cox Allowed dual card type support + * Can set board type in insmod + * Hooks for cops_setup routine + * (not yet implemented). + */ + +static const char *version = + "cops.c:v0.01 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n"; +/* + * Sources: + * COPS Localtalk SDK. This provides almost all of the information + * needed. + */ + +/* + * insmod/modprobe configurable stuff. + * - IO Port, choose one your card supports or 0 if you dare. + * - IRQ, also choose one your card supports or nothing and let + * the driver figure it out. + */ + +#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#endif + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/if_arp.h> +#include <linux/if_ltalk.h> /* For ltalk_setup() */ +#include <linux/delay.h> /* For udelay() */ +#include <linux/atalk.h> + +#include "cops.h" /* Our Stuff */ +#include "cops_ltdrv.h" /* Firmware code for Tangent type cards. */ +#include "cops_ffdrv.h" /* Firmware code for Dayna type cards. */ + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ + +static const char *cardname = "cops"; + +#ifdef CONFIG_COPS_DAYNA +static int board_type = DAYNA; /* Module exported */ +#else +static int board_type = TANGENT; +#endif + +#ifdef MODULE +static int io = 0x240; /* Default IO for Dayna */ +static int irq = 5; /* Default IRQ */ +#else +static int io = 0; /* Default IO for Dayna */ +static int irq = 0; /* Default IRQ */ +#endif + +/* + * COPS Autoprobe information. + * Right now if port address is right but IRQ is not 5 this will + * return a 5 no matter what since we will still get a status response. + * Need one more additional check to narrow down after we have gotten + * the ioaddr. But since only other possible IRQs is 3 and 4 so no real + * hurry on this. I *STRONGLY* recommend using IRQ 5 for your card with + * this driver. + * + * This driver has 2 modes and they are: Dayna mode and Tangent mode. + * Each mode corresponds with the type of card. It has been found + * that there are 2 main types of cards and all other cards are + * the same and just have different names or only have minor differences + * such as more IO ports. As this driver is tested it will + * become more clear on exactly what cards are supported. The driver + * defaults to using Dayna mode. To change the drivers mode adjust + * drivers/net/CONFIG, and the line COPS_OPTS = -DDAYNA to -DTANGENT. + * + * This driver should support: + * TANGENT driver mode: + * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200 + * DAYNA driver mode: + * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, Farallon PhoneNET PC III + * Other cards possibly supported mode unkown though: + * Farallon PhoneNET PC II + * Dayna DL2000 (Full length) + * + * Cards NOT supported by this driver but supported by the ltpc.c + * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu> + * Farallon PhoneNET PC + * Original Apple LocalTalk PC card + */ + +/* + * Zero terminated list of IO ports to probe. + */ + +static unsigned int cops_portlist[] = { + 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260, + 0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360, + 0 +}; + +/* + * Zero terminated list of IRQ ports to probe. + */ + +static int cops_irqlist[] = { + 5, 4, 3, 0 +}; + +/* use 0 for production, 1 for verification, 2 for debug, 3 for very verbose debug */ +#ifndef COPS_DEBUG +#define COPS_DEBUG 1 +#endif +static unsigned int cops_debug = COPS_DEBUG; + +/* The number of low I/O ports used by the card. */ +#define COPS_IO_EXTENT 8 + +/* Information that needs to be kept for each board. */ + +struct cops_local +{ + struct enet_statistics stats; + int board; /* Holds what board type is. */ + int nodeid; /* Set to 1 once have nodeid. */ + unsigned char node_acquire; /* Node ID when acquired. */ +}; + +/* Allocate a new device with the form of lt0, lt1, lt2, etc. */ +struct device *cops_dev_alloc(char *name) +{ + int i=0; + struct device *d=kmalloc(sizeof(struct device)+8, GFP_KERNEL); + + memset(d,0,sizeof(*d)); /* Clear the structure */ + if(d==NULL) + return NULL; + d->name=(char *)(d+1); /* Name string space */ + + /* Get next free device name */ + for(i=0;i<100;i++) + { + sprintf(d->name,name,i); + if(dev_get(d->name)==NULL) + return d; + } + return NULL; /* Over 100 of the things .. bail out! */ +} + +/* Index to functions, as function prototypes. */ +extern int cops_probe (struct device *dev); +static int cops_probe1 (struct device *dev, int ioaddr); +static int cops_irq (int ioaddr, int board); + +static int cops_open (struct device *dev); +static int cops_jumpstart (struct device *dev); +static void cops_reset (struct device *dev, int sleep); +static void cops_load (struct device *dev); +static int cops_nodeid (struct device *dev, int nodeid); + +static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs); +static void cops_rx (struct device *dev); +static int cops_send_packet (struct sk_buff *skb, struct device *dev); +static void set_multicast_list (struct device *dev); +static int cops_hard_header (struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len); + +static int cops_ioctl (struct device *dev, struct ifreq *rq, int cmd); +static int cops_close (struct device *dev); +static struct enet_statistics *cops_get_stats (struct device *dev); + + +/* + * Check for a network adaptor of this type, and return '0' iff one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr == 1, always return failure. + * If dev->base_addr == 2, allocate space for the device and return success + * (detachable devices only). + */ +int cops_probe(struct device *dev) +{ + int i; + int base_addr = dev ? dev->base_addr : 0; + + if (base_addr == 0 && io) + base_addr=io; + + if (base_addr > 0x1ff) /* Check a single specified location. */ + return cops_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + for (i=0; cops_portlist[i]; i++) { + int ioaddr = cops_portlist[i]; + if (check_region(ioaddr, COPS_IO_EXTENT)) + continue; + if (cops_probe1(dev, ioaddr) == 0) + return 0; + } + + /* No "lt" devices found. */ + printk(KERN_WARNING "%s: No COPS localtalk devices found!\n", dev->name); + return -ENODEV; +} + +/* + * This is the real probe routine. Linux has a history of friendly device + * probes on the ISA bus. A good device probes avoids doing writes, and + * verifies that the correct device exists and functions. + */ +static int cops_probe1(struct device *dev, int ioaddr) +{ + struct cops_local *lp; + static unsigned version_printed = 0; + int irqaddr = 0; + int irqval; + + int board = board_type; + +/* Defined here to save some trouble */ + + /* Allocate a new 'dev' if needed. */ + if (dev == NULL) + { + dev=cops_dev_alloc(dev->name); /* New "lt" device; beyond lt0. */ + if(dev==NULL) + return -ENOMEM; + } + + if (cops_debug && version_printed++ == 0) + printk("%s", version); + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + + /* + * Since this board has jumpered interrupts, allocate the interrupt + * vector now. There is no point in waiting since no other device + * can use the interrupt, and this marks the irq as busy. Jumpered + * interrupts are typically not reported by the boards, and we must + * used AutoIRQ to find them. + * + */ + + if (dev->irq < 2 && irq) + dev->irq = irq; + + if (dev->irq < 2) + { + irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */ + if (irqaddr == 0) + return -EAGAIN; /* No IRQ found on this port */ + else + dev->irq = irqaddr; + } + else if (dev->irq == 2) + /* + * Fixup for users that don't know that IRQ 2 is really + * IRQ 9, or don't know which one to set. + */ + dev->irq = 9; + + /* Snarf the interrupt now. */ + irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, NULL); + if (irqval) + { + printk(KERN_WARNING "%s: Unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, irqval); + return -EAGAIN; + } + + dev->hard_start_xmit = &cops_send_packet; + + /* Initialize the device structure. */ + dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + + lp = (struct cops_local *)dev->priv; + memset(lp, 0, sizeof(struct cops_local)); + + /* Copy local board variable to lp struct. */ + lp->board = board; + + /* Tell the user where the card is and what mode were in. */ + if(board==DAYNA) + printk("%s: %s found at %#3x, using IRQ %d, in Dayna mode.\n", + dev->name, cardname, ioaddr, dev->irq); + if(board==TANGENT) + printk("%s: %s found at %#3x, using IRQ %d, in Tangent mode.\n", + dev->name, cardname, ioaddr, dev->irq); + + /* Grab the region so no one else tries to probe our ioports. */ + request_region(ioaddr, COPS_IO_EXTENT, cardname); + + /* Fill in the fields of the device structure with LocalTalk values. */ + ltalk_setup(dev); + + dev->hard_header = cops_hard_header; + dev->get_stats = cops_get_stats; + dev->open = cops_open; + dev->stop = cops_close; + dev->do_ioctl = &cops_ioctl; + dev->set_multicast_list = &set_multicast_list; + dev->mc_list = NULL; + + return 0; +} + +static int cops_irq (int ioaddr, int board) +{ /* + * This does not use the IRQ to determine where the IRQ is. We just + * assume that when we get a correct status response that is the IRQ then. + * This really just verifies the IO port but since we only have access + * to such a small number of IRQs (5, 4, 3) this is not bad. + * This will probably not work for more than one card. + */ + int irqaddr=0; + int i, x, status; + + if(board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); + inb(ioaddr+DAYNA_RESET); + udelay(333333); + } + if(board==TANGENT) + { + inb(ioaddr); + outb(0, ioaddr); + outb(0, ioaddr+TANG_RESET); + } + + for(i=0; cops_irqlist[i] !=0; i++) + { + irqaddr = cops_irqlist[i]; + for(x = 0xFFFF; x>0; x --) /* wait for response */ + { + if(board==DAYNA) + { + status = (inb(ioaddr+DAYNA_CARD_STATUS)&3); + if (status == 1) + return irqaddr; + } + if(board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)& TANG_TX_READY) !=0) + return irqaddr; + } + } + } + return 0; /* no IRQ found */ +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + */ +static int cops_open(struct device *dev) +{ + irq2dev_map[dev->irq] = dev; + + cops_jumpstart(dev); /* Start the card up. */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; +} + +/* + * This allows for a dynamic start/restart of the entire card. + */ +static int cops_jumpstart(struct device *dev) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + + /* + * Once the card has the firmware loaded and has acquired + * the nodeid, if it is reset it will lose it all. + */ + cops_reset(dev,1); /* Need to reset card before load firmware. */ + cops_load(dev); /* Load the firmware. */ + + /* + * If atalkd already gave us a nodeid we will use that + * one again, else we wait for atalkd to give us a nodeid + * in cops_ioctl. This may cause a problem if someone steals + * our nodeid while we are resetting. + */ + if(lp->nodeid == 1) + cops_nodeid(dev,lp->node_acquire); + + return 0; +} + +static int tangent_wait_reset(int ioaddr) +{ + int timeout=0; + + while(timeout < 5000 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + udelay(1000); /* Wait 1000 useconds */ + + return 0; +} + +/* + * Reset the LocalTalk board. + */ +static void cops_reset(struct device *dev, int sleep) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + int ioaddr=dev->base_addr; + + if(lp->board==TANGENT) + { + inb(ioaddr); /* Clear request latch. */ + outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ + outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ + + /* Can take 5 seconds max - youch! */ + if(sleep) + { + long snapt=jiffies; + while(jiffies-snapt<5*HZ) + { + if(inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY) + break; + schedule(); + } + } + else + tangent_wait_reset(ioaddr); + outb(0, ioaddr+TANG_CLEAR_INT); + } + if(lp->board==DAYNA) + { + outb(0, ioaddr+DAYNA_RESET); /* Assert the reset port */ + inb(ioaddr+DAYNA_RESET); /* Clear the reset */ + if(sleep) + { + long snap=jiffies; + + /* Let card finish initializing, about 1/3 second */ + while(jiffies-snap<HZ/3) + schedule(); + } + else + udelay(333333); + } + dev->tbusy=0; + + return; +} + +static void cops_load (struct device *dev) +{ + struct ifreq ifr; + struct ltfirmware *ltf= (struct ltfirmware *)&ifr.ifr_data; + struct cops_local *lp=(struct cops_local *)dev->priv; + int ioaddr=dev->base_addr; + int length, i = 0; + + strcpy(ifr.ifr_name,"lt0"); + + /* Get card's firmware code and do some checks on it. */ +#ifdef CONFIG_COPS_DAYNA + if (lp->board==DAYNA) + { + ltf->length=sizeof(ffdrv_code); + ltf->data=ffdrv_code; + } + else +#endif +#ifdef CONFIG_COPS_TANGENT + if (lp->board==TANGENT) + { + ltf->length=sizeof(ltdrv_code); + ltf->data=ltdrv_code; + } + else +#endif + { + printk(KERN_INFO "%s; unsupported board type.\n", dev->name); + return; + } + + /* Check to make sure firmware is correct length. */ + if(lp->board==DAYNA && ltf->length!=5983) + { + printk(KERN_WARNING "%s: Firmware is not length of FFDRV.BIN.\n", dev->name); + return; + } + if(lp->board==TANGENT && ltf->length!=2501) + { + printk(KERN_WARNING "%s: Firmware is not length of DRVCODE.BIN.\n", dev->name); + return; + } + + if(lp->board==DAYNA) + { + /* + * We must wait for a status response + * with the DAYNA board. + */ + while(++i<65536) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&3)==1) + break; + } + + if(i==65536) + return; + } + + /* + * Upload the firmware and kick. Byte-by-byte works nicely here. + */ + i=0; + length = ltf->length; + while(length--) + { + outb(ltf->data[i], ioaddr); + i++; + } + + if(cops_debug > 1) + printk(KERN_DEBUG "%s: Uploaded firmware - %d bytes of %d bytes.\n", dev->name, i, ltf->length); + + if(lp->board==DAYNA) + outb(1, ioaddr+DAYNA_INT_CARD); /* Tell Dayna to run the firmware code. */ + else + inb(ioaddr); /* Tell Tang to run the firmware code. */ + + if(lp->board==TANGENT) + { + tangent_wait_reset(ioaddr); + inb(ioaddr); /* Clear initial ready signal. */ + } + + return; +} + +/* + * Get the LocalTalk Nodeid from the card. We can suggest + * any nodeid 1-254. The card will try and get that exact + * address else we can specify 0 as the nodeid and the card + * will autoprobe for a nodeid. + */ +static int cops_nodeid (struct device *dev, int nodeid) +{ + struct cops_local *lp = (struct cops_local *) dev->priv; + int ioaddr = dev->base_addr; + + if (lp->board == DAYNA) + { + /* Empty any pending adapter responses. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Kick out any packet waiting. */ + schedule(); + } + + outb(2, ioaddr); /* Output command packet length as 2. */ + outb(0, ioaddr); + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* Suggest node address. */ + } + + if (lp->board == TANGENT) + { + /* Empty any pending adapter responses. */ + while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + cops_rx(dev); /* Kick out any packet waiting. */ + schedule(); + } + + /* Not sure what Tangent does if random nodeid we picked is already used. */ + if(nodeid == 0) /* Seed. */ + nodeid = jiffies&0xFF; /* Get a random try .*/ + outb(2, ioaddr); /* Command length LSB. */ + outb(0, ioaddr); /* Command length MSB. */ + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* LAP address hint. */ + outb(0xFF, ioaddr); /* Interrupt level to use (NONE). */ + } + + lp->node_acquire=0; /* Set nodeid holder to 0. */ + while(lp->node_acquire==0) /* Get *True* nodeid finally. */ + { + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + + if(lp->board == DAYNA) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + if(lp->board == TANGENT) + { + if(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) + cops_rx(dev); /* Grab the nodeid put in lp->node_acquire. */ + } + schedule(); + } + + if(cops_debug > 1) + printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", dev->name, lp->node_acquire); + + lp->nodeid=1; /* Set got nodeid to 1. */ + + return 0; +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct device *dev = (struct device *) irq2dev_map[irq]; + struct cops_local *lp; + int ioaddr, status; + int boguscount = 0; + + if (dev == NULL) + { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); + return; + } + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct cops_local *)dev->priv; + + do + { + /* Clear any interrupt. */ + outb(0, ioaddr + COPS_CLEAR_INT); + + if(lp->board==DAYNA) + { + status=inb(ioaddr+DAYNA_CARD_STATUS); + if((status&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); + } + else + { + status=inb(ioaddr+TANG_CARD_STATUS); + if (status&TANG_RX_READY) + cops_rx(dev); + } + + dev->tbusy = 0; + mark_bh(NET_BH); + } while (++boguscount < 20 ); + dev->interrupt = 0; + + return; +} + +/* + * We have a good packet(s), get it/them out of the buffers. + */ +static void cops_rx(struct device *dev) +{ + int pkt_len = 0; + int rsp_type = 0; + struct sk_buff *skb; + struct cops_local *lp = (struct cops_local *)dev->priv; + int ioaddr = dev->base_addr; + int boguscount = 0; + + cli(); /* Disable interrupts. */ + + if(lp->board==DAYNA) + { + outb(0, ioaddr); /* Send out Zero length. */ + outb(0, ioaddr); + outb(DATA_READ, ioaddr); /* Send read command out. */ + + /* Wait for DMA to turn around. */ + while(++boguscount<1000000) + { + if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_READY) + break; + } + + if(boguscount==1000000) + { + printk(KERN_WARNING "%s: DMA timed out.\n",dev->name); + return; + } + } + + /* Get response length. */ + pkt_len = inb(ioaddr) & 0xFF; + pkt_len |= (inb(ioaddr) << 8); + /* Input IO code. */ + rsp_type=inb(ioaddr); + + /* Malloc up new buffer. */ + skb = dev_alloc_skb(pkt_len); + if (skb == NULL) + { + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + while(pkt_len--) /* Discard packet */ + inb(ioaddr); + return; + } + skb->dev = dev; + skb_put(skb, pkt_len); + skb->protocol = htons(ETH_P_LOCALTALK); + + insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ + + if(lp->board==DAYNA) + outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card. */ + + sti(); /* Restore interrupts. */ + + /* Check for bad response length */ + if (pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) + { + printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len); + lp->stats.tx_errors++; + kfree_skb(skb, FREE_READ); + return; + } + + /* Set nodeid and then get out. */ + if(rsp_type == LAP_INIT_RSP) + { + lp->node_acquire = skb->data[0]; /* Nodeid taken from received packet. */ + kfree_skb(skb, FREE_READ); + return; + } + + /* One last check to make sure we have a good packet. */ + if(rsp_type != LAP_RESPONSE) + { + printk("%s: Bad packet type %d.\n", dev->name, rsp_type); + lp->stats.tx_errors++; + kfree_skb(skb, FREE_READ); + return; + } + + skb->mac.raw = skb->data; /* Point to entire packet. */ + skb_pull(skb,3); + skb->h.raw = skb->data; /* Point to just the data (Skip header). */ + + /* Update the counters. */ + lp->stats.rx_packets++; + lp->stats.rx_bytes += skb->len; + + /* Send packet to a higher place. */ + netif_rx(skb); + + return; +} + +/* + * Make the card transmit a LocalTalk packet. + */ +static int cops_send_packet(struct sk_buff *skb, struct device *dev) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + int ioaddr = dev->base_addr; + + if (dev->tbusy) + { + /* + * If we get here, some higher level has decided we are broken. + * There should really be a "kick me" function call instead. + */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + lp->stats.tx_errors++; + if(lp->board==TANGENT) + { + if((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + printk(KERN_WARNING "%s: No TX complete interrupt.\n", dev->name); + } + printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); + cops_jumpstart(dev); /* Restart the card. */ + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* + * Block a timer-based transmit from overlapping. This could better be + * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + */ + if (test_and_set_bit(0, (void*) &dev->tbusy) != 0) + printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); + else + { + cli(); /* Disable interrupts. */ + if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); + if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ + while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0); + + /* Output IO length. */ + if(lp->board == DAYNA) + { + outb(skb->len, ioaddr); + outb(skb->len >> 8, ioaddr); + } + else + { + outb(skb->len&0x0FF, ioaddr); + outb((skb->len >> 8)&0x0FF, ioaddr); + } + + /* Output IO code. */ + outb(LAP_WRITE, ioaddr); + + if(lp->board == DAYNA) /* Check the transmit buffer again. */ + while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); + + outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ + + if(lp->board==DAYNA) /* The Dayna requires you kick the card. */ + outb(1, ioaddr+DAYNA_INT_CARD); + + sti(); /* Restore interrupts. */ + + /* Done sending packet, update counters and cleanup. */ + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + } + + dev_kfree_skb (skb, FREE_WRITE); + dev->tbusy = 0; + + return 0; +} + +/* + * Dummy function to keep the Appletalk layer happy. + */ + +static void set_multicast_list(struct device *dev) +{ + if(cops_debug >= 3) + printk("%s: set_mulicast_list executed. NeatO.\n", dev->name); +} + +/* + * Another Dummy function to keep the Appletalk layer happy. + */ + +static int cops_hard_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) +{ + if(cops_debug >= 3) + printk("%s: cops_hard_header executed. Wow!\n", dev->name); + return 0; +} + +/* + * System ioctls for the COPS LocalTalk card. + */ + +static int cops_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr; + struct at_addr *aa=(struct at_addr *)&dev->pa_addr; + + switch(cmd) + { + case SIOCSIFADDR: + /* Get and set the nodeid and network # atalkd wants. */ + cops_nodeid(dev, sa->sat_addr.s_node); + aa->s_net = sa->sat_addr.s_net; + aa->s_node = lp->node_acquire; + + /* Set broardcast address. */ + dev->broadcast[0] = 0xFF; + + /* Set hardware address. */ + dev->dev_addr[0] = aa->s_node; + dev->addr_len = 1; + return 0; + + case SIOCGIFADDR: + sa->sat_addr.s_net = aa->s_net; + sa->sat_addr.s_node = aa->s_node; + return 0; + + default: + return -EOPNOTSUPP; + } +} + +/* + * The inverse routine to cops_open(). + */ + +static int cops_close(struct device *dev) +{ + dev->tbusy = 1; + dev->start = 0; + irq2dev_map[dev->irq] = 0; + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct enet_statistics *cops_get_stats(struct device *dev) +{ + struct cops_local *lp = (struct cops_local *)dev->priv; + return &lp->stats; +} + +#ifdef MODULE +static struct device dev_cops = +{ + "lt0", /* device name */ + 0, 0, 0, 0, + 0x0, 0, /* I/O address, IRQ */ + 0, 0, 0, NULL, cops_probe +}; + + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(board_type, "i"); + +int init_module(void) +{ + int result; + + if (io == 0) + printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname); + + /* Copy the parameters from insmod into the device structure. */ + dev_cops.base_addr = io; + dev_cops.irq = irq; + + if ((result = register_netdev(&dev_cops)) != 0) + return result; + + return 0; +} + +void cleanup_module(void) +{ + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + + free_irq(dev_cops.irq, NULL); + release_region(dev_cops.base_addr, COPS_IO_EXTENT); + unregister_netdev(&dev_cops); + + if (dev_cops.priv) + kfree_s(dev_cops.priv, sizeof(struct cops_local)); +} +#endif /* MODULE */ diff --git a/drivers/net/cops.h b/drivers/net/cops.h new file mode 100644 index 000000000..a064bc12b --- /dev/null +++ b/drivers/net/cops.h @@ -0,0 +1,60 @@ +/* cops.h: LocalTalk driver for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + */ + +#ifndef __LINUX_COPSLTALK_H +#define __LINUX_COPSLTALK_H + +#ifdef __KERNEL__ + +/* Max LLAP size we will accept. */ +#define MAX_LLAP_SIZE 603 + +/* Tangent */ +#define TANG_CARD_STATUS 1 +#define TANG_CLEAR_INT 1 +#define TANG_RESET 3 + +#define TANG_TX_READY 1 +#define TANG_RX_READY 2 + +/* Dayna */ +#define DAYNA_CMD_DATA 0 +#define DAYNA_CLEAR_INT 1 +#define DAYNA_CARD_STATUS 2 +#define DAYNA_INT_CARD 3 +#define DAYNA_RESET 4 + +#define DAYNA_RX_READY 0 +#define DAYNA_TX_READY 1 +#define DAYNA_RX_REQUEST 3 + +/* Same on both card types */ +#define COPS_CLEAR_INT 1 + +/* LAP response codes recieved from the cards. */ +#define LAP_INIT 1 /* Init cmd */ +#define LAP_INIT_RSP 2 /* Init response */ +#define LAP_WRITE 3 /* Write cmd */ +#define DATA_READ 4 /* Data read */ +#define LAP_RESPONSE 4 /* Received ALAP frame response */ +#define LAP_GETSTAT 5 /* Get LAP and HW status */ +#define LAP_RSPSTAT 6 /* Status response */ + +#endif + +/* + * Structure to hold the firmware information. + */ +struct ltfirmware +{ + unsigned int length; + unsigned char * data; +}; + +#define DAYNA 1 +#define TANGENT 2 + +#endif diff --git a/drivers/net/cops_ffdrv.h b/drivers/net/cops_ffdrv.h new file mode 100644 index 000000000..d3e337afc --- /dev/null +++ b/drivers/net/cops_ffdrv.h @@ -0,0 +1,533 @@ + +/* + * The firmware this driver downloads into the Localtalk card is a + * seperate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ffdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + */ + +#include <linux/config.h> + +#ifdef CONFIG_COPS_DAYNA + +unsigned char ffdrv_code[] = { + 58,3,0,50,228,149,33,255,255,34,226,149, + 249,17,40,152,33,202,154,183,237,82,77,68, + 11,107,98,19,54,0,237,176,175,50,80,0, + 62,128,237,71,62,32,237,57,51,62,12,237, + 57,50,237,57,54,62,6,237,57,52,62,12, + 237,57,49,33,107,137,34,32,128,33,83,130, + 34,40,128,33,86,130,34,42,128,33,112,130, + 34,36,128,33,211,130,34,38,128,62,0,237, + 57,16,33,63,148,34,34,128,237,94,205,15, + 130,251,205,168,145,24,141,67,111,112,121,114, + 105,103,104,116,32,40,67,41,32,49,57,56, + 56,32,45,32,68,97,121,110,97,32,67,111, + 109,109,117,110,105,99,97,116,105,111,110,115, + 32,32,32,65,108,108,32,114,105,103,104,116, + 115,32,114,101,115,101,114,118,101,100,46,32, + 32,40,68,40,68,7,16,8,34,7,22,6, + 16,5,12,4,8,3,6,140,0,16,39,128, + 0,4,96,10,224,6,0,7,126,2,64,11, + 118,12,6,13,0,14,193,15,0,5,96,3, + 192,1,64,9,8,62,9,211,66,62,192,211, + 66,62,100,61,32,253,6,28,33,205,129,14, + 66,237,163,194,253,129,6,28,33,205,129,14, + 64,237,163,194,9,130,201,62,47,50,71,152, + 62,47,211,68,58,203,129,237,57,20,58,204, + 129,237,57,21,33,77,152,54,132,205,233,129, + 58,228,149,254,209,40,6,56,4,62,0,24, + 2,219,96,33,233,149,119,230,62,33,232,149, + 119,213,33,8,152,17,7,0,25,119,19,25, + 119,209,201,251,237,77,245,197,213,229,221,229, + 205,233,129,62,1,50,106,137,205,158,139,221, + 225,225,209,193,241,251,237,77,245,197,213,219, + 72,237,56,16,230,46,237,57,16,237,56,12, + 58,72,152,183,32,26,6,20,17,128,2,237, + 56,46,187,32,35,237,56,47,186,32,29,219, + 72,230,1,32,3,5,32,232,175,50,72,152, + 229,221,229,62,1,50,106,137,205,158,139,221, + 225,225,24,25,62,1,50,72,152,58,201,129, + 237,57,12,58,202,129,237,57,13,237,56,16, + 246,17,237,57,16,209,193,241,251,237,77,245, + 197,229,213,221,229,237,56,16,230,17,237,57, + 16,237,56,20,58,34,152,246,16,246,8,211, + 68,62,6,61,32,253,58,34,152,246,8,211, + 68,58,203,129,237,57,20,58,204,129,237,57, + 21,237,56,16,246,34,237,57,16,221,225,209, + 225,193,241,251,237,77,33,2,0,57,126,230, + 3,237,100,1,40,2,246,128,230,130,245,62, + 5,211,64,241,211,64,201,229,213,243,237,56, + 16,230,46,237,57,16,237,56,12,251,70,35, + 35,126,254,175,202,77,133,254,129,202,15,133, + 230,128,194,191,132,43,58,44,152,119,33,76, + 152,119,35,62,132,119,120,254,255,40,4,58, + 49,152,119,219,72,43,43,112,17,3,0,237, + 56,52,230,248,237,57,52,219,72,230,1,194, + 141,131,209,225,237,56,52,246,6,237,57,52, + 62,1,55,251,201,62,3,211,66,62,192,211, + 66,62,48,211,66,0,0,219,66,230,1,40, + 4,219,67,24,240,205,203,135,58,75,152,254, + 255,202,128,132,58,49,152,254,161,250,207,131, + 58,34,152,211,68,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,24,0,62, + 14,211,66,62,33,211,66,62,1,211,66,62, + 64,211,66,62,3,211,66,62,209,211,66,62, + 100,71,219,66,230,1,32,6,5,32,247,195, + 248,132,219,67,71,58,44,152,184,194,248,132, + 62,100,71,219,66,230,1,32,6,5,32,247, + 195,248,132,219,67,62,100,71,219,66,230,1, + 32,6,5,32,247,195,248,132,219,67,254,133, + 32,7,62,0,50,74,152,24,17,254,173,32, + 7,62,1,50,74,152,24,6,254,141,194,248, + 132,71,209,225,58,49,152,254,132,32,10,62, + 50,205,2,134,205,144,135,24,27,254,140,32, + 15,62,110,205,2,134,62,141,184,32,5,205, + 144,135,24,8,62,10,205,2,134,205,8,134, + 62,1,50,106,137,205,158,139,237,56,52,246, + 6,237,57,52,175,183,251,201,62,20,135,237, + 57,20,175,237,57,21,237,56,16,246,2,237, + 57,16,237,56,20,95,237,56,21,123,254,10, + 48,244,237,56,16,230,17,237,57,16,209,225, + 205,144,135,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,183,251,201,209, + 225,243,219,72,230,1,40,13,62,10,211,66, + 0,0,219,66,230,192,202,226,132,237,56,52, + 246,6,237,57,52,62,1,55,251,201,205,203, + 135,62,1,50,106,137,205,158,139,237,56,52, + 246,6,237,57,52,183,251,201,209,225,62,1, + 50,106,137,205,158,139,237,56,52,246,6,237, + 57,52,62,2,55,251,201,209,225,243,219,72, + 230,1,202,213,132,62,10,211,66,0,0,219, + 66,230,192,194,213,132,229,62,1,50,106,137, + 42,40,152,205,65,143,225,17,3,0,205,111, + 136,62,6,211,66,58,44,152,211,66,237,56, + 52,246,6,237,57,52,183,251,201,209,197,237, + 56,52,230,248,237,57,52,219,72,230,1,32, + 15,193,225,237,56,52,246,6,237,57,52,62, + 1,55,251,201,14,23,58,37,152,254,0,40, + 14,14,2,254,1,32,5,62,140,119,24,3, + 62,132,119,43,43,197,205,203,135,193,62,1, + 211,66,62,64,211,66,62,3,211,66,62,193, + 211,66,62,100,203,39,71,219,66,230,1,32, + 6,5,32,247,195,229,133,33,238,151,219,67, + 71,58,44,152,184,194,229,133,119,62,100,71, + 219,66,230,1,32,6,5,32,247,195,229,133, + 219,67,35,119,13,32,234,193,225,62,1,50, + 106,137,205,158,139,237,56,52,246,6,237,57, + 52,175,183,251,201,33,234,151,35,35,62,255, + 119,193,225,62,1,50,106,137,205,158,139,237, + 56,52,246,6,237,57,52,175,251,201,243,61, + 32,253,251,201,62,3,211,66,62,192,211,66, + 58,49,152,254,140,32,19,197,229,213,17,181, + 129,33,185,129,1,2,0,237,176,209,225,193, + 24,27,229,213,33,187,129,58,49,152,230,15, + 87,30,2,237,92,25,17,181,129,126,18,19, + 35,126,18,209,225,58,34,152,246,8,211,68, + 58,49,152,254,165,40,14,254,164,40,10,62, + 10,211,66,62,224,211,66,24,25,58,74,152, + 254,0,40,10,62,10,211,66,62,160,211,66, + 24,8,62,10,211,66,62,128,211,66,62,11, + 211,66,62,6,211,66,205,147,143,62,5,211, + 66,62,224,211,66,62,5,211,66,62,96,211, + 66,62,5,61,32,253,62,5,211,66,62,224, + 211,66,62,14,61,32,253,62,5,211,66,62, + 233,211,66,62,128,211,66,58,181,129,61,32, + 253,62,1,211,66,62,192,211,66,1,254,19, + 237,56,46,187,32,6,13,32,247,195,226,134, + 62,192,211,66,0,0,219,66,203,119,40,250, + 219,66,203,87,40,250,243,237,56,16,230,17, + 237,57,16,237,56,20,251,62,5,211,66,62, + 224,211,66,58,182,129,61,32,253,229,33,181, + 129,58,183,129,203,63,119,35,58,184,129,119, + 225,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,62,47,211,68,62,5,211, + 66,62,233,211,66,58,181,129,61,32,253,62, + 5,211,66,62,224,211,66,58,182,129,61,32, + 253,62,5,211,66,62,96,211,66,201,229,213, + 58,50,152,230,15,87,30,2,237,92,33,187, + 129,25,17,181,129,126,18,35,19,126,18,209, + 225,58,71,152,246,8,211,68,58,50,152,254, + 165,40,14,254,164,40,10,62,10,211,66,62, + 224,211,66,24,8,62,10,211,66,62,128,211, + 66,62,11,211,66,62,6,211,66,195,248,135, + 62,3,211,66,62,192,211,66,197,229,213,17, + 181,129,33,183,129,1,2,0,237,176,209,225, + 193,62,47,211,68,62,10,211,66,62,224,211, + 66,62,11,211,66,62,118,211,66,62,1,211, + 66,62,0,211,66,205,147,143,195,16,136,62, + 3,211,66,62,192,211,66,197,229,213,17,181, + 129,33,183,129,1,2,0,237,176,209,225,193, + 62,47,211,68,62,10,211,66,62,224,211,66, + 62,11,211,66,62,118,211,66,205,147,143,62, + 5,211,66,62,224,211,66,62,5,211,66,62, + 96,211,66,62,5,61,32,253,62,5,211,66, + 62,224,211,66,62,14,61,32,253,62,5,211, + 66,62,233,211,66,62,128,211,66,58,181,129, + 61,32,253,62,1,211,66,62,192,211,66,1, + 254,19,237,56,46,187,32,6,13,32,247,195, + 88,136,62,192,211,66,0,0,219,66,203,119, + 40,250,219,66,203,87,40,250,62,5,211,66, + 62,224,211,66,58,182,129,61,32,253,62,5, + 211,66,62,96,211,66,201,197,14,67,6,0, + 62,3,211,66,62,192,211,66,62,48,211,66, + 0,0,219,66,230,1,40,4,219,67,24,240, + 62,5,211,66,62,233,211,66,62,128,211,66, + 58,181,129,61,32,253,237,163,29,62,192,211, + 66,219,66,230,4,40,250,237,163,29,32,245, + 219,66,230,4,40,250,62,255,71,219,66,230, + 4,40,3,5,32,247,219,66,230,4,40,250, + 62,5,211,66,62,224,211,66,58,182,129,61, + 32,253,62,5,211,66,62,96,211,66,58,71, + 152,254,1,202,18,137,62,16,211,66,62,56, + 211,66,62,14,211,66,62,33,211,66,62,1, + 211,66,62,248,211,66,237,56,48,246,153,230, + 207,237,57,48,62,3,211,66,62,221,211,66, + 193,201,58,71,152,211,68,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,62,14, + 211,66,62,33,211,66,62,1,211,66,62,248, + 211,66,237,56,48,246,145,246,8,230,207,237, + 57,48,62,3,211,66,62,221,211,66,193,201, + 44,3,1,0,70,69,1,245,197,213,229,175, + 50,72,152,237,56,16,230,46,237,57,16,237, + 56,12,62,1,211,66,0,0,219,66,95,230, + 160,32,3,195,20,139,123,230,96,194,72,139, + 62,48,211,66,62,1,211,66,62,64,211,66, + 237,91,40,152,205,207,143,25,43,55,237,82, + 218,70,139,34,42,152,98,107,58,44,152,190, + 194,210,138,35,35,62,130,190,194,200,137,62, + 1,50,48,152,62,175,190,202,82,139,62,132, + 190,32,44,50,50,152,62,47,50,71,152,229, + 175,50,106,137,42,40,152,205,65,143,225,54, + 133,43,70,58,44,152,119,43,112,17,3,0, + 62,10,205,2,134,205,111,136,195,158,138,62, + 140,190,32,19,50,50,152,58,233,149,230,4, + 202,222,138,62,1,50,71,152,195,219,137,126, + 254,160,250,185,138,254,166,242,185,138,50,50, + 152,43,126,35,229,213,33,234,149,95,22,0, + 25,126,254,132,40,18,254,140,40,14,58,50, + 152,230,15,87,126,31,21,242,65,138,56,2, + 175,119,58,50,152,230,15,87,58,233,149,230, + 62,31,21,242,85,138,218,98,138,209,225,195, + 20,139,58,50,152,33,100,137,230,15,95,22, + 0,25,126,50,71,152,209,225,58,50,152,254, + 164,250,135,138,58,73,152,254,0,40,4,54, + 173,24,2,54,133,43,70,58,44,152,119,43, + 112,17,3,0,205,70,135,175,50,106,137,205, + 208,139,58,199,129,237,57,12,58,200,129,237, + 57,13,237,56,16,246,17,237,57,16,225,209, + 193,241,251,237,77,62,129,190,194,227,138,54, + 130,43,70,58,44,152,119,43,112,17,3,0, + 205,144,135,195,20,139,35,35,126,254,132,194, + 227,138,175,50,106,137,205,158,139,24,42,58, + 201,154,254,1,40,7,62,1,50,106,137,24, + 237,58,106,137,254,1,202,222,138,62,128,166, + 194,222,138,221,229,221,33,67,152,205,127,142, + 205,109,144,221,225,225,209,193,241,251,237,77, + 58,106,137,254,1,202,44,139,58,50,152,254, + 164,250,44,139,58,73,152,238,1,50,73,152, + 221,229,221,33,51,152,205,127,142,221,225,62, + 1,50,106,137,205,158,139,195,13,139,24,208, + 24,206,24,204,230,64,40,3,195,20,139,195, + 20,139,43,126,33,8,152,119,35,58,44,152, + 119,43,237,91,35,152,205,203,135,205,158,139, + 195,13,139,175,50,78,152,62,3,211,66,62, + 192,211,66,201,197,33,4,0,57,126,35,102, + 111,62,1,50,106,137,219,72,205,141,139,193, + 201,62,1,50,78,152,34,40,152,54,0,35, + 35,54,0,195,163,139,58,78,152,183,200,229, + 33,181,129,58,183,129,119,35,58,184,129,119, + 225,62,47,211,68,62,14,211,66,62,193,211, + 66,62,10,211,66,62,224,211,66,62,11,211, + 66,62,118,211,66,195,3,140,58,78,152,183, + 200,58,71,152,211,68,254,69,40,4,254,70, + 32,17,58,73,152,254,0,40,10,62,10,211, + 66,62,160,211,66,24,8,62,10,211,66,62, + 128,211,66,62,11,211,66,62,6,211,66,62, + 6,211,66,58,44,152,211,66,62,16,211,66, + 62,56,211,66,62,48,211,66,0,0,219,66, + 230,1,40,4,219,67,24,240,62,14,211,66, + 62,33,211,66,42,40,152,205,65,143,62,1, + 211,66,62,248,211,66,237,56,48,246,145,246, + 8,230,207,237,57,48,62,3,211,66,62,221, + 211,66,201,62,16,211,66,62,56,211,66,62, + 48,211,66,0,0,219,66,230,1,40,4,219, + 67,24,240,62,14,211,66,62,33,211,66,62, + 1,211,66,62,248,211,66,237,56,48,246,153, + 230,207,237,57,48,62,3,211,66,62,221,211, + 66,201,229,213,33,234,149,95,22,0,25,126, + 254,132,40,4,254,140,32,2,175,119,123,209, + 225,201,6,8,14,0,31,48,1,12,16,250, + 121,201,33,4,0,57,94,35,86,33,2,0, + 57,126,35,102,111,221,229,34,89,152,237,83, + 91,152,221,33,63,152,205,127,142,58,81,152, + 50,82,152,58,80,152,135,50,80,152,205,162, + 140,254,3,56,16,58,81,152,135,60,230,15, + 50,81,152,175,50,80,152,24,23,58,79,152, + 205,162,140,254,3,48,13,58,81,152,203,63, + 50,81,152,62,255,50,79,152,58,81,152,50, + 82,152,58,79,152,135,50,79,152,62,32,50, + 83,152,50,84,152,237,56,16,230,17,237,57, + 16,219,72,62,192,50,93,152,62,93,50,94, + 152,58,93,152,61,50,93,152,32,9,58,94, + 152,61,50,94,152,40,44,62,170,237,57,20, + 175,237,57,21,237,56,16,246,2,237,57,16, + 219,72,230,1,202,29,141,237,56,20,71,237, + 56,21,120,254,10,48,237,237,56,16,230,17, + 237,57,16,243,62,14,211,66,62,65,211,66, + 251,58,39,152,23,23,60,50,39,152,71,58, + 82,152,160,230,15,40,22,71,14,10,219,66, + 230,16,202,186,141,219,72,230,1,202,186,141, + 13,32,239,16,235,42,89,152,237,91,91,152, + 205,47,131,48,7,61,202,186,141,195,227,141, + 221,225,33,0,0,201,221,33,55,152,205,127, + 142,58,84,152,61,50,84,152,40,19,58,82, + 152,246,1,50,82,152,58,79,152,246,1,50, + 79,152,195,29,141,221,225,33,1,0,201,221, + 33,59,152,205,127,142,58,80,152,246,1,50, + 80,152,58,82,152,135,246,1,50,82,152,58, + 83,152,61,50,83,152,194,29,141,221,225,33, + 2,0,201,221,229,33,0,0,57,17,4,0, + 25,126,50,44,152,230,128,50,85,152,58,85, + 152,183,40,6,221,33,88,2,24,4,221,33, + 150,0,58,44,152,183,40,53,60,40,50,60, + 40,47,61,61,33,86,152,119,35,119,35,54, + 129,175,50,48,152,221,43,221,229,225,124,181, + 40,42,33,86,152,17,3,0,205,189,140,17, + 232,3,27,123,178,32,251,58,48,152,183,40, + 224,58,44,152,71,62,7,128,230,127,71,58, + 85,152,176,50,44,152,24,162,221,225,201,183, + 221,52,0,192,221,52,1,192,221,52,2,192, + 221,52,3,192,55,201,245,62,1,211,100,241, + 201,245,62,1,211,96,241,201,33,2,0,57, + 126,35,102,111,237,56,48,230,175,237,57,48, + 62,48,237,57,49,125,237,57,32,124,237,57, + 33,62,0,237,57,34,62,88,237,57,35,62, + 0,237,57,36,237,57,37,33,128,2,125,237, + 57,38,124,237,57,39,237,56,48,246,97,230, + 207,237,57,48,62,0,237,57,0,62,0,211, + 96,211,100,201,33,2,0,57,126,35,102,111, + 237,56,48,230,175,237,57,48,62,12,237,57, + 49,62,76,237,57,32,62,0,237,57,33,237, + 57,34,125,237,57,35,124,237,57,36,62,0, + 237,57,37,33,128,2,125,237,57,38,124,237, + 57,39,237,56,48,246,97,230,207,237,57,48, + 62,1,211,96,201,33,2,0,57,126,35,102, + 111,229,237,56,48,230,87,237,57,48,125,237, + 57,40,124,237,57,41,62,0,237,57,42,62, + 67,237,57,43,62,0,237,57,44,58,106,137, + 254,1,32,5,33,6,0,24,3,33,128,2, + 125,237,57,46,124,237,57,47,237,56,50,230, + 252,246,2,237,57,50,225,201,33,4,0,57, + 94,35,86,33,2,0,57,126,35,102,111,237, + 56,48,230,87,237,57,48,125,237,57,40,124, + 237,57,41,62,0,237,57,42,62,67,237,57, + 43,62,0,237,57,44,123,237,57,46,122,237, + 57,47,237,56,50,230,244,246,0,237,57,50, + 237,56,48,246,145,230,207,237,57,48,201,213, + 237,56,46,95,237,56,47,87,237,56,46,111, + 237,56,47,103,183,237,82,32,235,33,128,2, + 183,237,82,209,201,213,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,209,201,245, + 197,1,52,0,237,120,230,253,237,121,193,241, + 201,245,197,1,52,0,237,120,246,2,237,121, + 193,241,201,33,2,0,57,126,35,102,111,126, + 35,110,103,201,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,203,124,40,6,33,0,125,34,100, + 152,42,104,152,35,35,35,229,205,120,139,193, + 201,205,186,149,229,42,40,152,35,35,35,229, + 205,39,144,193,124,230,3,103,221,117,254,221, + 116,255,237,91,42,152,35,35,35,183,237,82, + 32,12,17,5,0,42,42,152,205,171,149,242, + 169,144,42,40,152,229,205,120,139,193,195,198, + 149,237,91,42,152,42,98,152,25,34,98,152, + 19,19,19,42,102,152,25,34,102,152,237,91, + 100,152,33,158,253,25,237,91,102,152,205,171, + 149,242,214,144,33,0,0,34,102,152,62,1, + 50,95,152,205,225,144,195,198,149,58,95,152, + 183,200,237,91,96,152,42,102,152,205,171,149, + 242,5,145,237,91,102,152,33,98,2,25,237, + 91,96,152,205,171,149,250,37,145,237,91,96, + 152,42,102,152,183,237,82,32,7,42,98,152, + 125,180,40,13,237,91,102,152,42,96,152,205, + 171,149,242,58,145,237,91,104,152,42,102,152, + 25,35,35,35,229,205,120,139,193,175,50,95, + 152,201,195,107,139,205,206,149,250,255,243,205, + 225,144,251,58,230,149,183,194,198,149,17,1, + 0,42,98,152,205,171,149,250,198,149,62,1, + 50,230,149,237,91,96,152,42,104,152,25,221, + 117,252,221,116,253,237,91,104,152,42,96,152, + 25,35,35,35,221,117,254,221,116,255,35,35, + 35,229,205,39,144,124,230,3,103,35,35,35, + 221,117,250,221,116,251,235,221,110,252,221,102, + 253,115,35,114,35,54,4,62,1,211,100,211, + 84,195,198,149,33,0,0,34,102,152,34,96, + 152,34,98,152,33,202,154,34,104,152,237,91, + 104,152,42,226,149,183,237,82,17,0,255,25, + 34,100,152,33,109,152,54,0,33,107,152,229, + 205,240,142,193,62,47,50,34,152,62,132,50, + 49,152,205,241,145,205,61,145,58,39,152,60, + 50,39,152,24,241,205,206,149,251,255,33,109, + 152,126,183,202,198,149,110,221,117,251,33,109, + 152,54,0,221,126,251,254,1,40,28,254,3, + 40,101,254,4,202,190,147,254,5,202,147,147, + 254,8,40,87,33,107,152,229,205,240,142,195, + 198,149,58,201,154,183,32,21,33,111,152,126, + 50,229,149,205,52,144,33,110,152,110,38,0, + 229,205,11,142,193,237,91,96,152,42,104,152, + 25,221,117,254,221,116,255,35,35,54,2,17, + 2,0,43,43,115,35,114,58,44,152,35,35, + 119,58,228,149,35,119,62,1,211,100,211,84, + 62,1,50,201,154,24,169,205,153,142,58,231, + 149,183,40,250,175,50,231,149,33,110,152,126, + 254,255,40,91,58,233,149,230,63,183,40,83, + 94,22,0,33,234,149,25,126,183,40,13,33, + 110,152,94,33,234,150,25,126,254,3,32,36, + 205,81,148,125,180,33,110,152,94,22,0,40, + 17,33,234,149,25,54,0,33,107,152,229,205, + 240,142,193,195,198,149,33,234,150,25,54,0, + 33,110,152,94,22,0,33,234,149,25,126,50, + 49,152,254,132,32,37,62,47,50,34,152,42, + 107,152,229,33,110,152,229,205,174,140,193,193, + 125,180,33,110,152,94,22,0,33,234,150,202, + 117,147,25,52,195,120,147,58,49,152,254,140, + 32,7,62,1,50,34,152,24,210,62,32,50, + 106,152,24,19,58,49,152,95,58,106,152,163, + 183,58,106,152,32,11,203,63,50,106,152,58, + 106,152,183,32,231,254,2,40,51,254,4,40, + 38,254,8,40,26,254,16,40,13,254,32,32, + 158,62,165,50,49,152,62,69,24,190,62,164, + 50,49,152,62,70,24,181,62,163,50,49,152, + 175,24,173,62,162,50,49,152,62,1,24,164, + 62,161,50,49,152,62,3,24,155,25,54,0, + 221,126,251,254,8,40,7,58,230,149,183,202, + 32,146,33,107,152,229,205,240,142,193,211,84, + 195,198,149,237,91,96,152,42,104,152,25,221, + 117,254,221,116,255,35,35,54,6,17,2,0, + 43,43,115,35,114,58,228,149,35,35,119,58, + 233,149,35,119,205,146,142,195,32,146,237,91, + 96,152,42,104,152,25,229,205,160,142,193,58, + 231,149,183,40,250,175,50,231,149,243,237,91, + 96,152,42,104,152,25,221,117,254,221,116,255, + 78,35,70,221,113,252,221,112,253,89,80,42, + 98,152,183,237,82,34,98,152,203,124,40,19, + 33,0,0,34,98,152,34,102,152,34,96,152, + 62,1,50,95,152,24,40,221,94,252,221,86, + 253,19,19,19,42,96,152,25,34,96,152,237, + 91,100,152,33,158,253,25,237,91,96,152,205, + 171,149,242,55,148,33,0,0,34,96,152,175, + 50,230,149,251,195,32,146,245,62,1,50,231, + 149,62,16,237,57,0,211,80,241,251,237,77, + 201,205,186,149,229,229,33,0,0,34,37,152, + 33,110,152,126,50,234,151,58,44,152,33,235, + 151,119,221,54,253,0,221,54,254,0,195,230, + 148,33,236,151,54,175,33,3,0,229,33,234, + 151,229,205,174,140,193,193,33,236,151,126,254, + 255,40,74,33,245,151,110,221,117,255,33,249, + 151,126,221,166,255,221,119,255,33,253,151,126, + 221,166,255,221,119,255,58,232,149,95,221,126, + 255,163,221,119,255,183,40,15,230,191,33,110, + 152,94,22,0,33,234,149,25,119,24,12,33, + 110,152,94,22,0,33,234,149,25,54,132,33, + 0,0,195,198,149,221,110,253,221,102,254,35, + 221,117,253,221,116,254,17,32,0,221,110,253, + 221,102,254,205,171,149,250,117,148,58,233,149, + 203,87,40,84,33,1,0,34,37,152,221,54, + 253,0,221,54,254,0,24,53,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,14,33,110, + 152,94,22,0,33,234,149,25,54,140,24,159, + 221,110,253,221,102,254,35,221,117,253,221,116, + 254,17,32,0,221,110,253,221,102,254,205,171, + 149,250,12,149,33,2,0,34,37,152,221,54, + 253,0,221,54,254,0,24,54,33,236,151,54, + 175,33,3,0,229,33,234,151,229,205,174,140, + 193,193,33,236,151,126,254,255,40,15,33,110, + 152,94,22,0,33,234,149,25,54,132,195,211, + 148,221,110,253,221,102,254,35,221,117,253,221, + 116,254,17,32,0,221,110,253,221,102,254,205, + 171,149,250,96,149,33,1,0,195,198,149,124, + 170,250,179,149,237,82,201,124,230,128,237,82, + 60,201,225,253,229,221,229,221,33,0,0,221, + 57,233,221,249,221,225,253,225,201,233,225,253, + 229,221,229,221,33,0,0,221,57,94,35,86, + 35,235,57,249,235,233,0,0,0,0,0,0, + 62,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 175,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,133,1,0,0,0,63, + 255,255,255,255,0,0,0,63,0,0,0,0, + 0,0,0,0,0,0,0,24,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0 + } ; + +#endif diff --git a/drivers/net/cops_ltdrv.h b/drivers/net/cops_ltdrv.h new file mode 100644 index 000000000..33f3d9a06 --- /dev/null +++ b/drivers/net/cops_ltdrv.h @@ -0,0 +1,242 @@ +/* + * The firmware this driver downloads into the Localtalk card is a + * seperate program and is not GPL'd source code, even though the Linux + * side driver and the routine that loads this data into the card are. + * + * It is taken from the COPS SDK and is under the following license + * + * This material is licensed to you strictly for use in conjunction with + * the use of COPS LocalTalk adapters. + * There is no charge for this SDK. And no waranty express or implied + * about its fitness for any purpose. However, we will cheerefully + * refund every penny you paid for this SDK... + * Regards, + * + * Thomas F. Divine + * Chief Scientist + */ + + +/* cops_ltdrv.h: LocalTalk driver firmware dump for Linux. + * + * Authors: + * - Jay Schulist <Jay.Schulist@spacs.k12.wi.us> + */ + +#include <linux/config.h> + +#ifdef CONFIG_COPS_TANGENT + +unsigned char ltdrv_code[] = { + 58,3,0,50,148,10,33,143,15,62,85,119, + 190,32,9,62,170,119,190,32,3,35,24,241, + 34,146,10,249,17,150,10,33,143,15,183,237, + 82,77,68,11,107,98,19,54,0,237,176,62, + 16,237,57,51,62,0,237,57,50,237,57,54, + 62,12,237,57,49,62,195,33,39,2,50,56, + 0,34,57,0,237,86,205,30,2,251,205,60, + 10,24,169,67,111,112,121,114,105,103,104,116, + 32,40,99,41,32,49,57,56,56,45,49,57, + 57,50,44,32,80,114,105,110,116,105,110,103, + 32,67,111,109,109,117,110,105,99,97,116,105, + 111,110,115,32,65,115,115,111,99,105,97,116, + 101,115,44,32,73,110,99,46,65,108,108,32, + 114,105,103,104,116,115,32,114,101,115,101,114, + 118,101,100,46,32,32,4,4,22,40,255,60, + 4,96,10,224,6,0,7,126,2,64,11,246, + 12,6,13,0,14,193,15,0,5,96,3,192, + 1,0,9,8,62,3,211,82,62,192,211,82, + 201,62,3,211,82,62,213,211,82,201,62,5, + 211,82,62,224,211,82,201,62,5,211,82,62, + 224,211,82,201,62,5,211,82,62,96,211,82, + 201,6,28,33,180,1,14,82,237,163,194,4, + 2,33,39,2,34,64,0,58,3,0,230,1, + 192,62,11,237,121,62,118,237,121,201,33,182, + 10,54,132,205,253,1,201,245,197,213,229,42, + 150,10,14,83,17,98,2,67,20,237,162,58, + 179,1,95,219,82,230,1,32,6,29,32,247, + 195,17,3,62,1,211,82,219,82,95,230,160, + 32,10,237,162,32,225,21,32,222,195,15,3, + 237,162,123,230,96,194,21,3,62,48,211,82, + 62,1,211,82,175,211,82,237,91,150,10,43, + 55,237,82,218,19,3,34,152,10,98,107,58, + 154,10,190,32,81,62,1,50,158,10,35,35, + 62,132,190,32,44,54,133,43,70,58,154,10, + 119,43,112,17,3,0,205,137,3,62,16,211, + 82,62,56,211,82,205,217,1,42,150,10,14, + 83,17,98,2,67,20,58,178,1,95,195,59, + 2,62,129,190,194,227,2,54,130,43,70,58, + 154,10,119,43,112,17,3,0,205,137,3,195, + 254,2,35,35,126,254,132,194,227,2,205,61, + 3,24,20,62,128,166,194,222,2,221,229,221, + 33,175,10,205,93,6,205,144,7,221,225,225, + 209,193,241,251,237,77,221,229,221,33,159,10, + 205,93,6,221,225,205,61,3,195,247,2,24, + 237,24,235,24,233,230,64,40,2,24,227,24, + 225,175,50,179,10,205,208,1,201,197,33,4, + 0,57,126,35,102,111,205,51,3,193,201,62, + 1,50,179,10,34,150,10,54,0,58,179,10, + 183,200,62,14,211,82,62,193,211,82,62,10, + 211,82,62,224,211,82,62,6,211,82,58,154, + 10,211,82,62,16,211,82,62,56,211,82,62, + 48,211,82,219,82,230,1,40,4,219,83,24, + 242,62,14,211,82,62,33,211,82,62,1,211, + 82,62,9,211,82,62,32,211,82,205,217,1, + 201,14,83,205,208,1,24,23,14,83,205,208, + 1,205,226,1,58,174,1,61,32,253,205,244, + 1,58,174,1,61,32,253,205,226,1,58,175, + 1,61,32,253,62,5,211,82,62,233,211,82, + 62,128,211,82,58,176,1,61,32,253,237,163, + 27,62,192,211,82,219,82,230,4,40,250,237, + 163,27,122,179,32,243,219,82,230,4,40,250, + 58,178,1,71,219,82,230,4,40,3,5,32, + 247,219,82,230,4,40,250,205,235,1,58,177, + 1,61,32,253,205,244,1,201,229,213,35,35, + 126,230,128,194,145,4,43,58,154,10,119,43, + 70,33,181,10,119,43,112,17,3,0,243,62, + 10,211,82,219,82,230,128,202,41,4,209,225, + 62,1,55,251,201,205,144,3,58,180,10,254, + 255,202,127,4,205,217,1,58,178,1,71,219, + 82,230,1,32,6,5,32,247,195,173,4,219, + 83,71,58,154,10,184,194,173,4,58,178,1, + 71,219,82,230,1,32,6,5,32,247,195,173, + 4,219,83,58,178,1,71,219,82,230,1,32, + 6,5,32,247,195,173,4,219,83,254,133,194, + 173,4,58,179,1,24,4,58,179,1,135,61, + 32,253,209,225,205,137,3,205,61,3,183,251, + 201,209,225,243,62,10,211,82,219,82,230,128, + 202,164,4,62,1,55,251,201,205,144,3,205, + 61,3,183,251,201,209,225,62,2,55,251,201, + 243,62,14,211,82,62,33,211,82,251,201,33, + 4,0,57,94,35,86,33,2,0,57,126,35, + 102,111,221,229,34,193,10,237,83,195,10,221, + 33,171,10,205,93,6,58,185,10,50,186,10, + 58,184,10,135,50,184,10,205,112,6,254,3, + 56,16,58,185,10,135,60,230,15,50,185,10, + 175,50,184,10,24,23,58,183,10,205,112,6, + 254,3,48,13,58,185,10,203,63,50,185,10, + 62,255,50,183,10,58,185,10,50,186,10,58, + 183,10,135,50,183,10,62,32,50,187,10,50, + 188,10,6,255,219,82,230,16,32,3,5,32, + 247,205,180,4,6,40,219,82,230,16,40,3, + 5,32,247,62,10,211,82,219,82,230,128,194, + 46,5,219,82,230,16,40,214,237,95,71,58, + 186,10,160,230,15,40,32,71,14,10,62,10, + 211,82,219,82,230,128,202,119,5,205,180,4, + 195,156,5,219,82,230,16,202,156,5,13,32, + 229,16,225,42,193,10,237,91,195,10,205,252, + 3,48,7,61,202,156,5,195,197,5,221,225, + 33,0,0,201,221,33,163,10,205,93,6,58, + 188,10,61,50,188,10,40,19,58,186,10,246, + 1,50,186,10,58,183,10,246,1,50,183,10, + 195,46,5,221,225,33,1,0,201,221,33,167, + 10,205,93,6,58,184,10,246,1,50,184,10, + 58,186,10,135,246,1,50,186,10,58,187,10, + 61,50,187,10,194,46,5,221,225,33,2,0, + 201,221,229,33,0,0,57,17,4,0,25,126, + 50,154,10,230,128,50,189,10,58,189,10,183, + 40,6,221,33,88,2,24,4,221,33,150,0, + 58,154,10,183,40,49,60,40,46,61,33,190, + 10,119,35,119,35,54,129,175,50,158,10,221, + 43,221,229,225,124,181,40,42,33,190,10,17, + 3,0,205,206,4,17,232,3,27,123,178,32, + 251,58,158,10,183,40,224,58,154,10,71,62, + 7,128,230,127,71,58,189,10,176,50,154,10, + 24,166,221,225,201,183,221,52,0,192,221,52, + 1,192,221,52,2,192,221,52,3,192,55,201, + 6,8,14,0,31,48,1,12,16,250,121,201, + 33,2,0,57,94,35,86,35,78,35,70,35, + 126,35,102,105,79,120,68,103,237,176,201,33, + 2,0,57,126,35,102,111,62,17,237,57,48, + 125,237,57,40,124,237,57,41,62,0,237,57, + 42,62,64,237,57,43,62,0,237,57,44,33, + 128,2,125,237,57,46,124,237,57,47,62,145, + 237,57,48,211,68,58,149,10,211,66,201,33, + 2,0,57,126,35,102,111,62,33,237,57,48, + 62,64,237,57,32,62,0,237,57,33,237,57, + 34,125,237,57,35,124,237,57,36,62,0,237, + 57,37,33,128,2,125,237,57,38,124,237,57, + 39,62,97,237,57,48,211,67,58,149,10,211, + 66,201,237,56,46,95,237,56,47,87,237,56, + 46,111,237,56,47,103,183,237,82,32,235,33, + 128,2,183,237,82,201,237,56,38,95,237,56, + 39,87,237,56,38,111,237,56,39,103,183,237, + 82,32,235,33,128,2,183,237,82,201,205,106, + 10,221,110,6,221,102,7,126,35,110,103,195, + 118,10,205,106,10,33,0,0,34,205,10,34, + 198,10,34,200,10,33,143,15,34,207,10,237, + 91,207,10,42,146,10,183,237,82,17,0,255, + 25,34,203,10,203,124,40,6,33,0,125,34, + 203,10,42,207,10,229,205,37,3,195,118,10, + 205,106,10,229,42,150,10,35,35,35,229,205, + 70,7,193,124,230,3,103,221,117,254,221,116, + 255,237,91,152,10,35,35,35,183,237,82,32, + 12,17,5,0,42,152,10,205,91,10,242,203, + 7,42,150,10,229,205,37,3,195,118,10,237, + 91,152,10,42,200,10,25,34,200,10,42,205, + 10,25,34,205,10,237,91,203,10,33,158,253, + 25,237,91,205,10,205,91,10,242,245,7,33, + 0,0,34,205,10,62,1,50,197,10,205,5, + 8,33,0,0,57,249,195,118,10,205,106,10, + 58,197,10,183,202,118,10,237,91,198,10,42, + 205,10,205,91,10,242,46,8,237,91,205,10, + 33,98,2,25,237,91,198,10,205,91,10,250, + 78,8,237,91,198,10,42,205,10,183,237,82, + 32,7,42,200,10,125,180,40,13,237,91,205, + 10,42,198,10,205,91,10,242,97,8,237,91, + 207,10,42,205,10,25,229,205,37,3,175,50, + 197,10,195,118,10,205,29,3,33,0,0,57, + 249,195,118,10,205,106,10,58,202,10,183,40, + 22,205,14,7,237,91,209,10,19,19,19,205, + 91,10,242,139,8,33,1,0,195,118,10,33, + 0,0,195,118,10,205,126,10,252,255,205,108, + 8,125,180,194,118,10,237,91,200,10,33,0, + 0,205,91,10,242,118,10,237,91,207,10,42, + 198,10,25,221,117,254,221,116,255,35,35,35, + 229,205,70,7,193,124,230,3,103,35,35,35, + 221,117,252,221,116,253,229,221,110,254,221,102, + 255,229,33,212,10,229,205,124,6,193,193,221, + 110,252,221,102,253,34,209,10,33,211,10,54, + 4,33,209,10,227,205,147,6,193,62,1,50, + 202,10,243,221,94,252,221,86,253,42,200,10, + 183,237,82,34,200,10,203,124,40,17,33,0, + 0,34,200,10,34,205,10,34,198,10,50,197, + 10,24,37,221,94,252,221,86,253,42,198,10, + 25,34,198,10,237,91,203,10,33,158,253,25, + 237,91,198,10,205,91,10,242,68,9,33,0, + 0,34,198,10,205,5,8,33,0,0,57,249, + 251,195,118,10,205,106,10,33,49,13,126,183, + 40,16,205,42,7,237,91,47,13,19,19,19, + 205,91,10,242,117,9,58,142,15,198,1,50, + 142,15,195,118,10,33,49,13,126,254,1,40, + 25,254,3,202,7,10,254,5,202,21,10,33, + 49,13,54,0,33,47,13,229,205,207,6,195, + 118,10,58,141,15,183,32,72,33,51,13,126, + 50,149,10,205,86,7,33,50,13,126,230,127, + 183,32,40,58,142,15,230,127,50,142,15,183, + 32,5,198,1,50,142,15,33,50,13,126,111, + 23,159,103,203,125,58,142,15,40,5,198,128, + 50,142,15,33,50,13,119,33,50,13,126,111, + 23,159,103,229,205,237,5,193,33,211,10,54, + 2,33,2,0,34,209,10,58,154,10,33,212, + 10,119,58,148,10,33,213,10,119,33,209,10, + 229,205,147,6,193,24,128,42,47,13,229,33, + 50,13,229,205,191,4,193,24,239,33,211,10, + 54,6,33,3,0,34,209,10,58,154,10,33, + 212,10,119,58,148,10,33,213,10,119,33,214, + 10,54,5,33,209,10,229,205,147,6,24,200, + 205,106,10,33,49,13,54,0,33,47,13,229, + 205,207,6,33,209,10,227,205,147,6,193,205, + 80,9,205,145,8,24,248,124,170,250,99,10, + 237,82,201,124,230,128,237,82,60,201,225,253, + 229,221,229,221,33,0,0,221,57,233,221,249, + 221,225,253,225,201,233,225,253,229,221,229,221, + 33,0,0,221,57,94,35,86,35,235,57,249, + 235,233,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0 + } ; + +#endif diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c index e0d71b202..d803e953e 100644 --- a/drivers/net/ibmtr.c +++ b/drivers/net/ibmtr.c @@ -55,7 +55,10 @@ * + completed multiple adapter support. (November 20 1996) * + implemented csum_partial_copy in tr_rx and increased receive * buffer size and count. Minor fixes. (March 15, 1997) -*/ + * + * Changes by Christopher Turcksin <wabbit@rtfc.demon.co.uk> + * + Now compiles ok as a module again. + */ #ifdef PCMCIA #define MODULE @@ -163,9 +166,9 @@ unsigned char ibmtr_debug_trace=0; int ibmtr_probe(struct device *dev); static int ibmtr_probe1(struct device *dev, int ioaddr); -unsigned char get_sram_size(struct tok_info *adapt_info); +static unsigned char get_sram_size(struct tok_info *adapt_info); static int tok_init_card(struct device *dev); -int trdev_init(struct device *dev); +static int trdev_init(struct device *dev); void tok_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void initial_tok_int(struct device *dev); static void open_sap(unsigned char type,struct device *dev); @@ -1583,7 +1586,7 @@ int init_module(void) dev_ibmtr[i]->init = &ibmtr_probe; if (register_trdev(dev_ibmtr[i]) != 0) { - kfree_s(dev_ibmtr[i], sizeof(struct dev)); + kfree_s(dev_ibmtr[i], sizeof(struct device)); dev_ibmtr[i] = NULL; if (i == 0) { printk("ibmtr: register_trdev() returned non-zero.\n"); diff --git a/drivers/net/lapbether.c b/drivers/net/lapbether.c index e282847a9..5f8cb5cca 100644 --- a/drivers/net/lapbether.c +++ b/drivers/net/lapbether.c @@ -407,7 +407,7 @@ static int lapbeth_close(struct device *dev) return 0; } -__initfunc(static int lapbeth_dev_init(struct device *dev)) +static int lapbeth_dev_init(struct device *dev) { return 0; } diff --git a/drivers/net/sdla_fr.c b/drivers/net/sdla_fr.c index d2dc0eb90..95f1ae739 100644 --- a/drivers/net/sdla_fr.c +++ b/drivers/net/sdla_fr.c @@ -10,6 +10,49 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* +* Jun 29, 1997 Alan Cox o Hacked up vendor source 1.0.3 to remove +* C++ style comments, and a massive security +* hole (the UDP management junk). +* +* May 29, 1997 Jaspreet Singh o Fixed major Flow Control Problem +* With multiple boards a problem was seen where +* the second board always stopped transmitting +* packet after running for a while. The code +* got into a stage where the interrupts were +* disabled and dev->tbusy was set to 1. +* This caused the If_send() routine to get into +* the if clause for set_bit(0,dev->tbusy) +* forever. +* The code got into this stage due to an +* interrupt occuring within the if clause for +* set_bit(0,dev->tbusy). Since an interrupt +* disables furhter transmit interrupt and +* makes dev->tbusy = 0, this effect was undone +* by making dev->tbusy = 1 in the if clause. +* The Fix checks to see if Transmit interrupts +* are disabled then do not make dev->tbusy = 1 +* Introduced a global variable: int_occur and +* added tx_int_enabled in the wan_device +* structure. +* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple +* boards. +* +* Apr 25, 1997 Farhan Thawar o added UDP Management stuff +* o fixed bug in if_send() and tx_intr() to +* sleep and wakeup all devices +* Mar 11, 1997 Farhan Thawar Version 3.1.1 +* o fixed (+1) bug in fr508_rx_intr() +* o changed if_send() to return 0 if +* wandev.critical() is true +* o free socket buffer in if_send() if +* returning 0 +* o added tx_intr() routine +* Jan 30, 1997 Gene Kozin Version 3.1.0 +* o implemented exec() entry point +* o fixed a bug causing driver configured as +* a FR switch to be stuck in WAN_DISCONNECTED +* mode * Jan 02, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -22,12 +65,13 @@ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/if_arp.h> /* ARPHRD_* defines */ #include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ #include <asm/io.h> /* for inb(), outb(), etc. */ +#include <asm/uaccess.h> #define _GNUC_ #include <linux/sdla_fr.h> /* frame relay firmware API definitions */ @@ -67,6 +111,9 @@ typedef struct dlci_status unsigned char state PACKED; } dlci_status_t; +static char TracingEnabled; +/* variable for checking interrupts within the ISR routine */ +static int int_occur = 0; /****** Function Prototypes *************************************************/ /* WAN link driver entry points. These are called by the WAN router module. */ @@ -75,14 +122,16 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf); static int del_if (wan_device_t* wandev, struct device* dev); +/* WANPIPE-specific entry points */ +static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data); + /* Network device interface */ static int if_init (struct device* dev); static int if_open (struct device* dev); static int if_close (struct device* dev); static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb); +static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); @@ -103,6 +152,8 @@ static int fr_configure (sdla_t* card, fr_conf_t *conf); static int fr_set_intr_mode (sdla_t* card, unsigned mode, unsigned mtu); static int fr_comm_enable (sdla_t* card); static int fr_comm_disable (sdla_t* card); +static int fr_get_err_stats (sdla_t* card); +static int fr_get_stats (sdla_t* card); static int fr_add_dlci (sdla_t* card, int dlci, int num); static int fr_activate_dlci (sdla_t* card, int dlci, int num); static int fr_issue_isf (sdla_t* card, int isf); @@ -120,7 +171,6 @@ static void set_chan_state (struct device* dev, int state); static struct device* find_channel (sdla_t* card, unsigned dlci); static int is_tx_ready (sdla_t* card); static unsigned int dec_to_uint (unsigned char* str, int len); -static unsigned int hex_to_uint (unsigned char* str, int len); /****** Public Functions ****************************************************/ @@ -198,7 +248,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) u.cfg.n392 = 3; u.cfg.n393 = 4; u.cfg.kbps = conf->bps / 1000; - u.cfg.cir_fwd = max(min(u.cfg.kbps, 512), 1); + u.cfg.cir_fwd = 16; u.cfg.cir_bwd = u.cfg.bc_fwd = u.cfg.bc_bwd = u.cfg.cir_fwd; u.cfg.options = 0x0081; /* direct Rx, no CIR check */ switch (conf->u.fr.signalling) @@ -214,7 +264,7 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) { u.cfg.station = 1; /* switch emulation mode */ card->u.f.node_dlci = conf->u.fr.dlci ? conf->u.fr.dlci : 16; - card->u.f.dlci_num = max(min(conf->u.fr.dlci_num, 1), 100); + card->u.f.dlci_num = min(max(conf->u.fr.dlci_num, 1), 100); } if (conf->clocking == WANOPT_INTERNAL) u.cfg.port |= 0x0001 @@ -270,11 +320,14 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) card->wandev.clocking = conf->clocking; card->wandev.station = conf->station; card->poll = &wpf_poll; + card->exec = &wpf_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; - return 0; + card->wandev.udp_port = conf->udp_port; + TracingEnabled = '0'; + return 0; } /******* WAN Device Driver Entry Points *************************************/ @@ -284,9 +337,22 @@ __initfunc(int wpf_init (sdla_t* card, wandev_conf_t* conf)) */ static int update (wan_device_t* wandev) { -/* - sdla_t* card = wandev->private; -*/ + sdla_t* card; + + /* sanity checks */ + if ((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT + ; + if (wandev->state == WAN_UNCONFIGURED) + return -ENODEV + ; + if (test_and_set_bit(0, (void*)&wandev->critical)) + return -EAGAIN + ; + card = wandev->private; + fr_get_err_stats(card); + fr_get_stats(card); + wandev->critical = 0; return 0; } @@ -377,6 +443,43 @@ static int del_if (wan_device_t* wandev, struct device* dev) return 0; } +/****** WANPIPE-specific entry points ***************************************/ + +/*============================================================================ + * Execute adapter interface command. + */ +static int wpf_exec (struct sdla* card, void* u_cmd, void* u_data) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err, len; + fr_cmd_t cmd; + + if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) + return -EFAULT; + /* execute command */ + do + { + memcpy(&mbox->cmd, &cmd, sizeof(cmd)); + if (cmd.length) + if(copy_from_user((void*)&mbox->data, u_data, cmd.length)) + return -EFAULT; + if (sdla_exec(mbox)) + err = mbox->cmd.result; + else + return -EIO; + } + while (err && retry-- && fr_event(card, err, mbox)); + + /* return result */ + if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(fr_cmd_t))) + return -EFAULT; + len = mbox->cmd.length; + if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; + return 0; +} + /****** Network Device Interface ********************************************/ /*============================================================================ @@ -416,6 +519,9 @@ static int if_init (struct device* dev) dev->mem_start = wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; + /* Set transmit buffer queue length */ + dev->tx_queue_len = 30; + /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) skb_queue_head_init(&dev->buffs[i]) @@ -451,12 +557,14 @@ static int if_open (struct device* dev) err = -EIO; goto done; } + wanpipe_set_state(card, WAN_CONNECTED); + if (card->wandev.station == WANOPT_CPE) { /* CPE: issue full status enquiry */ fr_issue_isf(card, FR_ISF_FSE); } - else /* Switch: activate DLCI(s) */ + else /* FR switch: activate DLCI(s) */ { fr_add_dlci(card, card->u.f.node_dlci, card->u.f.dlci_num) @@ -465,7 +573,6 @@ static int if_open (struct device* dev) card->u.f.node_dlci, card->u.f.dlci_num) ; } - wanpipe_set_state(card, WAN_CONNECTED); } dev->mtu = min(dev->mtu, card->wandev.mtu - FR_HEADER_LEN); dev->interrupt = 0; @@ -538,14 +645,13 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - fr_channel_t* chan = dev->priv; + fr_channel_t* chan = skb->dev->priv; sdla_t* card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name) + card->devname, skb->dev->name) ; return 1; } @@ -570,10 +676,11 @@ static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, */ static int if_send (struct sk_buff* skb, struct device* dev) { - fr_channel_t* chan = dev->priv; - sdla_t* card = chan->card; - int retry = 0; - + fr_channel_t *chan = dev->priv; + sdla_t *card = chan->card; + int retry=0, err; + struct device *dev2; + if (test_and_set_bit(0, (void*)&card->wandev.critical)) { #ifdef _DEBUG_ @@ -581,7 +688,8 @@ static int if_send (struct sk_buff* skb, struct device* dev) card->devname) ; #endif - return 1; + dev_kfree_skb(skb, FREE_WRITE); + return 0; } if (test_and_set_bit(0, (void*)&dev->tbusy)) @@ -592,23 +700,55 @@ static int if_send (struct sk_buff* skb, struct device* dev) ; #endif ++chan->ifstats.collisions; + ++card->wandev.stats.collisions; + retry = 1; + if(card->wandev.tx_int_enabled) + { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { + dev2->tbusy = 1; + } + } + } + else if (card->wandev.state != WAN_CONNECTED) + { + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + } + else if (chan->state != WAN_CONNECTED) + { + update_chan_state(dev); + ++chan->ifstats.tx_dropped; + ++card->wandev.stats.tx_dropped; + } + else if (!is_tx_ready(card)) + { + retry = 1; + if(card->wandev.tx_int_enabled) + { + for (dev2 = card->wandev.dev; dev2; dev2 = dev2->slave) + { + dev2->tbusy = 1; + } + } } - else if ((card->wandev.state != WAN_CONNECTED) || - (chan->state != WAN_CONNECTED)) - ++chan->ifstats.tx_dropped - ; - else if (!is_tx_ready(card)) - retry = 1 - ; else { - int err = (card->hw.fwid == SFID_FR508) ? + err = (card->hw.fwid == SFID_FR508) ? fr508_send(card, chan->dlci, 0, skb->len, skb->data) : fr502_send(card, chan->dlci, 0, skb->len, skb->data) ; - if (err) ++chan->ifstats.tx_errors; - else ++chan->ifstats.tx_packets; + if (err) + { + ++chan->ifstats.tx_errors; + ++card->wandev.stats.tx_errors; + } + else + { + ++chan->ifstats.tx_packets; + ++card->wandev.stats.tx_packets; + } } if (!retry) { @@ -619,6 +759,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) return retry; } + /*============================================================================ * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. @@ -663,7 +804,14 @@ static void fr508_isr (sdla_t* card) { fr508_flags_t* flags = card->flags; fr_buf_ctl_t* bctl; - + + if(int_occur){ +#ifdef _DEBUG_ + printk(KERN_INFO "%s:Interrupt Occurred within an ISR\n",card->devname); +#endif + return; + } + int_occur=1; switch (flags->iflag) { case 0x01: /* receive interrupt */ @@ -681,6 +829,7 @@ static void fr508_isr (sdla_t* card) default: spur_intr(card); } + int_occur = 0; flags->iflag = 0; } @@ -690,13 +839,14 @@ static void fr508_isr (sdla_t* card) static void fr502_rx_intr (sdla_t* card) { fr_mbox_t* mbox = card->rxmb; - struct sk_buff* skb; - struct device* dev; - fr_channel_t* chan; + struct sk_buff *skb; + struct device *dev; + fr_channel_t *chan; unsigned dlci, len; void* buf; - + sdla_mapmem(&card->hw, FR502_RX_VECTOR); + dlci = mbox->cmd.dlci; len = mbox->cmd.length; @@ -741,13 +891,14 @@ static void fr502_rx_intr (sdla_t* card) /* can't decapsulate packet */ dev_kfree_skb(skb, FREE_READ); ++chan->ifstats.rx_errors; + ++card->wandev.stats.rx_errors; } else { netif_rx(skb); ++chan->ifstats.rx_packets; + ++card->wandev.stats.rx_packets; } - rx_done: sdla_mapmem(&card->hw, FR_MB_VECTOR); } @@ -763,7 +914,7 @@ static void fr508_rx_intr (sdla_t* card) fr_channel_t* chan; unsigned dlci, len, offs; void* buf; - + if (frbuf->flag != 0x01) { printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", @@ -804,9 +955,9 @@ static void fr508_rx_intr (sdla_t* card) } /* Copy data to the socket buffer */ - if ((offs + len) > card->u.f.rx_top) + if ((offs + len) > card->u.f.rx_top + 1) { - unsigned tmp = card->u.f.rx_top - offs; + unsigned tmp = card->u.f.rx_top - offs + 1; buf = skb_put(skb, tmp); sdla_peek(&card->hw, offs, buf, tmp); @@ -814,8 +965,7 @@ static void fr508_rx_intr (sdla_t* card) len -= tmp; } buf = skb_put(skb, len); - sdla_peek(&card->hw, offs, buf, len); - + sdla_peek(&card->hw, offs, buf, len); /* Decapsulate packet and pass it up the protocol stack */ skb->dev = dev; buf = skb_pull(skb, 1); /* remove hardware header */ @@ -824,13 +974,14 @@ static void fr508_rx_intr (sdla_t* card) /* can't decapsulate packet */ dev_kfree_skb(skb, FREE_READ); ++chan->ifstats.rx_errors; + ++card->wandev.stats.rx_errors; } else { netif_rx(skb); ++chan->ifstats.rx_packets; + ++card->wandev.stats.rx_packets; } - rx_done: /* Release buffer element and calculate a pointer to the next one */ frbuf->flag = 0; @@ -848,7 +999,17 @@ rx_done: */ static void tx_intr (sdla_t* card) { + struct device* dev = card->wandev.dev; + + for (; dev; dev = dev->slave) { + if( !dev || !dev->start ) continue; + dev->tbusy = 0; + dev_tint(dev); + } + card->wandev.tx_int_enabled = 0; +/* printk(KERN_INFO "%s: transmit interrupt!\n", card->devname); +*/ } /*============================================================================ @@ -877,8 +1038,14 @@ static void spur_intr (sdla_t* card) */ static void wpf_poll (sdla_t* card) { - fr502_flags_t* flags = card->flags; + static unsigned long last_poll; + fr502_flags_t* flags; + if ((jiffies - last_poll) < HZ) + return + ; + + flags = card->flags; if (flags->event) { fr_mbox_t* mbox = card->mbox; @@ -889,6 +1056,7 @@ static void wpf_poll (sdla_t* card) err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; if (err) fr_event(card, err, mbox); } + last_poll = jiffies; } /****** Frame Relay Firmware-Specific Functions *****************************/ @@ -1025,6 +1193,65 @@ static int fr_comm_disable (sdla_t* card) } /*============================================================================ + * Get communications error statistics. + */ +static int fr_get_err_stats (sdla_t* card) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + mbox->cmd.command = FR_READ_ERROR_STATS; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) + { + fr_comm_stat_t* stats = (void*)mbox->data; + + card->wandev.stats.rx_over_errors = stats->rx_overruns; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_aborts; + card->wandev.stats.rx_length_errors = stats->rx_too_long; + card->wandev.stats.tx_aborted_errors = stats->tx_aborts; + } + return err; +} + +/*============================================================================ + * Get statistics. + */ +static int fr_get_stats (sdla_t* card) +{ + fr_mbox_t* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(fr_cmd_t)); + mbox->cmd.command = FR_READ_STATISTICS; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } + while (err && retry-- && fr_event(card, err, mbox)); + + if (!err) + { + fr_link_stat_t* stats = (void*)mbox->data; + + card->wandev.stats.rx_frame_errors = stats->rx_bad_format; + card->wandev.stats.rx_dropped = + stats->rx_dropped + stats->rx_dropped2 + ; + } + return err; +} + +/*============================================================================ * Add DLCI(s) (Access Node only!). */ static int fr_add_dlci (sdla_t* card, int dlci, int num) @@ -1363,6 +1590,7 @@ static int is_tx_ready (sdla_t* card) if (sb & 0x02) return 1; flags->imask |= 0x02; + card->wandev.tx_int_enabled = 1; } else { @@ -1389,27 +1617,4 @@ static unsigned int dec_to_uint (unsigned char* str, int len) return val; } -/*============================================================================ - * Convert hex string to unsigned integer. - * If len != 0 then only 'len' characters of the string are conferted. - */ -static unsigned int hex_to_uint (unsigned char* str, int len) -{ - unsigned val, ch; - - if (!len) len = strlen(str); - for (val = 0; len; ++str, --len) - { - ch = *str; - if (is_digit(ch)) - val = (val << 4) + (ch - (unsigned)'0') - ; - else if (is_hex_digit(ch)) - val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10) - ; - else break; - } - return val; -} - /****** End *****************************************************************/ diff --git a/drivers/net/sdla_ppp.c b/drivers/net/sdla_ppp.c index c5c5ecc55..fa636040b 100644 --- a/drivers/net/sdla_ppp.c +++ b/drivers/net/sdla_ppp.c @@ -10,6 +10,25 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Jun 29, 1997 Alan Cox o Dumped the idiot UDP management system. +* +* May 22, 1997 Jaspreet Singh o Added change in the PPP_SET_CONFIG command for +* 508 card to reflect changes in the new +* ppp508.sfm for supporting:continous transmission +* of Configure-Request packets without receiving a +* reply +* OR-ed 0x300 to conf_flags +* o Changed connect_tmout from 900 to 0 +* May 21, 1997 Jaspreet Singh o Fixed UDP Management for multiple boards +* Apr 25, 1997 Farhan Thawar o added UDP Management stuff +* Mar 11, 1997 Farhan Thawar Version 3.1.1 +* o fixed (+1) bug in rx_intr() +* o changed if_send() to return 0 if +* wandev.critical() is true +* o free socket buffer in if_send() if +* returning 0 +* Jan 15, 1997 Gene Kozin Version 3.1.0 +* o implemented exec() entry point * Jan 06, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -22,11 +41,12 @@ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/if_arp.h> /* ARPHRD_* defines */ #include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ +#include <asm/uaccess.h> #define _GNUC_ #include <linux/sdla_ppp.h> /* PPP firmware API definitions */ @@ -57,14 +77,16 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf); static int del_if (wan_device_t* wandev, struct device* dev); +/* WANPIPE-specific entry points */ +static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data); + /* Network device interface */ static int if_init (struct device* dev); static int if_open (struct device* dev); static int if_close (struct device* dev); static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb); +static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); @@ -74,6 +96,7 @@ static int ppp_configure (sdla_t* card, void* data); static int ppp_set_intr_mode (sdla_t* card, unsigned mode); static int ppp_comm_enable (sdla_t* card); static int ppp_comm_disable (sdla_t* card); +static int ppp_get_err_stats (sdla_t* card); static int ppp_send (sdla_t* card, void* data, unsigned len, unsigned proto); static int ppp_error (sdla_t *card, int err, ppp_mbox_t* mb); @@ -94,6 +117,7 @@ static int config508 (sdla_t* card); static void show_disc_cause (sdla_t* card, unsigned cause); static unsigned char bps_to_speed_code (unsigned long bps); +static char TracingEnabled; /****** Public Functions ****************************************************/ /*============================================================================ @@ -163,10 +187,13 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf)) card->wandev.station = conf->station; card->isr = &wpp_isr; card->poll = &wpp_poll; + card->exec = &wpp_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; card->wandev.state = WAN_DISCONNECTED; + card->wandev.udp_port = conf->udp_port; + TracingEnabled = '0'; return 0; } @@ -177,9 +204,22 @@ __initfunc(int wpp_init (sdla_t* card, wandev_conf_t* conf)) */ static int update (wan_device_t* wandev) { -/* - sdla_t* card = wandev->private; -*/ + sdla_t* card; + + /* sanity checks */ + if ((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT + ; + if (wandev->state == WAN_UNCONFIGURED) + return -ENODEV + ; + if (test_and_set_bit(0, (void*)&wandev->critical)) + return -EAGAIN + ; + card = wandev->private; + + ppp_get_err_stats(card); + wandev->critical = 0; return 0; } @@ -228,6 +268,38 @@ static int del_if (wan_device_t* wandev, struct device* dev) return 0; } +/****** WANPIPE-specific entry points ***************************************/ + +/*============================================================================ + * Execute adapter interface command. + */ +static int wpp_exec (struct sdla* card, void* u_cmd, void* u_data) +{ + ppp_mbox_t* mbox = card->mbox; + int len; + + if(copy_from_user((void*)&mbox->cmd, u_cmd, sizeof(ppp_cmd_t))) + return -EFAULT; + len = mbox->cmd.length; + if (len) + { + if(copy_from_user((void*)&mbox->data, u_data, len)) + return -EFAULT; + } + + /* execute command */ + if (!sdla_exec(mbox)) + return -EIO; + + /* return result */ + if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(ppp_cmd_t))) + return -EFAULT; + len = mbox->cmd.length; + if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; + return 0; +} + /****** Network Device Interface ********************************************/ /*============================================================================ @@ -264,6 +336,9 @@ static int if_init (struct device* dev) dev->mem_start = wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; + /* Set transmit buffer queue length */ + dev->tx_queue_len = 30; + /* Initialize socket buffers */ for (i = 0; i < DEV_NUMBUFFS; ++i) skb_queue_head_init(&dev->buffs[i]) @@ -408,13 +483,12 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - sdla_t* card = dev->priv; + sdla_t* card = skb->dev->priv; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name) + card->devname, skb->dev->name) ; return 1; } @@ -460,8 +534,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) #endif ++card->wandev.stats.collisions; retry = 1; - } - else if (card->wandev.state != WAN_CONNECTED) + } else if (card->wandev.state != WAN_CONNECTED) ++card->wandev.stats.tx_dropped ; else if (!skb->protocol) @@ -489,6 +562,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. */ + static struct enet_statistics* if_stats (struct device* dev) { sdla_t* card = dev->priv; @@ -599,6 +673,31 @@ static int ppp_comm_disable (sdla_t* card) } /*============================================================================ + * Get communications error statistics. + */ +static int ppp_get_err_stats (sdla_t* card) +{ + ppp_mbox_t* mb = card->mbox; + int err; + + memset(&mb->cmd, 0, sizeof(ppp_cmd_t)); + mb->cmd.command = PPP_READ_ERROR_STATS; + err = sdla_exec(mb) ? mb->cmd.result : CMD_TIMEOUT; + if (err == CMD_OK) + { + ppp_err_stats_t* stats = (void*)mb->data; + + card->wandev.stats.rx_over_errors = stats->rx_overrun; + card->wandev.stats.rx_crc_errors = stats->rx_bad_crc; + card->wandev.stats.rx_missed_errors = stats->rx_abort; + card->wandev.stats.rx_length_errors = stats->rx_lost; + card->wandev.stats.tx_aborted_errors = stats->tx_abort; + } + else ppp_error(card, err, mb); + return err; +} + +/*============================================================================ * Send packet. * Return: 0 - o.k. * 1 - no transmit buffers available @@ -701,7 +800,7 @@ static void rx_intr (sdla_t* card) struct sk_buff* skb; unsigned len; void* buf; - + if (rxbuf->flag != 0x01) { printk(KERN_INFO "%s: corrupted Rx buffer @ 0x%X!\n", @@ -738,9 +837,9 @@ static void rx_intr (sdla_t* card) { unsigned addr = rxbuf->buf.ptr; - if ((addr + len) > card->u.p.rx_top) + if ((addr + len) > card->u.p.rx_top + 1) { - unsigned tmp = card->u.p.rx_top - addr; + unsigned tmp = card->u.p.rx_top - addr + 1; buf = skb_put(skb, tmp); sdla_peek(&card->hw, addr, buf, tmp); @@ -751,8 +850,8 @@ static void rx_intr (sdla_t* card) sdla_peek(&card->hw, addr, buf, len); } - /* Decapsulate packet and pass it up the protocol stack */ - switch (rxbuf->proto) + /* Decapsulate packet */ + switch (rxbuf->proto) { case 0x00: skb->protocol = htons(ETH_P_IP); @@ -762,10 +861,11 @@ static void rx_intr (sdla_t* card) skb->protocol = htons(ETH_P_IPX); break; } + + /* Pass it up the protocol stack */ skb->dev = dev; netif_rx(skb); ++card->wandev.stats.rx_packets; - rx_done: /* Release buffer element and calculate a pointer to the next one */ rxbuf->flag = (card->hw.fwid == SFID_PPP502) ? 0xFF : 0x00; @@ -891,13 +991,14 @@ static int config502 (sdla_t* card) cfg.auth_wait_tmr = 300; cfg.mdm_fail_tmr = 5; cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 900; + cfg.connect_tmout = 0; /* changed it from 900 */ cfg.conf_retry = 10; cfg.term_retry = 2; cfg.fail_retry = 5; cfg.auth_retry = 10; cfg.ip_options = 0x80; cfg.ipx_options = 0xA0; + cfg.conf_flags |= 0x0E; /* cfg.ip_local = dev->pa_addr; cfg.ip_remote = dev->pa_dstaddr; @@ -921,6 +1022,7 @@ static int config508 (sdla_t* card) if (card->wandev.interface == WANOPT_RS232) cfg.conf_flags |= 0x0020; ; + cfg.conf_flags |= 0x300; /*send Configure-Request packets forever*/ cfg.txbuf_percent = 60; /* % of Tx bufs */ cfg.mtu_local = card->wandev.mtu; cfg.mtu_remote = card->wandev.mtu; @@ -929,7 +1031,7 @@ static int config508 (sdla_t* card) cfg.auth_wait_tmr = 300; cfg.mdm_fail_tmr = 5; cfg.dtr_drop_tmr = 1; - cfg.connect_tmout = 900; + cfg.connect_tmout = 0; /* changed it from 900 */ cfg.conf_retry = 10; cfg.term_retry = 2; cfg.fail_retry = 5; diff --git a/drivers/net/sdla_x25.c b/drivers/net/sdla_x25.c index 3296c2819..77dfc43b0 100644 --- a/drivers/net/sdla_x25.c +++ b/drivers/net/sdla_x25.c @@ -10,6 +10,17 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ +* Mar 11, 1997 Farhan Thawar Version 3.1.1 +* o added support for V35 +* o changed if_send() to return 0 if +* wandev.critical() is true +* o free socket buffer in if_send() if +* returning 0 +* o added support for single '@' address to +* accept all incoming calls +* o fixed bug in set_chan_state() to disconnect +* Jan 15, 1997 Gene Kozin Version 3.1.0 +* o implemented exec() entry point * Jan 07, 1997 Gene Kozin Initial version. *****************************************************************************/ @@ -22,10 +33,11 @@ #include <linux/errno.h> /* return codes */ #include <linux/string.h> /* inline memset(), etc. */ #include <linux/malloc.h> /* kmalloc(), kfree() */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ #include <linux/init.h> /* __initfunc et al. */ #include <asm/byteorder.h> /* htons(), etc. */ +#include <asm/uaccess.h> /* copy_from_user, etc */ #define _GNUC_ #include <linux/sdla_x25.h> /* X.25 firmware API definitions */ @@ -90,14 +102,16 @@ static int new_if (wan_device_t* wandev, struct device* dev, wanif_conf_t* conf); static int del_if (wan_device_t* wandev, struct device* dev); +/* WANPIPE-specific entry points */ +static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data); + /* Network device interface */ static int if_init (struct device* dev); static int if_open (struct device* dev); static int if_close (struct device* dev); static int if_header (struct sk_buff* skb, struct device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb); +static int if_rebuild_hdr (struct sk_buff* skb); static int if_send (struct sk_buff* skb, struct device* dev); static struct enet_statistics* if_stats (struct device* dev); @@ -118,6 +132,8 @@ static void poll_active (sdla_t* card); /* X.25 firmware interface functions */ static int x25_get_version (sdla_t* card, char* str); static int x25_configure (sdla_t* card, TX25Config* conf); +static int x25_get_err_stats (sdla_t* card); +static int x25_get_stats (sdla_t* card); static int x25_set_intr_mode (sdla_t* card, int mode); static int x25_close_hdlc (sdla_t* card); static int x25_open_hdlc (sdla_t* card); @@ -235,7 +251,9 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) { u.cfg.station = 0; /* DCE mode */ } - + if (conf->interface != WANOPT_RS232 ) { + u.cfg.hdlcOptions |= 0x80; /* V35 mode */ + } /* adjust MTU */ if (!conf->mtu || (conf->mtu >= 1024)) card->wandev.mtu = 1024 @@ -299,6 +317,7 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) card->wandev.station = conf->station; card->isr = &wpx_isr; card->poll = &wpx_poll; + card->exec = &wpx_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; @@ -313,9 +332,23 @@ __initfunc(int wpx_init (sdla_t* card, wandev_conf_t* conf)) */ static int update (wan_device_t* wandev) { -/* - sdla_t* card = wandev->private; -*/ + sdla_t* card; + + /* sanity checks */ + if ((wandev == NULL) || (wandev->private == NULL)) + return -EFAULT + ; + if (wandev->state == WAN_UNCONFIGURED) + return -ENODEV + ; + if (test_and_set_bit(0, (void*)&wandev->critical)) + return -EAGAIN + ; + card = wandev->private; + + x25_get_err_stats(card); + x25_get_stats(card); + wandev->critical = 0; return 0; } @@ -412,6 +445,45 @@ static int del_if (wan_device_t* wandev, struct device* dev) return 0; } +/****** WANPIPE-specific entry points ***************************************/ + +/*============================================================================ + * Execute adapter interface command. + */ +static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data) +{ + TX25Mbox* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err, len; + TX25Cmd cmd; + + if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd))) + return -EFAULT; + + /* execute command */ + do + { + memcpy(&mbox->cmd, &cmd, sizeof(cmd)); + if (cmd.length) + if(copy_from_user((void*)&mbox->data, u_data, cmd.length)) + return -EFAULT; + if (sdla_exec(mbox)) + err = mbox->cmd.result; + else + return -EIO; + } + while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn)); + + /* return result */ + if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd))) + return -EFAULT; + len = mbox->cmd.length; + if (len && u_data) + if(copy_to_user(u_data, (void*)&mbox->data, len)) + return -EFAULT; + return 0; +} + /****** Network Device Interface ********************************************/ /*============================================================================ @@ -555,14 +627,13 @@ static int if_header (struct sk_buff* skb, struct device* dev, * Return: 1 physical address resolved. * 0 physical address not resolved */ -static int if_rebuild_hdr (void* hdr, struct device* dev, unsigned long raddr, - struct sk_buff* skb) +static int if_rebuild_hdr (struct sk_buff* skb) { - x25_channel_t* chan = dev->priv; + x25_channel_t* chan = skb->dev->priv; sdla_t* card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", - card->devname, dev->name) + card->devname, skb->dev->name) ; return 1; } @@ -588,7 +659,7 @@ static int if_send (struct sk_buff* skb, struct device* dev) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; - int retry = 0, queued = 0; + int queued = 0; if (test_and_set_bit(0, (void*)&card->wandev.critical)) { @@ -597,7 +668,8 @@ static int if_send (struct sk_buff* skb, struct device* dev) card->devname) ; #endif - return 1; + dev_kfree_skb(skb, FREE_WRITE); + return 0; } if (test_and_set_bit(0, (void*)&dev->tbusy)) @@ -608,7 +680,10 @@ static int if_send (struct sk_buff* skb, struct device* dev) ; #endif ++chan->ifstats.collisions; - retry = 1; + ++card->wandev.stats.collisions; + dev_kfree_skb(skb, FREE_WRITE); + card->wandev.critical = 0; + return 0; } else if (chan->protocol && (chan->protocol != skb->protocol)) { @@ -646,13 +721,13 @@ static int if_send (struct sk_buff* skb, struct device* dev) ++chan->ifstats.tx_dropped; } - if (!retry && !queued) + if (!queued) { dev_kfree_skb(skb, FREE_WRITE); dev->tbusy = 0; } card->wandev.critical = 0; - return retry; + return 0; } /*============================================================================ @@ -996,6 +1071,62 @@ static int x25_configure (sdla_t* card, TX25Config* conf) } /*============================================================================ + * Get communications error statistics. + */ +static int x25_get_err_stats (sdla_t* card) +{ + TX25Mbox* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(TX25Cmd)); + mbox->cmd.command = X25_HDLC_READ_COMM_ERR; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } while (err && retry-- && + x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0)) + ; + if (!err) + { + THdlcCommErr* stats = (void*)mbox->data; + + card->wandev.stats.rx_over_errors = stats->rxOverrun; + card->wandev.stats.rx_crc_errors = stats->rxBadCrc; + card->wandev.stats.rx_missed_errors = stats->rxAborted; + card->wandev.stats.tx_aborted_errors = stats->txAborted; + } + return err; +} + +/*============================================================================ + * Get protocol statistics. + */ +static int x25_get_stats (sdla_t* card) +{ + TX25Mbox* mbox = card->mbox; + int retry = MAX_CMD_RETRY; + int err; + + do + { + memset(&mbox->cmd, 0, sizeof(TX25Cmd)); + mbox->cmd.command = X25_READ_STATISTICS; + err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; + } while (err && retry-- && + x25_error(card, err, X25_READ_STATISTICS, 0)) + ; + if (!err) + { + TX25Stats* stats = (void*)mbox->data; + + card->wandev.stats.rx_packets = stats->rxData; + card->wandev.stats.tx_packets = stats->rxData; + } + return err; +} + +/*============================================================================ * Close HDLC link. */ static int x25_close_hdlc (sdla_t* card) @@ -1478,6 +1609,10 @@ static int incomming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb) if (strcmp(info->src, chan->addr) == 0) break ; + // If just an '@' is specified, accept all incomming calls + if (strcmp(chan->addr, "") == 0) + break + ; } if (dev == NULL) @@ -1755,9 +1890,10 @@ static void set_chan_state (struct device* dev, int state) printk (KERN_INFO "%s: interface %s disconnected!\n", card->devname, dev->name) ; - if (chan->svc) - *(unsigned short*)dev->dev_addr = 0 - ; + if (chan->svc) { + *(unsigned short*)dev->dev_addr = 0; + chan->lcn = 0; + } break; } chan->state = state; diff --git a/drivers/net/sdlamain.c b/drivers/net/sdlamain.c index 75d7df944..eeb0b40f3 100644 --- a/drivers/net/sdlamain.c +++ b/drivers/net/sdlamain.c @@ -26,9 +26,10 @@ #include <linux/module.h> /* support for loadable modules */ #include <linux/ioport.h> /* request_region(), release_region() */ #include <linux/tqueue.h> /* for kernel task queues */ -#include <linux/router.h> /* WAN router definitions */ +#include <linux/wanrouter.h> /* WAN router definitions */ #include <linux/wanpipe.h> /* WANPIPE common user API definitions */ -#include <asm/segment.h> /* kernel <-> user copy */ +#include <asm/uaccess.h> /* kernel <-> user copy */ + /****** Defines & Macros ****************************************************/ @@ -404,20 +405,13 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump) unsigned long oldvec; /* DPM window vector */ int err = 0; - if ((u_dump == NULL) || - verify_area(VERIFY_READ, u_dump, sizeof(sdla_dump_t))) - return -EFAULT - ; - memcpy_fromfs((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t)); + if(copy_from_user((void*)&dump, (void*)u_dump, sizeof(sdla_dump_t))) + return -EFAULT; + if ((dump.magic != WANPIPE_MAGIC) || (dump.offset + dump.length > card->hw.memory)) - return -EINVAL - ; - if ((dump.ptr == NULL) || - verify_area(VERIFY_WRITE, dump.ptr, dump.length)) - return -EFAULT - ; - + return -EINVAL; + winsize = card->hw.dpmsize; cli(); /* >>> critical section start <<< */ oldvec = card->hw.vector; @@ -433,9 +427,12 @@ static int ioctl_dump (sdla_t* card, sdla_dump_t* u_dump) err = -EIO; break; } - memcpy_tofs((void*)(dump.ptr), - (void*)(card->hw.dpmbase + pos), len) - ; + /* FIXME::: COPY TO KERNEL BUFFER FIRST ?? */ + sti(); /* Not ideal but tough we have to do this */ + if(copy_to_user((void*)(dump.ptr), + (void*)(card->hw.dpmbase + pos), len)) + return -EFAULT; + cli(); dump.length -= len; dump.offset += len; (char*)dump.ptr += len; @@ -456,16 +453,12 @@ static int ioctl_exec (sdla_t* card, sdla_exec_t* u_exec) sdla_exec_t exec; if (card->exec == NULL) - return -ENODEV - ; - if ((u_exec == NULL) || - verify_area(VERIFY_READ, u_exec, sizeof(sdla_exec_t))) - return -EFAULT - ; - memcpy_fromfs((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t)); + return -ENODEV; + + if(copy_from_user((void*)&exec, (void*)u_exec, sizeof(sdla_exec_t))) + return -EFAULT; if ((exec.magic != WANPIPE_MAGIC) || (exec.cmd == NULL)) - return -EINVAL - ; + return -EINVAL; return card->exec(card, exec.cmd, exec.data); } diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index a40f16599..fff408c42 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -70,11 +70,11 @@ #include <linux/if_arp.h> #include <linux/init.h> #include <net/dst.h> -#include "shaper.h" +#include <linux/if_shaper.h> int sh_debug; /* Debug flag */ -#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.03 for Linux 2.1\n" +#define SHAPER_BANNER "CymruNet Traffic Shaper BETA 0.04 for Linux 2.1\n" /* * Locking @@ -426,17 +426,26 @@ static int shaper_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { struct shaper *sh=dev->priv; + int v; if(sh_debug) printk("Shaper header\n"); - return sh->hard_header(skb,sh->dev,type,daddr,saddr,len); + skb->dev=sh->dev; + v=sh->hard_header(skb,sh->dev,type,daddr,saddr,len); + skb->dev=dev; + return v; } static int shaper_rebuild_header(struct sk_buff *skb) { struct shaper *sh=skb->dev->priv; + struct device *dev=skb->dev; + int v; if(sh_debug) printk("Shaper rebuild header\n"); - return sh->rebuild_header(skb); + skb->dev=sh->dev; + v=sh->rebuild_header(skb); + skb->dev=dev; + return v; } static int shaper_cache(struct dst_entry *dst, struct neighbour *neigh, struct hh_cache *hh) @@ -483,6 +492,7 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device else shdev->rebuild_header = NULL; +#if 0 if(dev->hard_header_cache) { sh->hard_header_cache = dev->hard_header_cache; @@ -500,6 +510,10 @@ static int shaper_attach(struct device *shdev, struct shaper *sh, struct device } else shdev->header_cache_update= NULL; +#else + shdev->header_cache_update = NULL; + shdev->hard_header_cache = NULL; +#endif shdev->hard_header_len=dev->hard_header_len; shdev->type=dev->type; diff --git a/drivers/net/shaper.h b/drivers/net/shaper.h deleted file mode 100644 index abb6198af..000000000 --- a/drivers/net/shaper.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef __LINUX_SHAPER_H -#define __LINUX_SHAPER_H - -#ifdef __KERNEL__ - -#define SHAPER_QLEN 10 -/* - * This is a bit speed dependant (read it shouldnt be a constant!) - * - * 5 is about right for 28.8 upwards. Below that double for every - * halving of speed or so. - ie about 20 for 9600 baud. - */ -#define SHAPER_LATENCY (5*HZ) -#define SHAPER_MAXSLIP 2 -#define SHAPER_BURST (HZ/50) /* Good for >128K then */ - -struct shaper -{ - struct sk_buff_head sendq; - __u32 bytespertick; - __u32 shapelatency; - __u32 shapeclock; - __u32 recovery; /* Time we can next clock a packet out on - an empty queue */ - char locked; - struct device *dev; - int (*hard_start_xmit) (struct sk_buff *skb, - struct device *dev); - int (*hard_header) (struct sk_buff *skb, - struct device *dev, - unsigned short type, - void *daddr, - void *saddr, - unsigned len); - int (*rebuild_header)(struct sk_buff *skb); - int (*hard_header_cache)(struct dst_entry *dst, struct neighbour *neigh, - struct hh_cache *hh); - void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr); - struct net_device_stats* (*get_stats)(struct device *dev); - struct wait_queue *wait_queue; - struct timer_list timer; -}; - -#endif - -#define SHAPER_SET_DEV 0x0001 -#define SHAPER_SET_SPEED 0x0002 - -struct shaperconf -{ - __u16 ss_cmd; - union - { - char ssu_name[14]; - __u32 ssu_speed; - } ss_u; -#define ss_speed ss_u.ssu_speed -#define ss_name ss_u.ssu_name -}; - -#endif diff --git a/drivers/net/strip.c b/drivers/net/strip.c index 32c02b4f0..0104f6dc9 100644 --- a/drivers/net/strip.c +++ b/drivers/net/strip.c @@ -14,53 +14,62 @@ * for kernel-based devices like TTY. It interfaces between a * raw TTY, and the kernel's INET protocol layers (via DDI). * - * Version: @(#)strip.c 0.9.8 June 1996 + * Version: @(#)strip.c 1.2 February 1997 * * Author: Stuart Cheshire <cheshire@cs.stanford.edu> * - * Fixes: v0.9 12th Feb 1996. + * Fixes: v0.9 12th Feb 1996 (SC) * New byte stuffing (2+6 run-length encoding) * New watchdog timer task * New Protocol key (SIP0) * - * v0.9.1 3rd March 1996 + * v0.9.1 3rd March 1996 (SC) * Changed to dynamic device allocation -- no more compile * time (or boot time) limit on the number of STRIP devices. * - * v0.9.2 13th March 1996 + * v0.9.2 13th March 1996 (SC) * Uses arp cache lookups (but doesn't send arp packets yet) * - * v0.9.3 17th April 1996 + * v0.9.3 17th April 1996 (SC) * Fixed bug where STR_ERROR flag was getting set unneccessarily + * (causing otherwise good packets to be unneccessarily dropped) * - * v0.9.4 27th April 1996 + * v0.9.4 27th April 1996 (SC) * First attempt at using "&COMMAND" Starmode AT commands * - * v0.9.5 29th May 1996 + * v0.9.5 29th May 1996 (SC) * First attempt at sending (unicast) ARP packets * - * v0.9.6 5th June 1996 - * Elliot put "message level" tags in every "printk" statement + * v0.9.6 5th June 1996 (Elliot) + * Put "message level" tags in every "printk" statement * - * v0.9.7 13th June 1996 - * Added support for the /proc fs (laik) + * v0.9.7 13th June 1996 (laik) + * Added support for the /proc fs * - * v0.9.8 July 1996 - * Added packet logging (Mema) - */ - -/* - * Undefine this symbol if you don't have PROC_NET_STRIP_STATUS - * defined in include/linux/proc_fs.h + * v0.9.8 July 1996 (Mema) + * Added packet logging + * + * v1.0 November 1996 (SC) + * Fixed (severe) memory leaks in the /proc fs code + * Fixed race conditions in the logging code + * + * v1.1 January 1997 (SC) + * Deleted packet logging (use tcpdump instead) + * Added support for Metricom Firmware v204 features + * (like message checksums) + * + * v1.2 January 1997 (SC) + * Put portables list back in */ -#define DO_PROC_NET_STRIP_STATUS 1 - -/* - * Define this symbol if you want to enable STRIP packet tracing. - */ +#ifdef MODULE +static const char StripVersion[] = "1.2-STUART.CHESHIRE-MODULAR"; +#else +static const char StripVersion[] = "1.2-STUART.CHESHIRE"; +#endif -#define DO_PROC_NET_STRIP_TRACE 0 +#define TICKLE_TIMERS 0 +#define EXT_COUNTERS 1 /************************************************************************/ @@ -146,13 +155,19 @@ typedef struct __u8 c[24]; } MetricomAddressString; +/* Encapsulation can expand packet of size x to 65/64x + 1 + * Sent packet looks like "<CR>*<address>*<key><encaps payload><CR>" + * 1 1 1-18 1 4 ? 1 + * eg. <CR>*0000-1234*SIP0<encaps payload><CR> + * We allow 31 bytes for the stars, the key, the address and the <CR>s + */ +#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L) + /* - * Note: A Metricom packet looks like this: *<address>*<key><payload><CR> - * eg. *0000-1234*SIP0<payload><CR> - * A STRIP_Header is never really sent over the radio, but making a dummy header - * for internal use within the kernel that looks like an Ethernet header makes - * certain other software happier. For example, tcpdump already understands - * Ethernet headers. + * A STRIP_Header is never really sent over the radio, but making a dummy + * header for internal use within the kernel that looks like an Ethernet + * header makes certain other software happier. For example, tcpdump + * already understands Ethernet headers. */ typedef struct @@ -162,83 +177,20 @@ typedef struct unsigned short protocol; /* The protocol type, using Ethernet codes */ } STRIP_Header; -typedef struct GeographicLocation -{ - char s[18]; -} GeographicLocation; - -typedef enum { - NodeValid = 0x1, - NodeHasWAN = 0x2, - NodeIsRouter = 0x4 -} NodeType; - -typedef struct MetricomNode -{ - NodeType type; /* Some flags about the type of node */ - GeographicLocation gl; /* The location of the node. */ - MetricomAddress addr; /* The metricom address of this node */ - int poll_latency; /* The latency to poll that node ? */ - int rssi; /* The Receiver Signal Strength Indicator */ - struct MetricomNode *next; /* The next node */ -} MetricomNode; - -enum { FALSE = 0, TRUE = 1 }; - -/* - * Holds the packet signature for an IP packet. - */ typedef struct { - IPaddr src; - /* Data is stored in the following field in network byte order. */ - __u16 id; -} IPSignature; + char c[60]; +} MetricomNode; -/* - * Holds the packet signature for an ARP packet. - */ +#define NODE_TABLE_SIZE 32 typedef struct { - IPaddr src; - /* Data is stored in the following field in network byte order. */ - __u16 op; -} ARPSignature; - -/* - * Holds the signature of a packet. - */ -typedef union -{ - IPSignature ip_sig; - ARPSignature arp_sig; - __u8 print_sig[6]; -} PacketSignature; - -typedef enum { - EntrySend = 0, - EntryReceive = 1 -} LogEntry; - -/* Structure for Packet Logging */ -typedef struct stripLog -{ - LogEntry entry_type; - u_long seqNum; - int packet_type; - PacketSignature sig; - MetricomAddress src; - MetricomAddress dest; - struct timeval timeStamp; - u_long rawSize; - u_long stripSize; - u_long slipSize; - u_long valid; -} StripLog; + struct timeval timestamp; + int num_nodes; + MetricomNode node[NODE_TABLE_SIZE]; +} MetricomNodeTable; -#define ENTRY_TYPE_TO_STRING(X) ((X) ? "r" : "s") - -#define BOOLEAN_TO_STRING(X) ((X) ? "true" : "false") +enum { FALSE = 0, TRUE = 1 }; /* * Holds the radio's firmware version. @@ -246,7 +198,7 @@ typedef struct stripLog typedef struct { char c[50]; -} MetricomFirmwareVersion; +} FirmwareVersion; /* * Holds the radio's serial number. @@ -254,7 +206,7 @@ typedef struct typedef struct { char c[18]; -} MetricomSerialNumber; +} SerialNumber; /* * Holds the radio's battery voltage. @@ -262,7 +214,19 @@ typedef struct typedef struct { char c[11]; -} MetricomBatteryVoltage; +} BatteryVoltage; + +typedef struct +{ + char c[8]; +} char8; + +enum +{ + NoStructure = 0, /* Really old firmware */ + StructuredMessages = 1, /* Parsable AT response msgs */ + ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */ +} FirmwareLevel; struct strip { @@ -292,6 +256,25 @@ struct strip unsigned long tx_dropped; /* When MTU change */ unsigned long rx_over_errors; /* Frame bigger then STRIP buf. */ + unsigned long pps_timer; /* Timer to determine pps */ + unsigned long rx_pps_count; /* Counter to determine pps */ + unsigned long tx_pps_count; /* Counter to determine pps */ + unsigned long sx_pps_count; /* Counter to determine pps */ + unsigned long rx_average_pps; /* rx packets per second * 8 */ + unsigned long tx_average_pps; /* tx packets per second * 8 */ + unsigned long sx_average_pps; /* sent packets per second * 8 */ + +#ifdef EXT_COUNTERS + unsigned long rx_bytes; /* total received bytes */ + unsigned long tx_bytes; /* total received bytes */ + unsigned long rx_rbytes; /* bytes thru radio i/f */ + unsigned long tx_rbytes; /* bytes thru radio i/f */ + unsigned long rx_sbytes; /* tot bytes thru serial i/f */ + unsigned long tx_sbytes; /* tot bytes thru serial i/f */ + unsigned long rx_ebytes; /* tot stat/err bytes */ + unsigned long tx_ebytes; /* tot stat/err bytes */ +#endif + /* * Internal variables. */ @@ -300,52 +283,142 @@ struct strip struct strip **referrer; /* The pointer that points to us*/ int discard; /* Set if serial error */ int working; /* Is radio working correctly? */ - int structured_messages; /* Parsable AT response msgs? */ + int firmware_level; /* Message structuring level */ + int next_command; /* Next periodic command */ int mtu; /* Our mtu (to spot changes!) */ long watchdog_doprobe; /* Next time to test the radio */ long watchdog_doreset; /* Time to do next reset */ long gratuitous_arp; /* Time to send next ARP refresh*/ long arp_interval; /* Next ARP interval */ struct timer_list idle_timer; /* For periodic wakeup calls */ - MetricomNode *neighbor_list; /* The list of neighbor nodes */ - int neighbor_list_locked; /* Indicates the list is locked */ - MetricomFirmwareVersion firmware_version; /* The radio's firmware version */ - MetricomSerialNumber serial_number; /* The radio's serial number */ - MetricomBatteryVoltage battery_voltage; /* The radio's battery voltage */ + MetricomAddress true_dev_addr; /* True address of radio */ + int manual_dev_addr; /* Hack: See note below */ + + FirmwareVersion firmware_version; /* The radio's firmware version */ + SerialNumber serial_number; /* The radio's serial number */ + BatteryVoltage battery_voltage; /* The radio's battery voltage */ /* * Other useful structures. */ struct tty_struct *tty; /* ptr to TTY structure */ - char if_name[8]; /* Dynamically generated name */ + char8 if_name; /* Dynamically generated name */ struct device dev; /* Our device structure */ /* - * Packet Logging Structures. + * Neighbour radio records */ - u_long num_sent; - u_long num_received; - - int next_entry; /* The index of the oldest packet; */ - /* Also the next to be logged. */ - StripLog packetLog[610]; + MetricomNodeTable portables; + MetricomNodeTable poletops; }; +/* + * Note: manual_dev_addr hack + * + * It is not possible to change the hardware address of a Metricom radio, + * or to send packets with a user-specified hardware source address, thus + * trying to manually set a hardware source address is a questionable + * thing to do. However, if the user *does* manually set the hardware + * source address of a STRIP interface, then the kernel will believe it, + * and use it in certain places. For example, the hardware address listed + * by ifconfig will be the manual address, not the true one. + * (Both addresses are listed in /proc/net/strip.) + * Also, ARP packets will be sent out giving the user-specified address as + * the source address, not the real address. This is dangerous, because + * it means you won't receive any replies -- the ARP replies will go to + * the specified address, which will be some other radio. The case where + * this is useful is when that other radio is also connected to the same + * machine. This allows you to connect a pair of radios to one machine, + * and to use one exclusively for inbound traffic, and the other + * exclusively for outbound traffic. Pretty neat, huh? + * + * Here's the full procedure to set this up: + * + * 1. "slattach" two interfaces, e.g. st0 for outgoing packets, + * and st1 for incoming packets + * + * 2. "ifconfig" st0 (outbound radio) to have the hardware address + * which is the real hardware address of st1 (inbound radio). + * Now when it sends out packets, it will masquerade as st1, and + * replies will be sent to that radio, which is exactly what we want. + * + * 3. Set the route table entry ("route add default ..." or + * "route add -net ...", as appropriate) to send packets via the st0 + * interface (outbound radio). Do not add any route which sends packets + * out via the st1 interface -- that radio is for inbound traffic only. + * + * 4. "ifconfig" st1 (inbound radio) to have hardware address zero. + * This tells the STRIP driver to "shut down" that interface and not + * send any packets through it. In particular, it stops sending the + * periodic gratuitous ARP packets that a STRIP interface normally sends. + * Also, when packets arrive on that interface, it will search the + * interface list to see if there is another interface who's manual + * hardware address matches its own real address (i.e. st0 in this + * example) and if so it will transfer ownership of the skbuff to + * that interface, so that it looks to the kernel as if the packet + * arrived on that interface. This is necessary because when the + * kernel sends an ARP packet on st0, it expects to get a reply on + * st0, and if it sees the reply come from st1 then it will ignore + * it (to be accurate, it puts the entry in the ARP table, but + * labelled in such a way that st0 can't use it). + * + * Thanks to Petros Maniatis for coming up with the idea of splitting + * inbound and outbound traffic between two interfaces, which turned + * out to be really easy to implement, even if it is a bit of a hack. + * + * Having set a manual address on an interface, you can restore it + * to automatic operation (where the address is automatically kept + * consistent with the real address of the radio) by setting a manual + * address of all ones, e.g. "ifconfig st0 hw strip FFFFFFFFFFFF" + * This 'turns off' manual override mode for the device address. + * + * Note: The IEEE 802 headers reported in tcpdump will show the *real* + * radio addresses the packets were sent and received from, so that you + * can see what is really going on with packets, and which interfaces + * they are really going through. + */ + /************************************************************************/ /* Constants */ -#ifdef MODULE -static const char StripVersion[] = "0.9.8-STUART.CHESHIRE-MODULAR"; -#else -static const char StripVersion[] = "0.9.8-STUART.CHESHIRE"; -#endif +/* + * CommandString1 works on all radios + * Other CommandStrings are only used with firmware that provides structured responses. + * + * ats319=1 Enables Info message for node additions and deletions + * ats319=2 Enables Info message for a new best node + * ats319=4 Enables checksums + * ats319=8 Enables ACK messages + */ + +static const int MaxCommandStringLength = 32; +static const int CompatibilityCommand = 1; -static const char TickleString1[] = "***&COMMAND*ATS305?\r"; -static const char TickleString2[] = "***&COMMAND*ATS305?\r\r" - "*&COMMAND*ATS300?\r\r*&COMMAND*ATS325?\r\r*&COMMAND*AT~I2 nn\r\r"; +static const char CommandString0[] = "*&COMMAND*ATS319=7"; /* Turn on checksums & info messages */ +static const char CommandString1[] = "*&COMMAND*ATS305?"; /* Query radio name */ +static const char CommandString2[] = "*&COMMAND*ATS325?"; /* Query battery voltage */ +static const char CommandString3[] = "*&COMMAND*ATS300?"; /* Query version information */ +static const char CommandString4[] = "*&COMMAND*ATS311?"; /* Query poletop list */ +static const char CommandString5[] = "*&COMMAND*AT~LA"; /* Query portables list */ +typedef struct { const char *string; long length; } StringDescriptor; + +static const StringDescriptor CommandString[] = + { + { CommandString0, sizeof(CommandString0)-1 }, + { CommandString1, sizeof(CommandString1)-1 }, + { CommandString2, sizeof(CommandString2)-1 }, + { CommandString3, sizeof(CommandString3)-1 }, + { CommandString4, sizeof(CommandString4)-1 }, + { CommandString5, sizeof(CommandString5)-1 } + }; + +#define GOT_ALL_RADIO_INFO(S) \ + ((S)->firmware_version.c[0] && \ + (S)->battery_voltage.c[0] && \ + memcmp(&(S)->true_dev_addr, zero_address.c, sizeof(zero_address))) static const char hextable[16] = "0123456789ABCDEF"; @@ -354,26 +427,27 @@ static const MetricomAddress broadcast_address = { { 0xFF,0xFF,0xFF,0xFF,0xFF,0x static const MetricomKey SIP0Key = { { "SIP0" } }; static const MetricomKey ARP0Key = { { "ARP0" } }; -static const MetricomKey ERR_Key = { { "ERR_" } }; static const MetricomKey ATR_Key = { { "ATR " } }; +static const MetricomKey ACK_Key = { { "ACK_" } }; +static const MetricomKey INF_Key = { { "INF_" } }; +static const MetricomKey ERR_Key = { { "ERR_" } }; static const long MaxARPInterval = 60 * HZ; /* One minute */ /* - * Maximum Starmode packet length (including starmode address) is 1183 bytes. - * Allowing 32 bytes for header, and 65/64 expansion for STRIP encoding, - * that translates to a maximum payload MTU of 1132. + * Maximum Starmode packet length is 1183 bytes. Allowing 4 bytes for + * protocol key, 4 bytes for checksum, one byte for CR, and 65/64 expansion + * for STRIP encoding, that translates to a maximum payload MTU of 1155. + * Note: A standard NFS 1K data packet is a total of 0x480 (1152) bytes + * long, including IP header, UDP header, and NFS header. Setting the STRIP + * MTU to 1152 allows us to send default sized NFS packets without fragmentation. */ -static const unsigned short MAX_STRIP_MTU = 1132; -static const unsigned short DEFAULT_STRIP_MTU = 1024; +static const unsigned short MAX_SEND_MTU = 1152; +static const unsigned short MAX_RECV_MTU = 1500; /* Hoping for Ethernet sized packets in the future! */ +static const unsigned short DEFAULT_STRIP_MTU = 1152; static const int STRIP_MAGIC = 0x5303; static const long LongTime = 0x7FFFFFFF; -static const int STRIP_NODE_LEN = 64; -static const char STRIP_PORTABLE_CHAR = 'P'; -static const char STRIP_ROUTER_CHAR = 'r'; -static const int STRIP_PROC_BUFFER_SIZE = 4096; -static const int STRIP_LOG_INT_SIZE = 10; /************************************************************************/ /* Global variables */ @@ -384,10 +458,18 @@ static struct strip *struct_strip_list = NULL; /************************************************************************/ /* Macros */ +/* Returns TRUE if text T begins with prefix P */ +#define has_prefix(T,P) (!strncmp((T), (P), sizeof(P)-1)) + +/* Returns TRUE if text T of length L is equal to string S */ +#define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1)) + #define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' : \ (X)>='a' && (X)<='f' ? (X)-'a'+10 : \ (X)>='A' && (X)<='F' ? (X)-'A'+10 : 0 ) +#define READHEX16(X) ((__u16)(READHEX(X))) + #define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0) #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) @@ -395,17 +477,6 @@ static struct strip *struct_strip_list = NULL; #define ELEMENTS_OF(X) (sizeof(X) / sizeof((X)[0])) #define ARRAY_END(X) (&((X)[ELEMENTS_OF(X)])) -/* Encapsulation can expand packet of size x to 65/64x + 1 */ -/* Sent packet looks like "*<address>*<key><encaps payload><CR>" */ -/* 1 1-18 1 4 ? 1 */ -/* We allow 31 bytes for the stars, the key, the address and the <CR> */ -#define STRIP_ENCAP_SIZE(X) (32 + (X)*65L/64L) - -#define IS_RADIO_ADDRESS(p) ( \ - isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ - (p)[4] == '-' && \ - isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) - #define JIFFIE_TO_SEC(X) ((X) / HZ) @@ -605,7 +676,15 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr) } /* else, we only have one so far, so switch to Stuff_Diff code */ code = Stuff_Diff; - /* and fall through to Stuff_Diff case below */ + /* and fall through to Stuff_Diff case below + * Note cunning cleverness here: case Stuff_Diff compares + * the current character with the previous two to see if it + * has a run of three the same. Won't this be an error if + * there aren't two previous characters stored to compare with? + * No. Because we know the current character is *not* the same + * as the previous one, the first test below will necessarily + * fail and the send half of the "if" won't be executed. + */ /* Stuff_Diff: We have at least two *different* bytes encoded */ case Stuff_Diff: @@ -639,10 +718,10 @@ static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr) src++; /* Consume the byte */ break; } - if (count == Stuff_MaxCount) - { - StuffData_FinishBlock(code + count); - } + if (count == Stuff_MaxCount) + { + StuffData_FinishBlock(code + count); + } } if (code == Stuff_NoCode) { @@ -758,14 +837,21 @@ static __u8 *UnStuffData(__u8 *src, __u8 *end, __u8 *dst, __u32 dst_length) * Convert a string to a Metricom Address. */ -static void string_to_radio_address(MetricomAddress *addr, __u8 *p) +#define IS_RADIO_ADDRESS(p) ( \ + isdigit((p)[0]) && isdigit((p)[1]) && isdigit((p)[2]) && isdigit((p)[3]) && \ + (p)[4] == '-' && \ + isdigit((p)[5]) && isdigit((p)[6]) && isdigit((p)[7]) && isdigit((p)[8]) ) + +static int string_to_radio_address(MetricomAddress *addr, __u8 *p) { + if (!IS_RADIO_ADDRESS(p)) return(1); addr->c[0] = 0; addr->c[1] = 0; addr->c[2] = READHEX(p[0]) << 4 | READHEX(p[1]); addr->c[3] = READHEX(p[2]) << 4 | READHEX(p[3]); addr->c[4] = READHEX(p[5]) << 4 | READHEX(p[6]); addr->c[5] = READHEX(p[7]) << 4 | READHEX(p[8]); + return(0); } /* @@ -780,19 +866,18 @@ static __u8 *radio_address_to_string(const MetricomAddress *addr, MetricomAddres /* * Note: Must make sure sx_size is big enough to receive a stuffed - * MAX_STRIP_MTU packet. Additionally, we also want to ensure that it's + * MAX_RECV_MTU packet. Additionally, we also want to ensure that it's * big enough to receive a large radio neighbour list (currently 4K). */ static int allocate_buffers(struct strip *strip_info) { struct device *dev = &strip_info->dev; - int stuffedlen = STRIP_ENCAP_SIZE(dev->mtu); - int sx_size = MAX(stuffedlen, 4096); - int tx_size = stuffedlen + sizeof(TickleString2); - __u8 *r = kmalloc(MAX_STRIP_MTU, GFP_ATOMIC); - __u8 *s = kmalloc(sx_size, GFP_ATOMIC); - __u8 *t = kmalloc(tx_size, GFP_ATOMIC); + int sx_size = MAX(STRIP_ENCAP_SIZE(MAX_RECV_MTU), 4096); + int tx_size = STRIP_ENCAP_SIZE(dev->mtu) + MaxCommandStringLength; + __u8 *r = kmalloc(MAX_RECV_MTU, GFP_ATOMIC); + __u8 *s = kmalloc(sx_size, GFP_ATOMIC); + __u8 *t = kmalloc(tx_size, GFP_ATOMIC); if (r && s && t) { strip_info->rx_buff = r; @@ -824,10 +909,10 @@ static void strip_changedmtu(struct strip *strip_info) unsigned char *otbuff = strip_info->tx_buff; InterruptStatus intstat; - if (dev->mtu > MAX_STRIP_MTU) + if (dev->mtu > MAX_SEND_MTU) { printk(KERN_ERR "%s: MTU exceeds maximum allowable (%d), MTU change cancelled.\n", - strip_info->dev.name, MAX_STRIP_MTU); + strip_info->dev.name, MAX_SEND_MTU); dev->mtu = old_mtu; return; } @@ -856,9 +941,8 @@ static void strip_changedmtu(struct strip *strip_info) memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count); else { - strip_info->sx_count = 0; + strip_info->discard = strip_info->sx_count; strip_info->rx_over_errors++; - strip_info->discard = 1; } } @@ -889,7 +973,7 @@ static void strip_unlock(struct strip *strip_info) /* * Set the time to go off in one second. */ - strip_info->idle_timer.expires = jiffies + HZ; + strip_info->idle_timer.expires = jiffies + 1*HZ; add_timer(&strip_info->idle_timer); if (!test_and_clear_bit(0, (void *)&strip_info->dev.tbusy)) printk(KERN_ERR "%s: trying to unlock already unlocked device!\n", @@ -900,8 +984,6 @@ static void strip_unlock(struct strip *strip_info) /************************************************************************/ /* Callback routines for exporting information through /proc */ -#if DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE - /* * This function updates the total amount of data printed so far. It then * determines if the amount of data printed into a buffer has reached the @@ -912,29 +994,29 @@ static void strip_unlock(struct strip *strip_info) */ static int shift_buffer(char *buffer, int requested_offset, int requested_len, - int *total, int *slop, char **buf) + int *total, int *slop, char **buf) { int printed; /* printk(KERN_DEBUG "shift: buffer: %d o: %d l: %d t: %d buf: %d\n", - (int) buffer, requested_offset, requested_len, *total, - (int) *buf); */ + (int) buffer, requested_offset, requested_len, *total, + (int) *buf); */ printed = *buf - buffer; if (*total + printed <= requested_offset) { - *total += printed; - *buf = buffer; + *total += printed; + *buf = buffer; } else { - if (*total < requested_offset) { - *slop = requested_offset - *total; - } - *total = requested_offset + printed - *slop; + if (*total < requested_offset) { + *slop = requested_offset - *total; + } + *total = requested_offset + printed - *slop; } if (*total > requested_offset + requested_len) { - return 1; + return 1; } else { - return 0; + return 0; } } @@ -945,12 +1027,12 @@ shift_buffer(char *buffer, int requested_offset, int requested_len, */ static int calc_start_len(char *buffer, char **start, int requested_offset, - int requested_len, int total, char *buf) + int requested_len, int total, char *buf) { int return_len, buffer_len; buffer_len = buf - buffer; - if (buffer_len >= STRIP_PROC_BUFFER_SIZE - 1) { + if (buffer_len >= 4095) { printk(KERN_ERR "STRIP: exceeded /proc buffer size\n"); } @@ -960,20 +1042,16 @@ calc_start_len(char *buffer, char **start, int requested_offset, */ return_len = total - requested_offset; if (return_len < 0) { - return_len = 0; + return_len = 0; } *start = buf - return_len; if (return_len > requested_len) { - return_len = requested_len; + return_len = requested_len; } /* printk(KERN_DEBUG "return_len: %d\n", return_len); */ return return_len; } -#endif DO_PROC_NET_STRIP_STATUS | DO_PROC_NET_STRIP_TRACE - -#if DO_PROC_NET_STRIP_STATUS - /* * If the time is in the near future, time_delta prints the number of * seconds to go into the buffer and returns the address of the buffer. @@ -991,537 +1069,171 @@ static char *time_delta(char buffer[], long time) return(buffer); } -/* - * This function prints radio status information into the specified - * buffer. - */ -static int -sprintf_status_info(char *buffer, struct strip *strip_info) -{ - char temp_buffer[32]; - MetricomAddressString addr_string; - char *buf; - - buf = buffer; - buf += sprintf(buf, "Interface name\t\t%s\n", strip_info->if_name); - buf += sprintf(buf, " Radio working:\t\t%s\n", - strip_info->working && - (long)jiffies - strip_info->watchdog_doreset < 0 ? "Yes" : "No"); - (void) radio_address_to_string((MetricomAddress *) - &strip_info->dev.dev_addr, - &addr_string); - buf += sprintf(buf, " Device address:\t%s\n", addr_string.c); - buf += sprintf(buf, " Firmware version:\t%s\n", - !strip_info->working ? "Unknown" : - !strip_info->structured_messages ? "Should be upgraded" : - strip_info->firmware_version.c); - buf += sprintf(buf, " Serial number:\t\t%s\n", strip_info->serial_number.c); - buf += sprintf(buf, " Battery voltage:\t%s\n", strip_info->battery_voltage.c); - buf += sprintf(buf, " Transmit queue (bytes):%d\n", strip_info->tx_left); - buf += sprintf(buf, " Next watchdog probe:\t%s\n", - time_delta(temp_buffer, strip_info->watchdog_doprobe)); - buf += sprintf(buf, " Next watchdog reset:\t%s\n", - time_delta(temp_buffer, strip_info->watchdog_doreset)); - buf += sprintf(buf, " Next gratuitous ARP:\t%s\n", - time_delta(temp_buffer, strip_info->gratuitous_arp)); - buf += sprintf(buf, " Next ARP interval:\t%ld seconds\n", - JIFFIE_TO_SEC(strip_info->arp_interval)); - return buf - buffer; -} - -static int -sprintf_portables(char *buffer, struct strip *strip_info) -{ - - MetricomAddressString addr_string; - MetricomNode *node; - char *buf; - - buf = buffer; - buf += sprintf(buf, " portables: name\t\tpoll_latency\tsignal strength\n"); - for (node = strip_info->neighbor_list; node != NULL; - node = node->next) { - if (!(node->type & NodeValid)) { - break; - } - if (node->type & NodeHasWAN) { - continue; - } - (void) radio_address_to_string(&node->addr, &addr_string); - buf += sprintf(buf, " %s\t\t\t\t%d\t\t%d\n", - addr_string.c, node->poll_latency, node->rssi); - } - return buf - buffer; -} - -static int -sprintf_poletops(char *buffer, struct strip *strip_info) -{ - MetricomNode *node; - char *buf; - - buf = buffer; - buf += sprintf(buf, " poletops: GPS\t\t\tpoll_latency\tsignal strength\n"); - for (node = strip_info->neighbor_list; - node != NULL; node = node->next) { - if (!(node->type & NodeValid)) { - break; - } - if (!(node->type & NodeHasWAN)) { - continue; - } - buf += sprintf(buf, " %s\t\t\t%d\t\t%d\n", - node->gl.s, node->poll_latency, node->rssi); - } - return buf - buffer; -} - -/* - * This function is exports status information from the STRIP driver through - * the /proc file system. /proc filesystem should be fixed: - * 1) slow (sprintfs here, a memory copy in the proc that calls this one) - * 2) length of buffer not passed - * 3) dummy isn't client data set when the callback was registered - * 4) poorly documented (this function is called until the requested amount - * of data is returned, buffer is only 4K long, dummy is the permissions - * of the file (?), the proc_dir_entry passed to proc_net_register must - * be kmalloc-ed) - */ - -static int -strip_get_status_info(char *buffer, char **start, off_t requested_offset, - int requested_len, int dummy) +static int sprintf_neighbours(char *buffer, MetricomNodeTable *table, char *title) { - char *buf; - int total = 0, slop = 0, len_exceeded; - InterruptStatus i_status; - struct strip *strip_info; - - buf = buffer; - buf += sprintf(buf, "strip_version: %s\n", StripVersion); - - i_status = DisableInterrupts(); - strip_info = struct_strip_list; - RestoreInterrupts(i_status); - - while (strip_info != NULL) { - i_status = DisableInterrupts(); - buf += sprintf_status_info(buf, strip_info); - RestoreInterrupts(i_status); - len_exceeded = shift_buffer(buffer, requested_offset, requested_len, - &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - strip_info->neighbor_list_locked = TRUE; - buf += sprintf_portables(buf, strip_info); - strip_info->neighbor_list_locked = FALSE; - len_exceeded = shift_buffer(buffer, requested_offset, requested_len, - &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - strip_info->neighbor_list_locked = TRUE; - buf += sprintf_poletops(buf, strip_info); - strip_info->neighbor_list_locked = FALSE; - len_exceeded = shift_buffer(buffer, requested_offset, requested_len, - &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - strip_info = strip_info->next; - } -done: - return calc_start_len(buffer, start, requested_offset, requested_len, - total, buf); + /* We wrap this in a do/while loop, so if the table changes */ + /* while we're reading it, we just go around and try again. */ + struct timeval t; + char *ptr; + do + { + int i; + t = table->timestamp; + ptr = buffer; + if (table->num_nodes) ptr += sprintf(ptr, "\n %s\n", title); + for (i=0; i<table->num_nodes; i++) + { + InterruptStatus intstat = DisableInterrupts(); + MetricomNode node = table->node[i]; + RestoreInterrupts(intstat); + ptr += sprintf(ptr, " %s\n", node.c); + } + } while (table->timestamp.tv_sec != t.tv_sec || table->timestamp.tv_usec != t.tv_usec); + return ptr - buffer; } -#endif DO_PROC_NET_STRIP_STATUS - -#if DO_PROC_NET_STRIP_TRACE - /* - * Convert an Ethernet protocol to a string - * Returns the number of characters printed. + * This function prints radio status information into the specified buffer. + * I think the buffer size is 4K, so this routine should never print more + * than 4K of data into it. With the maximum of 32 portables and 32 poletops + * reported, the routine outputs 3107 bytes into the buffer. */ - -static int protocol_to_string(int protocol, __u8 *p) -{ - int printed; - - switch (protocol) { - case ETH_P_IP: - printed = sprintf(p, "IP"); - break; - case ETH_P_ARP: - printed = sprintf(p, "ARP"); - break; - default: - printed = sprintf(p, "%d", protocol); - } - return printed; -} - static int -sprintf_log_entry(char *buffer, struct strip *strip_info, int packet_index) +sprintf_status_info(char *buffer, struct strip *strip_info) { - StripLog *entry; + char temp[32]; + char *p = buffer; MetricomAddressString addr_string; - __u8 sig_buf[24], *s; - char *buf, proto_buf[10]; - - entry = &strip_info->packetLog[packet_index]; - if (!entry->valid) { - return 0; - } - buf = buffer; - buf += sprintf(buf, "%-4s %s %7lu ", strip_info->if_name, - ENTRY_TYPE_TO_STRING(entry->entry_type), entry->seqNum); - (void) protocol_to_string(entry->packet_type, proto_buf); - buf += sprintf(buf, "%-4s", proto_buf); - s = entry->sig.print_sig; - sprintf(sig_buf, "%d.%d.%d.%d.%d.%d", s[0], s[1], s[2], s[3], s[4], s[5]); - buf += sprintf(buf, "%-24s", sig_buf); - (void) radio_address_to_string((MetricomAddress *) &entry->src, - &addr_string); - buf += sprintf(buf, "%-10s", addr_string.c); - (void) radio_address_to_string((MetricomAddress *) &entry->dest, - &addr_string); - buf += sprintf(buf, "%-10s", addr_string.c); - buf += sprintf(buf, "%8d %6d %5lu %6lu %5lu\n", entry->timeStamp.tv_sec, - entry->timeStamp.tv_usec, entry->rawSize, - entry->stripSize, entry->slipSize); - return buf - buffer; -} - -/* - * This function exports trace information from the STRIP driver through the - * /proc file system. - */ -static int -strip_get_trace_info(char *buffer, char **start, off_t requested_offset, - int requested_len, int dummy) -{ - char *buf; - int len_exceeded, total = 0, slop = 0, packet_index, oldest; - InterruptStatus i_status; - struct strip *strip_info; - - buf = buffer; - buf += sprintf(buf, "if s/r seqnum t signature "); - buf += sprintf(buf, - "src dest sec usec raw strip slip\n"); - - i_status = DisableInterrupts(); - strip_info = struct_strip_list; - oldest = strip_info->next_entry; - RestoreInterrupts(i_status); - - /* - * If we disable interrupts for this entire loop, - * characters from the serial port could be lost, - * so we only disable interrupts when accessing - * a log entry. If more than STRIP_LOG_INT_SIZE - * packets are logged before the first entry is - * printed, then some of the entries could be - * printed out of order. - */ - while (strip_info != NULL) { - for (packet_index = oldest + STRIP_LOG_INT_SIZE; - packet_index != oldest; - packet_index = (packet_index + 1) % - ELEMENTS_OF(strip_info->packetLog)) { - i_status = DisableInterrupts(); - buf += sprintf_log_entry(buf, strip_info, packet_index); - RestoreInterrupts(i_status); - len_exceeded = shift_buffer(buffer, requested_offset, - requested_len, &total, &slop, &buf); - if (len_exceeded) { - goto done; - } - } - strip_info = strip_info->next; - } -done: - return calc_start_len(buffer, start, requested_offset, requested_len, - total, buf); -} + /* First, we must copy all of our data to a safe place, */ + /* in case a serial interrupt comes in and changes it. */ + InterruptStatus intstat = DisableInterrupts(); + int tx_left = strip_info->tx_left; + unsigned long rx_average_pps = strip_info->rx_average_pps; + unsigned long tx_average_pps = strip_info->tx_average_pps; + unsigned long sx_average_pps = strip_info->sx_average_pps; + int working = strip_info->working; + int firmware_level = strip_info->firmware_level; + long watchdog_doprobe = strip_info->watchdog_doprobe; + long watchdog_doreset = strip_info->watchdog_doreset; + long gratuitous_arp = strip_info->gratuitous_arp; + long arp_interval = strip_info->arp_interval; + FirmwareVersion firmware_version = strip_info->firmware_version; + SerialNumber serial_number = strip_info->serial_number; + BatteryVoltage battery_voltage = strip_info->battery_voltage; + char8 if_name = strip_info->if_name; + MetricomAddress true_dev_addr = strip_info->true_dev_addr; + MetricomAddress dev_dev_addr = *(MetricomAddress*)strip_info->dev.dev_addr; + int manual_dev_addr = strip_info->manual_dev_addr; +#ifdef EXT_COUNTERS + unsigned long rx_bytes = strip_info->rx_bytes; + unsigned long tx_bytes = strip_info->tx_bytes; + unsigned long rx_rbytes = strip_info->rx_rbytes; + unsigned long tx_rbytes = strip_info->tx_rbytes; + unsigned long rx_sbytes = strip_info->rx_sbytes; + unsigned long tx_sbytes = strip_info->tx_sbytes; + unsigned long rx_ebytes = strip_info->rx_ebytes; + unsigned long tx_ebytes = strip_info->tx_ebytes; +#endif + RestoreInterrupts(intstat); -static int slip_len(unsigned char *data, int len) -{ - static const unsigned char SLIP_END=0300; /* indicates end of SLIP frame */ - static const unsigned char SLIP_ESC=0333; /* indicates SLIP byte stuffing */ - int count = len; - while (--len >= 0) + p += sprintf(p, "\nInterface name\t\t%s\n", if_name.c); + p += sprintf(p, " Radio working:\t\t%s\n", working ? "Yes" : "No"); + radio_address_to_string(&true_dev_addr, &addr_string); + p += sprintf(p, " Radio address:\t\t%s\n", addr_string.c); + if (manual_dev_addr) { - if (*data == SLIP_END || *data == SLIP_ESC) count++; - data++; - } - return(count); -} - -/* Copied from kernel/sched.c */ -static void jiffiestotimeval(unsigned long jiffies, struct timeval *value) -{ - value->tv_usec = (jiffies % HZ) * (1000000.0 / HZ); - value->tv_sec = jiffies / HZ; - return; -} - -/* - * This function logs a packet. - * A pointer to the packet itself is passed so that some of the data can be - * used to compute a signature. The pointer should point the the - * part of the packet following the STRIP_header. - */ - -static void packet_log(struct strip *strip_info, __u8 *packet, - LogEntry entry_type, STRIP_Header *hdr, - int raw_size, int strip_size, int slip_size) -{ - StripLog *entry; - struct iphdr *iphdr; - struct arphdr *arphdr; - - entry = &strip_info->packetLog[strip_info->next_entry]; - if (entry_type == EntrySend) { - entry->seqNum = strip_info->num_sent++; + radio_address_to_string(&dev_dev_addr, &addr_string); + p += sprintf(p, " Device address:\t%s\n", addr_string.c); + } + p += sprintf(p, " Firmware version:\t%s", !working ? "Unknown" : + !firmware_level ? "Should be upgraded" : + firmware_version.c); + if (firmware_level >= ChecksummedMessages) p += sprintf(p, " (Checksums Enabled)"); + p += sprintf(p, "\n"); + p += sprintf(p, " Serial number:\t\t%s\n", serial_number.c); + p += sprintf(p, " Battery voltage:\t%s\n", battery_voltage.c); + p += sprintf(p, " Transmit queue (bytes):%d\n", tx_left); + p += sprintf(p, " Receive packet rate: %ld packets per second\n", rx_average_pps / 8); + p += sprintf(p, " Transmit packet rate: %ld packets per second\n", tx_average_pps / 8); + p += sprintf(p, " Sent packet rate: %ld packets per second\n", sx_average_pps / 8); + p += sprintf(p, " Next watchdog probe:\t%s\n", time_delta(temp, watchdog_doprobe)); + p += sprintf(p, " Next watchdog reset:\t%s\n", time_delta(temp, watchdog_doreset)); + p += sprintf(p, " Next gratuitous ARP:\t"); + + if (!memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address))) + p += sprintf(p, "Disabled\n"); + else + { + p += sprintf(p, "%s\n", time_delta(temp, gratuitous_arp)); + p += sprintf(p, " Next ARP interval:\t%ld seconds\n", JIFFIE_TO_SEC(arp_interval)); } - else { - entry->seqNum = strip_info->num_received++; - } - entry->entry_type = entry_type; - entry->packet_type = ntohs(hdr->protocol); - switch (entry->packet_type) { - case ETH_P_IP: - /* - * The signature for IP is the sender's ip address and - * the identification field. - */ - iphdr = (struct iphdr *) packet; - entry->sig.ip_sig.id = iphdr->id; - entry->sig.ip_sig.src.l = iphdr->saddr; - break; - case ETH_P_ARP: - /* - * The signature for ARP is the sender's ip address and - * the operation. - */ - arphdr = (struct arphdr *) packet; - entry->sig.arp_sig.op = arphdr->ar_op; - memcpy(&entry->sig.arp_sig.src.l, packet + 8 + arphdr->ar_hln, - sizeof(entry->sig.arp_sig.src.l)); - entry->sig.arp_sig.src.l = entry->sig.arp_sig.src.l; - break; - default: - printk(KERN_DEBUG "STRIP: packet_log: unknown packet type: %d\n", - entry->packet_type); - break; - } - memcpy(&entry->src, &hdr->src_addr, sizeof(MetricomAddress)); - memcpy(&entry->dest, &hdr->dst_addr, sizeof(MetricomAddress)); - - jiffiestotimeval(jiffies, &(entry->timeStamp)); - entry->rawSize = raw_size; - entry->stripSize = strip_size; - entry->slipSize = slip_size; - entry->valid = 1; - - strip_info->next_entry = (strip_info->next_entry + 1) % - ELEMENTS_OF(strip_info->packetLog); -} -#endif DO_PROC_NET_STRIP_TRACE + if (working) + { +#ifdef EXT_COUNTERS + p += sprintf(p, "\n"); + p += sprintf(p, " Total bytes: \trx:\t%lu\ttx:\t%lu\n", rx_bytes, tx_bytes); + p += sprintf(p, " thru radio: \trx:\t%lu\ttx:\t%lu\n", rx_rbytes, tx_rbytes); + p += sprintf(p, " thru serial port: \trx:\t%lu\ttx:\t%lu\n", rx_sbytes, tx_sbytes); + p += sprintf(p, " Total stat/err bytes:\trx:\t%lu\ttx:\t%lu\n", rx_ebytes, tx_ebytes); +#endif + p += sprintf_neighbours(p, &strip_info->poletops, "Poletops:"); + p += sprintf_neighbours(p, &strip_info->portables, "Portables:"); + } -/* - * This function parses the response to the ATS300? command, - * extracting the radio version and serial number. - */ -static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end) -{ - __u8 *p, *value_begin, *value_end; - int len; - - /* Determine the beginning of the second line of the payload */ - p = ptr; - while (p < end && *p != 10) p++; - if (p >= end) return; - p++; - value_begin = p; - - /* Determine the end of line */ - while (p < end && *p != 10) p++; - if (p >= end) return; - value_end = p; - p++; - - len = value_end - value_begin; - len = MIN(len, sizeof(MetricomFirmwareVersion) - 1); - sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin); - - /* Look for the first colon */ - while (p < end && *p != ':') p++; - if (p >= end) return; - /* Skip over the space */ - p += 2; - len = sizeof(MetricomSerialNumber) - 1; - if (p + len <= end) { - sprintf(strip_info->serial_number.c, "%.*s", len, p); - } - else { - printk(KERN_ERR "STRIP: radio serial number shorter (%d) than expected (%d)\n", - end - p, len); - } + return p - buffer; } /* - * This function parses the response to the ATS325? command, - * extracting the radio battery voltage. + * This function is exports status information from the STRIP driver through + * the /proc file system. */ -static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end) -{ - int len; - len = sizeof(MetricomBatteryVoltage) - 1; - if (ptr + len <= end) { - sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr); - } - else { - printk(KERN_ERR "STRIP: radio voltage string shorter (%d) than expected (%d)\n", - end - ptr, len); - } -} - -/* - * This function parses the response to the AT~I2 command, - * which gives the names of the radio's nearest neighbors. - * It relies on the format of the response. - */ -static void get_radio_neighbors(struct strip *strip_info, __u8 *ptr, __u8 *end) +static int get_status_info(char *buffer, char **start, off_t req_offset, int req_len, int dummy) { - __u8 *p, *line_begin; - int num_nodes_reported, num_nodes_counted; - MetricomNode *node, *last; - - /* Check if someone is reading the list */ - if (strip_info->neighbor_list_locked) { - return; - } - - /* Determine the number of Nodes */ - p = ptr; - num_nodes_reported = simple_strtoul(p, NULL, 10); - /* printk(KERN_DEBUG "num_nodes: %d\n", num_nodes_reported); */ - - /* Determine the beginning of the next line */ - while (p < end && *p != 10) p++; - if (p >= end) return; - p++; + int total = 0, slop = 0; + struct strip *strip_info = struct_strip_list; + char *buf = buffer; - /* - * The node list should never be empty because we allocate one empty - * node when the strip_info is allocated. The nodes which were allocated - * when the number of neighbors was high but are no longer needed because - * there aren't as many neighbors any more are marked invalid. Invalid nodes - * are kept at the end of the list. - */ - node = strip_info->neighbor_list; - last = node; - if (node == NULL) { - DumpData("Neighbor list is NULL:", strip_info, p, end); - return; - } - line_begin = p; - num_nodes_counted = 0; - while (line_begin < end) { - /* Check to see if the format is what we expect. */ - if ((line_begin + STRIP_NODE_LEN) > end) { - printk(KERN_ERR "STRIP: radio neighbor node string shorter (%d) than expected (%d)\n", - end - line_begin, STRIP_NODE_LEN); - break; - } - - /* Get a node */ - if (node == NULL) { - node = kmalloc(sizeof(MetricomNode), GFP_ATOMIC); - node->next = NULL; - } - node->type = NodeValid; - - /* Fill the node in */ - - /* Determine if it has a GPS location and fill it in if it does. */ - p = line_begin; - /* printk(KERN_DEBUG "node: %64s\n", p); */ - if (p[0] != STRIP_PORTABLE_CHAR) { - node->type |= NodeHasWAN; - sprintf(node->gl.s, "%.*s", (int) sizeof(GeographicLocation) - 1, p); - } - - /* Determine if it is a router */ - p = line_begin + 18; - if (p[0] == STRIP_ROUTER_CHAR) { - node->type |= NodeIsRouter; - } - - /* Could be a radio address or some weird poletop address. */ - p = line_begin + 20; - /* printk(KERN_DEBUG "before addr: %6s\n", p); */ - string_to_radio_address(&node->addr, p); - /* radio_address_to_string(&node->addr, addr_string); - printk(KERN_DEBUG "after addr: %s\n", addr_string); */ - - if (IS_RADIO_ADDRESS(p)) { - string_to_radio_address(&node->addr, p); - } - else { - memset(&node->addr, 0, sizeof(MetricomAddress)); - } - - /* Get the poll latency. %$#!@ simple_strtoul can't skip white space */ - p = line_begin + 41; - while (isspace(*p) && (p < end)) { - p++; - } - node->poll_latency = simple_strtoul(p, NULL, 10); - - /* Get the signal strength. simple_strtoul doesn't do minus signs */ - p = line_begin + 60; - node->rssi = -simple_strtoul(p, NULL, 10); - - if (last != node) { - last->next = node; - last = node; - } - node = node->next; - line_begin += STRIP_NODE_LEN; - num_nodes_counted++; - } - - /* invalidate all remaining nodes */ - for (;node != NULL; node = node->next) { - node->type &= ~NodeValid; - } + buf += sprintf(buf, "strip_version: %s\n", StripVersion); + if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) goto exit; - /* - * If the number of nodes reported is different - * from the number counted, might need to up the number - * requested. - */ - if (num_nodes_reported != num_nodes_counted) { - printk(KERN_DEBUG "nodes reported: %d \tnodes counted: %d\n", - num_nodes_reported, num_nodes_counted); - } + while (strip_info != NULL) + { + buf += sprintf_status_info(buf, strip_info); + if (shift_buffer(buffer, req_offset, req_len, &total, &slop, &buf)) break; + strip_info = strip_info->next; + } + exit: + return(calc_start_len(buffer, start, req_offset, req_len, total, buf)); } +static const char proc_strip_status_name[] = "strip"; +static struct proc_dir_entry proc_strip_get_status_info = +{ + PROC_NET_STRIP_STATUS, /* unsigned short low_ino */ + sizeof(proc_strip_status_name)-1, /* unsigned short namelen */ + proc_strip_status_name, /* const char *name */ + S_IFREG | S_IRUGO, /* mode_t mode */ + 1, /* nlink_t nlink */ + 0, 0, 0, /* uid_t uid, gid_t gid, unsigned long size */ + &proc_net_inode_operations, /* struct inode_operations * ops */ + &get_status_info, /* int (*get_info)(...) */ + NULL, /* void (*fill_inode)(struct inode *); */ + NULL, NULL, NULL, /* struct proc_dir_entry *next, *parent, *subdir; */ + NULL /* void *data; */ +}; + /************************************************************************/ /* Sending routines */ +#define InitString "ate0q1dt**starmode" + static void ResetRadio(struct strip *strip_info) -{ - static const char InitString[] = "\rat\r\rate0q1dt**starmode\r\r**"; +{ + static const char s[] = "\r" InitString "\r**"; /* If the radio isn't working anymore, we should clear the old status information. */ if (strip_info->working) @@ -1530,14 +1242,32 @@ static void ResetRadio(struct strip *strip_info) strip_info->firmware_version.c[0] = '\0'; strip_info->serial_number.c[0] = '\0'; strip_info->battery_voltage.c[0] = '\0'; + strip_info->portables.num_nodes = 0; + do_gettimeofday(&strip_info->portables.timestamp); + strip_info->poletops.num_nodes = 0; + do_gettimeofday(&strip_info->poletops.timestamp); } + + strip_info->pps_timer = jiffies; + strip_info->rx_pps_count = 0; + strip_info->tx_pps_count = 0; + strip_info->sx_pps_count = 0; + strip_info->rx_average_pps = 0; + strip_info->tx_average_pps = 0; + strip_info->sx_average_pps = 0; + /* Mark radio address as unknown */ - *(MetricomAddress*)&strip_info->dev.dev_addr = zero_address; + *(MetricomAddress*)&strip_info->true_dev_addr = zero_address; + if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = zero_address; strip_info->working = FALSE; - strip_info->structured_messages = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; strip_info->watchdog_doprobe = jiffies + 10 * HZ; strip_info->watchdog_doreset = jiffies + 1 * HZ; - strip_info->tty->driver.write(strip_info->tty, 0, (char *)InitString, sizeof(InitString)-1); + strip_info->tty->driver.write(strip_info->tty, 0, (char *)s, sizeof(s)-1); +#ifdef EXT_COUNTERS + strip_info->tx_ebytes += sizeof(s) - 1; +#endif } /* @@ -1573,6 +1303,9 @@ static void strip_write_some_more(struct tty_struct *tty) int num_written = tty->driver.write(tty, 0, strip_info->tx_head, strip_info->tx_left); strip_info->tx_left -= num_written; strip_info->tx_head += num_written; +#ifdef EXT_COUNTERS + strip_info->tx_sbytes += num_written; +#endif RestoreInterrupts(intstat); } else /* Else start transmission of another packet */ @@ -1583,12 +1316,21 @@ static void strip_write_some_more(struct tty_struct *tty) } } -static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_info, struct sk_buff *skb) +static __u8 *add_checksum(__u8 *buffer, __u8 *end) { -#if DO_PROC_NET_STRIP_TRACE - unsigned char *start_ptr; -#endif DO_PROC_NET_STRIP_TRACE + __u16 sum = 0; + __u8 *p = buffer; + while (p < end) sum += *p++; + end[3] = hextable[sum & 0xF]; sum >>= 4; + end[2] = hextable[sum & 0xF]; sum >>= 4; + end[1] = hextable[sum & 0xF]; sum >>= 4; + end[0] = hextable[sum & 0xF]; + return(end+4); +} +static unsigned char *strip_make_packet(unsigned char *buffer, struct strip *strip_info, struct sk_buff *skb) +{ + __u8 *ptr = buffer; __u8 *stuffstate = NULL; STRIP_Header *header = (STRIP_Header *)skb->data; MetricomAddress haddr = header->dst_addr; @@ -1603,7 +1345,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ { printk(KERN_ERR "%s: strip_make_packet: Unknown packet type 0x%04X\n", strip_info->dev.name, ntohs(header->protocol)); - strip_info->tx_dropped++; return(NULL); } @@ -1611,7 +1352,6 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ { printk(KERN_ERR "%s: Dropping oversized transmit packet: %d bytes\n", strip_info->dev.name, len); - strip_info->tx_dropped++; return(NULL); } @@ -1629,6 +1369,12 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ } } + /* + * If we're sending to ourselves, discard the packet. + * (Metricom radios choke if they try to send a packet to their own address.) + */ + if (!memcmp(haddr.c, strip_info->true_dev_addr.c, sizeof(haddr))) + return(NULL); *ptr++ = '*'; *ptr++ = hextable[haddr.c[2] >> 4]; *ptr++ = hextable[haddr.c[2] & 0xF]; @@ -1645,17 +1391,9 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ *ptr++ = key.c[2]; *ptr++ = key.c[3]; -#if DO_PROC_NET_STRIP_TRACE - start_ptr = ptr; -#endif DO_PROC_NET_STRIP_TRACE - ptr = StuffData(skb->data + sizeof(STRIP_Header), len, ptr, &stuffstate); -#if DO_PROC_NET_STRIP_TRACE - packet_log(strip_info, skb->data + sizeof(STRIP_Header), EntrySend, - header, len, ptr-start_ptr, - slip_len(skb->data + sizeof(STRIP_Header), len)); -#endif DO_PROC_NET_STRIP_TRACE + if (strip_info->firmware_level >= ChecksummedMessages) ptr = add_checksum(buffer+1, ptr); *ptr++ = 0x0D; return(ptr); @@ -1664,57 +1402,108 @@ static unsigned char *strip_make_packet(unsigned char *ptr, struct strip *strip_ static void strip_send(struct strip *strip_info, struct sk_buff *skb) { unsigned char *ptr = strip_info->tx_buff; + int doreset = (long)jiffies - strip_info->watchdog_doreset >= 0; + int doprobe = (long)jiffies - strip_info->watchdog_doprobe >= 0 && !doreset; - /* If we have a packet, encapsulate it and put it in the buffer */ + /* + * 1. If we have a packet, encapsulate it and put it in the buffer + */ if (skb) { - ptr = strip_make_packet(ptr, strip_info, skb); - /* If error, unlock and return */ - if (!ptr) { strip_unlock(strip_info); return; } - strip_info->tx_packets++; /* Count another successful packet */ - /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/ - /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/ - } - - /* Set up the strip_info ready to send the data */ - strip_info->tx_head = strip_info->tx_buff; - strip_info->tx_left = ptr - strip_info->tx_buff; - strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - - /* If watchdog has expired, reset the radio */ - if ((long)jiffies - strip_info->watchdog_doreset >= 0) - { - ResetRadio(strip_info); - return; - /* Note: if there's a packet to send, strip_write_some_more - will do it after the reset has finished */ + char *newptr = strip_make_packet(ptr, strip_info, skb); + strip_info->tx_pps_count++; + if (!newptr) strip_info->tx_dropped++; + else + { + ptr = newptr; + strip_info->sx_pps_count++; + strip_info->tx_packets++; /* Count another successful packet */ +#ifdef EXT_COUNTERS + strip_info->tx_bytes += skb->len; + strip_info->tx_rbytes += ptr - strip_info->tx_buff; +#endif + /*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr);*/ + /*HexDump("Sending", strip_info, strip_info->tx_buff, ptr);*/ + } } - /* No reset. - * If it is time for another tickle, tack it on the end of the packet + /* + * 2. If it is time for another tickle, tack it on, after the packet */ - if ((long)jiffies - strip_info->watchdog_doprobe >= 0) + if (doprobe) { - /* Send tickle to make radio protest */ - /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/ - const char *TickleString = TickleString1; - int length = sizeof(TickleString1)-1; - if (strip_info->structured_messages) + StringDescriptor ts = CommandString[strip_info->next_command]; +#if TICKLE_TIMERS { - TickleString = TickleString2; - length = sizeof(TickleString2)-1; + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Sending tickle string %d at %02d.%06d\n", + strip_info->next_command, tv.tv_sec % 100, tv.tv_usec); } - memcpy(ptr, TickleString, length); - strip_info->tx_left += length; +#endif + if (ptr == strip_info->tx_buff) *ptr++ = 0x0D; + + *ptr++ = '*'; /* First send "**" to provoke an error message */ + *ptr++ = '*'; + + /* Then add the command */ + memcpy(ptr, ts.string, ts.length); + + /* Add a checksum ? */ + if (strip_info->firmware_level < ChecksummedMessages) ptr += ts.length; + else ptr = add_checksum(ptr, ptr + ts.length); + + *ptr++ = 0x0D; /* Terminate the command with a <CR> */ + + /* Cycle to next periodic command? */ + if (strip_info->firmware_level >= StructuredMessages) + if (++strip_info->next_command >= ELEMENTS_OF(CommandString)) + strip_info->next_command = 0; +#ifdef EXT_COUNTERS + strip_info->tx_ebytes += ts.length; +#endif strip_info->watchdog_doprobe = jiffies + 10 * HZ; strip_info->watchdog_doreset = jiffies + 1 * HZ; + /*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev.name);*/ } /* - * If it is time for a periodic ARP, queue one up to be sent + * 3. Set up the strip_info ready to send the data (if any). + */ + strip_info->tx_head = strip_info->tx_buff; + strip_info->tx_left = ptr - strip_info->tx_buff; + strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + + /* + * 4. Debugging check to make sure we're not overflowing the buffer. + */ + if (strip_info->tx_size - strip_info->tx_left < 20) + printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name, + strip_info->tx_left, strip_info->tx_size - strip_info->tx_left); + + /* + * 5. If watchdog has expired, reset the radio. Note: if there's data waiting in + * the buffer, strip_write_some_more will send it after the reset has finished + */ + if (doreset) { ResetRadio(strip_info); return; } + + /* + * 6. If it is time for a periodic ARP, queue one up to be sent. + * We only do this if: + * 1. The radio is working + * 2. It's time to send another periodic ARP + * 3. We really know what our address is (and it is not manually set to zero) + * 4. We have a designated broadcast address configured + * If we queue up an ARP packet when we don't have a designated broadcast + * address configured, then the packet will just have to be discarded in + * strip_make_packet. This is not fatal, but it causes misleading information + * to be displayed in tcpdump. tcpdump will report that periodic APRs are + * being sent, when in fact they are not, because they are all being dropped + * in the strip_make_packet routine. */ if (strip_info->working && (long)jiffies - strip_info->gratuitous_arp >= 0 && - memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address))) + memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) && + *strip_info->dev.broadcast!=0xFF) { /*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n", strip_info->dev.name, strip_info->arp_interval / HZ);*/ @@ -1722,16 +1511,18 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb) strip_info->arp_interval *= 2; if (strip_info->arp_interval > MaxARPInterval) strip_info->arp_interval = MaxARPInterval; - arp_send(ARPOP_REPLY, ETH_P_ARP, strip_info->dev.pa_addr, - &strip_info->dev, strip_info->dev.pa_addr, - NULL, strip_info->dev.dev_addr, NULL); + arp_send(ARPOP_REPLY, ETH_P_ARP, + strip_info->dev.pa_addr, /* Target address of ARP packet is our address */ + &strip_info->dev, /* Device to send packet on */ + strip_info->dev.pa_addr, /* Source IP address this ARP packet comes from */ + NULL, /* Destination HW address is NULL (broadcast it) */ + strip_info->dev.dev_addr, /* Source HW address is our HW address */ + strip_info->dev.dev_addr); /* Target HW address is our HW address (redundant) */ } - if (strip_info->tx_size - strip_info->tx_left < 20) - printk(KERN_ERR "%s: Sending%5d bytes;%5d bytes free.\n", strip_info->dev.name, - strip_info->tx_left, strip_info->tx_size - strip_info->tx_left); - - /* All ready. Start the transmission */ + /* + * 7. All ready. Start the transmission + */ strip_write_some_more(strip_info->tty); } @@ -1752,6 +1543,33 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev) if (strip_info->mtu != strip_info->dev.mtu) strip_changedmtu(strip_info); + if (jiffies - strip_info->pps_timer > HZ) + { + unsigned long t = jiffies - strip_info->pps_timer; + unsigned long rx_pps_count = (strip_info->rx_pps_count * HZ * 8 + t/2) / t; + unsigned long tx_pps_count = (strip_info->tx_pps_count * HZ * 8 + t/2) / t; + unsigned long sx_pps_count = (strip_info->sx_pps_count * HZ * 8 + t/2) / t; + + strip_info->pps_timer = jiffies; + strip_info->rx_pps_count = 0; + strip_info->tx_pps_count = 0; + strip_info->sx_pps_count = 0; + + strip_info->rx_average_pps = (strip_info->rx_average_pps + rx_pps_count + 1) / 2; + strip_info->tx_average_pps = (strip_info->tx_average_pps + tx_pps_count + 1) / 2; + strip_info->sx_average_pps = (strip_info->sx_average_pps + sx_pps_count + 1) / 2; + + if (rx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Receiving %ld packets per second.\n", + strip_info->dev.name, rx_pps_count / 8); + if (tx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Tx %ld packets per second.\n", + strip_info->dev.name, tx_pps_count / 8); + if (sx_pps_count / 8 >= 10) + printk(KERN_INFO "%s: WARNING: Sending %ld packets per second.\n", + strip_info->dev.name, sx_pps_count / 8); + } + strip_send(strip_info, skb); if (skb) dev_kfree_skb(skb, FREE_WRITE); @@ -1759,6 +1577,17 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev) } /* + * IdleTask periodically calls strip_xmit, so even when we have no IP packets + * to send for an extended period of time, the watchdog processing still gets + * done to ensure that the radio stays in Starmode + */ + +static void strip_IdleTask(unsigned long parameter) +{ + strip_xmit(NULL, (struct device *)parameter); +} + +/* * Create the MAC header for an arbitrary protocol layer * * saddr!=NULL means use this specific address (n/a for Metricom) @@ -1772,19 +1601,20 @@ static int strip_xmit(struct sk_buff *skb, struct device *dev) static int strip_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { + struct strip *strip_info = (struct strip *)(dev->priv); STRIP_Header *header = (STRIP_Header *)skb_push(skb, sizeof(STRIP_Header)); /*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type, type == ETH_P_IP ? "IP" : type == ETH_P_ARP ? "ARP" : "");*/ - memcpy(header->src_addr.c, dev->dev_addr, dev->addr_len); + header->src_addr = strip_info->true_dev_addr; header->protocol = htons(type); /*HexDump("strip_header", (struct strip *)(dev->priv), skb->data, skb->data + skb->len);*/ if (!daddr) return(-dev->hard_header_len); - memcpy(header->dst_addr.c, daddr, dev->addr_len); + header->dst_addr = *(MetricomAddress*)daddr; return(dev->hard_header_len); } @@ -1811,43 +1641,130 @@ static int strip_rebuild_header(struct sk_buff *skb) #endif } + +/************************************************************************/ +/* Receiving routines */ + +static int strip_receive_room(struct tty_struct *tty) +{ + return 0x10000; /* We can handle an infinite amount of data. :-) */ +} + /* - * IdleTask periodically calls strip_xmit, so even when we have no IP packets - * to send for an extended period of time, the watchdog processing still gets - * done to ensure that the radio stays in Starmode + * This function parses the response to the ATS300? command, + * extracting the radio version and serial number. */ - -static void strip_IdleTask(unsigned long parameter) +static void get_radio_version(struct strip *strip_info, __u8 *ptr, __u8 *end) { - strip_xmit(NULL, (struct device *)parameter); + __u8 *p, *value_begin, *value_end; + int len; + + /* Determine the beginning of the second line of the payload */ + p = ptr; + while (p < end && *p != 10) p++; + if (p >= end) return; + p++; + value_begin = p; + + /* Determine the end of line */ + while (p < end && *p != 10) p++; + if (p >= end) return; + value_end = p; + p++; + + len = value_end - value_begin; + len = MIN(len, sizeof(FirmwareVersion) - 1); + if (strip_info->firmware_version.c[0] == 0) + printk(KERN_INFO "%s: Radio Firmware: %.*s\n", + strip_info->dev.name, len, value_begin); + sprintf(strip_info->firmware_version.c, "%.*s", len, value_begin); + + /* Look for the first colon */ + while (p < end && *p != ':') p++; + if (p >= end) return; + /* Skip over the space */ + p += 2; + len = sizeof(SerialNumber) - 1; + if (p + len <= end) { + sprintf(strip_info->serial_number.c, "%.*s", len, p); + } + else { + printk(KERN_DEBUG "STRIP: radio serial number shorter (%d) than expected (%d)\n", + end - p, len); + } } +/* + * This function parses the response to the ATS325? command, + * extracting the radio battery voltage. + */ +static void get_radio_voltage(struct strip *strip_info, __u8 *ptr, __u8 *end) +{ + int len; -/************************************************************************/ -/* Receiving routines */ + len = sizeof(BatteryVoltage) - 1; + if (ptr + len <= end) { + sprintf(strip_info->battery_voltage.c, "%.*s", len, ptr); + } + else { + printk(KERN_DEBUG "STRIP: radio voltage string shorter (%d) than expected (%d)\n", + end - ptr, len); + } +} -static int strip_receive_room(struct tty_struct *tty) +/* + * This function parses the responses to the AT~LA and ATS311 commands, + * which list the radio's neighbours. + */ +static void get_radio_neighbours(MetricomNodeTable *table, __u8 *ptr, __u8 *end) { - return 0x10000; /* We can handle an infinite amount of data. :-) */ + table->num_nodes = 0; + while (ptr < end && table->num_nodes < NODE_TABLE_SIZE) + { + MetricomNode *node = &table->node[table->num_nodes++]; + char *dst = node->c, *limit = dst + sizeof(*node) - 1; + while (ptr < end && *ptr <= 32) ptr++; + while (ptr < end && dst < limit && *ptr != 10) *dst++ = *ptr++; + *dst++ = 0; + while (ptr < end && ptr[-1] != 10) ptr++; + } + do_gettimeofday(&table->timestamp); } -static void get_radio_address(struct strip *strip_info, __u8 *p) +static int get_radio_address(struct strip *strip_info, __u8 *p) { MetricomAddress addr; - string_to_radio_address(&addr, p); + if (string_to_radio_address(&addr, p)) return(1); /* See if our radio address has changed */ - if (memcmp(strip_info->dev.dev_addr, addr.c, sizeof(addr))) + if (memcmp(strip_info->true_dev_addr.c, addr.c, sizeof(addr))) { MetricomAddressString addr_string; radio_address_to_string(&addr, &addr_string); - printk(KERN_INFO "%s: My radio address = %s\n", strip_info->dev.name, addr_string.c); - memcpy(strip_info->dev.dev_addr, addr.c, sizeof(addr)); + printk(KERN_INFO "%s: Radio address = %s\n", strip_info->dev.name, addr_string.c); + strip_info->true_dev_addr = addr; + if (!strip_info->manual_dev_addr) *(MetricomAddress*)strip_info->dev.dev_addr = addr; /* Give the radio a few seconds to get its head straight, then send an arp */ - strip_info->gratuitous_arp = jiffies + 6 * HZ; + strip_info->gratuitous_arp = jiffies + 15 * HZ; strip_info->arp_interval = 1 * HZ; } + return(0); +} + +static int verify_checksum(struct strip *strip_info) +{ + __u8 *p = strip_info->sx_buff; + __u8 *end = strip_info->sx_buff + strip_info->sx_count - 4; + u_short sum = (READHEX16(end[0]) << 12) | (READHEX16(end[1]) << 8) | + (READHEX16(end[2]) << 4) | (READHEX16(end[3])); + while (p < end) sum -= *p++; + if (sum == 0 && strip_info->firmware_level == StructuredMessages) + { + strip_info->firmware_level = ChecksummedMessages; + printk(KERN_INFO "%s: Radio provides message checksums\n", strip_info->dev.name); + } + return(sum == 0); } static void RecvErr(char *msg, struct strip *strip_info) @@ -1860,114 +1777,156 @@ static void RecvErr(char *msg, struct strip *strip_info) static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, const __u8 *msg) { - static const char ERR_001[] = "001"; /* Not in StarMode! */ - static const char ERR_002[] = "002"; /* Remap handle */ - static const char ERR_003[] = "003"; /* Can't resolve name */ - static const char ERR_004[] = "004"; /* Name too small or missing */ - static const char ERR_005[] = "005"; /* Bad count specification */ - static const char ERR_006[] = "006"; /* Header too big */ - static const char ERR_007[] = "007"; /* Body too big */ - static const char ERR_008[] = "008"; /* Bad character in name */ - static const char ERR_009[] = "009"; /* No count or line terminator */ - - if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1)) + if (has_prefix(msg, "001")) /* Not in StarMode! */ { RecvErr("Error Msg:", strip_info); printk(KERN_INFO "%s: Radio %s is not in StarMode\n", strip_info->dev.name, sendername); } - else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1)) + + else if (has_prefix(msg, "002")) /* Remap handle */ { - RecvErr("Error Msg:", strip_info); -#ifdef notyet /*Kernel doesn't have scanf!*/ - int handle; - __u8 newname[64]; - sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname); - printk(KERN_INFO "%s: Radio name %s is handle %d\n", - strip_info->dev.name, newname, handle); -#endif + /* We ignore "Remap handle" messages for now */ } - else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1)) + + else if (has_prefix(msg, "003")) /* Can't resolve name */ { RecvErr("Error Msg:", strip_info); printk(KERN_INFO "%s: Destination radio name is unknown\n", strip_info->dev.name); } - else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1)) + + else if (has_prefix(msg, "004")) /* Name too small or missing */ { strip_info->watchdog_doreset = jiffies + LongTime; +#if TICKLE_TIMERS + { + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Got ERR_004 response at %02d.%06d\n", + tv.tv_sec % 100, tv.tv_usec); + } +#endif if (!strip_info->working) { strip_info->working = TRUE; - printk(KERN_INFO "%s: Radio now in starmode\n", - strip_info->dev.name); + printk(KERN_INFO "%s: Radio now in starmode\n", strip_info->dev.name); /* * If the radio has just entered a working state, we should do our first * probe ASAP, so that we find out our radio address etc. without delay. */ strip_info->watchdog_doprobe = jiffies; } - if (!strip_info->structured_messages && sendername) + if (strip_info->firmware_level == NoStructure && sendername) + { + strip_info->firmware_level = StructuredMessages; + strip_info->next_command = 0; /* Try to enable checksums ASAP */ + printk(KERN_INFO "%s: Radio provides structured messages\n", strip_info->dev.name); + } + if (strip_info->firmware_level >= StructuredMessages) { - strip_info->structured_messages = TRUE; - printk(KERN_INFO "%s: Radio provides structured messages\n", - strip_info->dev.name); + verify_checksum(strip_info); + /* + * If the radio has structured messages but we don't yet have all our information about it, we should do + * probes without delay, until we have gathered all the information + */ + if (!GOT_ALL_RADIO_INFO(strip_info)) strip_info->watchdog_doprobe = jiffies; } } - else if (!strncmp(msg, ERR_005, sizeof(ERR_005)-1)) + + else if (has_prefix(msg, "005")) /* Bad count specification */ RecvErr("Error Msg:", strip_info); - else if (!strncmp(msg, ERR_006, sizeof(ERR_006)-1)) + + else if (has_prefix(msg, "006")) /* Header too big */ RecvErr("Error Msg:", strip_info); - else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1)) + + else if (has_prefix(msg, "007")) /* Body too big */ { - /* - * Note: This error knocks the radio back into - * command mode. - */ RecvErr("Error Msg:", strip_info); - printk(KERN_ERR "%s: Error! Packet size too big for radio.", + printk(KERN_ERR "%s: Error! Packet size too big for radio.\n", strip_info->dev.name); - strip_info->watchdog_doreset = jiffies; /* Do reset ASAP */ } - else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1)) + + else if (has_prefix(msg, "008")) /* Bad character in name */ { RecvErr("Error Msg:", strip_info); printk(KERN_ERR "%s: Radio name contains illegal character\n", strip_info->dev.name); } - else if (!strncmp(msg, ERR_009, sizeof(ERR_009)-1)) + + else if (has_prefix(msg, "009")) /* No count or line terminator */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, "010")) /* Invalid checksum */ RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, "011")) /* Checksum didn't match */ + RecvErr("Error Msg:", strip_info); + + else if (has_prefix(msg, "012")) /* Failed to transmit packet */ + RecvErr("Error Msg:", strip_info); + else RecvErr("Error Msg:", strip_info); } static void process_AT_response(struct strip *strip_info, __u8 *ptr, __u8 *end) { - static const char ATS305[] = "ATS305?"; - static const char ATS300[] = "ATS300?"; - static const char ATS325[] = "ATS325?"; - static const char ATI2[] = "AT~I2 nn"; - - /* Skip to the first newline character */ __u8 *p = ptr; - while (p < end && *p != 10) p++; - if (p >= end) return; - p++; + while (p < end && p[-1] != 10) p++; /* Skip past first newline character */ + /* Now ptr points to the AT command, and p points to the text of the response. */ - if (!strncmp(ptr, ATS305, sizeof(ATS305)-1)) +#if TICKLE_TIMERS { - if (IS_RADIO_ADDRESS(p)) get_radio_address(strip_info, p); - } - else if (!strncmp(ptr, ATS300, sizeof(ATS300)-1)) { - get_radio_version(strip_info, p, end); - } - else if (!strncmp(ptr, ATS325, sizeof(ATS325)-1)) { - get_radio_voltage(strip_info, p, end); + struct timeval tv; + do_gettimeofday(&tv); + printk(KERN_INFO "**** Got AT response %.7s at %02d.%06d\n", + ptr, tv.tv_sec % 100, tv.tv_usec); } - else if (!strncmp(ptr, ATI2, sizeof(ATI2)-1)) { - get_radio_neighbors(strip_info, p, end); +#endif + + if (has_prefix(ptr, "ATS300?" )) get_radio_version(strip_info, p, end); + else if (has_prefix(ptr, "ATS305?" )) get_radio_address(strip_info, p); + else if (has_prefix(ptr, "ATS311?" )) get_radio_neighbours(&strip_info->poletops, p, end); + else if (has_prefix(ptr, "ATS319=7")) verify_checksum(strip_info); + else if (has_prefix(ptr, "ATS325?" )) get_radio_voltage(strip_info, p, end); + else if (has_prefix(ptr, "AT~LA" )) get_radio_neighbours(&strip_info->portables, p, end); + else RecvErr("Unknown AT Response:", strip_info); +} + +static void process_ACK(struct strip *strip_info, __u8 *ptr, __u8 *end) +{ + /* Currently we don't do anything with ACKs from the radio */ +} + +static void process_Info(struct strip *strip_info, __u8 *ptr, __u8 *end) +{ + if (ptr+16 > end) RecvErr("Bad Info Msg:", strip_info); +} + +static struct device *get_strip_dev(struct strip *strip_info) +{ + /* If our hardware address is *manually set* to zero, and we know our */ + /* real radio hardware address, try to find another strip device that has been */ + /* manually set to that address that we can 'transfer ownership' of this packet to */ + if (strip_info->manual_dev_addr && + !memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) && + memcmp(&strip_info->true_dev_addr, zero_address.c, sizeof(zero_address))) + { + struct device *dev = dev_base; + while (dev) + { + if (dev->type == strip_info->dev.type && + !memcmp(dev->dev_addr, &strip_info->true_dev_addr, sizeof(MetricomAddress))) + { + printk(KERN_INFO "%s: Transferred packet ownership to %s.\n", + strip_info->dev.name, dev->name); + return(dev); + } + dev = dev->next; + } } - else RecvErr("Unknown AT Response:", strip_info); + return(&strip_info->dev); } /* @@ -1979,14 +1938,14 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16 struct sk_buff *skb = dev_alloc_skb(sizeof(STRIP_Header) + packetlen); if (!skb) { - printk(KERN_INFO "%s: memory squeeze, dropping packet.\n", strip_info->dev.name); + printk(KERN_ERR "%s: memory squeeze, dropping packet.\n", strip_info->dev.name); strip_info->rx_dropped++; } else { memcpy(skb_put(skb, sizeof(STRIP_Header)), header, sizeof(STRIP_Header)); memcpy(skb_put(skb, packetlen), strip_info->rx_buff, packetlen); - skb->dev = &strip_info->dev; + skb->dev = get_strip_dev(strip_info); skb->protocol = header->protocol; skb->mac.raw = skb->data; @@ -1997,6 +1956,10 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header *header, __u16 /* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */ strip_info->rx_packets++; + strip_info->rx_pps_count++; +#ifdef EXT_COUNTERS + strip_info->rx_bytes += packetlen; +#endif netif_rx(skb); } } @@ -2005,10 +1968,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __ { __u16 packetlen; -#if DO_PROC_NET_STRIP_TRACE - __u8 *start_ptr = ptr; -#endif DO_PROC_NET_STRIP_TRACE - /* Decode start of the IP packet header */ ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4); if (!ptr) @@ -2019,9 +1978,9 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __ packetlen = ((__u16)strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3]; - if (packetlen > MAX_STRIP_MTU) + if (packetlen > MAX_RECV_MTU) { - printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n", + printk(KERN_INFO "%s: Dropping oversized received IP packet: %d bytes\n", strip_info->dev.name, packetlen); strip_info->rx_dropped++; return; @@ -2045,11 +2004,6 @@ static void process_IP_packet(struct strip *strip_info, STRIP_Header *header, __ header->protocol = htons(ETH_P_IP); -#if DO_PROC_NET_STRIP_TRACE - packet_log(strip_info, strip_info->rx_buff, EntryReceive, header, - packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen)); -#endif DO_PROC_NET_STRIP_TRACE - deliver_packet(strip_info, header, packetlen); } @@ -2058,10 +2012,6 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _ __u16 packetlen; struct arphdr *arphdr = (struct arphdr *)strip_info->rx_buff; -#if DO_PROC_NET_STRIP_TRACE - __u8 *start_ptr = ptr; -#endif DO_PROC_NET_STRIP_TRACE - /* Decode start of the ARP packet */ ptr = UnStuffData(ptr, end, strip_info->rx_buff, 8); if (!ptr) @@ -2072,9 +2022,9 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _ packetlen = 8 + (arphdr->ar_hln + arphdr->ar_pln) * 2; - if (packetlen > MAX_STRIP_MTU) + if (packetlen > MAX_RECV_MTU) { - printk(KERN_ERR "%s: Dropping oversized receive packet: %d bytes\n", + printk(KERN_INFO "%s: Dropping oversized received ARP packet: %d bytes\n", strip_info->dev.name, packetlen); strip_info->rx_dropped++; return; @@ -2100,15 +2050,47 @@ static void process_ARP_packet(struct strip *strip_info, STRIP_Header *header, _ header->protocol = htons(ETH_P_ARP); -#if DO_PROC_NET_STRIP_TRACE - packet_log(strip_info, strip_info->rx_buff, EntryReceive, header, - packetlen, end-start_ptr, slip_len(strip_info->rx_buff, packetlen)); -#endif DO_PROC_NET_STRIP_TRACE - deliver_packet(strip_info, header, packetlen); } -static void process_packet(struct strip *strip_info) +/* + * process_text_message processes a <CR>-terminated block of data received + * from the radio that doesn't begin with a '*' character. All normal + * Starmode communication messages with the radio begin with a '*', + * so any text that does not indicates a serial port error, a radio that + * is in Hayes command mode instead of Starmode, or a radio with really + * old firmware that doesn't frame its Starmode responses properly. + */ +static void process_text_message(struct strip *strip_info) +{ + __u8 *msg = strip_info->sx_buff; + int len = strip_info->sx_count; + + /* Check for anything that looks like it might be our radio name */ + /* (This is here for backwards compatibility with old firmware) */ + if (len == 9 && get_radio_address(strip_info, msg) == 0) return; + + if (text_equal(msg, len, "OK" )) return; /* Ignore 'OK' responses from prior commands */ + if (text_equal(msg, len, "ERROR" )) return; /* Ignore 'ERROR' messages */ + if (text_equal(msg, len, InitString)) return; /* Ignore character echo back from the radio */ + + /* Catch other error messages */ + /* (This is here for backwards compatibility with old firmware) */ + if (has_prefix(msg, "ERR_")) { RecvErr_Message(strip_info, NULL, &msg[4]); return; } + + RecvErr("No initial *", strip_info); +} + +/* + * process_message processes a <CR>-terminated block of data received + * from the radio. If the radio is not in Starmode or has old firmware, + * it may be a line of text in response to an AT command. Ideally, with + * a current radio that's properly in Starmode, all data received should + * be properly framed and checksummed radio message blocks, containing + * either a starmode packet, or a other communication from the radio + * firmware, like "INF_" Info messages and &COMMAND responses. + */ +static void process_message(struct strip *strip_info) { STRIP_Header header = { zero_address, zero_address, 0 }; __u8 *ptr = strip_info->sx_buff; @@ -2116,29 +2098,11 @@ static void process_packet(struct strip *strip_info) __u8 sendername[32], *sptr = sendername; MetricomKey key; - /* Ignore 'OK' responses from prior commands */ - if (strip_info->sx_count == 2 && ptr[0] == 'O' && ptr[1] == 'K') return; - - /* Check for anything that looks like it might be our radio name: dddd-dddd */ - /* (This is here for backwards compatibility with old firmware) */ - if (strip_info->sx_count == 9 && IS_RADIO_ADDRESS(ptr)) - { - get_radio_address(strip_info, ptr); - return; - } - /*HexDump("Receiving", strip_info, ptr, end);*/ /* Check for start of address marker, and then skip over it */ - if (*ptr != '*') - { - /* Catch other error messages */ - if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_') - RecvErr_Message(strip_info, NULL, &ptr[4]); - else RecvErr("No initial *", strip_info); - return; - } - ptr++; /* Skip the initial '*' */ + if (*ptr == '*') ptr++; + else { process_text_message(strip_info); return; } /* Copy out the return address */ while (ptr < end && *ptr != '*' && sptr < ARRAY_END(sendername)-1) *sptr++ = *ptr++; @@ -2156,55 +2120,85 @@ static void process_packet(struct strip *strip_info) /* (This is here for backwards compatibility with old firmware) */ if (!strcmp(sendername, "&COMMAND")) { - strip_info->structured_messages = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; return; } - if (ptr+4 >= end) + if (ptr+4 > end) { RecvErr("No proto key", strip_info); return; } - /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/ - - /* - * Fill in (pseudo) source and destination addresses in the packet. - * We assume that the destination address was our address (the radio does not - * tell us this). If the radio supplies a source address, then we use it. - */ - memcpy(&header.dst_addr, strip_info->dev.dev_addr, sizeof(MetricomAddress)); - if (IS_RADIO_ADDRESS(sendername)) string_to_radio_address(&header.src_addr, sendername); - /* Get the protocol key out of the buffer */ key.c[0] = *ptr++; key.c[1] = *ptr++; key.c[2] = *ptr++; key.c[3] = *ptr++; - if (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end); - else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end); - else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); - else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr); - else /* RecvErr("Unrecognized protocol key", strip_info); */ - - /* Note, this "else" block is temporary, until Metricom fix their */ - /* packet corruption bug */ + /* If we're using checksums, verify the checksum at the end of the packet */ + if (strip_info->firmware_level >= ChecksummedMessages) { - RecvErr("Unrecognized protocol key (retrying)", strip_info); - ptr -= 3; /* Back up and try again */ - key.c[0] = *ptr++; - key.c[1] = *ptr++; - key.c[2] = *ptr++; - key.c[3] = *ptr++; - if (key.l == SIP0Key.l) process_IP_packet(strip_info, &header, ptr, end); - else if (key.l == ARP0Key.l) process_ARP_packet(strip_info, &header, ptr, end); - else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); - else if (key.l == ERR_Key.l) RecvErr_Message(strip_info, sendername, ptr); - else RecvErr("Unrecognized protocol key", strip_info); + end -= 4; /* Chop the last four bytes off the packet (they're the checksum) */ + if (ptr > end) + { + RecvErr("Missing Checksum", strip_info); + return; + } + if (!verify_checksum(strip_info)) + { + RecvErr("Bad Checksum", strip_info); + return; + } } + + /*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev.name, sendername);*/ + + /* + * Fill in (pseudo) source and destination addresses in the packet. + * We assume that the destination address was our address (the radio does not + * tell us this). If the radio supplies a source address, then we use it. + */ + header.dst_addr = strip_info->true_dev_addr; + string_to_radio_address(&header.src_addr, sendername); + +#ifdef EXT_COUNTERS + if (key.l == SIP0Key.l) { + strip_info->rx_rbytes += (end - ptr); + process_IP_packet(strip_info, &header, ptr, end); + } else if (key.l == ARP0Key.l) { + strip_info->rx_rbytes += (end - ptr); + process_ARP_packet(strip_info, &header, ptr, end); + } else if (key.l == ATR_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_AT_response(strip_info, ptr, end); + } else if (key.l == ACK_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_ACK(strip_info, ptr, end); + } else if (key.l == INF_Key.l) { + strip_info->rx_ebytes += (end - ptr); + process_Info(strip_info, ptr, end); + } else if (key.l == ERR_Key.l) { + strip_info->rx_ebytes += (end - ptr); + RecvErr_Message(strip_info, sendername, ptr); + } else RecvErr("Unrecognized protocol key", strip_info); +#else + if (key.l == SIP0Key.l) process_IP_packet (strip_info, &header, ptr, end); + else if (key.l == ARP0Key.l) process_ARP_packet (strip_info, &header, ptr, end); + else if (key.l == ATR_Key.l) process_AT_response(strip_info, ptr, end); + else if (key.l == ACK_Key.l) process_ACK (strip_info, ptr, end); + else if (key.l == INF_Key.l) process_Info (strip_info, ptr, end); + else if (key.l == ERR_Key.l) RecvErr_Message (strip_info, sendername, ptr); + else RecvErr("Unrecognized protocol key", strip_info); +#endif } +#define TTYERROR(X) ((X) == TTY_BREAK ? "Break" : \ + (X) == TTY_FRAME ? "Framing Error" : \ + (X) == TTY_PARITY ? "Parity Error" : \ + (X) == TTY_OVERRUN ? "Hardware Overrun" : "Unknown Error") + /* * Handle the 'receiver data ready' interrupt. * This function is called by the 'tty_io' module in the kernel when @@ -2229,17 +2223,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int { struct timeval tv; do_gettimeofday(&tv); - printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %d.%06d\n", + printk(KERN_INFO "**** strip_receive_buf: %3d bytes at %02d.%06d\n", count, tv.tv_sec % 100, tv.tv_usec); } #endif +#ifdef EXT_COUNTERS + strip_info->rx_sbytes += count; +#endif + /* Read the characters out of the buffer */ while (cp < end) { + if (fp && *fp) printk(KERN_INFO "%s: %s on serial port\n", strip_info->dev.name, TTYERROR(*fp)); if (fp && *fp++ && !strip_info->discard) /* If there's a serial error, record it */ { - strip_info->discard = 1; + /* If we have some characters in the buffer, discard them */ + strip_info->discard = strip_info->sx_count; strip_info->rx_errors++; } @@ -2249,21 +2249,23 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int if (*cp == 0x0D) /* If end of packet, decide what to do with it */ { if (strip_info->sx_count > 3000) - printk(KERN_INFO "Cut a %d byte packet (%d bytes remaining)%s\n", - strip_info->sx_count, end-cp-1, + printk(KERN_INFO "%s: Cut a %d byte packet (%d bytes remaining)%s\n", + strip_info->dev.name, strip_info->sx_count, end-cp-1, strip_info->discard ? " (discarded)" : ""); if (strip_info->sx_count > strip_info->sx_size) { - strip_info->discard = 1; strip_info->rx_over_errors++; printk(KERN_INFO "%s: sx_buff overflow (%d bytes total)\n", strip_info->dev.name, strip_info->sx_count); } - if (!strip_info->discard) process_packet(strip_info); + else if (strip_info->discard) + printk(KERN_INFO "%s: Discarding bad packet (%d/%d)\n", + strip_info->dev.name, strip_info->discard, strip_info->sx_count); + else process_message(strip_info); strip_info->discard = 0; strip_info->sx_count = 0; } - else if (!strip_info->discard) /* If we're not discarding, store the character */ + else { /* Make sure we have space in the buffer */ if (strip_info->sx_count < strip_info->sx_size) @@ -2279,9 +2281,28 @@ strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int /************************************************************************/ /* General control routines */ -static int strip_set_dev_mac_address(struct device *dev, void *addr) +static int set_mac_address(struct strip *strip_info, MetricomAddress *addr) { - return -1; /* You cannot override a Metricom radio's address */ + /* + * We're using a manually specified address if the address is set + * to anything other than all ones. Setting the address to all ones + * disables manual mode and goes back to automatic address determination + * (tracking the true address that the radio has). + */ + strip_info->manual_dev_addr = memcmp(addr->c, broadcast_address.c, sizeof(broadcast_address)); + if (strip_info->manual_dev_addr) + *(MetricomAddress*)strip_info->dev.dev_addr = *addr; + else *(MetricomAddress*)strip_info->dev.dev_addr = strip_info->true_dev_addr; + return 0; +} + +static int dev_set_mac_address(struct device *dev, void *addr) +{ + struct strip *strip_info = (struct strip *)(dev->priv); + struct sockaddr *sa = addr; + printk(KERN_INFO "%s: strip_set_dev_mac_address called\n", dev->name); + set_mac_address(strip_info, (MetricomAddress *)sa->sa_data); + return 0; } static struct net_device_stats *strip_get_stats(struct device *dev) @@ -2341,7 +2362,8 @@ static int strip_open_low(struct device *dev) strip_info->discard = 0; strip_info->working = FALSE; - strip_info->structured_messages = FALSE; + strip_info->firmware_level = NoStructure; + strip_info->next_command = CompatibilityCommand; strip_info->sx_count = 0; strip_info->tx_left = 0; @@ -2442,7 +2464,7 @@ static int strip_dev_init(struct device *dev) dev->rebuild_header = strip_rebuild_header; /* dev->type_trans unused */ /* dev->set_multicast_list unused */ - dev->set_mac_address = strip_set_dev_mac_address; + dev->set_mac_address = dev_set_mac_address; /* dev->do_ioctl unused */ /* dev->set_config unused */ dev->get_stats = strip_get_stats; @@ -2455,19 +2477,10 @@ static int strip_dev_init(struct device *dev) static void strip_free(struct strip *strip_info) { - MetricomNode *node, *free; - *(strip_info->referrer) = strip_info->next; if (strip_info->next) strip_info->next->referrer = strip_info->referrer; strip_info->magic = 0; - - for (node = strip_info->neighbor_list; node != NULL; ) - { - free = node; - node = node->next; - kfree(free); - } kfree(strip_info); } @@ -2522,13 +2535,9 @@ static struct strip *strip_alloc(void) strip_info->idle_timer.data = (long)&strip_info->dev; strip_info->idle_timer.function = strip_IdleTask; - strip_info->neighbor_list = kmalloc(sizeof(MetricomNode), GFP_KERNEL); - strip_info->neighbor_list->type = 0; - strip_info->neighbor_list->next = NULL; - /* Note: strip_info->if_name is currently 8 characters long */ - sprintf(strip_info->if_name, "st%d", channel_id); - strip_info->dev.name = strip_info->if_name; + sprintf(strip_info->if_name.c, "st%d", channel_id); + strip_info->dev.name = strip_info->if_name.c; strip_info->dev.base_addr = channel_id; strip_info->dev.priv = (void*)strip_info; strip_info->dev.next = NULL; @@ -2598,6 +2607,9 @@ static int strip_open(struct tty_struct *tty) #ifdef MODULE MOD_INC_USE_COUNT; #endif + + printk(KERN_INFO "STRIP: device \"%s\" activated\n", strip_info->if_name.c); + /* * Done. We have linked the TTY line to a channel. */ @@ -2627,6 +2639,7 @@ static void strip_close(struct tty_struct *tty) tty->disc_data = 0; strip_info->tty = NULL; + printk(KERN_INFO "STRIP: device \"%s\" closed down\n", strip_info->if_name.c); strip_free(strip_info); tty->disc_data = NULL; #ifdef MODULE @@ -2657,12 +2670,17 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file, err = verify_area(VERIFY_WRITE, (void*)arg, 16); if (err) return -err; - copy_to_user((void*)arg, strip_info->dev.name, - strlen(strip_info->dev.name) + 1); - return 0; + return copy_to_user((void*)arg, strip_info->dev.name, + strlen(strip_info->dev.name) + 1)?-EFAULT:0; case SIOCSIFHWADDR: - return -EINVAL; + { + MetricomAddress addr; + printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev.name); + if(copy_from_user(&addr, (void*)arg, sizeof(MetricomAddress))) + return -EFAULT; + return(set_mac_address(strip_info, &addr)); + } /* * Allow stty to read, but not set, the serial port @@ -2683,32 +2701,6 @@ static int strip_ioctl(struct tty_struct *tty, struct file *file, /* Initialization */ /* - * Registers with the /proc file system to create different /proc/net files. - */ - -static int strip_proc_net_register(unsigned short type, char *file_name, - int (*get_info)(char *, char **, off_t, int, int)) -{ - struct proc_dir_entry *strip_entry; - - strip_entry = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC); - - memset(strip_entry, 0, sizeof(struct proc_dir_entry)); - strip_entry->low_ino = type; - strip_entry->namelen = strlen(file_name); - strip_entry->name = file_name; - strip_entry->mode = S_IFREG | S_IRUGO; - strip_entry->nlink = 1; - strip_entry->uid = 0; - strip_entry->gid = 0; - strip_entry->size = 0; - strip_entry->ops = &proc_net_inode_operations; - strip_entry->get_info = get_info; - - return proc_net_register(strip_entry); -} - -/* * Initialize the STRIP driver. * This routine is called at boot time, to bootstrap the multi-channel * STRIP driver @@ -2722,7 +2714,7 @@ int strip_init_ctrl_dev(struct device *dummy) static struct tty_ldisc strip_ldisc; int status; - printk("STRIP: version %s (unlimited channels)\n", StripVersion); + printk(KERN_INFO "STRIP: Version %s (unlimited channels)\n", StripVersion); /* * Fill in our line protocol discipline, and register it @@ -2747,24 +2739,12 @@ int strip_init_ctrl_dev(struct device *dummy) } /* - * Register the status and trace files with /proc + * Register the status file with /proc */ - -#if DO_PROC_NET_STRIP_STATUS - if (strip_proc_net_register(PROC_NET_STRIP_STATUS, "strip_status", - &strip_get_status_info) != 0) + if (proc_net_register(&proc_strip_get_status_info) != 0) { - printk(KERN_ERR "strip: status strip_proc_net_register() failed.\n"); + printk(KERN_ERR "strip: status proc_net_register() failed.\n"); } -#endif - -#if DO_PROC_NET_STRIP_TRACE - if (strip_proc_net_register(PROC_NET_STRIP_TRACE, "strip_trace", - &strip_get_trace_info) != 0) - { - printk(KERN_ERR "strip: trace strip_proc_net_register() failed.\n"); - } -#endif #ifdef MODULE return status; @@ -2794,16 +2774,12 @@ void cleanup_module(void) while (struct_strip_list) strip_free(struct_strip_list); - /* Unregister with the /proc/net files here. */ - -#if DO_PROC_NET_STRIP_TRACE - proc_net_unregister(PROC_NET_STRIP_TRACE); -#endif -#if DO_PROC_NET_STRIP_STATUS + /* Unregister with the /proc/net file here. */ proc_net_unregister(PROC_NET_STRIP_STATUS); -#endif if ((i = tty_register_ldisc(N_STRIP, NULL))) printk(KERN_ERR "STRIP: can't unregister line discipline (err = %d)\n", i); + + printk(KERN_INFO "STRIP: Module Unloaded\n"); } #endif /* MODULE */ diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 7299b0f4b..dca243c4f 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -946,7 +946,8 @@ static void happy_meal_init_rings(struct happy_meal *hp, int from_irq) /* Because we reserve afterwards. */ skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET)); - hb->happy_meal_rxd[i].rx_addr = (unsigned int) skb->data; + hb->happy_meal_rxd[i].rx_addr = + (u32) ((unsigned long)skb->data); skb_reserve(skb, RX_OFFSET); hb->happy_meal_rxd[i].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); @@ -1615,7 +1616,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev, /* Return it to the Happy meal. */ drop_it: hp->net_stats.rx_dropped++; - this->rx_addr = (unsigned int) hp->rx_skbs[elem]->data; + this->rx_addr = + (u32) ((unsigned long)hp->rx_skbs[elem]->data); this->rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); goto next; @@ -1634,7 +1636,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev, hp->rx_skbs[elem] = new_skb; new_skb->dev = dev; skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET)); - rxbase[elem].rx_addr = (unsigned int) new_skb->data; + rxbase[elem].rx_addr = + (u32) ((unsigned long)new_skb->data); skb_reserve(new_skb, RX_OFFSET); rxbase[elem].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); @@ -1655,7 +1658,8 @@ static inline void happy_meal_rx(struct happy_meal *hp, struct device *dev, memcpy(copy_skb->data, skb->data, len); /* Reuse original ring buffer. */ - rxbase[elem].rx_addr = (unsigned int) skb->data; + rxbase[elem].rx_addr = + (u32) ((unsigned long)skb->data); rxbase[elem].rx_flags = (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)); @@ -1913,7 +1917,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct device *dev) SXD(("SX<l[%d]e[%d]>", len, entry)); hp->tx_skbs[entry] = skb; - hp->happy_block->happy_meal_txd[entry].tx_addr = (unsigned int) skb->data; + hp->happy_block->happy_meal_txd[entry].tx_addr = + (u32) ((unsigned long)skb->data); hp->happy_block->happy_meal_txd[entry].tx_flags = (TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | (len & TXFLAG_SIZE)); hp->tx_new = NEXT_TX(entry); @@ -2087,6 +2092,8 @@ static inline int happy_meal_ether_init(struct device *dev, struct linux_sbus_de printk("\n"); hp = (struct happy_meal *) dev->priv; + memset(hp, 0, sizeof(*hp)); + hp->happy_sbus_dev = sdev; if(sdev->num_registers != 5) { diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 18e758c80..6b417e34b 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -152,8 +152,8 @@ static void qe_init_rings(struct sunqe *qep, int from_irq) skb_put(skb, ETH_FRAME_LEN); skb_reserve(skb, 34); - /* FIX FOR ULTRA */ - qb->qe_rxd[i].rx_addr = (unsigned int) skb->data; + qb->qe_rxd[i].rx_addr = + (unsigned int) ((unsigned long)skb->data); qb->qe_rxd[i].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); } @@ -491,7 +491,8 @@ static inline void qe_rx(struct sunqe *qep) drop_it: /* Return it to the QE. */ qep->net_stats.rx_dropped++; - this->rx_addr = (unsigned int) qep->rx_skbs[elem]->data; + this->rx_addr = + (unsigned int) ((unsigned long)qep->rx_skbs[elem]->data); this->rx_flags = (RXD_OWN | (RX_BUF_ALLOC_SIZE & RXD_LENGTH)); goto next; @@ -512,8 +513,8 @@ static inline void qe_rx(struct sunqe *qep) skb_put(new_skb, ETH_FRAME_LEN); skb_reserve(new_skb, 34); - /* FIX FOR ULTRA */ - rxbase[elem].rx_addr = (unsigned int) new_skb->data; + rxbase[elem].rx_addr = + (unsigned int) ((unsigned long)new_skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); @@ -533,7 +534,8 @@ static inline void qe_rx(struct sunqe *qep) eth_copy_and_sum(copy_skb, (unsigned char *)skb->data, len, 0); /* Reuse original ring buffer. */ - rxbase[elem].rx_addr = (unsigned int) skb->data; + rxbase[elem].rx_addr = + (unsigned int) ((unsigned long)skb->data); rxbase[elem].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c new file mode 100644 index 000000000..d3d9d0e1c --- /dev/null +++ b/drivers/net/tlan.c @@ -0,0 +1,2309 @@ +/******************************************************************** + * + * Linux ThunderLAN Driver + * + * tlan.c + * by James Banks, james.banks@caldera.com + * + * (C) 1997 Caldera, Inc. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4 and colums>=132. + * + ** Useful (if not required) reading: + * + * Texas Instruments, ThunderLAN Programmer's Guide, + * TI Literature Number SPWU013A + * available in PDF format from www.ti.com + * National Semiconductor, DP83840A Data Sheet + * available in PDF format from www.national.com + * Microchip Technology, 24C01A/02A/04A Data Sheet + * available in PDF format from www.microchip.com + * + ********************************************************************/ + + +#include <linux/module.h> + + +#include "tlan.h" + + +#include <linux/bios32.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/etherdevice.h> + + + + +#ifdef MODULE + static struct device *TLanDevices = NULL; + static int TLanDevicesInstalled = 0; +#endif + static int debug = 0; + static u8 *TLanPadBuffer; + static char TLanSignature[] = "TLAN"; + static int TLanVersionMajor = 0; + static int TLanVersionMinor = 27; + + static TLanPciId TLanDeviceList[] = { + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10, "Compaq Netelligent 10" }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETELLIGENT_10_100, "Compaq Netelligent 10/100" }, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_NETFLEX_3_INTEGRATED, "Compaq Integrated NetFlex-3" }, + { 0, 0, NULL } /* End of List */ + }; + + + static int TLan_MiiReadReg(u16, u16, u16, u16 *); + static void TLan_MiiSendData( u16, u32, unsigned ); + static void TLan_MiiSync(u16); + static void TLan_MiiWriteReg(u16, u16, u16, u16); + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver MII Routines + + These routines are based on the information in Chap. 2 of the + "ThunderLAN Programmer's Guide", pp. 15-24. + +****************************************************************************** +*****************************************************************************/ + + + /************************************************************************* + * TLan_MiiReadReg + * + * Returns: 0 if ack received ok, 1 otherwise. + * Parms: base_port The base IO port of the adapter in question. + * dev The address of the PHY to be queried. + * reg The register whose contents are to be + * retreived. + * val A pointer to a variable to store the retrieved + * value. + * + * This function uses the TLAN's MII bus to retreive the contents of a + * given register on a PHY. It sends the appropriate info and then + * reads the 16-bit register value from the MII bus via the TLAN SIO + * register. + * + ************************************************************************/ + + int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) + { + u8 nack; + u16 sio, tmp; + u32 i; + int err; + + err = FALSE; + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync(base_port); + + TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); /* Disable PHY ints */ + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + + TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock through Idle bit */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Use this to wait 300ns */ + + nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK clock cycle */ + if (nack) { /* No ACK, so fake it */ + for (i = 0; i < 16; i++) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + tmp = 0xffff; + err = TRUE; + } else { /* ACKed, so read data */ + for (tmp = 0, i = 0x8000; i; i >>= 1) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) + tmp |= i; + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + } + + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + + TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); /* Enable PHY ints */ + + *val = tmp; + + sti(); + + return err; + + } /* TLan_MiiReadReg */ + + + + + /************************************************************************* + * TLan_MiiSendData + * + * Returns: Nothing + * Parms: base_port The base IO port of the adapter in question. + * dev The address of the PHY to be queried. + * data The value to be placed on the MII bus. + * num_bits The number of bits in data that are to be + * placed on the MII bus. + * + * This function sends on sequence of bits on the MII configuration + * bus. + * + ************************************************************************/ + + void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits ) + { + u16 sio; + u32 i; + + if ( num_bits == 0 ) + return; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit( TLAN_NET_SIO_MTXEN, sio ); + + for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + if ( data & i ) + TLan_SetBit( TLAN_NET_SIO_MDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_MDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + } + + } /* TLan_MiiSendData */ + + + + + /************************************************************************* + * TLan_MiiSync + * + * Returns: Nothing + * Parms: base_port The base IO port of the adapter in question. + * + * This functions syncs all PHYs in terms of the MII configuration bus. + * + ************************************************************************/ + + void TLan_MiiSync( u16 base_port ) + { + int i; + u16 sio; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio ); + for ( i = 0; i < 32; i++ ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + } + + } /* TLan_MiiSync */ + + + + + /************************************************************************* + * TLan_MiiWriteReg + * + * Returns: Nothing + * Parms: base_port The base IO port of the adapter in question. + * dev The address of the PHY to be written to. + * reg The register whose contents are to be + * written. + * val The value to be written to the register. + * + * This function uses the TLAN's MII bus to write the contents of a + * given register on a PHY. It sends the appropriate info and then + * writes the 16-bit register value from the MII configuration bus + * via the TLAN SIO register. + * + ************************************************************************/ + + void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) + { + u16 sio; + + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync( base_port ); + + TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */ + TLan_MiiSendData( base_port, val, 16 ); /* Send Data */ + + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */ + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + + TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); + + sti(); + + } /* TLan_MiiWriteReg */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver PHY Layer Routines + + The TLAN chip can drive any number of PHYs (physical devices). Rather + than having lots of 'if' or '#ifdef' statements, I have created a + second driver layer for the PHYs. Each PHY can be identified from its + id in registers 2 and 3, and can be given a Check and Service routine + that will be called when the adapter is reset and when the adapter + receives a Network Status interrupt, respectively. + +****************************************************************************** +*****************************************************************************/ + + static int TLan_PhyNop( struct device * ); + static void TLan_PhyPrint( struct device * ); + static void TLan_PhySelect( struct device * ); + static int TLan_PhyInternalCheck( struct device * ); + static int TLan_PhyInternalService( struct device * ); + static int TLan_PhyDp83840aCheck( struct device * ); + + + + + static TLanPhyIdEntry TLanPhyIdTable[] = { + { 0x4000, 0x5014, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY }, + { 0x4000, 0x5015, &TLan_PhyInternalCheck, &TLan_PhyInternalService, TLAN_PHY_ACTIVITY }, + { 0x2000, 0x5C01, &TLan_PhyDp83840aCheck, &TLan_PhyNop, TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, + { 0x0000, 0x0000, NULL, NULL, 0 } + }; + + + + + /************************************************************************* + * TLan_PhyPrint + * + * Returns: Nothing + * Parms: dev A pointer to the device structure of the adapter + * which the desired PHY is located. + * + * This function prints the registers a PHY. + * + ************************************************************************/ + + void TLan_PhyPrint( struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 i, data0, data1, data2, data3, phy; + u32 io; + + phy = priv->phyAddr; + io = dev->base_addr; + + if ( ( phy > 0 ) || ( phy <= TLAN_PHY_MAX_ADDR ) ) { + printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy ); + printk( "TLAN: Off. +0 +1 +2 +3 \n" ); + for ( i = 0; i < 0x20; i+= 4 ) { + TLan_MiiReadReg( io, phy, i, &data0 ); + TLan_MiiReadReg( io, phy, i + 1, &data1 ); + TLan_MiiReadReg( io, phy, i + 2, &data2 ); + TLan_MiiReadReg( io, phy, i + 3, &data3 ); + printk( "TLAN: 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", i, data0, data1, data2, data3 ); + } + } else { + printk( "TLAN: Device %s, PHY 0x%02x (Invalid).\n", dev->name, phy ); + } + + } /* TLan_PhyPrint */ + + + + + /************************************************************************* + * TLan_PhySelect + * + * Returns: Nothing + * Parms: dev A pointer to the device structure of the adapter + * for which the PHY needs determined. + * + * This function decides which PHY amoung those attached to the TLAN chip + * is to be used. The TLAN chip can be attached to multiple PHYs, and + * the driver needs to decide which one to talk to. Currently this + * routine picks the PHY with the lowest address as the internal PHY + * address is 0x1F, the highest possible. This strategy assumes that + * there can be only one other PHY, and, if it exists, it is the one to + * be used. If token ring PHYs are ever supported, this routine will + * become a little more interesting... + * + ************************************************************************/ + + void TLan_PhySelect( struct device *dev ) + { + int err; + int phy; + int entry; + u16 id_hi; + u16 id_lo; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 val; + + priv->phyCheck = &TLan_PhyNop; // Make absolutely sure these aren't NULL + priv->phyService = &TLan_PhyNop; + + for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { + err = TLan_MiiReadReg( dev->base_addr, phy, 0, &val ); + if ( ! err ) { + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &id_hi ); + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &id_lo ); + entry = 0; + while ( ( TLanPhyIdTable[entry].idHi != 0 ) && ( TLanPhyIdTable[entry].idLo != 0 ) ) { + if ( ( TLanPhyIdTable[entry].idHi == id_hi ) && ( TLanPhyIdTable[entry].idLo == id_lo ) ) { + priv->phyAddr = phy; + priv->phyEntry = entry; + priv->phyCheck = TLanPhyIdTable[entry].check; + priv->phyService = TLanPhyIdTable[entry].service; + priv->phyFlags = TLanPhyIdTable[entry].flags; + break; + } + entry++; + } + break; + } + } + + } /* TLan_PhySelect */ + + + + + /************************************************************************* + * TLan_PhyNop + * + * Returns: Nothing + * Parms: dev A pointer to a device structure. + * + * This function does nothing and is meant as a stand-in for when + * a Check or Service function would be meaningless. + * + ************************************************************************/ + + int TLan_PhyNop( struct device *dev ) + { + dev = NULL; + return 0; + + } /* TLan_PhyNop */ + + + + + /************************************************************************* + * TLan_PhyInternalCheck + * + * Returns: Nothing + * Parms: dev A pointer to a device structure of the adapter + * holding the PHY to be checked. + * + * This function resets the internal PHY on a TLAN chip. See Chap. 7, + * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide" + * + ************************************************************************/ + + int TLan_PhyInternalCheck( struct device *dev ) + { + u16 gen_ctl; + int i; + u32 io; + u16 phy; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 value; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + + // Read Possible Latched Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + // Read Real Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( value & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + // Enable Interrupts + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + + return 0; + + } /* TLanPhyInternalCheck */ + + + + + /************************************************************************* + * TLan_PhyInternalService + * + * Returns: Nothing + * Parms: dev A pointer to a device structure of the adapter + * holding the PHY to be serviced. + * + * This function services an interrupt generated by the internal PHY. + * It can turn on/off the link LED. See Chap. 7, + * "Physical Interface (PHY)" of "ThunderLAN Programmer's Guide". + * + ************************************************************************/ + + int TLan_PhyInternalService( struct device *dev ) + { + u16 tlphy_sts; + u16 gen_sts; + u16 an_exp; + u32 io; + u16 phy; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts ); + TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); + TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); + if ( gen_sts & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + return 0; + + } /* TLan_PhyInternalService */ + + + + + /************************************************************************* + * TLan_PhyDp83840aCheck + * + * Returns: Nothing + * Parms: dev A pointer to a device structure of the adapter + * holding the PHY to be reset. + * + * This function resets a National Semiconductor DP83840A 10/100 Mb/s + * PHY device. See National Semiconductor's data sheet for more info. + * This PHY is used on Compaq Netelligent 10/100 cards. + * + ************************************************************************/ + + static int TLan_PhyDp83840aCheck( struct device *dev ) + { + u16 gen_ctl; + int i; + u32 io; + u16 phy; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 value; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 ); + + for ( i = 0; i < 50000; i++ ) + SLOW_DOWN_IO; + +/* + // Read Possible Latched Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + // Read Real Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( value & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + // Enable Interrupts + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); +*/ + priv->phyOnline = 1; + + return 0; + + } /* TLan_PhyDp83840aCheck */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Adapter Related Routines + +****************************************************************************** +*****************************************************************************/ + + + + static void TLan_ResetLists( struct device * ); + static void TLan_PrintDio( u16 ); + static void TLan_PrintList( TLanList *, char *, int ); + static void TLan_ReadAndClearStats( struct device *, int ); + static int TLan_Reset( struct device * ); + static void TLan_SetMac( struct device *, int areg, char *mac ); + + + + + /************************************************************************* + * TLan_ResetLists + * + * Returns: Nothing + * Parms: dev The device structure with the list stuctures to + * be reset. + * + * This routine sets the variables associated with managing the TLAN + * lists to their initial values. + * + ************************************************************************/ + + void TLan_ResetLists( struct device *dev ) + { + int i; + TLanList *list; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + priv->txHead = 0; + priv->txTail = 0; + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { + list = priv->txList + i; + list->cStat = TLAN_CSTAT_UNUSED; + list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[2].count = 0; + list->buffer[2].address = 0; + } + + priv->rxHead = 0; + priv->rxTail = TLAN_NUM_RX_LISTS - 1; + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { + list = priv->rxList + i; + list->cStat = TLAN_CSTAT_READY; + list->frameSize = TLAN_MAX_FRAME_SIZE; + list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[1].count = 0; + list->buffer[1].address = 0; + if ( i < TLAN_NUM_RX_LISTS - 1 ) + list->forward = virt_to_bus( list + 1 ); + else + list->forward = 0; + } + + } /* TLan_ResetLists */ + + + + + /************************************************************************* + * TLan_PrintDio + * + * Returns: Nothing + * Parms: io_base Base IO port of the device of which to print + * DIO registers. + * + * This function prints out all the the internal (DIO) registers of a + * TLAN chip. + * + ************************************************************************/ + + void TLan_PrintDio( u16 io_base ) + { + u32 data0, data1; + int i; + + printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base ); + printk( "TLAN: Off. +0 +4\n" ); + for ( i = 0; i < 0x4C; i+= 8 ) { + data0 = TLan_DioRead32( io_base, i ); + data1 = TLan_DioRead32( io_base, i + 0x4 ); + printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 ); + } + + } /* TLan_PrintDio */ + + + + + /************************************************************************* + * TLan_PrintList + * + * Returns: Nothing + * Parms: list A pointer to the TLanList structure to be printed. + * type A string to designate type of list, "Rx" or "Tx". + * num The index of the list. + * + * This function prints out the contents of the list pointed to by the + * list parameter. + * + ************************************************************************/ + + void TLan_PrintList( TLanList *list, char *type, int num) + { + int i; + + printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list ); + printk( "TLAN: Forward = 0x%08x\n", list->forward ); + printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat ); + printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize ); + for ( i = 0; i < 10; i++ ) { + printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); + } + + } /* TLan_PrintList */ + + + + + /************************************************************************* + * TLan_ReadAndClearStats + * + * Returns: Nothing + * Parms: dev Pointer to device structure of adapter to which + * to read stats. + * record Flag indicating whether to add + * + * This functions reads all the internal status registers of the TLAN + * chip, which clears them as a side effect. It then either adds the + * values to the device's status struct, or discards them, depending + * on whether record is TLAN_RECORD (!=0) or TLAN_IGNORE (==0). + * + ************************************************************************/ + + void TLan_ReadAndClearStats( struct device *dev, int record ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 tx_good, tx_under; + u32 rx_good, rx_over; + u32 def_tx, crc, code; + u32 multi_col, single_col; + u32 excess_col, late_col, loss; + + outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + tx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + rx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR ); + def_tx = inb( dev->base_addr + TLAN_DIO_DATA ); + def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + code = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + multi_col = inb( dev->base_addr + TLAN_DIO_DATA ); + multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8; + + outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + excess_col = inb( dev->base_addr + TLAN_DIO_DATA ); + late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 ); + loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + + if ( record ) { + priv->stats.rx_packets += rx_good; + priv->stats.rx_errors += rx_over + crc + code; + priv->stats.tx_packets += tx_good; + priv->stats.tx_errors += tx_under + loss; + priv->stats.collisions += multi_col + single_col + excess_col + late_col; + + priv->stats.rx_over_errors += rx_over; + priv->stats.rx_crc_errors += crc; + priv->stats.rx_frame_errors += code; + + priv->stats.tx_aborted_errors += tx_under; + priv->stats.tx_carrier_errors += loss; + } + + } /* TLan_ReadAndClearStats */ + + + + + /************************************************************************* + * TLan_Reset + * + * Returns: 0 + * Parms: dev Pointer to device structure of adapter to be + * reset. + * + * This function resets the adapter and it's physical device. See + * Chap. 3, pp. 9-10 of the "ThunderLAN Programmer's Guide" for details. + * The routine tries to implement what is detailed there, though + * adjustments have been made. + * + ************************************************************************/ + + int TLan_Reset( struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + u32 data; + u8 data8; + + // 1. Assume Stat registers have been dealt with. + // 2. Assert reset bit. + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_AD_RST; + outl(data, dev->base_addr + TLAN_HOST_CMD); + // 3. Turn off interrupts. + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_INT_OFF; + outl(data, dev->base_addr + TLAN_HOST_CMD); + // 4. Setup AREGs and HASHs. + for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) + TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); + // 5. Setup NetConfig register. + outw(TLAN_NET_CONFIG, dev->base_addr + TLAN_DIO_ADR); + outw(TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN, dev->base_addr + TLAN_DIO_DATA); + // 6. Setup BSIZE register. + // Accept defaults, 0x22, for now. + // 7. Setup TX commit in Acommit. + // Allow it to manage itself. + // 8. Load Ld_Tmr in HOST_CMD. + // I don't know what this value should be. I'll try 3. + outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); + // 9. Load Ld_Thr in HOST_CMD. + // Try 1 for now. + outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); + // 10. Unreset the MII by setting NMRST (in NetSio) to 1. + outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); + TLan_SetBit( TLAN_NET_SIO_NMRST, dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO ); + // 10a. Other. + // 12. Setup the NetMask register. + TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, TLAN_ID_TX_EOC | TLAN_ID_RX_EOC ); + //TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP | TLAN_NET_CMD_DUPLEX | TLAN_NET_CMD_CAF ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP ); + // 11. Initialize PHYs. + TLan_PhySelect( dev ); + (*priv->phyCheck)( dev ); + if ( debug >= 1 ) + TLan_PhyPrint( dev ); + + //data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK6 | TLAN_NET_MASK_MASK7; + data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5 | TLAN_NET_MASK_MASK7; + TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 ); + TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, 1550 ); + /* + * 13. Turn on interrupts to host. + * I don't want any interrupts, yet. + */ + /* + data = inl(base_port + TLAN_HOST_CMD); + data |= TLAN_HC_INT_ON; + outl(data, base_port + TLAN_HOST_CMD); + */ + + return 0; + + } /* TLan_Reset */ + + + + + /************************************************************************* + * TLan_SetMac + * + * Returns: Nothing + * Parms: dev Pointer to device structure of adapter on which to + * change the AREG. + * areg The AREG to set the address in (0 - 3). + * mac A pointer to an array of chars. Each element + * stores one byte of the address. IE, it isn't + * in ascii. + * + * This function transfers a MAC address to one of the TLAN AREGs + * (address registers). The TLAN chip locks the register on writing to + * offset 0 and unlocks the register after writing to offset 5. If NULL + * is passed in mac, then the AREG is filled with 0's. + * + ************************************************************************/ + + void TLan_SetMac( struct device *dev, int areg, char *mac ) + { + int i; + + areg *= 6; + + if ( mac != NULL ) { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] ); + } else { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 ); + } + + } /* TLan_SetMac */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Eeprom routines + + The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A EEPROM. + These functions are based on information in Microchip's data sheet. I + don't know how well this functions will work with other EEPROMs. + +****************************************************************************** +*****************************************************************************/ + + static void TLan_EeSendStart( u16 ); + static int TLan_EeSendByte( u16, u8, int ); + static void TLan_EeReceiveByte( u16, u8 *, int ); + static int TLan_EeReadByte( u16, u8, u8 * ); + + + + + /************************************************************************* + * TLan_EeSendStart + * + * Returns: Nothing + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * + * This function sends a start cycle to an EEPROM attached to a TLAN + * chip. + * + ************************************************************************/ + + void TLan_EeSendStart( u16 io_base ) + { + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + // + // FIXME: Do I really need these SLOW_DOWN_IOs? + // + SLOW_DOWN_IO; + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + SLOW_DOWN_IO; + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + SLOW_DOWN_IO; + + } /* TLan_EeSendStart */ + + + + + /************************************************************************* + * TLan_EeSendByte + * + * Returns: If the correct ack was received, 0, otherwise 1 + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * data The 8 bits of information to send to the + * EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a stop cycle is + * sent after the byte is sent after the ack is + * read. + * + * This function sends a byte on the serial EEPROM line, driving the + * clock to send each bit. The function then reverses transmission + * direction and reads an acknowledge bit. + * + ************************************************************************/ + + int TLan_EeSendByte( u16 io_base, u8 data, int stop ) + { + int err; + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + // Assume clock is low, tx is enabled; + for ( place = 0x80; place; place >>= 1 ) { + if ( place & data ) + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + + if ( ( ! err ) && stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } + + return ( err ); + + } /* TLan_EeSendByte */ + + + + + /************************************************************************* + * TLan_EeReceiveByte + * + * Returns: Nothing + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * data An address to a char to hold the data sent + * from the EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a stop cycle is + * sent after the byte is received, and no ack is + * sent. + * + * This function receives 8 bits of data from the EEPROM over the serial + * link. It then sends and ack bit, or no ack and a stop bit. This + * function is used to retrieve data after the address of a byte in the + * EEPROM has been sent. + * + ************************************************************************/ + + void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) + { + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + *data = 0; + + // Assume clock is low, tx is enabled; + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + for ( place = 0x80; place; place >>= 1 ) { + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) ) + *data |= place; + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + if ( ! stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0 + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } else { + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?) + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } + + } /* TLan_EeReceiveByte */ + + + + + /************************************************************************* + * TLan_EeReadByte + * + * Returns: No error = 0, else, the stage at which the error occured. + * Parms: io_base The IO port base address for the TLAN device + * with the EEPROM to use. + * ee_addr The address of the byte in the EEPROM whose + * contents are to be retrieved. + * data An address to a char to hold the data obtained + * from the EEPROM. + * + * This function reads a byte of information from an byte cell in the + * EEPROM. + * + ************************************************************************/ + + int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data ) + { + int err; + + cli(); + + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK ); + if (err) + return 1; + err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK ); + if (err) + return 2; + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK ); + if (err) + return 3; + TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP ); + + sti(); + + return 0; + + } /* TLan_EeReadByte */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Interrupt Vectors and Table + + Please see Chap. 4, "Interrupt Handling" of the + "ThunderLAN Programmer's Guide" for more informations on handling + interrupts generated by TLAN based adapters. + +****************************************************************************** +*****************************************************************************/ + + static u32 TLan_HandleInvalid( struct device *, u16 ); + static u32 TLan_HandleTxEOF( struct device *, u16 ); + static u32 TLan_HandleStatOverflow( struct device *, u16 ); + static u32 TLan_HandleRxEOF( struct device *, u16 ); + static u32 TLan_HandleDummy( struct device *, u16 ); + static u32 TLan_HandleTxEOC( struct device *, u16 ); + static u32 TLan_HandleStatusCheck( struct device *, u16 ); + static u32 TLan_HandleRxEOC( struct device *, u16 ); + + + + + typedef u32 (TLanIntVectorFunc)( struct device *, u16 ); + + + + + /************************************************************************* + * TLan_HandleInvalid + * + * Returns: 0 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles invalid interrupts. This should never happen + * unless some other adapter is trying to use the IRQ line assigned to + * the device. + * + ************************************************************************/ + + u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) + { + host_int = 0; + // printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); + return 0; + + } /* TLan_HandleInvalid */ + + + /************************************************************************* + * TLan_HandleTxEOF + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles Tx EOF interrupts which are raised by the + * adapter when it has completed sending the contents of a buffer. If + * detemines which list/buffer was completed and resets it. If the + * buffer was the last in the channel (EOC), then the function checks + * to see if another buffer is ready to send, and if so, sends a Tx Go + * command. Finally, the driver activates/continues the activity LED. + * + ************************************************************************/ + + u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int eoc = 0; + TLanList *head_list; + u32 ack = 1; + + // printk( "TLAN: Handling Tx EOF\n" ); + host_int = 0; + head_list = priv->txList + priv->txHead; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); + } + // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat ); + head_list->cStat = TLAN_CSTAT_UNUSED; + dev->tbusy = 0; + priv->txHead++; + if ( priv->txHead >= TLAN_NUM_TX_LISTS ) + priv->txHead = 0; + if ( eoc ) { + // printk( "TLAN: Handling Tx EOC\n" ); + head_list = priv->txList + priv->txHead; + if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + // printk("TxEOF Starting timer...\n"); + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + priv->timerSetAt = jiffies; + // printk("TxEOF continuing timer...\n"); + } + } + + return ack; + + } /* TLan_HandleTxEOF */ + + + + + /************************************************************************* + * TLan_HandleStatOverflow + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles the Statistics Overflow interrupt which means + * that one or more of the TLAN statistics registers has reached 1/2 + * capacity and needs to be read. + * + ************************************************************************/ + + u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) + { + host_int = 0; + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + + return 1; + + } /* TLan_HandleStatOverflow */ + + + + + /************************************************************************* + * TLan_HandleRxEOF + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles the Rx EOF interrupt which indicates a frame + * has been received by the adapter from the net and the frame has been + * transferred to memory. The function determines the bounce buffer + * the frame has been loaded into, creates a new sk_buff big enough to + * hold the frame, and sends it to protocol stack. It then resets the + * used buffer and appends it to the end of the list. If the frame was + * the last in the Rx channel (EOC), the function restarts the receive + * channel by sending an Rx Go command to the adapter. Then it activates/ + * continues the the activity LED. + * + ************************************************************************/ + + u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) + { + u32 ack = 1; + int eoc = 0; + u8 *head_buffer; + TLanList *head_list; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + struct sk_buff *skb; + TLanList *tail_list; + void *t; + + // printk( "TLAN: Handling Rx EOF Head=%d Tail=%d\n", priv->rxHead, priv->rxTail ); + host_int = 0; + head_list = priv->rxList + priv->rxHead; + tail_list = priv->rxList + priv->rxTail; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted RX frame.\n" ); + } else { + skb = dev_alloc_skb( head_list->frameSize + 7 ); + if ( skb == NULL ) { + printk( "TLAN: Couldn't allocate memory for received data.\n" ); + } else { + head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE ); + skb->dev = dev; + skb_reserve( skb, 2 ); + t = (void *) skb_put( skb, head_list->frameSize ); + // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t ); + memcpy( t, head_buffer, head_list->frameSize ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + } + } + head_list->forward = 0; + head_list->frameSize = TLAN_MAX_FRAME_SIZE; + head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + tail_list->forward = virt_to_bus( head_list ); + priv->rxHead++; + if ( priv->rxHead >= TLAN_NUM_RX_LISTS ) + priv->rxHead = 0; + priv->rxTail++; + if ( priv->rxTail >= TLAN_NUM_RX_LISTS ) + priv->rxTail = 0; + if ( eoc ) { + // printk( "TLAN: Handling Rx EOC Head=%d Tail=%d\n", priv->rxHead, priv->rxTail ); + head_list = priv->rxList + priv->rxHead; + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO | TLAN_HC_RT; + priv->rxEocCount++; + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + // printk("RxEOF Starting timer...\n"); + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + // printk("RxEOF tarting continuing timer...\n"); + priv->timerSetAt = jiffies; + } + } + dev->last_rx = jiffies; + + return ack; + + } /* TLan_HandleRxEOF */ + + + + + /************************************************************************* + * TLan_HandleDummy + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles the Dummy interrupt, which is raised whenever + * a test interrupt is generated by setting the Req_Int bit of HOST_CMD + * to 1. + * + ************************************************************************/ + + u32 TLan_HandleDummy( struct device *dev, u16 host_int ) + { + host_int = 0; + printk( "TLAN: Dummy interrupt on %s.\n", dev->name ); + return 1; + + } /* TLan_HandleDummy */ + + + + + /************************************************************************* + * TLan_HandleTxEOC + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This driver is structured to determine EOC occurances by reading the + * CSTAT member of the list structure. Tx EOC interrupts are disabled + * via the DIO INTDIS register. + * + ************************************************************************/ + + u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) + { + host_int = 0; + printk( "TLAN: Tx EOC interrupt on %s.\n", dev->name ); + return 1; + + } /* TLan_HandleTxEOC */ + + + + + /************************************************************************* + * TLan_HandleStatusCheck + * + * Returns: 0 if Adapter check, 1 if Network Status check. + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This function handles Adapter Check/Network Status interrupts + * generated by the adapter. It checks the vector in the HOST_INT + * register to determine if it is an Adapter Check interrupt. If so, + * it resets the adapter. Otherwise it clears the status registers + * and services the PHY. + * + ************************************************************************/ + + u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) + { + u32 ack; + u32 error; + u8 net_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + ack = 1; + if ( host_int & TLAN_HI_IV_MASK ) { + error = inl( dev->base_addr + TLAN_CH_PARM ); + printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error ); + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + TLan_ResetLists( dev ); + TLan_Reset( dev ); + dev->tbusy = 0; + TLan_SetMac( dev, 0, dev->dev_addr ); + if ( priv->timerType == 0 ) { + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + //printk( " RX GO---->\n" ); + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + } + ack = 0; + } else { + net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); + if ( net_sts ) + TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); + if ( net_sts & TLAN_NET_STS_MIRQ ) + (*priv->phyService)( dev ); + + TLAN_DBG( 1, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts ); + } + + return ack; + + } /* TLan_HandleStatusCheck */ + + + + + /************************************************************************* + * TLan_HandleRxEOC + * + * Returns: 1 + * Parms: dev Device assigned the IRQ that was raised. + * host_int The contents of the HOST_INT port. + * + * This driver is structured to determine EOC occurances by reading the + * CSTAT member of the list structure. Rx EOC interrupts are disabled + * via the DIO INTDIS register. + * + ************************************************************************/ + + u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) + { + host_int = 0; + printk( "TLAN: Rx EOC interrupt on %s.\n", dev->name ); + return 1; + + } /* TLan_HandleRxEOC */ + + + + static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { + TLan_HandleInvalid, + TLan_HandleTxEOF, + TLan_HandleStatOverflow, + TLan_HandleRxEOF, + TLan_HandleDummy, + TLan_HandleTxEOC, + TLan_HandleStatusCheck, + TLan_HandleRxEOC + }; + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Timer Function + +****************************************************************************** +*****************************************************************************/ + + static void TLan_Timer( unsigned long ); + + + + + /************************************************************************* + * TLan_Timer + * + * Returns: Nothing + * Parms: data A value given to add timer when add_timer was + * called. + * + * This function handles timed functionality for the TLAN driver. The + * two current timer uses are for delaying for autonegotionation and + * driving the ACT LED. + * - Autonegotiation requires being allowed about 2 1/2 seconds before + * attempting to transmit a packet. It would be a very bad thing + * to hang the kernel this long, so the driver doesn't allow + * transmission 'til after this time, for certain PHYs. It would + * be much nicer if all PHYs were interrupt-capable like the + * internal PHY. + * - The ACT LED, which shows adapter activity, is driven by the driver, + * and so must be left on for a short period to power up the LED so + * it can be seen. This delay can be changed by changing the + * TLAN_TIMER_ACT_DELAY in tlan.h, if desired. 10 jiffies produces a + * slightly sluggish response. + * + ************************************************************************/ + + void TLan_Timer( unsigned long data ) + { + struct device *dev = (struct device *) data; + u16 gen_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType ); + + switch ( priv->timerType ) { + case TLAN_TIMER_LINK: + TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts ); + if ( gen_sts & MII_GS_LINK ) { + priv->phyOnline = 1; + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 ); + add_timer( &priv->timer ); + } + break; + case TLAN_TIMER_ACT: + if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; + add_timer( &priv->timer ); + } + break; + default: + break; + } + + } /* TLan_Timer */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Primary Functions + + These functions are more or less common to all Linux network drivers. + +****************************************************************************** +*****************************************************************************/ + + static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * ); + static int TLan_Init( struct device * ); + static int TLan_Open(struct device *dev); + static int TLan_StartTx(struct sk_buff *, struct device *); + static void TLan_HandleInterrupt(int, void *, struct pt_regs *); + static int TLan_Close(struct device *); + static struct enet_statistics *TLan_GetStats( struct device * ); + static void TLan_SetMulticastList( struct device * ); + + +#ifdef MODULE + + /************************************************************************* + * init_module + * + * Returns: 0 if module installed ok, non-zero if not. + * Parms: None + * + * This function begins the setup of the driver creating a pad buffer, + * finding all TLAN devices (matching TLanDeviceList entries), and + * creating and initializing a device structure for each adapter. + * + ************************************************************************/ + + extern int init_module(void) + { + int failed, found; + size_t dev_size; + struct device *dev; + TLanPrivateInfo *priv; + u8 bus, dfn, irq, rev; + u32 io_base, dl_ix; + + printk("TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n", + TLanVersionMajor, + TLanVersionMinor + ); + + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + return -ENOMEM; + } + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + + dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); + while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) { + dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); + if ( dev == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + continue; + } + memset( dev, 0, dev_size ); + + dev->priv = priv = ( (void *) dev ) + sizeof(struct device); + dev->name = priv->devName; + strcpy( priv->devName, " " ); + dev->base_addr = io_base; + dev->irq = irq; + dev->init = TLan_Init; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + ether_setup( dev ); + + failed = register_netdev( dev ); + + if ( failed ) { + printk( "TLAN: Could not register network device. Freeing struct.\n" ); + kfree( dev ); + } else { + priv->nextDevice = TLanDevices; + TLanDevices = dev; + TLanDevicesInstalled++; + printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName ); + } + } + + // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); + + return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); + + } /* init_module */ + + + + + /************************************************************************* + * cleanup_module + * + * Returns: Nothing + * Parms: None + * + * Goes through the TLanDevices list and frees the device structs and + * memory associated with each device (lists and buffers). It also + * ureserves the IO port regions associated with this device. + * + ************************************************************************/ + + extern void cleanup_module(void) + { + struct device *dev; + TLanPrivateInfo *priv; + + while (TLanDevicesInstalled) { + dev = TLanDevices; + priv = (TLanPrivateInfo *) dev->priv; + if ( priv->dmaStorage ) + kfree( priv->dmaStorage ); + release_region( dev->base_addr, 0x10 ); + unregister_netdev( dev ); + TLanDevices = priv->nextDevice; + kfree( dev ); + TLanDevicesInstalled--; + } + kfree( TLanPadBuffer ); + + } /* cleanup_module */ + + +#else /* MODULE */ + + + + + /************************************************************************* + * tlan_probe + * + * Returns: 0 on success, error code on error + * Parms: dev device struct to use if adapter is found. + * + * The name is lower case to fit in with all the rest of the + * netcard_probe names. This function looks for a/another TLan based + * adapter, setting it up with the provided device struct if one is + * found. + * + ************************************************************************/ + + extern int tlan_probe( struct device *dev ) + { + static int pad_allocated = 0; + int found; + TLanPrivateInfo *priv; + u8 bus, dfn, irq, rev; + u32 io_base, dl_ix; + + found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ); + TLAN_DBG( 1, "TLAN: Probing...\n" ); + if ( found ) { + dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); + if ( dev->priv == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + } + memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); + priv = (TLanPrivateInfo *) dev->priv; + + dev->name = priv->devName; + strcpy( priv->devName, " " ); + + dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); + + dev->base_addr = io_base; + dev->irq = irq; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + if ( ! pad_allocated ) { + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + } else { + pad_allocated = 1; + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + } + } + printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n", TLanVersionMajor, + TLanVersionMinor, + dev->name, + (int) irq, + io_base, + TLanDeviceList[dl_ix].deviceName ); + TLan_Init( dev ); + } + + return ( ( found ) ? 0 : -ENODEV ); + + } /* tlan_probe */ + + +#endif /* MODULE */ + + + + + /************************************************************************* + * TLan_PciProbe + * + * Returns: 1 if another TLAN card was found, 0 if not. + * Parms: pci_bus The PCI bus the card was found on. + * pci_dfn The PCI whatever the card was found at. + * pci_irq The IRQ of the found adapter. + * pci_rev The revision of the adapter. + * pci_io_base The first IO port used by the adapter. + * dl_ix The index in the device list of the adapter. + * + * This function searches for an adapter with PCI vendor and device + * IDs matching those in the TLanDeviceList. The function 'remembers' + * the last device it found, and so finds a new device (if anymore are + * to be found) each time the function is called. It then looks up + * pertinent PCI info and returns it to the caller. + * + ************************************************************************/ + + int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix ) + { + static int dl_index = 0; + static int pci_index = 0; + + int not_found; + u8 pci_latency; + u16 pci_command; + + + if ( ! pcibios_present() ) { + printk( "TLAN: PCI Bios not present.\n" ); + return 0; + } + + for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) { + not_found = pcibios_find_device( TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId, + pci_index, + pci_bus, + pci_dfn + ); + if ( ! not_found ) { + TLAN_DBG( 1, "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", + TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId + ); + + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq); + pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command); + pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 0x10) { + pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff); + TLAN_DBG( 1, "TLAN: Setting latency timer to max.\n"); + } + + if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) { + *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK; + TLAN_DBG( 1, "TLAN: IO mapping is available at %x.\n", *pci_io_base); + } else { + *pci_io_base = 0; + printk("TLAN: IO mapping not available, ignoring device.\n"); + } + + if (pci_command & PCI_COMMAND_MASTER) { + TLAN_DBG( 1, "TLAN: Bus mastering is active.\n"); + } + + pci_index++; + + if ( *pci_io_base ) { + *dl_ix = dl_index; + return 1; + } + + } else { + pci_index = 0; + } + } + + return 0; + + } /* TLan_PciProbe */ + + + + + /************************************************************************* + * TLan_Init + * + * Returns: 0 on success, error code otherwise. + * Parms: dev The structure of the device to be init'ed. + * + * This function completes the initialization of the device structure + * and driver. It reserves the IO addresses, allocates memory for the + * lists and bounce buffers, retrieves the MAC address from the eeprom + * and assignes the device's methods. + * + ************************************************************************/ + + int TLan_Init( struct device *dev ) + { + int dma_size; + int err; + int i; + TLanPrivateInfo *priv; + + priv = (TLanPrivateInfo *) dev->priv; + + err = check_region( dev->base_addr, 0x10 ); + if ( err ) { + printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", dev->name, dev->base_addr, 0x10 ); + return -EIO; + } + request_region( dev->base_addr, 0x10, TLanSignature ); + + dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); + priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); + if ( priv->dmaStorage == NULL ) { + printk( "TLAN: Could not allocate lists and buffers for %s.\n", dev->name ); + return -ENOMEM; + } + memset( priv->dmaStorage, 0, dma_size ); + priv->rxList = (TLanList *) ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); + priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; + priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); + priv->txBuffer = priv->rxBuffer + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); + + err = 0; + for ( i = 0; i < 6 ; i++ ) + err |= TLan_EeReadByte( dev->base_addr, (u8) 0x83 + i, (u8 *) &dev->dev_addr[i] ); + if ( err ) + printk( "TLAN: %s: Error reading MAC Address from eeprom: %d\n", dev->name, err ); + dev->addr_len = 6; + + dev->open = &TLan_Open; + dev->hard_start_xmit = &TLan_StartTx; + dev->stop = &TLan_Close; + dev->get_stats = &TLan_GetStats; + dev->set_multicast_list = &TLan_SetMulticastList; + + return 0; + + } /* TLan_Init */ + + + + + /************************************************************************* + * TLan_Open + * + * Returns: 0 on success, error code otherwise. + * Parms: dev Structure of device to be opened. + * + * This routine puts the driver and TLAN adapter in a state where it is + * ready to send and receive packets. It allocates the IRQ, resets and + * brings the adapter out of reset, and allows interrupts. It also + * delays the startup for autonegotiation or sends a Rx GO command to + * the adapter, as appropriate. + * + ************************************************************************/ + + int TLan_Open( struct device *dev ) + { + int err; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); + if ( err ) { + printk( "TLAN: %s: IRQ %d already in use.\n", dev->name, dev->irq ); + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* NOTE: It might not be necessary to read the stats before a + reset if you don't care what the values are. + */ + TLan_ResetLists( dev ); + TLan_ReadAndClearStats( dev, TLAN_IGNORE ); + TLan_Reset( dev ); + TLan_SetMac( dev, 0, dev->dev_addr ); + outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + if ( debug >= 1 ) + outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + + init_timer( &priv->timer ); + priv->timer.data = (unsigned long) dev; + priv->timer.function = &TLan_Timer; + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + TLAN_DBG( 1, "TLAN: RX GO\n" ); + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + + TLAN_DBG( 1, "TLAN: Device %s opened.\n", dev->name ); + + return 0; + + } /* TLan_Open */ + + + + + /************************************************************************* + * TLan_StartTx + * + * Returns: 0 on success, non-zero on failure. + * Parms: skb A pointer to the sk_buff containing the frame to + * be sent. + * dev The device to send the data on. + * + * This function adds a frame to the Tx list to be sent ASAP. First it + * verifies that the adapter is ready and there is room in the queue. + * Then it sets up the next available list, copies the frame to the + * corresponding buffer. If the adapter Tx channel is idle, it gives + * adapter a Tx Go command on the list, otherwise it sets the forward + * address of the previous list to point to this one. Then it frees + * the sk_buff. + * + ************************************************************************/ + + int TLan_StartTx( struct sk_buff *skb, struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *tail_list; + u8 *tail_buffer; + int pad; + + // printk( "Entering StartTx\n" ); + if ( ! priv->phyOnline ) { + dev_kfree_skb( skb, FREE_WRITE ); + return 0; + } + + tail_list = priv->txList + priv->txTail; + if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { + // printk( "TLAN: %s TX is busy, Head=%d Tail=%d\n", dev->name, priv->txHead, priv->txTail ); + dev->tbusy = 1; + // printk( "TLAN: Tx is busy.\n"); + priv->txBusyCount++; + return 1; + } + tail_list->forward = 0; + tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); + memcpy( tail_buffer, skb->data, skb->len ); + pad = TLAN_MIN_FRAME_SIZE - skb->len; + if ( pad > 0 ) { + tail_list->frameSize = (u16) skb->len + pad; + tail_list->buffer[0].count = (u32) skb->len; + tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad; + tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer ); + } else { + tail_list->frameSize = (u16) skb->len; + tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len; + tail_list->buffer[1].count = 0; + tail_list->buffer[1].address = 0; + } + // are we transferring? + cli(); + tail_list->cStat = TLAN_CSTAT_READY; + if ( ! priv->txInProgress ) { + priv->txInProgress = 1; + outw( 0x4, dev->base_addr + TLAN_HOST_INT ); + // printk("TLAN: Sending GO for 0%d\n", priv->txTail ); + outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); + } else { + // printk("TLAN: Adding 0%d\n", priv->txTail ); + // Assign previous list to point to this one. If previous has + // already been read, the EOC check in the TX EOF interrupt handler + // will start another transfer. + if ( priv->txTail == 0 ) + ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); + else + ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); + } + sti(); + priv->txTail++; + if ( priv->txTail >= TLAN_NUM_TX_LISTS ) + priv->txTail = 0; + + dev_kfree_skb( skb, FREE_WRITE ); + + dev->trans_start = jiffies; + // printk( "Leaving StartTx\n" ); + return 0; + + } /* TLan_StartTx */ + + + + + /************************************************************************* + * TLan_HandleInterrupt + * + * Returns: Nothing + * Parms: irq The line on which the interrupt occurred. + * dev_id A pointer to the device assigned to this irq line. + * regs ??? + * + * This function handles an interrupt generated by its assigned TLAN + * adapter. The function deactivates interrupts on its adapter, records + * the type of interrupt, executes the appropriate subhandler, and + * acknowdges the interrupt to the adapter (thus re-enabling adapter + * interrupts. + * + ************************************************************************/ + + void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) + { + u32 ack; + struct device *dev; + u32 host_cmd; + u16 host_int; + int type; + + dev = (struct device *) dev_id; + + if ( dev->interrupt ) + TLAN_DBG( 1, "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); + dev->interrupt++; + + cli(); + + host_int = inw( dev->base_addr + TLAN_HOST_INT ); + outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints + + type = ( host_int & TLAN_HI_IT_MASK ) >> 2; + + ack = TLanIntVector[type]( dev, host_int ); + + sti(); + + if ( ack ) { + host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); + outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); + } + + dev->interrupt--; + + } /* TLan_HandleInterrupts */ + + + + + /************************************************************************* + * TLan_Close + * + * Returns: An error code. + * Parms: dev The device structure of the device to close. + * + * This function shuts down the adapter. It records any stats, puts + * the adapter into reset state, deactivates its time as needed, and + * frees the irq it is using. + * + ************************************************************************/ + + int TLan_Close(struct device *dev) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + dev->start = 0; + dev->tbusy = 1; + + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + if ( priv->timerSetAt != 0 ) + del_timer( &priv->timer ); + free_irq( dev->irq, dev ); + TLAN_DBG( 1, "TLAN: Device %s closed.\n", dev->name ); + + MOD_DEC_USE_COUNT; + + return 0; + + } /* TLan_Close */ + + + + + /************************************************************************* + * TLan_GetStats + * + * Returns: A pointer to the device's statistics structure. + * Parms: dev The device structure to return the stats for. + * + * This function updates the devices statistics by reading the TLAN + * chip's onboard registers. Then it returns the address of the + * statistics structure. + * + ************************************************************************/ + + struct enet_statistics *TLan_GetStats( struct device *dev ) + { + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + + /* Should only read stats if open ? */ + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + printk( "TLAN: %s Rx EOC Count: %d\n", dev->name, priv->rxEocCount ); + printk( "TLAN: %s Tx Busy Count: %d\n", dev->name, priv->txBusyCount ); + // printk( "TLAN: Got stats for %s.\n", dev->name ); + TLan_PrintDio( dev->base_addr ); + TLan_PhyPrint( dev ); + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) + TLan_PrintList( priv->rxList + i, "RX", i ); + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) + TLan_PrintList( priv->txList + i, "TX", i ); + + return ( &( (TLanPrivateInfo *) dev->priv )->stats ); + + } /* TLan_GetStats */ + + + + + /************************************************************************* + * TLan_SetMulticastList + * + * Returns: Nothing + * Parms: dev The device structure to set the multicast list for. + * + * This function sets the TLAN adaptor to various receive modes. If the + * IFF_PROMISC flag is set, promiscuous mode is acitviated. Otherwise, + * promiscuous mode is turned off. If the IFF_ALLMULTI flag is set, then + * the hash table is set to receive all group addresses. Otherwise, the + * first three multicast addresses are stored in AREG_1-3, and the rest + * are selected via the hash table, as necessary. + * + ************************************************************************/ + + void TLan_SetMulticastList( struct device *dev ) + { + struct dev_mc_list *dmi = dev->mc_list; + u32 hash1 = 0; + u32 hash2 = 0; + int i; + u32 offset; + u8 tmp; + + if ( dev->flags & IFF_PROMISC ) { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF ); + } else { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF ); + if ( dev->flags & IFF_ALLMULTI ) { + for ( i = 0; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); + } else { + for ( i = 0; i < dev->mc_count; i++ ) { + if ( i < 3 ) { + TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr ); + } else { + offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); + if ( offset < 32 ) + hash1 |= ( 1 << offset ); + else + hash2 |= ( 1 << ( offset - 32 ) ); + } + dmi = dmi->next; + } + for ( ; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 ); + } + } + + } /* TLan_SetRxMode */ diff --git a/drivers/net/tlan.h b/drivers/net/tlan.h new file mode 100644 index 000000000..9919fd275 --- /dev/null +++ b/drivers/net/tlan.h @@ -0,0 +1,485 @@ +/******************************************************************** + * + * Linux ThunderLAN Driver + * + * tlan.h + * by James Banks, james.banks@caldera.com + * + * (C) 1997 Caldera, Inc. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4, colums>=132 + * + ********************************************************************/ + +#include <asm/io.h> +#include <asm/types.h> +#include <linux/netdevice.h> + + + + /***************************************************************** + * TLan Definitions + * + ****************************************************************/ + + #define FALSE 0 + #define TRUE 1 + + #define TLAN_MIN_FRAME_SIZE 64 + #define TLAN_MAX_FRAME_SIZE 1600 + + #define TLAN_NUM_RX_LISTS 4 + #define TLAN_NUM_TX_LISTS 8 + + #define TLAN_IGNORE 0 + #define TLAN_RECORD 1 + + #define TLAN_DBG(lvl, format, args...) if ( debug >= lvl ) printk( format, ##args ); + + + + + /***************************************************************** + * Device Identification Definitions + * + ****************************************************************/ + + /* NOTE: These should be moved to pci.h someday */ + #define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 + #define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 + #define PCI_DEVICE_ID_NETFLEX_3_INTEGRATED 0xAE35 + + + typedef struct tlan_pci_id { + u16 vendorId; + u16 deviceId; + char *deviceName; + } TLanPciId; + + + + + /***************************************************************** + * Rx/Tx List Definitions + * + ****************************************************************/ + + #define TLAN_BUFFERS_PER_LIST 10 + #define TLAN_LAST_BUFFER 0x80000000 + #define TLAN_CSTAT_UNUSED 0x8000 + #define TLAN_CSTAT_FRM_CMP 0x4000 + #define TLAN_CSTAT_READY 0x3000 + #define TLAN_CSTAT_EOC 0x0800 + #define TLAN_CSTAT_RX_ERROR 0x0400 + #define TLAN_CSTAT_PASS_CRC 0x0200 + #define TLAN_CSTAT_DP_PR 0x0100 + + + typedef struct tlan_buffer_ref_tag { + u32 count; + u32 address; + } TLanBufferRef; + + + typedef struct tlan_list_tag { + u32 forward; + u16 cStat; + u16 frameSize; + TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST]; + } TLanList; + + + typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; + + + + + /***************************************************************** + * PHY definitions + * + ****************************************************************/ + + #define TLAN_PHY_MAX_ADDR 0x1F + #define TLAN_PHY_INTERNAL 0x1F + + #define TLAN_PHY_ACTIVITY 0x00000001 + #define TLAN_PHY_AUTONEG 0x00000002 + + + typedef int (TLanPhyFunc)( struct device * ); + + + typedef struct tlan_phy_id_entry_tag { + u16 idHi; + u16 idLo; + TLanPhyFunc *check; + TLanPhyFunc *service; + u32 flags; + } TLanPhyIdEntry; + + + + + /***************************************************************** + * TLAN Private Information Structure + * + ****************************************************************/ + + typedef struct tlan_private_tag { + struct device *nextDevice; + void *dmaStorage; + u8 *padBuffer; + TLanList *rxList; + u8 *rxBuffer; + u32 rxHead; + u32 rxTail; + u32 rxEocCount; + TLanList *txList; + u8 *txBuffer; + u32 txHead; + u32 txInProgress; + u32 txTail; + u32 txBusyCount; + u32 phyAddr; + u32 phyEntry; + u32 phyOnline; + u32 phyFlags; + TLanPhyFunc *phyCheck; + TLanPhyFunc *phyService; + u32 timerSetAt; + u32 timerType; + struct timer_list timer; + struct enet_statistics stats; + u32 pciEntry; + u8 pciRevision; + u8 pciBus; + u8 pciDeviceFn; + char devName[8]; + } TLanPrivateInfo; + + + + + /***************************************************************** + * TLan Driver Timer Definitions + * + ****************************************************************/ + + #define TLAN_TIMER_LINK 1 + #define TLAN_TIMER_ACT 2 + + #define TLAN_TIMER_LINK_DELAY 230 + #define TLAN_TIMER_ACT_DELAY 10 + + + + + /***************************************************************** + * TLan Driver Eeprom Definitions + * + ****************************************************************/ + + #define TLAN_EEPROM_ACK 0 + #define TLAN_EEPROM_STOP 1 + + + + + /***************************************************************** + * Host Register Offsets and Contents + * + ****************************************************************/ + + #define TLAN_HOST_CMD 0x00 + #define TLAN_HC_GO 0x80000000 + #define TLAN_HC_STOP 0x40000000 + #define TLAN_HC_ACK 0x20000000 + #define TLAN_HC_CS_MASK 0x1FE00000 + #define TLAN_HC_EOC 0x00100000 + #define TLAN_HC_RT 0x00080000 + #define TLAN_HC_NES 0x00040000 + #define TLAN_HC_AD_RST 0x00008000 + #define TLAN_HC_LD_TMR 0x00004000 + #define TLAN_HC_LD_THR 0x00002000 + #define TLAN_HC_REQ_INT 0x00001000 + #define TLAN_HC_INT_OFF 0x00000800 + #define TLAN_HC_INT_ON 0x00000400 + #define TLAN_HC_AC_MASK 0x000000FF + #define TLAN_CH_PARM 0x04 + #define TLAN_DIO_ADR 0x08 + #define TLAN_DA_ADR_INC 0x8000 + #define TLAN_DA_RAM_ADR 0x4000 + #define TLAN_HOST_INT 0x0A + #define TLAN_HI_IV_MASK 0x1FE0 + #define TLAN_HI_IT_MASK 0x001C + #define TLAN_DIO_DATA 0x0C + + +/* ThunderLAN Internal Register DIO Offsets */ + +#define TLAN_NET_CMD 0x00 +#define TLAN_NET_CMD_NRESET 0x80 +#define TLAN_NET_CMD_NWRAP 0x40 +#define TLAN_NET_CMD_CSF 0x20 +#define TLAN_NET_CMD_CAF 0x10 +#define TLAN_NET_CMD_NOBRX 0x08 +#define TLAN_NET_CMD_DUPLEX 0x04 +#define TLAN_NET_CMD_TRFRAM 0x02 +#define TLAN_NET_CMD_TXPACE 0x01 +#define TLAN_NET_SIO 0x01 +#define TLAN_NET_SIO_MINTEN 0x80 +#define TLAN_NET_SIO_ECLOK 0x40 +#define TLAN_NET_SIO_ETXEN 0x20 +#define TLAN_NET_SIO_EDATA 0x10 +#define TLAN_NET_SIO_NMRST 0x08 +#define TLAN_NET_SIO_MCLK 0x04 +#define TLAN_NET_SIO_MTXEN 0x02 +#define TLAN_NET_SIO_MDATA 0x01 +#define TLAN_NET_STS 0x02 +#define TLAN_NET_STS_MIRQ 0x80 +#define TLAN_NET_STS_HBEAT 0x40 +#define TLAN_NET_STS_TXSTOP 0x20 +#define TLAN_NET_STS_RXSTOP 0x10 +#define TLAN_NET_STS_RSRVD 0x0F +#define TLAN_NET_MASK 0x03 +#define TLAN_NET_MASK_MASK7 0x80 +#define TLAN_NET_MASK_MASK6 0x40 +#define TLAN_NET_MASK_MASK5 0x20 +#define TLAN_NET_MASK_MASK4 0x10 +#define TLAN_NET_MASK_RSRVD 0x0F +#define TLAN_NET_CONFIG 0x04 +#define TLAN_NET_CFG_RCLK 0x8000 +#define TLAN_NET_CFG_TCLK 0x4000 +#define TLAN_NET_CFG_BIT 0x2000 +#define TLAN_NET_CFG_RXCRC 0x1000 +#define TLAN_NET_CFG_PEF 0x0800 +#define TLAN_NET_CFG_1FRAG 0x0400 +#define TLAN_NET_CFG_1CHAN 0x0200 +#define TLAN_NET_CFG_MTEST 0x0100 +#define TLAN_NET_CFG_PHY_EN 0x0080 +#define TLAN_NET_CFG_MSMASK 0x007F +#define TLAN_MAN_TEST 0x06 +#define TLAN_DEF_VENDOR_ID 0x08 +#define TLAN_DEF_DEVICE_ID 0x0A +#define TLAN_DEF_REVISION 0x0C +#define TLAN_DEF_SUBCLASS 0x0D +#define TLAN_DEF_MIN_LAT 0x0E +#define TLAN_DEF_MAX_LAT 0x0F +#define TLAN_AREG_0 0x10 +#define TLAN_AREG_1 0x16 +#define TLAN_AREG_2 0x1C +#define TLAN_AREG_3 0x22 +#define TLAN_HASH_1 0x28 +#define TLAN_HASH_2 0x2C +#define TLAN_GOOD_TX_FRMS 0x30 +#define TLAN_TX_UNDERUNS 0x33 +#define TLAN_GOOD_RX_FRMS 0x34 +#define TLAN_RX_OVERRUNS 0x37 +#define TLAN_DEFERRED_TX 0x38 +#define TLAN_CRC_ERRORS 0x3A +#define TLAN_CODE_ERRORS 0x3B +#define TLAN_MULTICOL_FRMS 0x3C +#define TLAN_SINGLECOL_FRMS 0x3E +#define TLAN_EXCESSCOL_FRMS 0x40 +#define TLAN_LATE_COLS 0x41 +#define TLAN_CARRIER_LOSS 0x42 +#define TLAN_ACOMMIT 0x43 +#define TLAN_LED_REG 0x44 +#define TLAN_LED_ACT 0x10 +#define TLAN_LED_LINK 0x01 +#define TLAN_BSIZE_REG 0x45 +#define TLAN_MAX_RX 0x46 +#define TLAN_INT_DIS 0x48 +#define TLAN_ID_TX_EOC 0x04 +#define TLAN_ID_RX_EOF 0x02 +#define TLAN_ID_RX_EOC 0x01 + + + +/* ThunderLAN Interrupt Codes */ + +#define TLAN_INT_NUMBER_OF_INTS 8 + +#define TLAN_INT_NONE 0x0000 +#define TLAN_INT_TX_EOF 0x0001 +#define TLAN_INT_STAT_OVERFLOW 0x0002 +#define TLAN_INT_RX_EOF 0x0003 +#define TLAN_INT_DUMMY 0x0004 +#define TLAN_INT_TX_EOC 0x0005 +#define TLAN_INT_STATUS_CHECK 0x0006 +#define TLAN_INT_RX_EOC 0x0007 + + + +/* ThunderLAN MII Registers */ + +/* Generic MII/PHY Registers */ + +#define MII_GEN_CTL 0x00 +#define MII_GC_RESET 0x8000 +#define MII_GC_LOOPBK 0x4000 +#define MII_GC_SPEEDSEL 0x2000 +#define MII_GC_AUTOENB 0x1000 +#define MII_GC_PDOWN 0x0800 +#define MII_GC_ISOLATE 0x0400 +#define MII_GC_AUTORSRT 0x0200 +#define MII_GC_DUPLEX 0x0100 +#define MII_GC_COLTEST 0x0080 +#define MII_GC_RESERVED 0x007F +#define MII_GEN_STS 0x01 +#define MII_GS_100BT4 0x8000 +#define MII_GS_100BTXFD 0x4000 +#define MII_GS_100BTXHD 0x2000 +#define MII_GS_10BTFD 0x1000 +#define MII_GS_10BTHD 0x0800 +#define MII_GS_RESERVED 0x07C0 +#define MII_GS_AUTOCMPLT 0x0020 +#define MII_GS_RFLT 0x0010 +#define MII_GS_AUTONEG 0x0008 +#define MII_GS_LINK 0x0004 +#define MII_GS_JABBER 0x0002 +#define MII_GS_EXTCAP 0x0001 +#define MII_GEN_ID_HI 0x02 +#define MII_GEN_ID_LO 0x03 +#define MII_GIL_OUI 0xFC00 +#define MII_GIL_MODEL 0x03F0 +#define MII_GIL_REVISION 0x000F +#define MII_AN_ADV 0x04 +#define MII_AN_LPA 0x05 +#define MII_AN_EXP 0x06 + +/* ThunderLAN Specific MII/PHY Registers */ + +#define TLAN_TLPHY_ID 0x10 +#define TLAN_TLPHY_CTL 0x11 +#define TLAN_TC_IGLINK 0x8000 +#define TLAN_TC_SWAPOL 0x4000 +#define TLAN_TC_AUISEL 0x2000 +#define TLAN_TC_SQEEN 0x1000 +#define TLAN_TC_MTEST 0x0800 +#define TLAN_TC_RESERVED 0x07F8 +#define TLAN_TC_NFEW 0x0004 +#define TLAN_TC_INTEN 0x0002 +#define TLAN_TC_TINT 0x0001 +#define TLAN_TLPHY_STS 0x12 +#define TLAN_TS_MINT 0x8000 +#define TLAN_TS_PHOK 0x4000 +#define TLAN_TS_POLOK 0x2000 +#define TLAN_TS_TPENERGY 0x1000 +#define TLAN_TS_RESERVED 0x0FFF + + + +/* Routines to access internal registers. */ + +inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3))); + +} /* TLan_DioRead8 */ + + + + +inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2))); + +} /* TLan_DioRead16 */ + + + + +inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inl(base_addr + TLAN_DIO_DATA)); + +} /* TLan_DioRead32 */ + + + + +inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3)); + +} + + + + +inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_ClearBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) & ~bit, port); +} + + + + +inline int TLan_GetBit(u8 bit, u16 port) +{ + return ((int) (inb_p(port) & bit)); +} + + + + +inline void TLan_SetBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) | bit, port); +} + + +inline u32 xor( u32 a, u32 b ) +{ + return ( ( a && ! b ) || ( ! a && b ) ); +} +#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) ) +#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) ) + +inline u32 TLan_HashFunc( u8 *a ) +{ + u32 hash; + + hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) ); + hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1; + hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2; + hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3; + hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4; + hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5; + + return hash; + +} + + + + +#endif diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c index f87beba00..03b7fc10c 100644 --- a/drivers/net/tulip.c +++ b/drivers/net/tulip.c @@ -1078,6 +1078,7 @@ tulip_rx(struct device *dev) skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; + lp->stats.rx_bytes+=skb->len; } lp->rx_ring[entry].status = TRING_OWN; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7118164b6..16dcfafa0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -204,7 +204,7 @@ struct pci_dev_info dev_info[] = { DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C576, "VT 82C576 3V"), DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"), - DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"), + DEVICE( VIA, VIA_82C586, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c index 77574991b..7adfac2da 100644 --- a/drivers/pnp/parport_procfs.c +++ b/drivers/pnp/parport_procfs.c @@ -1,4 +1,4 @@ -/* $Id: parport_procfs.c,v 1.3.2.6 1997/04/16 21:30:38 phil Exp $ +/* $Id: parport_procfs.c,v 1.2 1997/06/03 07:28:11 ralf Exp $ * Parallel port /proc interface code. * * Authors: David Campbell <campbell@tirian.che.curtin.edu.au> @@ -13,7 +13,6 @@ #include <asm/io.h> #include <asm/dma.h> -#include <linux/config.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/ioport.h> diff --git a/drivers/sbus/char/bwtwo.c b/drivers/sbus/char/bwtwo.c index ae81e1260..615fef33d 100644 --- a/drivers/sbus/char/bwtwo.c +++ b/drivers/sbus/char/bwtwo.c @@ -1,4 +1,4 @@ -/* $Id: bwtwo.c,v 1.16 1997/06/04 08:27:26 davem Exp $ +/* $Id: bwtwo.c,v 1.18 1997/07/17 02:21:43 davem Exp $ * bwtwo.c: bwtwo console driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -74,7 +74,8 @@ static int bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { - uint size, map_offset, r; + uint size, r; + unsigned long map_offset; int map_size; map_size = size = vma->vm_end - vma->vm_start; @@ -91,9 +92,10 @@ bwtwo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset = get_phys ((unsigned long) fb->base); r = io_remap_page_range (vma->vm_start, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + if (r) + return -EAGAIN; + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/cgfourteen.c b/drivers/sbus/char/cgfourteen.c index 2cb4c21c9..40fb9fd70 100644 --- a/drivers/sbus/char/cgfourteen.c +++ b/drivers/sbus/char/cgfourteen.c @@ -1,4 +1,4 @@ -/* $Id: cgfourteen.c,v 1.22 1997/06/04 08:27:27 davem Exp $ +/* $Id: cgfourteen.c,v 1.24 1997/07/17 02:21:44 davem Exp $ * cgfourteen.c: Sun SparcStation console support. * * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -166,7 +166,7 @@ cg14_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0; + unsigned long map_offset = 0; uint ram_size = fb->info.cg14.ramsize; printk ("RAMSIZE=%d\n", ram_size); @@ -269,11 +269,12 @@ cg14_mmap (struct inode *inode, struct file *file, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/cgsix.c b/drivers/sbus/char/cgsix.c index e53fcf09e..825d49b4a 100644 --- a/drivers/sbus/char/cgsix.c +++ b/drivers/sbus/char/cgsix.c @@ -1,4 +1,4 @@ -/* $Id: cgsix.c,v 1.30 1997/06/04 08:27:28 davem Exp $ +/* $Id: cgsix.c,v 1.35 1997/07/17 02:21:45 davem Exp $ * cgsix.c: cgsix frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -233,12 +233,33 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0; - + unsigned long map_offset = 0; + size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) return -ENXIO; +#ifdef __sparc_v9__ + /* Try to align RAM */ +#define ALIGNMENT 0x80000 + map_offset = vma->vm_offset + size; + if (vma->vm_offset <= CG6_RAM && map_offset >= CG6_RAM + fb->type.fb_size) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + int alignment = ALIGNMENT - ((vma->vm_start + CG6_RAM - vma->vm_offset) & (ALIGNMENT - 1)); + int sz = 0, fbsz; + + if (alignment == ALIGNMENT) alignment = 0; + fbsz = ((fb->type.fb_size + ALIGNMENT - 1) & ~(ALIGNMENT - 1)); + if (map_offset < CG6_RAM + fbsz) + sz = fbsz - map_offset + CG6_RAM; + if ((sz || alignment) && (!vmm || vmm->vm_start >= vma->vm_end + sz + alignment)) { + vma->vm_start += alignment; + vma->vm_end += alignment + sz; + } + } +#undef ALIGNMENT +#endif + /* To stop the swapper from even considering these pages */ vma->vm_flags |= FB_MMAP_VM_FLAGS; @@ -247,7 +268,7 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, switch (vma->vm_offset+page){ case CG6_TEC: map_size = PAGE_SIZE; - map_offset = get_phys ((unsigned long)fb->info.cg6.tec); + map_offset = get_phys ((unsigned long)fb->info.cg6.tec) & PAGE_MASK; break; case CG6_FBC: map_size = PAGE_SIZE; @@ -259,18 +280,25 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, break; case CG6_THC: map_size = PAGE_SIZE; - map_offset = get_phys ((unsigned long)fb->info.cg6.thc); + map_offset = get_phys ((unsigned long)fb->info.cg6.thc) & PAGE_MASK; break; case CG6_BTREGS: map_size = PAGE_SIZE; map_offset = get_phys ((unsigned long)fb->info.cg6.bt); break; + + /* For Ultra, make sure the following two are right. + * The above two happen to work out (for example FBC and + * TEC will get mapped by one I/O page mapping because + * of the 8192 byte page size, same for FHC/THC. -DaveM + */ + case CG6_DHC: - map_size = PAGE_SIZE * 40; + map_size = /* PAGE_SIZE * 40 */ (4096 * 40); map_offset = get_phys ((unsigned long)fb->info.cg6.dhc); break; case CG6_ROM: - map_size = PAGE_SIZE * 16; + map_size = /* PAGE_SIZE * 16 */ (4096 * 16); map_offset = get_phys ((unsigned long)fb->info.cg6.rom); break; case CG6_RAM: @@ -293,11 +321,12 @@ cg6_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -449,14 +478,22 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io)) sizeof (struct bt_regs), "cgsix_dac", cg6_io, 0); cg6info->fhc = sparc_alloc_io (cg6+CG6_FHC_OFFSET, 0, sizeof (int), "cgsix_fhc", cg6_io, 0); +#if PAGE_SHIFT <= 12 cg6info->thc = sparc_alloc_io (cg6+CG6_THC_OFFSET, 0, sizeof (struct cg6_thc), "cgsix_thc", cg6_io, 0); +#else + cg6info->thc = (struct cg6_thc *)(((char *)cg6info->fhc)+0x1000); +#endif + cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0, + 0x1000, "cgsix_fbc", cg6_io, 0); +#if PAGE_SHIFT <= 12 cg6info->tec = sparc_alloc_io (cg6+CG6_TEC_OFFSET, 0, sizeof (struct cg6_tec), "cgsix_tec", cg6_io, 0); +#else + cg6info->tec = (struct cg6_tec *)(((char *)cg6info->fbc)+0x1000); +#endif cg6info->dhc = sparc_alloc_io (cg6+CG6_DHC_OFFSET, 0, 0x40000, "cgsix_dhc", cg6_io, 0); - cg6info->fbc = sparc_alloc_io (cg6+CG6_FBC_OFFSET, 0, - 0x1000, "cgsix_fbc", cg6_io, 0); cg6info->rom = sparc_alloc_io (cg6+CG6_ROM_OFFSET, 0, 0x10000, "cgsix_rom", cg6_io, 0); if (!fb->base) { @@ -477,6 +514,10 @@ __initfunc(void cg6_setup (fbinfo_t *fb, int slot, u32 cg6, int cg6_io)) cg6info->bt->addr = 0x07 << 24; cg6info->bt->control = 0x00 << 24; +#ifdef __sparc_v9__ + printk("VA %016lx ", fb->base); +#endif + printk("TEC Rev %x ", (cg6info->thc->thc_misc >> CG6_THC_MISC_REV_SHIFT) & CG6_THC_MISC_REV_MASK); diff --git a/drivers/sbus/char/cgthree.c b/drivers/sbus/char/cgthree.c index 0e1446c0e..aea1bff32 100644 --- a/drivers/sbus/char/cgthree.c +++ b/drivers/sbus/char/cgthree.c @@ -1,4 +1,4 @@ -/* $Id: cgthree.c,v 1.21 1997/06/04 08:27:29 davem Exp $ +/* $Id: cgthree.c,v 1.23 1997/07/17 02:21:46 davem Exp $ * cgtree.c: cg3 frame buffer driver * * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -96,7 +96,7 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0; + unsigned long map_offset = 0; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) @@ -128,11 +128,12 @@ cg3_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/creator.c b/drivers/sbus/char/creator.c index 4ff2caf31..086f9b40e 100644 --- a/drivers/sbus/char/creator.c +++ b/drivers/sbus/char/creator.c @@ -1,13 +1,13 @@ -/* - * creator.c: Linux/Sun Ultra Creator console support. - * - * Copyright (C) 1997 MIguel de Icaza (miguel@nuclecu.unam.mx) +/* $Id: creator.c,v 1.7 1997/07/17 02:21:47 davem Exp $ + * creator.c: Creator/Creator3D frame buffer driver * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/kd.h> #include <linux/tty.h> #include <linux/malloc.h> #include <linux/proc_fs.h> +#include <linux/config.h> #include <asm/sbus.h> #include <asm/io.h> @@ -20,31 +20,575 @@ #include <linux/selection.h> #include <linux/console_struct.h> #include "fb.h" +#include "cg_common.h" + +#define FFB_SFB8R_VOFF 0x00000000 +#define FFB_SFB8G_VOFF 0x00400000 +#define FFB_SFB8B_VOFF 0x00800000 +#define FFB_SFB8X_VOFF 0x00c00000 +#define FFB_SFB32_VOFF 0x01000000 +#define FFB_SFB64_VOFF 0x02000000 +#define FFB_FBC_REGS_VOFF 0x04000000 +#define FFB_BM_FBC_REGS_VOFF 0x04002000 +#define FFB_DFB8R_VOFF 0x04004000 +#define FFB_DFB8G_VOFF 0x04404000 +#define FFB_DFB8B_VOFF 0x04804000 +#define FFB_DFB8X_VOFF 0x04c04000 +#define FFB_DFB24_VOFF 0x05004000 +#define FFB_DFB32_VOFF 0x06004000 +#define FFB_DFB422A_VOFF 0x07004000 /* DFB 422 mode write to A */ +#define FFB_DFB422AD_VOFF 0x07804000 /* DFB 422 mode with line doubling */ +#define FFB_DFB24B_VOFF 0x08004000 /* DFB 24bit mode write to B */ +#define FFB_DFB422B_VOFF 0x09004000 /* DFB 422 mode write to B */ +#define FFB_DFB422BD_VOFF 0x09804000 /* DFB 422 mode with line doubling */ +#define FFB_SFB16Z_VOFF 0x0a004000 /* 16bit mode Z planes */ +#define FFB_SFB8Z_VOFF 0x0a404000 /* 8bit mode Z planes */ +#define FFB_SFB422_VOFF 0x0ac04000 /* SFB 422 mode write to A/B */ +#define FFB_SFB422D_VOFF 0x0b404000 /* SFB 422 mode with line doubling */ +#define FFB_FBC_KREGS_VOFF 0x0bc04000 +#define FFB_DAC_VOFF 0x0bc06000 +#define FFB_PROM_VOFF 0x0bc08000 +#define FFB_EXP_VOFF 0x0bc18000 + +#define FFB_SFB8R_POFF 0x04000000 +#define FFB_SFB8G_POFF 0x04400000 +#define FFB_SFB8B_POFF 0x04800000 +#define FFB_SFB8X_POFF 0x04c00000 +#define FFB_SFB32_POFF 0x05000000 +#define FFB_SFB64_POFF 0x06000000 +#define FFB_FBC_REGS_POFF 0x00600000 +#define FFB_BM_FBC_REGS_POFF 0x00600000 +#define FFB_DFB8R_POFF 0x01000000 +#define FFB_DFB8G_POFF 0x01400000 +#define FFB_DFB8B_POFF 0x01800000 +#define FFB_DFB8X_POFF 0x01c00000 +#define FFB_DFB24_POFF 0x02000000 +#define FFB_DFB32_POFF 0x03000000 +#define FFB_FBC_KREGS_POFF 0x00610000 +#define FFB_DAC_POFF 0x00400000 +#define FFB_PROM_POFF 0x00000000 +#define FFB_EXP_POFF 0x00200000 + +#define FFB_Y_BYTE_ADDR_SHIFT 11 +#define FFB_Y_ADDR_SHIFT 13 + +#define FFB_PPC_ACE_DISABLE 1 +#define FFB_PPC_ACE_AUX_ADD 3 +#define FFB_PPC_ACE_SHIFT 18 +#define FFB_PPC_DCE_DISABLE 2 +#define FFB_PPC_DCE_SHIFT 16 +#define FFB_PPC_ABE_DISABLE 2 +#define FFB_PPC_ABE_SHIFT 14 +#define FFB_PPC_VCE_DISABLE 1 +#define FFB_PPC_VCE_2D 2 +#define FFB_PPC_VCE_SHIFT 12 +#define FFB_PPC_APE_DISABLE 2 +#define FFB_PPC_APE_SHIFT 10 +#define FFB_PPC_CS_VARIABLE 2 +#define FFB_PPC_CS_SHIFT 0 + +#define FFB_FBC_WB_A 1 +#define FFB_FBC_WB_SHIFT 29 +#define FFB_FBC_PGE_MASK 3 +#define FFB_FBC_BE_SHIFT 4 +#define FFB_FBC_GE_SHIFT 2 +#define FFB_FBC_RE_SHIFT 0 + +#define FFB_ROP_NEW 0x83 +#define FFB_ROP_RGB_SHIFT 0 + +#define FFB_UCSR_FIFO_MASK 0x00000fff +#define FFB_UCSR_RP_BUSY 0x02000000 + +struct ffb_fbc { + u8 xxx1[0x200]; + volatile u32 ppc; + u8 xxx2[0x50]; + volatile u32 fbc; + volatile u32 rop; + u8 xxx3[0x34]; + volatile u32 pmask; + u8 xxx4[12]; + volatile u32 clip0min; + volatile u32 clip0max; + volatile u32 clip1min; + volatile u32 clip1max; + volatile u32 clip2min; + volatile u32 clip2max; + volatile u32 clip3min; + volatile u32 clip3max; + u8 xxx5[0x3c]; + volatile u32 unk1; + u8 xxx6[0x500]; + volatile u32 unk2; + u8 xxx7[0xfc]; + volatile u32 ucsr; +}; + +struct ffb_dac { + volatile u32 type; + volatile u32 value; + volatile u32 type2; + volatile u32 value2; +}; + +static void +ffb_restore_palette (fbinfo_t *fbinfo) +{ +} + +static void ffb_blitc(unsigned short, int, int); +static void ffb_setw(int, int, unsigned short, int); +static void ffb_cpyw(int, int, unsigned short *, int); +static void ffb_fill(int, int, int *); + +static struct { + unsigned long voff; + unsigned long poff; + unsigned long size; +} ffbmmap [] = { + { FFB_SFB8R_VOFF, FFB_SFB8R_POFF, 0x0400000 }, + { FFB_SFB8G_VOFF, FFB_SFB8G_POFF, 0x0400000 }, + { FFB_SFB8B_VOFF, FFB_SFB8B_POFF, 0x0400000 }, + { FFB_SFB8X_VOFF, FFB_SFB8X_POFF, 0x0400000 }, + { FFB_SFB32_VOFF, FFB_SFB32_POFF, 0x1000000 }, + { FFB_SFB64_VOFF, FFB_SFB64_POFF, 0x2000000 }, + { FFB_FBC_REGS_VOFF, FFB_FBC_REGS_POFF, 0x0002000 }, + { FFB_BM_FBC_REGS_VOFF, FFB_BM_FBC_REGS_POFF, 0x0002000 }, + { FFB_DFB8R_VOFF, FFB_DFB8R_POFF, 0x0400000 }, + { FFB_DFB8G_VOFF, FFB_DFB8G_POFF, 0x0400000 }, + { FFB_DFB8B_VOFF, FFB_DFB8B_POFF, 0x0400000 }, + { FFB_DFB8X_VOFF, FFB_DFB8X_POFF, 0x0400000 }, + { FFB_DFB24_VOFF, FFB_DFB24_POFF, 0x1000000 }, + { FFB_DFB32_VOFF, FFB_DFB32_POFF, 0x1000000 }, + { FFB_FBC_KREGS_VOFF, FFB_FBC_KREGS_POFF, 0x0002000 }, + { FFB_DAC_VOFF, FFB_DAC_POFF, 0x0002000 }, + { FFB_PROM_VOFF, FFB_PROM_POFF, 0x0010000 }, + { FFB_EXP_VOFF, FFB_EXP_POFF, 0x0002000 } +}; + +/* Ugh: X wants to mmap a bunch of cute stuff at the same time :-( */ +/* So, we just mmap the things that are being asked for */ +static int +ffb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, + long base, fbinfo_t *fb) +{ + uint size, page, r, map_size; + unsigned long map_offset = 0; + int i; + + size = vma->vm_end - vma->vm_start; + if (vma->vm_offset & ~PAGE_MASK) + return -ENXIO; + + /* Try to align RAM */ +#define ALIGNMENT 0x400000 + map_offset = vma->vm_offset + size; + if (vma->vm_offset < FFB_FBC_REGS_VOFF) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + int alignment = ALIGNMENT - ((vma->vm_start - vma->vm_offset) & (ALIGNMENT - 1)); + + if (alignment == ALIGNMENT) alignment = 0; + if (alignment && (!vmm || vmm->vm_start >= vma->vm_end + alignment)) { + vma->vm_start += alignment; + vma->vm_end += alignment; + } + } +#undef ALIGNMENT + + /* To stop the swapper from even considering these pages */ + vma->vm_flags |= FB_MMAP_VM_FLAGS; + + /* Each page, see which map applies */ + for (page = 0; page < size; ){ + map_size = 0; + for (i = 0; i < sizeof (ffbmmap) / sizeof (ffbmmap[0]); i++) + if (ffbmmap[i].voff == vma->vm_offset+page) { + map_size = ffbmmap[i].size; + map_offset = fb->info.ffb.physbase + ffbmmap[i].poff; + } + + if (!map_size){ + page += PAGE_SIZE; + continue; + } + if (page + map_size > size) + map_size = size - page; + r = io_remap_page_range (vma->vm_start+page, + map_offset, + map_size, vma->vm_page_prot, 0); + if (r) + return -EAGAIN; + page += map_size; + } + + vma->vm_dentry = dget(file->f_dentry); + return 0; +} + +/* XXX write body of these two... */ +static inline int +ffb_wid_get (fbinfo_t *fb, struct fb_wid_list *wl) +{ + struct fb_wid_item *wi; + struct fb_wid_list wlt; + struct fb_wid_item wit[30]; + char *km = NULL; + int i, j; + u32 l; + int err; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (copy_from_user (&wlt, wl, 2 * sizeof (__u32)) || + __get_user ((long)wlt.wl_list, (((__u32 *)wl)+2))) + return -EFAULT; + } else +#endif + if (copy_from_user (&wlt, wl, sizeof (wlt))) + return -EFAULT; + if (wlt.wl_count <= 30) { + if (copy_from_user (wit, wlt.wl_list, wlt.wl_count * sizeof(*wi))) + return -EFAULT; + wi = wit; + } else if (wlt.wl_count > 120) + return -EINVAL; + else { + wi = (struct fb_wid_item *) km = kmalloc (wlt.wl_count * sizeof (*wi), GFP_KERNEL); + if (!wi) return -ENOMEM; + if (copy_from_user (wi, wlt.wl_list, wlt.wl_count * sizeof(*wi))) { + kfree (wi); + return -EFAULT; + } + } + for (i = 0; i < wlt.wl_count; i++, wi++) { + switch (wi->wi_type) { + case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break; + case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break; + default: return -EINVAL; + } + wi->wi_attrs = 0xffff; + for (j = 0; j < 32; j++) + wi->wi_values [j] = 0; + } + err = 0; + if (copy_to_user (wlt.wl_list, km ? km : (char *)wit, wlt.wl_count * sizeof (*wi))) + err = -EFAULT; + if (km) + kfree (km); + return err; +} + +static inline int +ffb_wid_put (fbinfo_t *fb, struct fb_wid_list *wl) +{ + struct fb_wid_item *wi; + struct fb_wid_list wlt; + struct fb_wid_item wit[30]; + char *km = NULL; + int i, j; + u32 l; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (copy_from_user (&wlt, wl, 2 * sizeof (__u32)) || + __get_user ((long)wlt.wl_list, (((__u32 *)wl)+2))) + return -EFAULT; + } else +#endif + if (copy_from_user (&wlt, wl, sizeof (wlt))) + return -EFAULT; + if (wlt.wl_count <= 30) { + if (copy_from_user (wit, wlt.wl_list, wlt.wl_count * sizeof(*wi))) + return -EFAULT; + wi = wit; + } else if (wlt.wl_count > 120) + return -EINVAL; + else { + wi = (struct fb_wid_item *) km = kmalloc (wlt.wl_count * sizeof (*wi), GFP_KERNEL); + if (!wi) return -ENOMEM; + if (copy_from_user (wi, wlt.wl_list, wlt.wl_count * sizeof(*wi))) { + kfree (wi); + return -EFAULT; + } + } + for (i = 0; i < wlt.wl_count; i++, wi++) { + switch (wi->wi_type) { + case FB_WID_DBL_8: j = (wi->wi_index & 0xf) + 0x40; break; + case FB_WID_DBL_24: j = wi->wi_index & 0x3f; break; + default: return -EINVAL; + } + /* x = wi->wi_values [j] */; + } + if (km) + kfree (km); + return 0; +} + +static inline void +ffb_curs_enable (fbinfo_t *fb, int enable) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + + dac->type2 = 0x100; + if (fb->info.ffb.dac_rev <= 2) + dac->value2 = enable ? 3 : 0; + else + dac->value2 = enable ? 0 : 3; +} -__initfunc(void creator_setup (fbinfo_t *fb, int slot, int con_node, unsigned long creator, int creator_io)) -{ - uint bases [2]; - unsigned long *p; - - if (!creator) { - prom_getproperty (con_node, "address", (char *) &bases[0], 4); - prom_printf ("Bases: %x %x\n", bases [0], bases [1]); - p = (unsigned long *) creator = bases[0]; - fb->base = creator; - fb->base = 0xff168000; - } - - fb->type.fb_cmsize = 256; - fb->mmap = 0; - fb->loadcmap = 0; - fb->setcursor = 0; - fb->setcursormap = 0; - fb->setcurshape = 0; - fb->ioctl = 0; - fb->switch_from_graph = 0; - fb->postsetup = sun_cg_postsetup; - fb->reset = 0; - fb->blank = 0; - fb->unblank = 0; - fb->type.fb_depth = 8; +static void +ffb_setcursormap (fbinfo_t *fb, unsigned char *red, + unsigned char *green, + unsigned char *blue) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + + ffb_curs_enable (fb, 0); + dac->type2 = 0x102; + dac->value2 = (red[0] | (green[0]<<8) | (blue[0]<<16)); + dac->value2 = (red[1] | (green[1]<<8) | (blue[1]<<16)); +} + +/* Set cursor shape */ +static void +ffb_setcurshape (fbinfo_t *fb) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + int i, j; + + ffb_curs_enable (fb, 0); + for (j = 0; j < 2; j++) { + dac->type2 = j ? 0 : 0x80; + for (i = 0; i < 0x40; i++) { + if (fb->cursor.size.fbx <= 32) { + dac->value2 = fb->cursor.bits [j][i]; + dac->value2 = 0; + } else { + dac->value2 = fb->cursor.bits [j][2*i]; + dac->value2 = fb->cursor.bits [j][2*i+1]; + } + } + } +} + +/* Load cursor information */ +static void +ffb_setcursor (fbinfo_t *fb) +{ + struct ffb_dac *dac = fb->info.ffb.dac; + struct cg_cursor *c = &fb->cursor; + + dac->type2 = 0x104; +/* Should this be just 0x7ff?? Should I do some margin handling and setcurshape + in that case? */ + dac->value2 = (((c->cpos.fbx - c->chot.fbx) & 0xffff) << 16) + |((c->cpos.fby - c->chot.fby) & 0xffff); + ffb_curs_enable (fb, fb->cursor.enable); +} + +static void +ffb_blank (fbinfo_t *fb) +{ +/* XXX Write this */ +} + +static void +ffb_unblank (fbinfo_t *fb) +{ +/* XXX Write this */ +} + +static int ffb_clutstore (fbinfo_t *fb, int offset, int count) +{ + int i; + u32 *clut = fb->info.ffb.clut + offset; + struct ffb_dac *dac = fb->info.ffb.dac; + + dac->type = 0x2000 | offset; + for (i = 0; i < count; i++) + dac->value = *clut++; /* Feed the colors in :)) */ + return 0; +} + +static int ffb_clutpost (fbinfo_t *fb, struct fb_clut *fc) +{ + int i; + u32 *clut; + struct fb_clut fct; + u8 red[256], green[256], blue[256]; + +#ifdef CONFIG_SPARC32_COMPAT + if (current->tss.flags & SPARC_FLAG_32BIT) { + if (copy_from_user (&fct, fc, 3 * sizeof (__u32)) || + __get_user ((long)fct.red, &(((struct fb_clut32 *)fc)->red)) || + __get_user ((long)fct.green, &(((struct fb_clut32 *)fc)->green)) || + __get_user ((long)fct.blue, &(((struct fb_clut32 *)fc)->blue))) + return -EFAULT; + } else +#endif + if (copy_from_user (&fct, fc, sizeof (struct fb_clut))) + return -EFAULT; + i = fct.offset + fct.count; + if (fct.clutid || i <= 0 || i > 256) return -EINVAL; + if (copy_from_user (red, fct.red, fct.count) || + copy_from_user (green, fct.green, fct.count) || + copy_from_user (blue, fct.blue, fct.count)) + return -EFAULT; + clut = fb->info.ffb.clut + fct.offset; + for (i = 0; i < fct.count; i++) + *clut++ = ((red[i])|(green[i]<<8)|(blue[i]<<16)); + return ffb_clutstore (fb, fct.offset, fct.count); +} + +static int ffb_clutread (fbinfo_t *fb, struct fb_clut *fc) +{ +/* XXX write this */ + return 0; +} + +static void +ffb_loadcmap (fbinfo_t *fb, int index, int count) +{ + u32 *clut = fb->info.ffb.clut + index; + int i, j = count; + + for (i = index; j--; i++) + *clut++ = ((fb->color_map CM(i,0))) | + ((fb->color_map CM(i,1)) << 8) | + ((fb->color_map CM(i,2)) << 16); + ffb_clutstore (fb, index, count); +} + +/* Handle ffb-specific ioctls */ +static int +ffb_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long arg, fbinfo_t *fb) +{ + int i; + + switch (cmd) { + case FBIO_WID_GET: + return ffb_wid_get (fb, (struct fb_wid_list *)arg); + case FBIO_WID_PUT: + return ffb_wid_put (fb, (struct fb_wid_list *)arg); + case FFB_CLUTPOST: + return ffb_clutpost (fb, (struct fb_clut *)arg); + case FFB_CLUTREAD: + return ffb_clutread (fb, (struct fb_clut *)arg); + + default: + return -ENOSYS; + } +} + +void +ffb_reset (fbinfo_t *fb) +{ + struct ffb_info *ffb = &(fb->info.ffb); + int fifo; + + if (fb->setcursor) + sun_hw_hide_cursor (); + + while ((fifo = (ffb->fbc->ucsr & FFB_UCSR_FIFO_MASK)) < 8); + ffb->fbc->ppc = (FFB_PPC_ACE_DISABLE << FFB_PPC_ACE_SHIFT) | + (FFB_PPC_DCE_DISABLE << FFB_PPC_DCE_SHIFT) | + (FFB_PPC_ABE_DISABLE << FFB_PPC_ABE_SHIFT) | + (FFB_PPC_VCE_DISABLE << FFB_PPC_VCE_SHIFT) | + (FFB_PPC_APE_DISABLE << FFB_PPC_APE_SHIFT) | + (FFB_PPC_CS_VARIABLE << FFB_PPC_CS_SHIFT); + ffb->fbc->fbc = (FFB_FBC_WB_A << FFB_FBC_WB_SHIFT) | + (FFB_FBC_PGE_MASK << FFB_FBC_BE_SHIFT) | + (FFB_FBC_PGE_MASK << FFB_FBC_GE_SHIFT) | + (FFB_FBC_PGE_MASK << FFB_FBC_RE_SHIFT); + ffb->fbc->rop = (FFB_ROP_NEW << FFB_ROP_RGB_SHIFT); + ffb->fbc->pmask = 0x00ffffff; + while (ffb->fbc->ucsr & FFB_UCSR_RP_BUSY); +} + +__initfunc(static unsigned long ffb_postsetup (fbinfo_t *fb, unsigned long memory_start)) +{ + fb->info.ffb.clut = (u32 *)(memory_start); + fb->color_map = (u8 *)(memory_start+256*4+256); + return memory_start + 256*4 + 256*3; +} + +__initfunc(void creator_setup (fbinfo_t *fb, int slot, int ffb_node, unsigned long ffb, int ffb_io)) +{ + struct ffb_info *ffbinfo; + struct linux_prom64_registers regs[2*PROMREG_MAX]; + + if (prom_getproperty(ffb_node, "reg", (void *) regs, sizeof(regs)) <= 0) + return; + ffb = regs[0].phys_addr; + printk ("creator%d at 0x%016lx ", slot, ffb); + + fb->base = ffb; /* ??? */ + + /* Fill in parameters we left out */ + fb->type.fb_cmsize = 256; + fb->mmap = ffb_mmap; + fb->loadcmap = ffb_loadcmap; + fb->reset = ffb_reset; + fb->blank = ffb_blank; + fb->unblank = ffb_unblank; + fb->setcursor = ffb_setcursor; + fb->setcursormap = ffb_setcursormap; + fb->setcurshape = ffb_setcurshape; + fb->postsetup = ffb_postsetup; + fb->blitc = ffb_blitc; + fb->setw = ffb_setw; + fb->cpyw = ffb_cpyw; + fb->fill = ffb_fill; + fb->ioctl = ffb_ioctl; + fb->cursor.hwsize.fbx = 64; + fb->cursor.hwsize.fby = 64; + + ffbinfo = (struct ffb_info *) &fb->info.ffb; + + ffbinfo->physbase = ffb; + + ffbinfo->fbc = (struct ffb_fbc *)(ffb + FFB_FBC_REGS_POFF); + ffbinfo->dac = (struct ffb_dac *)(ffb + FFB_DAC_POFF); + + ffbinfo->dac->type = 0x8000; + ffbinfo->dac_rev = (ffbinfo->dac->value >> 0x1c); + + if (slot == sun_prom_console_id) + fb_restore_palette = ffb_restore_palette; + + /* Initialize Brooktree DAC */ + + printk("DAC %d\n", ffbinfo->dac_rev); + + if (slot && sun_prom_console_id == slot) + return; + + /* Reset the ffb */ + ffb_reset(fb); + + if (!slot) { + /* Enable Video */ + ffb_unblank(fb); + } else { + ffb_blank(fb); + } +} + +extern unsigned char vga_font[]; + +static void ffb_blitc(unsigned short charattr, int xoff, int yoff) +{ +} + +static void ffb_setw(int xoff, int yoff, unsigned short c, int count) +{ +} + +static void ffb_cpyw(int xoff, int yoff, unsigned short *p, int count) +{ +} + +static void ffb_fill(int attrib, int count, int *boxes) +{ } diff --git a/drivers/sbus/char/fb.h b/drivers/sbus/char/fb.h index 029eac81b..0aa9f2b48 100644 --- a/drivers/sbus/char/fb.h +++ b/drivers/sbus/char/fb.h @@ -1,4 +1,4 @@ -/* $Id: fb.h,v 1.26 1997/04/17 02:29:33 miguel Exp $ +/* $Id: fb.h,v 1.29 1997/07/15 09:48:48 jj Exp $ * fb.h: contains the definitions of the structures that various sun * frame buffer can use to do console driver stuff. * @@ -33,7 +33,7 @@ struct cg_cursor { struct fbcurpos chot; /* hot-spot */ struct fbcurpos size; /* size of mask & image fields */ struct fbcurpos hwsize; /* hw max size */ - int bits[2][32]; /* space for mask & image bits */ + int bits[2][128]; /* space for mask & image bits */ char color [6]; /* cursor colors */ }; @@ -57,6 +57,14 @@ struct tcx_info { int lowdepth; }; +struct ffb_info { + unsigned long physbase; + struct ffb_fbc *fbc; + struct ffb_dac *dac; + int dac_rev; + u32 *clut; +}; + struct leo_info { struct leo_cursor *cursor; struct leo_lc_ss0_krn *lc_ss0_krn; @@ -113,6 +121,7 @@ typedef struct fbinfo { struct cg14_info cg14; struct tcx_info tcx; struct leo_info leo; + struct ffb_info ffb; } info; /* per frame information */ int space; /* I/O space this card resides in */ int blanked; /* true if video blanked */ @@ -183,7 +192,7 @@ extern int con_height, con_linebytes; extern int ints_per_line; /* used in the mmap routines */ -extern unsigned int get_phys (unsigned long addr); +extern unsigned long get_phys (unsigned long addr); extern int get_iospace (unsigned long addr); extern void render_screen(void); diff --git a/drivers/sbus/char/leo.c b/drivers/sbus/char/leo.c index 61e646e9f..1b5d06e4b 100644 --- a/drivers/sbus/char/leo.c +++ b/drivers/sbus/char/leo.c @@ -1,7 +1,7 @@ -/* $Id: leo.c,v 1.18 1997/06/04 08:27:30 davem Exp $ +/* $Id: leo.c,v 1.21 1997/07/17 02:21:48 davem Exp $ * leo.c: SUNW,leo 24/8bit frame buffer driver * - * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz) */ @@ -143,7 +143,7 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size = 0; - uint map_offset = 0; + unsigned long map_offset = 0; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) @@ -218,11 +218,12 @@ leo_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } @@ -376,7 +377,7 @@ static int leo_clutstore (fbinfo_t *fb, int clutid) return 0; } -static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc) +static int leo_clutpost (fbinfo_t *fb, struct fb_clut *lc) { int xlate = 0, i; u32 *clut; @@ -398,7 +399,7 @@ static int leo_clutpost (fbinfo_t *fb, struct leo_clut *lc) return leo_clutstore (fb, lc->clutid); } -static int leo_clutread (fbinfo_t *fb, struct leo_clut *lc) +static int leo_clutread (fbinfo_t *fb, struct fb_clut *lc) { int i; u32 u; @@ -463,29 +464,29 @@ leo_ioctl (struct inode *inode, struct file *file, unsigned cmd, unsigned long a if (i) return i; return leo_wid_put (fb, (struct fb_wid_list *)arg); case LEO_CLUTPOST: - i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut)); + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_clut)); if (i) return i; - i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count; + i = ((struct fb_clut *)arg)->offset + ((struct fb_clut *)arg)->count; if (i <= 0 || i > 256) return -EINVAL; - i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->red, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->green, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_READ, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_READ, ((struct fb_clut *)arg)->blue, ((struct fb_clut *)arg)->count); if (i) return i; - return leo_clutpost (fb, (struct leo_clut *)arg); + return leo_clutpost (fb, (struct fb_clut *)arg); case LEO_CLUTREAD: - i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct leo_clut)); + i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct fb_clut)); if (i) return i; - i = ((struct leo_clut *)arg)->offset + ((struct leo_clut *)arg)->count; + i = ((struct fb_clut *)arg)->offset + ((struct fb_clut *)arg)->count; if (i <= 0 || i > 256) return -EINVAL; - i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->red, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->red, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->green, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->green, ((struct fb_clut *)arg)->count); if (i) return i; - i = verify_area (VERIFY_WRITE, ((struct leo_clut *)arg)->blue, ((struct leo_clut *)arg)->count); + i = verify_area (VERIFY_WRITE, ((struct fb_clut *)arg)->blue, ((struct fb_clut *)arg)->count); if (i) return i; - return leo_clutread (fb, (struct leo_clut *)arg); + return leo_clutread (fb, (struct fb_clut *)arg); default: return -ENOSYS; diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index 548abb601..75b950071 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -137,6 +137,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, struct openpromio *opp; unsigned long flags; int bufsize, len, error = 0; + extern char saved_command_line[]; if (cmd == OPROMSETOPT) bufsize = getstrings((void *)arg, &opp); @@ -172,7 +173,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, case OPROMNXTOPT: case OPROMNXTPROP: save_and_cli(flags); - buf = prom_nextprop(node, opp->oprom_array); + buf = prom_nextprop(node, opp->oprom_array, buffer); restore_flags(flags); len = strlen(buf); @@ -229,9 +230,7 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, break; case OPROMGETBOOTARGS: - save_and_cli(flags); - buf = prom_getbootargs(); - restore_flags(flags); + buf = saved_command_line; len = strlen(buf); @@ -315,6 +314,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, unsigned long flags; int error, node, len; char *str, *tmp; + char buffer[64]; switch (cmd) { case OPIOCGET: @@ -378,7 +378,7 @@ static int openprom_bsd_ioctl(struct inode * inode, struct file * file, return error; save_and_cli(flags); - tmp = prom_nextprop(op.op_nodeid,str); + tmp = prom_nextprop(op.op_nodeid,str,buffer); restore_flags(flags); if (tmp) { diff --git a/drivers/sbus/char/suncons.c b/drivers/sbus/char/suncons.c index 1d3815dd3..cad446904 100644 --- a/drivers/sbus/char/suncons.c +++ b/drivers/sbus/char/suncons.c @@ -1,4 +1,4 @@ -/* $Id: suncons.c,v 1.63 1997/05/31 18:33:25 mj Exp $ +/* $Id: suncons.c,v 1.66 1997/07/15 09:48:47 jj Exp $ * * suncons.c: Sun SparcStation console support. * @@ -759,7 +759,7 @@ console_restore_palette (void) (*fb_restore_palette) (&fbinfo[0]); } -unsigned int +unsigned long get_phys (unsigned long addr) { return __get_phys(addr); @@ -828,10 +828,14 @@ __initfunc(static int creator_present (void)) { int root, n; +#ifdef __sparc_v9__ root = prom_getchild (prom_root_node); if ((n = prom_searchsiblings (root, "SUNW,ffb")) == 0) return 0; return n; +#else + return 0; +#endif } __initfunc(static void @@ -1108,7 +1112,6 @@ __initfunc(static int sparc_console_probe(void)) if (!card_found) card_found = cg14 = cg14_present (); if (!card_found){ - prom_printf ("Searching for a creator\n"); card_found = creator = creator_present (); } if (!card_found){ @@ -1172,7 +1175,7 @@ __initfunc(static int sparc_console_probe(void)) if (creator){ sparc_framebuffer_setup (!sbdprom, creator, FBTYPE_CREATOR, 0, 0, 0, prom_console_node == creator, - prom_getchild (prom_root_node)); + prom_root_node); } break; default: @@ -1641,57 +1644,67 @@ void memcpyw(unsigned short *to, unsigned short *from, unsigned int count) int sun_hw_scursor (struct fbcursor *cursor, fbinfo_t *fb) { - int op = cursor->set; + int op; int i, bytes = 0; + struct fbcursor f; + char red[2], green[2], blue[2]; + if (copy_from_user (&f, cursor, sizeof(struct fbcursor))) + return -EFAULT; + op = f.set; if (op & FB_CUR_SETSHAPE){ - if ((uint) cursor->size.fbx > fb->cursor.hwsize.fbx) + if ((uint) f.size.fbx > fb->cursor.hwsize.fbx) return -EINVAL; - if ((uint) cursor->size.fby > fb->cursor.hwsize.fby) + if ((uint) f.size.fby > fb->cursor.hwsize.fby) return -EINVAL; - bytes = (cursor->size.fby * 32)/8; - i = verify_area (VERIFY_READ, cursor->image, bytes); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->mask, bytes); - if (i) return i; + if (f.size.fbx > 32) + bytes = f.size.fby << 3; + else + bytes = f.size.fby << 2; } if (op & FB_CUR_SETCMAP){ - if (cursor->cmap.index && cursor->cmap.count != 2) + if (f.cmap.index || f.cmap.count != 2) return -EINVAL; - i = verify_area (VERIFY_READ, cursor->cmap.red, 2); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->cmap.green, 2); - if (i) return i; - i = verify_area (VERIFY_READ, cursor->cmap.blue, 2); - if (i) return i; - } - if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){ - if (op & FB_CUR_SETCUR) - fb->cursor.enable = cursor->enable; - if (op & FB_CUR_SETPOS) - fb->cursor.cpos = cursor->pos; - if (op & FB_CUR_SETHOT) - fb->cursor.chot = cursor->hot; - (*fb->setcursor) (fb); + if (copy_from_user (red, f.cmap.red, 2) || + copy_from_user (green, f.cmap.green, 2) || + copy_from_user (blue, f.cmap.blue, 2)) + return -EFAULT; } if (op & FB_CUR_SETCMAP) - (*fb->setcursormap) (fb, cursor->cmap.red, cursor->cmap.green, cursor->cmap.blue); + (*fb->setcursormap) (fb, red, green, blue); if (op & FB_CUR_SETSHAPE){ uint u; - fb->cursor.size = cursor->size; + fb->cursor.size = f.size; memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits)); - memcpy (fb->cursor.bits [0], cursor->mask, bytes); - memcpy (fb->cursor.bits [1], cursor->image, bytes); - u = ~0; - if (cursor->size.fbx < fb->cursor.hwsize.fbx) - u = ~(u >> cursor->size.fbx); - for (i = fb->cursor.size.fby - 1; i >= 0; i--) { - fb->cursor.bits [0][i] &= u; - fb->cursor.bits [1][i] &= fb->cursor.bits [0][i]; + if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) || + copy_from_user (fb->cursor.bits [1], f.image, bytes)) + return -EFAULT; + if (f.size.fbx <= 32) { + u = ~(0xffffffff >> f.size.fbx); + for (i = fb->cursor.size.fby - 1; i >= 0; i--) { + fb->cursor.bits [0][i] &= u; + fb->cursor.bits [1][i] &= fb->cursor.bits [0][i]; + } + } else { + u = ~(0xffffffff >> (f.size.fbx - 32)); + for (i = fb->cursor.size.fby - 1; i >= 0; i--) { + fb->cursor.bits [0][2*i+1] &= u; + fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i]; + fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1]; + } } (*fb->setcurshape) (fb); } + if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){ + if (op & FB_CUR_SETCUR) + fb->cursor.enable = f.enable; + if (op & FB_CUR_SETPOS) + fb->cursor.cpos = f.pos; + if (op & FB_CUR_SETHOT) + fb->cursor.chot = f.hot; + (*fb->setcursor) (fb); + } return 0; } diff --git a/drivers/sbus/char/sunfb.c b/drivers/sbus/char/sunfb.c index 68856c9ee..885581975 100644 --- a/drivers/sbus/char/sunfb.c +++ b/drivers/sbus/char/sunfb.c @@ -1,4 +1,4 @@ -/* $Id: sunfb.c,v 1.23 1997/05/31 18:33:26 mj Exp $ +/* $Id: sunfb.c,v 1.26 1997/07/17 02:21:48 davem Exp $ * sunfb.c: Sun generic frame buffer support. * * Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) @@ -132,7 +132,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) if ((index < 0) || (index > 255)) return -EINVAL; if (index + count > 256) - count = 256 - cmap->index; + count = 256 - index; __get_user_ret(rp, &cmap->red, -EFAULT); __get_user_ret(gp, &cmap->green, -EFAULT); __get_user_ret(bp, &cmap->blue, -EFAULT); @@ -146,7 +146,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) __put_user_ret(fb->color_map CM(i,2), bp, -EFAULT); rp++; gp++; bp++; } - (*fb->loadcmap)(fb, cmap->index, count); + (*fb->loadcmap)(fb, index, count); break; } @@ -164,13 +164,13 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) if ((index < 0) || (index > 255)) return -EINVAL; if (index + count > 256) - count = 256 - cmap->index; + count = 256 - index; __get_user_ret(rp, &cmap->red, -EFAULT); __get_user_ret(gp, &cmap->green, -EFAULT); __get_user_ret(bp, &cmap->blue, -EFAULT); - if(verify_area (VERIFY_READ, rp, cmap->count)) return -EFAULT; - if(verify_area (VERIFY_READ, gp, cmap->count)) return -EFAULT; - if(verify_area (VERIFY_READ, bp, cmap->count)) return -EFAULT; + if(verify_area (VERIFY_READ, rp, count)) return -EFAULT; + if(verify_area (VERIFY_READ, gp, count)) return -EFAULT; + if(verify_area (VERIFY_READ, bp, count)) return -EFAULT; end = index + count; for (i = index; i < end; i++){ @@ -179,7 +179,7 @@ fb_ioctl (struct inode *inode, struct file *file, uint cmd, unsigned long arg) __get_user_ret(fb->color_map CM(i,2), bp, -EFAULT); rp++; gp++; bp++; } - (*fb->loadcmap)(fb, cmap->index, count); + (*fb->loadcmap)(fb, index, count); break; } @@ -275,7 +275,9 @@ fb_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) int v; v = (*fb->mmap)(inode, file, vma, fb->base, fb); - if (v) return v; + if (v) + return v; + vma->vm_flags |= VM_IO; if (!fb->mmaped) { fb->mmaped = 1; if (!minor) { diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 1ddaaecd4..54b09cd76 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -419,27 +419,27 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) if(ch == SKBD_RESET) { kbd_reset_pending = 1; - return; + goto out; } if(ch == SKBD_LYOUT) { kbd_layout_pending = 1; - return; + goto out; } if(kbd_reset_pending) { sunkbd_type = ch; kbd_reset_pending = 0; if(ch == SUNKBD_TYPE4) send_cmd(SKBDCMD_GLAYOUT); - return; + goto out; } else if(kbd_layout_pending) { sunkbd_layout = ch; kbd_layout_pending = 0; - return; + goto out; } else if(ch == SKBD_ALLUP) { del_timer (&auto_repeat_timer); memset(key_down, 0, sizeof(key_down)); compute_shiftstate(); - return; + goto out; } #ifdef SKBD_DEBUG if(ch == 0x7f) @@ -456,11 +456,11 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) } else { keycode = ch; } - add_keyboard_randomness(keycode); - mark_bh(KEYBOARD_BH); do_poke_blanked_console = 1; mark_bh(CONSOLE_BH); + add_keyboard_randomness(keycode); + kbd = kbd_table + fg_console; tty = ttytab[fg_console]; if((raw_mode = (kbd->kbdmode == VC_RAW))) { @@ -491,11 +491,11 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) } if(raw_mode) - return; + goto out; if(kbd->kbdmode == VC_MEDIUMRAW) { put_queue(keycode + up_flag); - return; + goto out; } /* @@ -545,6 +545,8 @@ void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) compute_shiftstate(); } } +out: + mark_bh(KEYBOARD_BH); } static void put_queue(int ch) diff --git a/drivers/sbus/char/sunmouse.c b/drivers/sbus/char/sunmouse.c index 371fb3004..789a332c0 100644 --- a/drivers/sbus/char/sunmouse.c +++ b/drivers/sbus/char/sunmouse.c @@ -171,7 +171,7 @@ void mouse_baud_detection(unsigned char c) ctr = 0; wait_for_synchron = 1; if(mouse_baud_changing == 1) { - printk("sunmouse: Successfully adjusted to %d baud.\n", + printk(KERN_DEBUG "sunmouse: Successfully adjusted to %d baud.\n", mouse_baud); mouse_baud_changing = 0; } diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c index e89384a3a..c0e6ab71d 100644 --- a/drivers/sbus/char/sunserial.c +++ b/drivers/sbus/char/sunserial.c @@ -1,4 +1,4 @@ -/* $Id: sunserial.c,v 1.42 1997/05/26 20:10:20 davem Exp $ +/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $ * serial.c: Serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -348,10 +348,12 @@ static void batten_down_hatches(void) */ printk("\n"); flush_user_windows(); +#ifndef __sparc_v9__ if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) && (((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR)) sp_enter_debugger(); else +#endif prom_cmdline(); /* XXX We want to notify the keyboard driver that all @@ -396,7 +398,9 @@ static _INLINE_ void rs_sched_event(struct sun_serial *info, mark_bh(SERIAL_BH); } +#ifndef __sparc_v9__ extern void breakpoint(void); /* For the KGDB frame character */ +#endif static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs) { @@ -453,6 +457,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs /* It is a 'keyboard interrupt' ;-) */ wake_up(&keypress_wait); } +#ifndef __sparc_v9__ /* Look for kgdb 'stop' character, consult the gdb * documentation for remote target debugging and * arch/sparc/kernel/sparc-stub.c to see how all this works. @@ -461,7 +466,7 @@ static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs breakpoint(); return; } - +#endif if(!tty) return; @@ -1867,7 +1872,7 @@ int rs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.42 $"; + char *revision = "$Revision: 1.2 $"; char *version, *p; version = strchr(revision, ' '); diff --git a/drivers/sbus/char/sunserial.h b/drivers/sbus/char/sunserial.h index b8ae23305..ae4260cad 100644 --- a/drivers/sbus/char/sunserial.h +++ b/drivers/sbus/char/sunserial.h @@ -1,4 +1,4 @@ -/* $Id: sunserial.h,v 1.9 1997/04/12 23:33:12 ecd Exp $ +/* $Id: sunserial.h,v 1.11 1997/07/08 10:17:23 davem Exp $ * serial.h: Definitions for the Sparc Zilog serial driver. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) diff --git a/drivers/sbus/char/tcx.c b/drivers/sbus/char/tcx.c index db66383ac..20c14a687 100644 --- a/drivers/sbus/char/tcx.c +++ b/drivers/sbus/char/tcx.c @@ -1,4 +1,4 @@ -/* $Id: tcx.c,v 1.15 1997/06/04 08:27:32 davem Exp $ +/* $Id: tcx.c,v 1.17 1997/07/17 02:21:50 davem Exp $ * tcx.c: SUNW,tcx 24/8bit frame buffer driver * * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -110,7 +110,8 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { uint size, page, r, map_size; - uint map_offset = 0, i; + unsigned long map_offset = 0; + uint i; long offsets[13] = { -1, TCX_RAM24BIT, TCX_UNK3, TCX_UNK4, -1, TCX_UNK6, TCX_UNK7, -1, -1, -1, TCX_UNK2, TCX_DHC, TCX_ALT }; @@ -168,11 +169,12 @@ tcx_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 00cdfe35c..007174a82 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -577,11 +577,12 @@ static int vfc_mmap(struct inode *inode, struct file *file, if(vma->vm_offset & ~PAGE_MASK) return -ENXIO; vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO | VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE; map_offset=(unsigned int)dev->phys_regs; - ret=io_remap_page_range(vma->vm_start,map_offset,map_size, - vma->vm_page_prot, dev->which_io); - if(ret) return -EAGAIN; - vma->vm_inode=inode; - atomic_inc(&inode->i_count); + ret = io_remap_page_range(vma->vm_start,map_offset,map_size, + vma->vm_page_prot, dev->which_io); + if(ret) + return -EAGAIN; + + vma->vm_dentry = dget(file->f_dentry); return 0; } diff --git a/drivers/sbus/char/weitek.c b/drivers/sbus/char/weitek.c index d2ac4d135..7b7b1bd72 100644 --- a/drivers/sbus/char/weitek.c +++ b/drivers/sbus/char/weitek.c @@ -1,4 +1,4 @@ -/* $Id: weitek.c,v 1.12 1997/06/04 08:27:34 davem Exp $ +/* $Id: weitek.c,v 1.14 1997/07/17 02:21:53 davem Exp $ * weitek.c: Tadpole P9100/P9000 console driver * * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) @@ -41,7 +41,7 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, long base, fbinfo_t *fb) { unsigned int size, page, r, map_size; - unsigned int map_offset = 0; + unsigned long map_offset = 0; size = vma->vm_end - vma->vm_start; if (vma->vm_offset & ~PAGE_MASK) @@ -79,11 +79,12 @@ weitek_mmap(struct inode *inode, struct file *file, struct vm_area_struct *vma, map_offset, map_size, vma->vm_page_prot, fb->space); - if (r) return -EAGAIN; + if (r) + return -EAGAIN; page += map_size; } - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + + vma->vm_dentry = dget(file->f_dentry); return 0; } #endif diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h index 7cd6ba436..515e84b64 100644 --- a/drivers/scsi/BusLogic.h +++ b/drivers/scsi/BusLogic.h @@ -27,6 +27,9 @@ */ +#include <linux/config.h> + + /* Define types for some of the structures that interface with the rest of the Linux Kernel and SCSI Subsystem. diff --git a/drivers/scsi/README.in2000 b/drivers/scsi/README.in2000 index 06d0fee22..861d6efb2 100644 --- a/drivers/scsi/README.in2000 +++ b/drivers/scsi/README.in2000 @@ -1,4 +1,16 @@ +UPDATE NEWS: version 1.31 - 6 Jul 97 + + Fixed a bug that caused incorrect SCSI status bytes to be + returned from commands sent to LUN's greater than 0. This + means that CDROM changers work now! Fixed a bug in the + handling of command-line arguments when loaded as a module. + Also put all the header data in in2000.h where it belongs. + There are no longer any differences between this driver in + the 2.1.xx source tree and the 2.0.xx tree, as of 2.0.31 + and 2.1.45 (or is it .46?) - this makes things much easier + for me... + UPDATE NEWS: version 1.30 - 14 Oct 96 Fixed a bug in the code that sets the transfer direction @@ -105,15 +117,10 @@ to see what happens: my tests showed little difference either way. There's also a define called 'DEFAULT_SX_PER'; this sets the data transfer speed for the asynchronous mode. I've put it at 500 ns despite the fact that the card could handle settings of 376 or -252, because I'm not really sure if certain devices or maybe bad -cables might have trouble at higher speeds. I couldn't find any -info in my various SCSI references that talk about this in language -I could understand, so decided to compromise with 500. This is still -faster than the old driver was set at (I think). Can someone explain -the significance of the bus transfer speed setting? Do devices on -the bus ever care what it is? Is cable quality a factor here? -Regardless, you can choose your own default through the command- -line with the 'period' keyword. +252, because higher speeds may be a problem with poor quality +cables or improper termination; 500 ns is a compromise. You can +choose your own default through the command-line with the +'period' keyword. ------------------------------------------------ diff --git a/drivers/scsi/advansys.h b/drivers/scsi/advansys.h index 6cf629921..eb3d65e65 100644 --- a/drivers/scsi/advansys.h +++ b/drivers/scsi/advansys.h @@ -1,4 +1,4 @@ -/* $Id: advansys.h,v 1997/05/28 00:23:06 bobf Exp bobf $ */ +/* $Id: advansys.h,v 1.6 1997/05/30 19:25:12 davem Exp $ */ /* * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters diff --git a/drivers/scsi/amiga7xx.c b/drivers/scsi/amiga7xx.c index 6edd01320..f07d5e2cd 100644 --- a/drivers/scsi/amiga7xx.c +++ b/drivers/scsi/amiga7xx.c @@ -36,7 +36,7 @@ struct proc_dir_entry proc_scsi_amiga7xx = { int amiga7xx_detect(Scsi_Host_Template *tpnt) { static unsigned char called = 0; - int key; + int key, clock; int num = 0; unsigned long address; long long options; @@ -59,8 +59,7 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); zorro_config_board(key, 0); num++; @@ -74,16 +73,16 @@ int amiga7xx_detect(Scsi_Host_Template *tpnt) clock = 50000000; /* 50MHz SCSI Clock */ - ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), - 0, IRQ_AMIGA_PORTS, DMA_NONE, - options, clock); + ncr53c7xx_init(tpnt, 0, 710, + (u32)(unsigned char *)ZTWO_VADDR(0xDD0040), + 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock); num++; } #endif #ifdef CONFIG_A4091_SCSI while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) || - (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) + (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) ) { cd = zorro_get_board(key); address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr, diff --git a/drivers/scsi/in2000.c b/drivers/scsi/in2000.c index 6f24da81c..d493fa167 100644 --- a/drivers/scsi/in2000.c +++ b/drivers/scsi/in2000.c @@ -104,7 +104,7 @@ * */ - +#include <linux/module.h> #include <asm/system.h> #include <linux/sched.h> @@ -115,46 +115,44 @@ #include <linux/ioport.h> #include <linux/blkdev.h> +#include <linux/blk.h> +#include <linux/stat.h> + #include "scsi.h" #include "sd.h" #include "hosts.h" -#include "in2000.h" - -#include <linux/blk.h> -#include <linux/stat.h> -#ifdef MODULE -#include <linux/module.h> -#endif -#define uchar unsigned char +#define IN2000_VERSION "1.31" +#define IN2000_DATE "06/July/1997" -#define IN2000_VERSION "1.30" -#define IN2000_DATE "14/Oct/1996" +/* + * Note - the following defines have been moved to 'in2000.h': + * + * PROC_INTERFACE + * PROC_STATISTICS + * SYNC_DEBUG + * DEBUGGING_ON + * DEBUG_DEFAULTS + * FAST_READ_IO + * FAST_WRITE_IO + * + */ -#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */ -#define SYNC_DEBUG /* extra info on sync negotiation printed */ -#define DEBUGGING_ON /* enable command-line debugging bitmask */ -#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */ -#define FAST_READ_IO /* No problems with these on my machine */ -#define FAST_WRITE_IO +#include "in2000.h" -#ifdef DEBUGGING_ON -#define DB(f,a) if (hostdata->args & (f)) a; -#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */ -#else -#define DB(f,a) -#define CHECK_NULL(p,s) -#endif /* - * setup_strings is an array of strings that define some of the operating - * parameters and settings for this driver. It is used unless a LILO - * or insmod command line has been specified, in which case those settings - * are combined with the ones here. The driver recognizes the following - * keywords (lower case required) and arguments: + * 'setup_strings' is a single string used to pass operating parameters and + * settings from the kernel/module command-line to the driver. 'setup_args[]' + * is an array of strings that define the compile-time default values for + * these settings. If Linux boots with a LILO or insmod command-line, those + * settings are combined with 'setup_args[]'. Note that LILO command-lines + * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix. + * The driver recognizes the following keywords (lower case required) and + * arguments: * * - ioport:addr -Where addr is IO address of a (usually ROM-less) card. * - noreset -No optional args. Prevents SCSI bus reset at boot time. @@ -179,13 +177,11 @@ * _must_ be a colon between a keyword and its numeric argument, with no * spaces. * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner, except in the case of 'setup_strings[]' (see - * below), which is simply a C array of pointers to char. Each element - * in the array is a string comprising one keyword & argument. + * command-line manner. * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_strings[]. A blank command-line member (in + * the 'nth' element of setup_args[]. A blank command-line member (in * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_strings[] element. + * the corresponding setup_args[] element. * * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'): * - in2000=ioport:0x220,noreset @@ -194,334 +190,20 @@ * - in2000=proc:3 */ -static char *setup_strings[] = - {"","","","","","","","","","","",""}; +/* Normally, no defaults are specified... */ +static char *setup_args[] = + {"","","","","","","","",""}; -static struct Scsi_Host *instance_list = 0; +/* filled in by 'insmod' */ +static char *setup_strings = 0; -#ifdef PROC_INTERFACE -static unsigned long disc_allowed_total; -static unsigned long disc_taken_total; +#ifdef MODULE_PARM +MODULE_PARM(setup_strings, "s"); #endif -/* IN2000 io_port offsets */ -#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ -#define ASR_INT 0x80 -#define ASR_LCI 0x40 -#define ASR_BSY 0x20 -#define ASR_CIP 0x10 -#define ASR_PE 0x02 -#define ASR_DBR 0x01 -#define IO_WD_ADDR 0x00 /* W - 3393 address reg */ -#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */ -#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */ -#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */ -#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */ -#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */ -#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */ -#define IO_FIFO_READ 0x07 /* W - start fifo read */ -#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */ -#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */ -#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */ -#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */ -#define SW_DISINT 0x04 /* bit 2 true if ints disabled */ -#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */ -#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */ -#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */ -#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */ -#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */ -#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */ -#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */ -#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */ -#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */ -#define IMASK_WD 0x01 /* WD33c93 interrupt mask */ -#define IMASK_FIFO 0x02 /* FIFO interrupt mask */ - -/* wd register names */ -#define WD_OWN_ID 0x00 -#define WD_CONTROL 0x01 -#define WD_TIMEOUT_PERIOD 0x02 -#define WD_CDB_1 0x03 -#define WD_CDB_2 0x04 -#define WD_CDB_3 0x05 -#define WD_CDB_4 0x06 -#define WD_CDB_5 0x07 -#define WD_CDB_6 0x08 -#define WD_CDB_7 0x09 -#define WD_CDB_8 0x0a -#define WD_CDB_9 0x0b -#define WD_CDB_10 0x0c -#define WD_CDB_11 0x0d -#define WD_CDB_12 0x0e -#define WD_TARGET_LUN 0x0f -#define WD_COMMAND_PHASE 0x10 -#define WD_SYNCHRONOUS_TRANSFER 0x11 -#define WD_TRANSFER_COUNT_MSB 0x12 -#define WD_TRANSFER_COUNT 0x13 -#define WD_TRANSFER_COUNT_LSB 0x14 -#define WD_DESTINATION_ID 0x15 -#define WD_SOURCE_ID 0x16 -#define WD_SCSI_STATUS 0x17 -#define WD_COMMAND 0x18 -#define WD_DATA 0x19 -#define WD_QUEUE_TAG 0x1a -#define WD_AUXILIARY_STATUS 0x1f - -/* WD commands */ -#define WD_CMD_RESET 0x00 -#define WD_CMD_ABORT 0x01 -#define WD_CMD_ASSERT_ATN 0x02 -#define WD_CMD_NEGATE_ACK 0x03 -#define WD_CMD_DISCONNECT 0x04 -#define WD_CMD_RESELECT 0x05 -#define WD_CMD_SEL_ATN 0x06 -#define WD_CMD_SEL 0x07 -#define WD_CMD_SEL_ATN_XFER 0x08 -#define WD_CMD_SEL_XFER 0x09 -#define WD_CMD_RESEL_RECEIVE 0x0a -#define WD_CMD_RESEL_SEND 0x0b -#define WD_CMD_WAIT_SEL_RECEIVE 0x0c -#define WD_CMD_TRANS_ADDR 0x18 -#define WD_CMD_TRANS_INFO 0x20 -#define WD_CMD_TRANSFER_PAD 0x21 -#define WD_CMD_SBT_MODE 0x80 - -/* SCSI Bus Phases */ -#define PHS_DATA_OUT 0x00 -#define PHS_DATA_IN 0x01 -#define PHS_COMMAND 0x02 -#define PHS_STATUS 0x03 -#define PHS_MESS_OUT 0x06 -#define PHS_MESS_IN 0x07 - -/* Command Status Register definitions */ - - /* reset state interrupts */ -#define CSR_RESET 0x00 -#define CSR_RESET_AF 0x01 - - /* successful completion interrupts */ -#define CSR_RESELECT 0x10 -#define CSR_SELECT 0x11 -#define CSR_SEL_XFER_DONE 0x16 -#define CSR_XFER_DONE 0x18 - - /* paused or aborted interrupts */ -#define CSR_MSGIN 0x20 -#define CSR_SDP 0x21 -#define CSR_SEL_ABORT 0x22 -#define CSR_RESEL_ABORT 0x25 -#define CSR_RESEL_ABORT_AM 0x27 -#define CSR_ABORT 0x28 - - /* terminated interrupts */ -#define CSR_INVALID 0x40 -#define CSR_UNEXP_DISC 0x41 -#define CSR_TIMEOUT 0x42 -#define CSR_PARITY 0x43 -#define CSR_PARITY_ATN 0x44 -#define CSR_BAD_STATUS 0x45 -#define CSR_UNEXP 0x48 - - /* service required interrupts */ -#define CSR_RESEL 0x80 -#define CSR_RESEL_AM 0x81 -#define CSR_DISC 0x85 -#define CSR_SRV_REQ 0x88 - - /* Own ID/CDB Size register */ -#define OWNID_EAF 0x08 -#define OWNID_EHP 0x10 -#define OWNID_RAF 0x20 -#define OWNID_FS_8 0x00 -#define OWNID_FS_12 0x40 -#define OWNID_FS_16 0x80 - - /* Control register */ -#define CTRL_HSP 0x01 -#define CTRL_HA 0x02 -#define CTRL_IDI 0x04 -#define CTRL_EDI 0x08 -#define CTRL_HHP 0x10 -#define CTRL_POLLED 0x00 -#define CTRL_BURST 0x20 -#define CTRL_BUS 0x40 -#define CTRL_DMA 0x80 - - /* Timeout Period register */ -#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */ - - /* Synchronous Transfer Register */ -#define STR_FSS 0x80 - - /* Destination ID register */ -#define DSTID_DPD 0x40 -#define DATA_OUT_DIR 0 -#define DATA_IN_DIR 1 -#define DSTID_SCC 0x80 - - /* Source ID register */ -#define SRCID_MASK 0x07 -#define SRCID_SIV 0x08 -#define SRCID_DSP 0x20 -#define SRCID_ES 0x40 -#define SRCID_ER 0x80 - - - -#define DEFAULT_SX_PER 500 /* (ns) fairly safe */ -#define DEFAULT_SX_OFF 0 /* aka async */ - -#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */ -#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */ - - -/* defines for hostdata->chip */ - -#define C_WD33C93 0 -#define C_WD33C93A 1 -#define C_WD33C93B 2 -#define C_UNKNOWN_CHIP 100 - -/* defines for hostdata->state */ - -#define S_UNCONNECTED 0 -#define S_SELECTING 1 -#define S_RUNNING_LEVEL2 2 -#define S_CONNECTED 3 -#define S_PRE_TMP_DISC 4 -#define S_PRE_CMP_DISC 5 - -/* defines for hostdata->fifo */ - -#define FI_FIFO_UNUSED 0 -#define FI_FIFO_READING 1 -#define FI_FIFO_WRITING 2 - -/* defines for hostdata->level2 */ -/* NOTE: only the first 3 are trustworthy at this point - - * having trouble when more than 1 device is reading/writing - * at the same time... - */ - -#define L2_NONE 0 /* no combination commands - we get lots of ints */ -#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */ -#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */ -#define L2_DATA 3 /* resume after DATA_IN/OUT ints */ -#define L2_MOST 4 /* resume after anything except a RESELECT int */ -#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */ -#define L2_ALL 6 /* always resume */ - -/* defines for hostdata->disconnect */ - -#define DIS_NEVER 0 -#define DIS_ADAPTIVE 1 -#define DIS_ALWAYS 2 - -/* defines for hostdata->args */ - -#define DB_TEST 1<<0 -#define DB_FIFO 1<<1 -#define DB_QUEUE_COMMAND 1<<2 -#define DB_EXECUTE 1<<3 -#define DB_INTR 1<<4 -#define DB_TRANSFER 1<<5 -#define DB_MASK 0x3f -#define A_NO_SCSI_RESET 1<<15 - - -/* defines for hostdata->sync_xfer[] */ - -#define SS_UNSET 0 -#define SS_FIRST 1 -#define SS_WAITING 2 -#define SS_SET 3 - -/* defines for hostdata->proc */ - -#define PR_VERSION 1<<0 -#define PR_INFO 1<<1 -#define PR_TOTALS 1<<2 -#define PR_CONNECTED 1<<3 -#define PR_INPUTQ 1<<4 -#define PR_DISCQ 1<<5 -#define PR_TEST 1<<6 -#define PR_STOP 1<<7 - - -#define read1_io(a) (inb(hostdata->io_base+(a))) -#define read2_io(a) (inw(hostdata->io_base+(a))) -#define write1_io(b,a) (outb((b),hostdata->io_base+(a))) -#define write2_io(w,a) (outw((w),hostdata->io_base+(a))) - - -struct sx_period { - unsigned int period_ns; - uchar reg_value; - }; - - -struct IN2000_hostdata { - struct Scsi_Host *next; - uchar chip; /* what kind of wd33c93 chip? */ - uchar microcode; /* microcode rev if 'B' */ - unsigned short io_base; /* IO port base */ - unsigned int dip_switch; /* dip switch settings */ - unsigned int hrev; /* hardware revision of card */ - volatile uchar busy[8]; /* index = target, bit = lun */ - volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ - volatile Scsi_Cmnd *selecting; /* trying to select this command */ - volatile Scsi_Cmnd *connected; /* currently connected command */ - volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */ - uchar state; /* what we are currently doing */ - uchar fifo; /* what the FIFO is up to */ - uchar level2; /* extent to which Level-2 commands are used */ - uchar disconnect; /* disconnect/reselect policy */ - unsigned int args; /* set from command-line argument */ - uchar incoming_msg[8]; /* filled during message_in phase */ - int incoming_ptr; /* mainly used with EXTENDED messages */ - uchar outgoing_msg[8]; /* send this during next message_out */ - int outgoing_len; /* length of outgoing message */ - unsigned int default_sx_per; /* default transfer period for SCSI bus */ - uchar sync_xfer[8]; /* sync_xfer reg settings per target */ - uchar sync_stat[8]; /* status of sync negotiation per target */ - uchar sync_off; /* bit mask: don't use sync with these targets */ - uchar proc; /* bit mask: what's in proc output */ - }; - -/* These inline assembly defines are derived from a patch - * sent to me by Bill Earnest. He's done a lot of very - * valuable thinking, testing, and coding during his effort - * to squeeze more speed out of this driver. I really think - * that we are doing IO at close to the maximum now with - * the fifo. (And yes, insw uses 'edi' while outsw uses - * 'esi'. Thanks Bill!) - */ +static struct Scsi_Host *instance_list = 0; -#define FAST_READ2_IO() \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - insw %%dx \n \ -1: " \ - : "=D" (sp) /* output */ \ - : "d" (f), "D" (sp), "c" (i) /* input */ \ - : "edx", "ecx", "edi" ) /* trashed */ - -#define FAST_WRITE2_IO() \ - __asm__ __volatile__ ("\n \ - cld \n \ - orl %%ecx, %%ecx \n \ - jz 1f \n \ - rep \n \ - outsw %%dx \n \ -1: " \ - : "=S" (sp) /* output */ \ - : "d" (f), "S" (sp), "c" (i) /* input */ \ - : "edx", "ecx", "esi" ) /* trashed */ static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num) @@ -596,10 +278,10 @@ static int is_dir_out(Scsi_Cmnd *cmd) switch (cmd->cmnd[0]) { case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: - case WRITE_VERIFY: case WRITE_VERIFY_12: + case WRITE_VERIFY: case WRITE_VERIFY_12: case COMPARE: case COPY: case COPY_VERIFY: case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: @@ -624,7 +306,7 @@ static struct sx_period sx_table[] = { {1000,0x00}, {0, 0} }; -int round_period(unsigned int period) +static int round_period(unsigned int period) { int x; @@ -657,7 +339,6 @@ struct IN2000_hostdata *hostdata; Scsi_Cmnd *tmp; unsigned long flags; - hostdata = (struct IN2000_hostdata *)cmd->host->hostdata; DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid)) @@ -675,7 +356,7 @@ DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid)) /* We use the Scsi_Pointer structure that's included with each command * as a scratchpad (as it's intended to be used!). The handy thing about * the SCp.xxx fields is that they're always associated with a given - * cmd, and are preserved across disconnect-reconnect. This means we + * cmd, and are preserved across disconnect-reselect. This means we * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages * if we keep all the critical pointers and counters in SCp: * - SCp.ptr is the pointer into the RAM buffer @@ -703,9 +384,24 @@ DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld(",cmd->target,cmd->cmnd[0],cmd->pid)) /* We don't set SCp.phase here - that's done in in2000_execute() */ -/* Preset the command status to GOOD, since that's the normal case */ +/* WD docs state that at the conclusion of a "LEVEL2" command, the + * status byte can be retrieved from the LUN register. Apparently, + * this is the case only for *uninterrupted* LEVEL2 commands! If + * there are any unexpected phases entered, even if they are 100% + * legal (different devices may choose to do things differently), + * the LEVEL2 command sequence is exited. This often occurs prior + * to receiving the status byte, in which case the driver does a + * status phase interrupt and gets the status byte on its own. + * While such a command can then be "resumed" (ie restarted to + * finish up as a LEVEL2 command), the LUN register will NOT be + * a valid status byte at the command's conclusion, and we must + * use the byte obtained during the earlier interrupt. Here, we + * preset SCp.Status to an illegal value (0xff) so that when + * this command finally completes, we can tell where the actual + * status byte is stored. + */ - cmd->SCp.Status = GOOD; + cmd->SCp.Status = ILLEGAL_STATUS_BYTE; save_flags(flags); cli(); @@ -803,6 +499,10 @@ DB(DB_EXECUTE,printk(")EX-1 ")) else hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble; +#ifdef PROC_STATISTICS + hostdata->cmd_cnt[cmd->target]++; +#endif + /* * Start the selection process */ @@ -860,8 +560,8 @@ DB(DB_EXECUTE,printk(")EX-1 ")) yes: cmd->SCp.phase = 1; -#ifdef PROC_INTERFACE - disc_allowed_total++; +#ifdef PROC_STATISTICS + hostdata->disc_allowed_cnt[cmd->target]++; #endif no: @@ -1094,7 +794,8 @@ int i; if (data_in_dir) { write1_io(0,IO_FIFO_READ); - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_3393(hostdata,WD_COMMAND_PHASE,0x45); write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -1111,7 +812,8 @@ int i; * write any bytes that don't make it at this stage. */ - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_3393(hostdata,WD_COMMAND_PHASE,0x45); write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -1177,6 +879,10 @@ unsigned short f; save_flags(flags); sti(); +#ifdef PROC_STATISTICS + hostdata->int_cnt++; +#endif + /* The IN2000 card has 2 interrupt sources OR'ed onto its IRQ line - the * WD3393 chip and the 2k fifo (which is actually a dual-port RAM combined * with a big logic array, so it's a little different than what you might @@ -1484,9 +1190,10 @@ DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid)) case CSR_XFER_DONE|PHS_STATUS: case CSR_UNEXP |PHS_STATUS: case CSR_SRV_REQ |PHS_STATUS: -DB(DB_INTR,printk("STATUS")) +DB(DB_INTR,printk("STATUS=")) cmd->SCp.Status = read_1_byte(hostdata); +DB(DB_INTR,printk("%02x",cmd->SCp.Status)) if (hostdata->level2 >= L2_BASIC) { sr = read_3393(hostdata,WD_SCSI_STATUS); /* clear interrupt */ hostdata->state = S_RUNNING_LEVEL2; @@ -1494,7 +1201,6 @@ DB(DB_INTR,printk("STATUS")) write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); } else { -DB(DB_INTR,printk("=%02x",cmd->SCp.Status)) hostdata->state = S_CONNECTED; } break; @@ -1665,15 +1371,16 @@ printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]); DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid)) cmd->SCp.Message = COMMAND_COMPLETE; lun = read_3393(hostdata,WD_TARGET_LUN); - if (cmd->SCp.Status == GOOD) - cmd->SCp.Status = lun; +DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun)) hostdata->connected = NULL; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; + if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) + cmd->SCp.Status = lun; + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1755,10 +1462,10 @@ DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid)) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1788,10 +1495,11 @@ DB(DB_INTR,printk("DISC-%ld",cmd->pid)) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); - else if (cmd->SCp.Status != GOOD) +DB(DB_INTR,printk(":%d",cmd->SCp.Status)) + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); cmd->scsi_done(cmd); break; case S_PRE_TMP_DISC: @@ -1801,8 +1509,8 @@ DB(DB_INTR,printk("DISC-%ld",cmd->pid)) hostdata->connected = NULL; hostdata->state = S_UNCONNECTED; -#ifdef PROC_INTERFACE - disc_taken_total++; +#ifdef PROC_STATISTICS + hostdata->disc_done_cnt[cmd->target]++; #endif break; @@ -2158,10 +1866,11 @@ unsigned long timeout; #define MAX_IN2000_HOSTS 3 -#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *)) #define SETUP_BUFFER_SIZE 200 static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_STRINGS]; +static char setup_used[MAX_SETUP_ARGS]; +static int done_setup = 0; void in2000_setup (char *str, int *ints) { @@ -2172,43 +1881,44 @@ char *p1,*p2; setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; p1 = setup_buffer; i = 0; - while (*p1 && (i < MAX_SETUP_STRINGS)) { + while (*p1 && (i < MAX_SETUP_ARGS)) { p2 = strchr(p1, ','); if (p2) { *p2 = '\0'; if (p1 != p2) - setup_strings[i] = p1; + setup_args[i] = p1; p1 = p2 + 1; i++; } else { - setup_strings[i] = p1; + setup_args[i] = p1; break; } } - for (i=0; i<MAX_SETUP_STRINGS; i++) + for (i=0; i<MAX_SETUP_ARGS; i++) setup_used[i] = 0; + done_setup = 1; } -/* check_setup_strings() returns index if key found, 0 if not +/* check_setup_args() returns index if key found, 0 if not */ -static int check_setup_strings(char *key, int *flags, int *val, char *buf) +static int check_setup_args(char *key, int *flags, int *val, char *buf) { int x; char *cp; - for (x=0; x<MAX_SETUP_STRINGS; x++) { + for (x=0; x<MAX_SETUP_ARGS; x++) { if (setup_used[x]) continue; - if (!strncmp(setup_strings[x], key, strlen(key))) + if (!strncmp(setup_args[x], key, strlen(key))) break; } - if (x == MAX_SETUP_STRINGS) + if (x == MAX_SETUP_ARGS) return 0; setup_used[x] = 1; - cp = setup_strings[x] + strlen(key); + cp = setup_args[x] + strlen(key); *val = -1; if (*cp != ':') return ++x; @@ -2221,16 +1931,10 @@ char *cp; -struct proc_dir_entry proc_scsi_in2000 = { - PROC_SCSI_IN2000, 6, "in2000", - S_IFDIR | S_IRUGO | S_IXUGO, 2 - }; - - -/* As of the 2.1.x kernel series, memory-mapped hardware such - * as the IN2000 EPROM and dip switch must be accessed through - * special macros declared in 'asm/io.h'. We use readb() and - * readl() when reading from the card's BIOS area in in2000_detect(). +/* The "correct" (ie portable) way to access memory-mapped hardware + * such as the IN2000 EPROM and dip switch is through the use of + * special macros declared in 'asm/io.h'. We use readb() and readl() + * when reading from the card's BIOS area in in2000_detect(). */ static const unsigned int *bios_tab[] = { (unsigned int *)0xc8000, @@ -2253,6 +1957,7 @@ static const int int_tab[] = { 10 }; + int in2000_detect(Scsi_Host_Template * tpnt) { struct Scsi_Host *instance; @@ -2268,29 +1973,33 @@ int val; char buf[32]; /* Thanks to help from Bill Earnest, probing for IN2000 cards is a - * pretty straightforward and fool-proof operation. We do require - * that cards have their BIOS enabled, although I hope to be able - * to detect and use BIOS-less cards in the future. There are 3 + * pretty straightforward and fool-proof operation. There are 3 * possible locations for the IN2000 EPROM in memory space - if we * find a BIOS signature, we can read the dip switch settings from * the byte at BIOS+32 (shadowed in by logic on the card). From 2 * of the switch bits we get the card's address in IO space. There's * an image of the dip switch there, also, so we have a way to back- - * check that this really is an IN2000 card. Very nifty. - * - * There have been a couple of BIOS versions with different layouts - * for the obvious ID strings. We look for the 2 most common ones and - * hope that they cover all the cases... + * check that this really is an IN2000 card. Very nifty. Use the + * 'ioport:xx' command-line parameter if your BIOS EPROM is absent + * or disabled. */ + if (!done_setup && setup_strings) + in2000_setup(setup_strings,0); + detect_count = 0; for (bios = 0; bios_tab[bios]; bios++) { - if (check_setup_strings("ioport",&flags,&val,buf)) { + if (check_setup_args("ioport",&flags,&val,buf)) { base = val; switches = ~inb(base + IO_SWITCHES) & 0xff; printk("Forcing IN2000 detection at IOport 0x%x ",base); bios = 2; } +/* + * There have been a couple of BIOS versions with different layouts + * for the obvious ID strings. We look for the 2 most common ones and + * hope that they cover all the cases... + */ else if (readl(bios_tab[bios]+0x04) == 0x41564f4e || readl(bios_tab[bios]+0x0c) == 0x61776c41) { printk("Found IN2000 BIOS at 0x%x ",(unsigned int)bios_tab[bios]); @@ -2374,6 +2083,11 @@ char buf[32]; hostdata->busy[x] = 0; hostdata->sync_xfer[x] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF); hostdata->sync_stat[x] = SS_UNSET; /* using default sync values */ +#ifdef PROC_STATISTICS + hostdata->cmd_cnt[x] = 0; + hostdata->disc_allowed_cnt[x] = 0; + hostdata->disc_done_cnt[x] = 0; +#endif } hostdata->input_Q = NULL; hostdata->selecting = NULL; @@ -2395,36 +2109,42 @@ char buf[32]; else hostdata->sync_off = 0xff; /* sync defaults to off */ - hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS| +#ifdef PROC_INTERFACE + hostdata->proc = PR_VERSION|PR_INFO|PR_STATISTICS| PR_CONNECTED|PR_INPUTQ|PR_DISCQ| PR_STOP; - -#ifdef PROC_INTERFACE - disc_allowed_total = 0; - disc_taken_total = 0; +#ifdef PROC_STATISTICS + hostdata->int_cnt = 0; +#endif #endif - if (check_setup_strings("nosync",&flags,&val,buf)) + if (check_setup_args("nosync",&flags,&val,buf)) hostdata->sync_off = val; - if (check_setup_strings("period",&flags,&val,buf)) + if (check_setup_args("period",&flags,&val,buf)) hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns; - if (check_setup_strings("disconnect",&flags,&val,buf)) { + if (check_setup_args("disconnect",&flags,&val,buf)) { if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) hostdata->disconnect = val; else hostdata->disconnect = DIS_ADAPTIVE; } - if (check_setup_strings("noreset",&flags,&val,buf)) + if (check_setup_args("noreset",&flags,&val,buf)) hostdata->args ^= A_NO_SCSI_RESET; - if (check_setup_strings("debug",&flags,&val,buf)) + if (check_setup_args("level2",&flags,&val,buf)) + hostdata->level2 = val; + + if (check_setup_args("debug",&flags,&val,buf)) hostdata->args = (val & DB_MASK); - if (check_setup_strings("proc",&flags,&val,buf)) +#ifdef PROC_INTERFACE + if (check_setup_args("proc",&flags,&val,buf)) hostdata->proc = val; +#endif + x = reset_hardware(instance,(hostdata->args & A_NO_SCSI_RESET)?RESET_CARD:RESET_CARD_AND_BUS); @@ -2450,9 +2170,9 @@ char buf[32]; (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown", hostdata->microcode); #ifdef DEBUGGING_ON - printk("setup_strings = "); - for (x=0; x<8; x++) - printk("%s,",setup_strings[x]); + printk("setup_args = "); + for (x=0; x<MAX_SETUP_ARGS; x++) + printk("%s,",setup_args[x]); printk("\n"); #endif if (hostdata->sync_off == 0xff) @@ -2496,23 +2216,18 @@ int size; iinfo[0] = 255; iinfo[1] = 63; iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]); - -/* This next little bit of code was intended to prevent the number of - * tracks from exceeding 1023. As Andries Brouwer (aeb@cwi.nl) pointed - * out in his "Large Disk HOWTO" (June 1996), this kind of DOS - * compatibility is pointless. And wasteful on disks larger than 8 gigs. - */ - -#if 0 - if (iinfo[2] > 1023) - iinfo[2] = 1023; -#endif - } return 0; } + +struct proc_dir_entry proc_scsi_in2000 = { + PROC_SCSI_IN2000, 6, "in2000", + S_IFDIR | S_IRUGO | S_IXUGO, 2 + }; + + int in2000_proc_info(char *buf, char **start, off_t off, int len, int hn, int in) { @@ -2576,6 +2291,10 @@ static int stop = 0; bp += 5; hd->proc = simple_strtoul(bp,NULL,0); } + else if (!strncmp(bp,"level2:",7)) { + bp += 7; + hd->level2 = simple_strtoul(bp,NULL,0); + } return len; } @@ -2594,12 +2313,38 @@ static int stop = 0; (hd->dip_switch & 0x40)?"Yes":"No", (hd->dip_switch & 0x20)?"Yes":"No"); strcat(bp,tbuf); + strcat(bp,"\nsync_xfer[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_xfer[x]); + strcat(bp,tbuf); + } + strcat(bp,"\nsync_stat[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_stat[x]); + strcat(bp,tbuf); + } } - if (hd->proc & PR_TOTALS) { - sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken", - disc_allowed_total,disc_taken_total); +#ifdef PROC_STATISTICS + if (hd->proc & PR_STATISTICS) { + strcat(bp,"\ncommands issued: "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]); + strcat(bp,tbuf); + } + strcat(bp,"\ndisconnects allowed:"); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]); + strcat(bp,tbuf); + } + strcat(bp,"\ndisconnects done: "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]); + strcat(bp,tbuf); + } + sprintf(tbuf,"\ninterrupts: \t%ld",hd->int_cnt); strcat(bp,tbuf); } +#endif if (hd->proc & PR_CONNECTED) { strcat(bp,"\nconnected: "); if (hd->connected) { diff --git a/drivers/scsi/in2000.h b/drivers/scsi/in2000.h index 4bae99bc1..bb75a08c5 100644 --- a/drivers/scsi/in2000.h +++ b/drivers/scsi/in2000.h @@ -2,7 +2,7 @@ * in2000.h - Linux device driver definitions for the * Always IN2000 ISA SCSI card. * - * IMPORTANT: This file is for version 1.30 - 14/Oct/1996 + * IMPORTANT: This file is for version 1.31 - 06/Jul/1997 * * Copyright (c) 1996 John Shifflett, GeoLog Consulting * john@geolog.com @@ -23,13 +23,366 @@ #ifndef IN2000_H #define IN2000_H -extern struct proc_dir_entry proc_scsi_in2000; +#include <asm/io.h> + +#define PROC_INTERFACE /* add code for /proc/scsi/in2000/xxx interface */ +#ifdef PROC_INTERFACE +#define PROC_STATISTICS /* add code for keeping various real time stats */ +#endif + +#define SYNC_DEBUG /* extra info on sync negotiation printed */ +#define DEBUGGING_ON /* enable command-line debugging bitmask */ +#define DEBUG_DEFAULTS 0 /* default bitmask - change from command-line */ + +#define FAST_READ_IO /* No problems with these on my machine */ +#define FAST_WRITE_IO + +#ifdef DEBUGGING_ON +#define DB(f,a) if (hostdata->args & (f)) a; +#define CHECK_NULL(p,s) /* if (!(p)) {printk("\n"); while (1) printk("NP:%s\r",(s));} */ +#else +#define DB(f,a) +#define CHECK_NULL(p,s) +#endif + +#define uchar unsigned char + +#define read1_io(a) (inb(hostdata->io_base+(a))) +#define read2_io(a) (inw(hostdata->io_base+(a))) +#define write1_io(b,a) (outb((b),hostdata->io_base+(a))) +#define write2_io(w,a) (outw((w),hostdata->io_base+(a))) + +/* These inline assembly defines are derived from a patch + * sent to me by Bill Earnest. He's done a lot of very + * valuable thinking, testing, and coding during his effort + * to squeeze more speed out of this driver. I really think + * that we are doing IO at close to the maximum now with + * the fifo. (And yes, insw uses 'edi' while outsw uses + * 'esi'. Thanks Bill!) + */ + +#define FAST_READ2_IO() \ + __asm__ __volatile__ ("\n \ + cld \n \ + orl %%ecx, %%ecx \n \ + jz 1f \n \ + rep \n \ + insw %%dx \n \ +1: " \ + : "=D" (sp) /* output */ \ + : "d" (f), "D" (sp), "c" (i) /* input */ \ + : "edx", "ecx", "edi" ) /* trashed */ + +#define FAST_WRITE2_IO() \ + __asm__ __volatile__ ("\n \ + cld \n \ + orl %%ecx, %%ecx \n \ + jz 1f \n \ + rep \n \ + outsw %%dx \n \ +1: " \ + : "=S" (sp) /* output */ \ + : "d" (f), "S" (sp), "c" (i) /* input */ \ + : "edx", "ecx", "esi" ) /* trashed */ + + +/* IN2000 io_port offsets */ +#define IO_WD_ASR 0x00 /* R - 3393 auxstat reg */ +#define ASR_INT 0x80 +#define ASR_LCI 0x40 +#define ASR_BSY 0x20 +#define ASR_CIP 0x10 +#define ASR_PE 0x02 +#define ASR_DBR 0x01 +#define IO_WD_ADDR 0x00 /* W - 3393 address reg */ +#define IO_WD_DATA 0x01 /* R/W - rest of 3393 regs */ +#define IO_FIFO 0x02 /* R/W - in2000 dual-port fifo (16 bits) */ +#define IN2000_FIFO_SIZE 2048 /* fifo capacity in bytes */ +#define IO_CARD_RESET 0x03 /* W - in2000 start master reset */ +#define IO_FIFO_COUNT 0x04 /* R - in2000 fifo counter */ +#define IO_FIFO_WRITE 0x05 /* W - clear fifo counter, start write */ +#define IO_FIFO_READ 0x07 /* W - start fifo read */ +#define IO_LED_OFF 0x08 /* W - turn off in2000 activity LED */ +#define IO_SWITCHES 0x08 /* R - read in2000 dip switch */ +#define SW_ADDR0 0x01 /* bit 0 = bit 0 of index to io addr */ +#define SW_ADDR1 0x02 /* bit 1 = bit 1 of index io addr */ +#define SW_DISINT 0x04 /* bit 2 true if ints disabled */ +#define SW_INT0 0x08 /* bit 3 = bit 0 of index to interrupt */ +#define SW_INT1 0x10 /* bit 4 = bit 1 of index to interrupt */ +#define SW_INT_SHIFT 3 /* shift right this amount to right justify int bits */ +#define SW_SYNC_DOS5 0x20 /* bit 5 used by Always BIOS */ +#define SW_FLOPPY 0x40 /* bit 6 true if floppy enabled */ +#define SW_BIT7 0x80 /* bit 7 hardwired true (ground) */ +#define IO_LED_ON 0x09 /* W - turn on in2000 activity LED */ +#define IO_HARDWARE 0x0a /* R - read in2000 hardware rev, stop reset */ +#define IO_INTR_MASK 0x0c /* W - in2000 interrupt mask reg */ +#define IMASK_WD 0x01 /* WD33c93 interrupt mask */ +#define IMASK_FIFO 0x02 /* FIFO interrupt mask */ + +/* wd register names */ +#define WD_OWN_ID 0x00 +#define WD_CONTROL 0x01 +#define WD_TIMEOUT_PERIOD 0x02 +#define WD_CDB_1 0x03 +#define WD_CDB_2 0x04 +#define WD_CDB_3 0x05 +#define WD_CDB_4 0x06 +#define WD_CDB_5 0x07 +#define WD_CDB_6 0x08 +#define WD_CDB_7 0x09 +#define WD_CDB_8 0x0a +#define WD_CDB_9 0x0b +#define WD_CDB_10 0x0c +#define WD_CDB_11 0x0d +#define WD_CDB_12 0x0e +#define WD_TARGET_LUN 0x0f +#define WD_COMMAND_PHASE 0x10 +#define WD_SYNCHRONOUS_TRANSFER 0x11 +#define WD_TRANSFER_COUNT_MSB 0x12 +#define WD_TRANSFER_COUNT 0x13 +#define WD_TRANSFER_COUNT_LSB 0x14 +#define WD_DESTINATION_ID 0x15 +#define WD_SOURCE_ID 0x16 +#define WD_SCSI_STATUS 0x17 +#define WD_COMMAND 0x18 +#define WD_DATA 0x19 +#define WD_QUEUE_TAG 0x1a +#define WD_AUXILIARY_STATUS 0x1f + +/* WD commands */ +#define WD_CMD_RESET 0x00 +#define WD_CMD_ABORT 0x01 +#define WD_CMD_ASSERT_ATN 0x02 +#define WD_CMD_NEGATE_ACK 0x03 +#define WD_CMD_DISCONNECT 0x04 +#define WD_CMD_RESELECT 0x05 +#define WD_CMD_SEL_ATN 0x06 +#define WD_CMD_SEL 0x07 +#define WD_CMD_SEL_ATN_XFER 0x08 +#define WD_CMD_SEL_XFER 0x09 +#define WD_CMD_RESEL_RECEIVE 0x0a +#define WD_CMD_RESEL_SEND 0x0b +#define WD_CMD_WAIT_SEL_RECEIVE 0x0c +#define WD_CMD_TRANS_ADDR 0x18 +#define WD_CMD_TRANS_INFO 0x20 +#define WD_CMD_TRANSFER_PAD 0x21 +#define WD_CMD_SBT_MODE 0x80 + +/* SCSI Bus Phases */ +#define PHS_DATA_OUT 0x00 +#define PHS_DATA_IN 0x01 +#define PHS_COMMAND 0x02 +#define PHS_STATUS 0x03 +#define PHS_MESS_OUT 0x06 +#define PHS_MESS_IN 0x07 + +/* Command Status Register definitions */ + + /* reset state interrupts */ +#define CSR_RESET 0x00 +#define CSR_RESET_AF 0x01 + + /* successful completion interrupts */ +#define CSR_RESELECT 0x10 +#define CSR_SELECT 0x11 +#define CSR_SEL_XFER_DONE 0x16 +#define CSR_XFER_DONE 0x18 + + /* paused or aborted interrupts */ +#define CSR_MSGIN 0x20 +#define CSR_SDP 0x21 +#define CSR_SEL_ABORT 0x22 +#define CSR_RESEL_ABORT 0x25 +#define CSR_RESEL_ABORT_AM 0x27 +#define CSR_ABORT 0x28 + + /* terminated interrupts */ +#define CSR_INVALID 0x40 +#define CSR_UNEXP_DISC 0x41 +#define CSR_TIMEOUT 0x42 +#define CSR_PARITY 0x43 +#define CSR_PARITY_ATN 0x44 +#define CSR_BAD_STATUS 0x45 +#define CSR_UNEXP 0x48 + + /* service required interrupts */ +#define CSR_RESEL 0x80 +#define CSR_RESEL_AM 0x81 +#define CSR_DISC 0x85 +#define CSR_SRV_REQ 0x88 + + /* Own ID/CDB Size register */ +#define OWNID_EAF 0x08 +#define OWNID_EHP 0x10 +#define OWNID_RAF 0x20 +#define OWNID_FS_8 0x00 +#define OWNID_FS_12 0x40 +#define OWNID_FS_16 0x80 + + /* Control register */ +#define CTRL_HSP 0x01 +#define CTRL_HA 0x02 +#define CTRL_IDI 0x04 +#define CTRL_EDI 0x08 +#define CTRL_HHP 0x10 +#define CTRL_POLLED 0x00 +#define CTRL_BURST 0x20 +#define CTRL_BUS 0x40 +#define CTRL_DMA 0x80 + + /* Timeout Period register */ +#define TIMEOUT_PERIOD_VALUE 20 /* results in 200 ms. */ + + /* Synchronous Transfer Register */ +#define STR_FSS 0x80 + + /* Destination ID register */ +#define DSTID_DPD 0x40 +#define DATA_OUT_DIR 0 +#define DATA_IN_DIR 1 +#define DSTID_SCC 0x80 + + /* Source ID register */ +#define SRCID_MASK 0x07 +#define SRCID_SIV 0x08 +#define SRCID_DSP 0x20 +#define SRCID_ES 0x40 +#define SRCID_ER 0x80 + + + +#define ILLEGAL_STATUS_BYTE 0xff + + +#define DEFAULT_SX_PER 500 /* (ns) fairly safe */ +#define DEFAULT_SX_OFF 0 /* aka async */ + +#define OPTIMUM_SX_PER 252 /* (ns) best we can do (mult-of-4) */ +#define OPTIMUM_SX_OFF 12 /* size of in2000 fifo */ + +struct sx_period { + unsigned int period_ns; + uchar reg_value; + }; + + +struct IN2000_hostdata { + struct Scsi_Host *next; + uchar chip; /* what kind of wd33c93 chip? */ + uchar microcode; /* microcode rev if 'B' */ + unsigned short io_base; /* IO port base */ + unsigned int dip_switch; /* dip switch settings */ + unsigned int hrev; /* hardware revision of card */ + volatile uchar busy[8]; /* index = target, bit = lun */ + volatile Scsi_Cmnd *input_Q; /* commands waiting to be started */ + volatile Scsi_Cmnd *selecting; /* trying to select this command */ + volatile Scsi_Cmnd *connected; /* currently connected command */ + volatile Scsi_Cmnd *disconnected_Q;/* commands waiting for reconnect */ + uchar state; /* what we are currently doing */ + uchar fifo; /* what the FIFO is up to */ + uchar level2; /* extent to which Level-2 commands are used */ + uchar disconnect; /* disconnect/reselect policy */ + unsigned int args; /* set from command-line argument */ + uchar incoming_msg[8]; /* filled during message_in phase */ + int incoming_ptr; /* mainly used with EXTENDED messages */ + uchar outgoing_msg[8]; /* send this during next message_out */ + int outgoing_len; /* length of outgoing message */ + unsigned int default_sx_per; /* default transfer period for SCSI bus */ + uchar sync_xfer[8]; /* sync_xfer reg settings per target */ + uchar sync_stat[8]; /* status of sync negotiation per target */ + uchar sync_off; /* bit mask: don't use sync with these targets */ +#ifdef PROC_INTERFACE + uchar proc; /* bit mask: what's in proc output */ +#ifdef PROC_STATISTICS + unsigned long cmd_cnt[8]; /* # of commands issued per target */ + unsigned long int_cnt; /* # of interrupts serviced */ + unsigned long disc_allowed_cnt[8]; /* # of disconnects allowed per target */ + unsigned long disc_done_cnt[8]; /* # of disconnects done per target*/ +#endif +#endif + }; + + +/* defines for hostdata->chip */ + +#define C_WD33C93 0 +#define C_WD33C93A 1 +#define C_WD33C93B 2 +#define C_UNKNOWN_CHIP 100 + +/* defines for hostdata->state */ + +#define S_UNCONNECTED 0 +#define S_SELECTING 1 +#define S_RUNNING_LEVEL2 2 +#define S_CONNECTED 3 +#define S_PRE_TMP_DISC 4 +#define S_PRE_CMP_DISC 5 + +/* defines for hostdata->fifo */ + +#define FI_FIFO_UNUSED 0 +#define FI_FIFO_READING 1 +#define FI_FIFO_WRITING 2 + +/* defines for hostdata->level2 */ +/* NOTE: only the first 3 are trustworthy at this point - + * having trouble when more than 1 device is reading/writing + * at the same time... + */ + +#define L2_NONE 0 /* no combination commands - we get lots of ints */ +#define L2_SELECT 1 /* start with SEL_ATN_XFER, but never resume it */ +#define L2_BASIC 2 /* resume after STATUS ints & RDP messages */ +#define L2_DATA 3 /* resume after DATA_IN/OUT ints */ +#define L2_MOST 4 /* resume after anything except a RESELECT int */ +#define L2_RESELECT 5 /* resume after everything, including RESELECT ints */ +#define L2_ALL 6 /* always resume */ + +/* defines for hostdata->disconnect */ + +#define DIS_NEVER 0 +#define DIS_ADAPTIVE 1 +#define DIS_ALWAYS 2 + +/* defines for hostdata->args */ + +#define DB_TEST 1<<0 +#define DB_FIFO 1<<1 +#define DB_QUEUE_COMMAND 1<<2 +#define DB_EXECUTE 1<<3 +#define DB_INTR 1<<4 +#define DB_TRANSFER 1<<5 +#define DB_MASK 0x3f + +#define A_NO_SCSI_RESET 1<<15 + + +/* defines for hostdata->sync_xfer[] */ + +#define SS_UNSET 0 +#define SS_FIRST 1 +#define SS_WAITING 2 +#define SS_SET 3 + +/* defines for hostdata->proc */ + +#define PR_VERSION 1<<0 +#define PR_INFO 1<<1 +#define PR_STATISTICS 1<<2 +#define PR_CONNECTED 1<<3 +#define PR_INPUTQ 1<<4 +#define PR_DISCQ 1<<5 +#define PR_TEST 1<<6 +#define PR_STOP 1<<7 + int in2000_detect(Scsi_Host_Template *); int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int in2000_abort(Scsi_Cmnd *); void in2000_setup(char *, int *); int in2000_proc_info(char *, char **, off_t, int, int, int); +struct proc_dir_entry proc_scsi_in2000; int in2000_biosparam(struct scsi_disk *, kdev_t, int *); int in2000_reset(Scsi_Cmnd *, unsigned int); @@ -40,7 +393,7 @@ int in2000_reset(Scsi_Cmnd *, unsigned int); #define IN2000_HOST_ID 7 #define IN2000 { NULL, /* link pointer for modules */ \ - NULL, /* module pointer for modules */ \ + NULL, /* usage_count for modules */ \ &proc_scsi_in2000, /* pointer to /proc/scsi directory entry */ \ in2000_proc_info, /* pointer to proc info function */ \ "Always IN2000", /* device name */ \ diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 581f280e8..e9bdcee9b 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -115,6 +115,8 @@ * [Curtin-1-08-STABLE] */ +#include <linux/config.h> + /* The following #define is to avoid a clash with hosts.c */ #define PPA_CODE 1 #include "ppa.h" diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 5e14ea594..b8620dfdc 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -653,7 +653,7 @@ __initfunc(int qlogicpti_detect(Scsi_Host_Template *tpnt)) } qpti_host->base = (unsigned char *)qregs; - qpti_host->io_port = (unsigned int) qregs; + qpti_host->io_port = (unsigned int) ((unsigned long)qregs); qpti_host->n_io_port = (unsigned char) qpti->qdev->reg_addrs[0].reg_size; @@ -805,7 +805,7 @@ static inline void cmd_frob(struct Command_Entry *cmd, Scsi_Cmnd *Cmnd, memset(cmd, 0, sizeof(struct Command_Entry)); cmd->hdr.entry_cnt = 1; cmd->hdr.entry_type = ENTRY_COMMAND; - cmd->handle = (u_int) Cmnd; /* magic mushroom */ + cmd->handle = (u_int) ((unsigned long)Cmnd); /* magic mushroom */ cmd->target_id = Cmnd->target; cmd->target_lun = Cmnd->lun; cmd->cdb_length = Cmnd->cmd_len; @@ -890,7 +890,7 @@ static inline u_int load_cmd(Scsi_Cmnd *Cmnd, struct Command_Entry *cmd, Cmnd->request_bufflen, qpti->qdev->my_bus)); - cmd->dataseg[0].d_base = (u_int) Cmnd->SCp.ptr; + cmd->dataseg[0].d_base = (u_int) ((unsigned long)Cmnd->SCp.ptr); cmd->dataseg[0].d_count = Cmnd->request_bufflen; cmd->segment_cnt = 1; } @@ -1062,7 +1062,7 @@ repeat: while(out_ptr != in_ptr) { sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr]; out_ptr = NEXT_RES_PTR(out_ptr); - Cmnd = (Scsi_Cmnd *) sts->handle; /* but_to_virt?!?! */ + Cmnd = (Scsi_Cmnd *) ((unsigned long)sts->handle); if(sts->completion_status == CS_RESET_OCCURRED || sts->completion_status == CS_ABORTED || (sts->status_flags & STF_BUS_RESET)) @@ -1111,8 +1111,8 @@ int qlogicpti_abort(Scsi_Cmnd *Cmnd) qlogicpti_disable_irqs(qpti->qregs); param[0] = MBOX_ABORT; param[1] = (((u_short) Cmnd->target) << 8) | Cmnd->lun; - param[2] = ((unsigned int)Cmnd) >> 16; - param[3] = ((unsigned int)Cmnd) & 0xffff; + param[2] = ((unsigned int)((unsigned long)Cmnd)) >> 16; + param[3] = ((unsigned int)((unsigned long)Cmnd)) & 0xffff; if(qlogicpti_mbox_command(qpti, param, 0) || (param[0] != MBOX_COMMAND_COMPLETE)) { printk(KERN_EMERG "qlogicpti : scsi abort failure: %x\n", param[0]); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c9b8064b3..1f0080bd9 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -12,6 +12,7 @@ * Rik Faith <faith@cs.unc.edu> * Tommy Thorn <tthorn> * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de> + * Andrea Arcangeli <arcangeli@mbox.queen.it> * * Modified by Eric Youngdale eric@aib.com to * add scatter-gather, multiple outstanding request, and other @@ -65,7 +66,7 @@ #undef USE_STATIC_SCSI_MEMORY /* -static const char RCSid[] = "$Header: /vger/u4/cvs/linux/drivers/scsi/scsi.c,v 1.38 1997/01/19 23:07:18 davem Exp $"; +static const char RCSid[] = "$Header: /src/cvs/linux/drivers/scsi/scsi.c,v 1.1.1.1 1997/06/01 03:17:37 ralf Exp $"; */ @@ -3512,6 +3513,7 @@ int init_module(void) { void cleanup_module( void) { + timer_active &= ~(1 << SCSI_TIMER); #if CONFIG_PROC_FS proc_scsi_unregister(0, PROC_SCSI_SCSI); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index eb9e7fec3..d83b94e26 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -488,7 +488,7 @@ static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsig static unsigned int sg_poll(struct file *file, poll_table * wait) { - int dev = MINOR(file->f_inode->i_rdev); + int dev = MINOR(file->f_dentry->d_inode->i_rdev); struct scsi_generic *device = &scsi_generics[dev]; unsigned int mask = 0; diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index a10e9545d..bc870dfea 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -1,3 +1,4 @@ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index f5ceb1371..3008f1b21 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -50,13 +50,8 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/config.h> - -#include <linux/version.h> -#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */ -#include "../block/blk.h" -#else +#include <linux/init.h> #include <linux/blk.h> -#endif #include "scsi.h" #include "hosts.h" @@ -69,13 +64,10 @@ #define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI - -#ifndef VERSION_ELF_1_2_13 struct proc_dir_entry proc_scsi_tmscsim ={ PROC_SCSI_DC390T, 7 ,"tmscsim", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; -#endif static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); @@ -675,11 +667,7 @@ DoNextCmd( PACB pACB, PDCB pDCB ) * Description: * Return the disk geometry for the given SCSI device. ***********************************************************************/ -#ifdef VERSION_ELF_1_2_13 -int DC390_bios_param(Disk *disk, int devno, int geom[]) -#else int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]) -#endif { int heads, sectors, cylinders; PACB pACB; @@ -1046,14 +1034,10 @@ void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ) ***********************************************************************/ void DC390_initSRB( PSRB psrb ) { -#ifndef VERSION_ELF_1_2_13 #ifdef DC390_DEBUG0 printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb)); #endif psrb->PhysSRB = virt_to_bus( psrb ); -#else - psrb->PhysSRB = (ULONG) psrb; -#endif } @@ -1084,7 +1068,7 @@ void DC390_linkSRB( PACB pACB ) * Inputs : psh - pointer to this host adapter's structure * ***********************************************************************/ -void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +__initfunc(void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index )) { PACB pACB; USHORT i; @@ -1098,7 +1082,6 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) pACB = (PACB) psh->hostdata; -#ifndef VERSION_ELF_1_2_13 psh->max_id = 8; #ifdef CONFIG_SCSI_MULTI_LUN if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) @@ -1106,7 +1089,6 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) else #endif psh->max_lun = 1; -#endif pACB->max_id = 7; if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] ) @@ -1155,7 +1137,7 @@ void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) * Inputs : psh - pointer to this host adapter's structure * ***********************************************************************/ -int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) +__initfunc(int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index)) { USHORT ioport; UCHAR bval; @@ -1179,11 +1161,7 @@ int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) if( !used_irq ) { -#ifdef VERSION_ELF_1_2_13 - if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim")) -#else if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim", NULL)) -#endif { printk("DC390: register IRQ error!\n"); return( -1 ); @@ -1533,8 +1511,8 @@ DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum ) * field of the pACB structure MUST have been set. ***********************************************************************/ -static int -DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum) +__initfunc(static int +DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum)) { PSH psh; PACB pACB; @@ -1614,8 +1592,8 @@ DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum) * ***********************************************************************/ -int -DC390_detect(Scsi_Host_Template *psht) +__initfunc(int +DC390_detect(Scsi_Host_Template *psht)) { #ifdef FOR_PCI_OK UCHAR pci_bus, pci_device_fn; @@ -1626,19 +1604,13 @@ DC390_detect(Scsi_Host_Template *psht) UCHAR irq; UCHAR istatus; -#ifndef VERSION_ELF_1_2_13 UINT io_port; -#else - ULONG io_port; -#endif USHORT adaptCnt = 0; /* Number of boards detected */ USHORT pci_index = 0; /* Device index to PCI BIOS calls */ USHORT MechNum, BusDevFunNum; ULONG wlval; -#ifndef VERSION_ELF_1_2_13 psht->proc_dir = &proc_scsi_tmscsim; -#endif InitialTime = 1; pSHT_start = psht; @@ -1726,8 +1698,6 @@ DC390_detect(Scsi_Host_Template *psht) } -#ifndef VERSION_ELF_1_2_13 - /******************************************************************** * Function: tmscsim_set_info() * @@ -1848,7 +1818,6 @@ int tmscsim_proc_info(char *buffer, char **start, else return length; } -#endif /* VERSION_ELF_1_2_13 */ #ifdef MODULE @@ -1909,11 +1878,7 @@ int DC390_release(struct Scsi_Host *host) #ifdef DC390_DEBUG0 printk("DC390: Free IRQ %i.",host->irq); #endif -#ifndef VERSION_ELF_1_2_13 free_irq(host->irq,NULL); -#else - free_irq(host->irq); -#endif } } diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 14cb0f37a..6d60d12ae 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -84,8 +84,9 @@ #include "scsi.h" #include "hosts.h" -#define WD33C93_VERSION "1.24" -#define WD33C93_DATE "29/Jan/1997" +#define WD33C93_VERSION "1.25" +#define WD33C93_DATE "09/Jul/1997" +/* NOTE: 1.25 for m68k is related to in2000-1.31 for x86 */ /* * Note - the following defines have been moved to 'wd33c93.h': @@ -103,11 +104,15 @@ -/* setup_strings is an array of strings that define some of the operating - * parameters and settings for this driver. It is used unless an amiboot - * or insmod command line has been specified, in which case those settings - * are combined with the ones here. The driver recognizes the following - * keywords (lower case required) and arguments: +/* + * 'setup_strings' is a single string used to pass operating parameters and + * settings from the kernel/module command-line to the driver. 'setup_args[]' + * is an array of strings that define the compile-time default values for + * these settings. If Linux boots with an amiboot or insmod command-line, + * those settings are combined with 'setup_args[]'. Note that amiboot + * command-lines are prefixed with "wd33c93=" while insmod uses a + * "setup_strings=" prefix. The driver recognizes the following keywords + * (lower case required) and arguments: * * - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with * the 7 possible SCSI devices. Set a bit to negotiate for @@ -135,13 +140,11 @@ * _must_ be a colon between a keyword and its numeric argument, with no * spaces. * - Keywords are separated by commas, no spaces, in the standard kernel - * command-line manner, except in the case of 'setup_strings[]' (see - * below), which is simply a C array of pointers to char. Each element - * in the array is a string comprising one keyword & argument. + * command-line manner. * - A keyword in the 'nth' comma-separated command-line member will overwrite - * the 'nth' element of setup_strings[]. A blank command-line member (in + * the 'nth' element of setup_args[]. A blank command-line member (in * other words, a comma with no preceding keyword) will _not_ overwrite - * the corresponding setup_strings[] element. + * the corresponding setup_args[] element. * - If a keyword is used more than once, the first one applies to the first * SCSI host found, the second to the second card, etc, unless the 'next' * keyword is used to change the order. @@ -154,8 +157,16 @@ * - wd33c93=debug:0x1c */ -static char *setup_strings[] = - {"","","","","","","","","","","",""}; +/* Normally, no defaults are specified */ +static char *setup_args[] = + {"","","","","","","","",""}; + +/* filled in by 'insmod' */ +static char *setup_strings = 0; + +#ifdef MODULE_PARM +MODULE_PARM(setup_strings, "s"); +#endif static inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num) @@ -319,8 +330,24 @@ int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) cmd->SCp.this_residual = cmd->request_bufflen; } - /* Preset the command status to GOOD, since that's the normal case */ - cmd->SCp.Status = GOOD; + /* WD docs state that at the conclusion of a "LEVEL2" command, the + * status byte can be retrieved from the LUN register. Apparently, + * this is the case only for *uninterrupted* LEVEL2 commands! If + * there are any unexpected phases entered, even if they are 100% + * legal (different devices may choose to do things differently), + * the LEVEL2 command sequence is exited. This often occurs prior + * to receiving the status byte, in which case the driver does a + * status phase interrupt and gets the status byte on its own. + * While such a command can then be "resumed" (ie restarted to + * finish up as a LEVEL2 command), the LUN register will NOT be + * a valid status byte at the command's conclusion, and we must + * use the byte obtained during the earlier interrupt. Here, we + * preset SCp.Status to an illegal value (0xff) so that when + * this command finally completes, we can tell where the actual + * status byte is stored. + */ + + cmd->SCp.Status = ILLEGAL_STATUS_BYTE; /* Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE * commands are added to the head of the queue so that the desired @@ -641,7 +668,8 @@ use_transfer_pio: write_wd33c93(regp, WD_CONTROL, (CTRL_IDI | CTRL_EDI | CTRL_DMA)); /* write_wd33c93_count(regp, cmd->SCp.this_residual); */ - if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) { + if ((hostdata->level2 >= L2_DATA) || + (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_wd33c93(regp, WD_COMMAND_PHASE, 0x45); write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; @@ -808,8 +836,10 @@ void wd33c93_intr (struct Scsi_Host *instance) case CSR_XFER_DONE|PHS_STATUS: case CSR_UNEXP |PHS_STATUS: case CSR_SRV_REQ |PHS_STATUS: - DB(DB_INTR,printk("STATUS")); + DB(DB_INTR,printk("STATUS=")); + cmd->SCp.Status = read_1_byte(regp); + DB(DB_INTR,printk("%02x",cmd->SCp.Status)); if (hostdata->level2 >= L2_BASIC) { /* clear interrupt */ sr = read_wd33c93(regp, WD_SCSI_STATUS); @@ -817,7 +847,6 @@ void wd33c93_intr (struct Scsi_Host *instance) write_wd33c93(regp, WD_COMMAND_PHASE, 0x50); write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER); } else { - DB(DB_INTR, printk("=%02x", cmd->SCp.Status)); hostdata->state = S_CONNECTED; } break; @@ -999,21 +1028,22 @@ void wd33c93_intr (struct Scsi_Host *instance) DB(DB_INTR, printk("SX-DONE-%ld", cmd->pid)); cmd->SCp.Message = COMMAND_COMPLETE; lun = read_wd33c93(regp, WD_TARGET_LUN); - if (cmd->SCp.Status == GOOD) - cmd->SCp.Status = lun; + DB(DB_INTR,printk(":%d.%d",cmd->SCp.Status,lun)); hostdata->connected = NULL; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | - (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = ((cmd->result & 0x00ffff) | - (DID_ERROR << 16)); hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; + if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) + cmd->SCp.Status = lun; + if (cmd->cmnd[0] == REQUEST_SENSE + && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); cmd->scsi_done(cmd); - /* We are no longer connected to a target - check to see if - * there are commands waiting to be executed. + /* We are no longer connected to a target - check to + * see if there are commands waiting to be executed. */ restore_flags(flags); wd33c93_execute(instance); @@ -1081,10 +1111,13 @@ void wd33c93_intr (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16); + if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | + (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); + cmd->scsi_done(cmd); /* We are no longer connected to a target - check to see if @@ -1110,12 +1143,14 @@ void wd33c93_intr (struct Scsi_Host *instance) hostdata->connected = NULL; hostdata->busy[cmd->target] &= ~(1 << cmd->lun); hostdata->state = S_UNCONNECTED; - if (cmd->cmnd[0] != REQUEST_SENSE) - cmd->result = (cmd->SCp.Status | - (cmd->SCp.Message << 8)); - else if (cmd->SCp.Status != GOOD) - cmd->result = ((cmd->result & 0x00ffff) | - (DID_ERROR << 16)); + DB(DB_INTR,printk(":%d",cmd->SCp.Status)) + if (cmd->cmnd[0] == REQUEST_SENSE && + cmd->SCp.Status != GOOD) + cmd->result = (cmd->result & 0x00ffff) | + (DID_ERROR << 16); + else + cmd->result = cmd->SCp.Status | + (cmd->SCp.Message << 8); cmd->scsi_done(cmd); restore_flags(flags); break; @@ -1455,10 +1490,11 @@ int wd33c93_abort (Scsi_Cmnd *cmd) } #define MAX_WD33C93_HOSTS 4 -#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *)) +#define MAX_SETUP_ARGS (sizeof(setup_args) / sizeof(char *)) #define SETUP_BUFFER_SIZE 200 static char setup_buffer[SETUP_BUFFER_SIZE]; -static char setup_used[MAX_SETUP_STRINGS]; +static char setup_used[MAX_SETUP_ARGS]; +static int done_setup = 0; void wd33c93_setup (char *str, int *ints) { @@ -1486,41 +1522,42 @@ void wd33c93_setup (char *str, int *ints) setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; p1 = setup_buffer; i = 0; - while (*p1 && (i < MAX_SETUP_STRINGS)) { + while (*p1 && (i < MAX_SETUP_ARGS)) { p2 = strchr(p1, ','); if (p2) { *p2 = '\0'; if (p1 != p2) - setup_strings[i] = p1; + setup_args[i] = p1; p1 = p2 + 1; i++; } else { - setup_strings[i] = p1; + setup_args[i] = p1; break; } } - for (i = 0; i < MAX_SETUP_STRINGS; i++) + for (i = 0; i < MAX_SETUP_ARGS; i++) setup_used[i] = 0; + done_setup = 1; } -/* check_setup_strings() returns index if key found, 0 if not */ -static int check_setup_strings(char *key, int *flags, int *val, char *buf) +/* check_setup_args() returns index if key found, 0 if not */ +static int check_setup_args(char *key, int *flags, int *val, char *buf) { int x; char *cp; - for (x = 0; x < MAX_SETUP_STRINGS; x++) { + for (x = 0; x < MAX_SETUP_ARGS; x++) { if (setup_used[x]) continue; - if (!strncmp(setup_strings[x], key, strlen(key))) + if (!strncmp(setup_args[x], key, strlen(key))) break; - if (!strncmp(setup_strings[x], "next", strlen("next"))) + if (!strncmp(setup_args[x], "next", strlen("next"))) return 0; } - if (x == MAX_SETUP_STRINGS) + if (x == MAX_SETUP_ARGS) return 0; setup_used[x] = 1; - cp = setup_strings[x] + strlen(key); + cp = setup_args[x] + strlen(key); *val = -1; if (*cp != ':') return ++x; @@ -1535,12 +1572,16 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, dma_setup_t setup, dma_stop_t stop, int clock_freq) { static int shown = 0; - struct WD33C93_hostdata *hostdata = INSTHOSTDATA(instance); + struct WD33C93_hostdata *hostdata; int i; int flags; int val; char buf[32]; + if (!done_setup && setup_strings) + wd33c93_setup(setup_strings,0); + hostdata = INSTHOSTDATA(instance); + hostdata->regp = regs; hostdata->clock_freq = clock_freq; hostdata->dma_setup = setup; @@ -1580,27 +1621,30 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, #endif - if (check_setup_strings("nosync", &flags, &val, buf)) + if (check_setup_args("nosync",&flags,&val,buf)) hostdata->no_sync = val; - if (check_setup_strings("nodma",&flags,&val,buf)) + if (check_setup_args("nodma",&flags,&val,buf)) hostdata->no_dma = (val == -1) ? 1 : val; - if (check_setup_strings("period", &flags, &val, buf)) + if (check_setup_args("period",&flags,&val,buf)) hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns; - if (check_setup_strings("disconnect", &flags, &val, buf)) { + if (check_setup_args("disconnect",&flags,&val,buf)) { if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) hostdata->disconnect = val; else hostdata->disconnect = DIS_ADAPTIVE; } - if (check_setup_strings("debug", &flags, &val, buf)) + if (check_setup_args("level2",&flags,&val,buf)) + hostdata->level2 = val; + + if (check_setup_args("debug",&flags,&val,buf)) hostdata->args = val & DB_MASK; - if (check_setup_strings("clock", &flags, &val, buf)) { + if (check_setup_args("clock",&flags,&val,buf)) { if ((val > 7) && (val < 11)) val = WD33C93_FS_8_10; else if ((val > 11) && (val < 16)) @@ -1612,13 +1656,13 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, hostdata->clock_freq = val; } - if ((i = check_setup_strings("next", &flags, &val, buf))) { + if ((i = check_setup_args("next",&flags,&val,buf))) { while (i) setup_used[--i] = 1; } #ifdef PROC_INTERFACE - if (check_setup_strings("proc", &flags, &val, buf)) + if (check_setup_args("proc",&flags,&val,buf)) hostdata->proc = val; #endif @@ -1635,9 +1679,9 @@ void wd33c93_init (struct Scsi_Host *instance, wd33c93_regs *regs, printk(" debugging=OFF\n"); #endif #if 0 - printk("wd33c93-%d: setup_strings=", instance->host_no); - for (i = 0; i < MAX_SETUP_STRINGS; i++) - printk("%s,", setup_strings[i]); + printk("wd33c93-%d: setup_args=", instance->host_no); + for (i = 0; i < MAX_SETUP_ARGS; i++) + printk("%s,", setup_args[i]); printk("\n"); printk("wd33c93-%d: debug_flags = %04x\n", instance->host_no, hostdata->args); @@ -1715,6 +1759,11 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i bp += 6; hd->no_dma = simple_strtoul(bp,NULL,0); } + else if (!strncmp(bp,"level2:",7)) { + bp += 7; + hd->level2 = simple_strtoul(bp,NULL,0); + } + return len; } @@ -1731,32 +1780,32 @@ int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int i sprintf(tbuf,"\nclock_freq=%02x no_sync=%02x no_dma=%d", hd->clock_freq,hd->no_sync,hd->no_dma); strcat(bp,tbuf); - strcat(bp,"\nsync_xfer[] ="); - for (x=0; x<8; x++) { - sprintf(tbuf," %02x",hd->sync_xfer[x]); + strcat(bp,"\nsync_xfer[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_xfer[x]); strcat(bp,tbuf); } - strcat(bp,"\nsync_stat[] ="); - for (x=0; x<8; x++) { - sprintf(tbuf," %02x",hd->sync_stat[x]); + strcat(bp,"\nsync_stat[] = "); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%02x",hd->sync_stat[x]); strcat(bp,tbuf); } } #ifdef PROC_STATISTICS if (hd->proc & PR_STATISTICS) { strcat(bp,"\ncommands issued: "); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->cmd_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->cmd_cnt[x]); strcat(bp,tbuf); } strcat(bp,"\ndisconnects allowed:"); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->disc_allowed_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_allowed_cnt[x]); strcat(bp,tbuf); } strcat(bp,"\ndisconnects done: "); - for (x=0; x<8; x++) { - sprintf(tbuf," %ld",hd->disc_done_cnt[x]); + for (x=0; x<7; x++) { + sprintf(tbuf,"\t%ld",hd->disc_done_cnt[x]); strcat(bp,tbuf); } sprintf(tbuf, diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index 3b8b4cf3a..c0584037b 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h @@ -2,7 +2,7 @@ * wd33c93.h - Linux device driver definitions for the * Commodore Amiga A2091/590 SCSI controller card * - * IMPORTANT: This file is for version 1.24 - 29/Jan/1997 + * IMPORTANT: This file is for version 1.25 - 09/Jul/1997 * * Copyright (c) 1996 John Shifflett, GeoLog Consulting * john@geolog.com @@ -34,6 +34,8 @@ #define DEBUG_DEFAULTS 0 /* default debugging bitmask */ +#define ILLEGAL_STATUS_BYTE 0xff + #ifdef DEBUGGING_ON #define DB(f,a) if (hostdata->args & (f)) a; #else diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 310bf01bf..d14653d45 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -1,15 +1,277 @@ -# -# Sound driver configuration -# -#-------- -# There is another confic script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 +bool 'ProAudioSpectrum 16 support' CONFIG_PAS +bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB +bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB +bool 'Gravis Ultrasound support' CONFIG_GUS +bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 +bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS +bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 +bool 'GUS MAX support' CONFIG_GUSMAX +bool 'Microsoft Sound System support' CONFIG_MSS +bool 'Ensoniq SoundScape support' CONFIG_SSCAPE +bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX +bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16 +bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 +bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI +bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 + +if [ "$CONFIG_AEDSP16" = "y" ]; then +hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330 +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' +fi + + +if [ "$CONFIG_SB" = "y" ]; then +comment 'Enter -1 to the following question if you have something else such as SB16/32.' +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 +fi + + +if [ "$CONFIG_MAUI" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with Maui.' +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 +fi + + +if [ "$CONFIG_PSS" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with PSS cards.' +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS I/O base 220 or 240' PSS_BASE 220 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3 +fi +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 +fi + + +if [ "$CONFIG_TRIX" = "y" ]; then +comment 'ERROR! You have to use old sound configuration method with AudioTrix.' +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 +fi +# +$MAKE -C drivers/sound kernelconfig || exit 1 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then diff --git a/drivers/sound/dev_table.h b/drivers/sound/dev_table.h index 7013c3c8d..a7e4026e8 100644 --- a/drivers/sound/dev_table.h +++ b/drivers/sound/dev_table.h @@ -15,6 +15,7 @@ #ifndef _DEV_TABLE_H_ #define _DEV_TABLE_H_ +#include <linux/config.h> /* * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 957150f7b..a9af5956b 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -246,7 +246,7 @@ sound_poll (struct file *file, poll_table * wait) struct inode *inode; int ret = 0; - inode = file->f_inode; + inode = file->f_dentry->d_inode; if (sound_select (inode, file, SEL_IN, wait)) ret |= POLLIN; @@ -326,8 +326,7 @@ sound_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma) vma->vm_page_prot)) return -EAGAIN; - vma->vm_inode = inode; - atomic_inc(&inode->i_count); + vma->vm_dentry = dget(file->f_dentry); dmap->mapping_flags |= DMA_MAP_MAPPED; |