summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-07-20 14:56:40 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-07-20 14:56:40 +0000
commite308faf24f68e262d92d294a01ddca7a17e76762 (patch)
tree22c47cb315811834861f013067878ff664e95abd /drivers
parent30c6397ce63178fcb3e7963ac247f0a03132aca9 (diff)
Sync with Linux 2.1.46.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/floppy.c2
-rw-r--r--drivers/block/ide.c1
-rw-r--r--drivers/block/ide.h2
-rw-r--r--drivers/block/loop.c4
-rw-r--r--drivers/block/md.c4
-rw-r--r--drivers/block/rd.c29
-rw-r--r--drivers/block/triton.c114
-rw-r--r--drivers/char/ChangeLog24
-rw-r--r--drivers/char/Config.in2
-rw-r--r--drivers/char/Makefile18
-rw-r--r--drivers/char/console.c58
-rw-r--r--drivers/char/cyclades.c353
-rw-r--r--drivers/char/joystick.c376
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mem.c11
-rw-r--r--drivers/char/misc.c3
-rw-r--r--drivers/char/n_tty.c93
-rw-r--r--drivers/char/pc110pad.c690
-rw-r--r--drivers/char/pc110pad.h31
-rw-r--r--drivers/char/psaux.c32
-rw-r--r--drivers/char/pty.c97
-rw-r--r--drivers/char/random.c150
-rw-r--r--drivers/char/rtc.c10
-rw-r--r--drivers/char/serial.c40
-rw-r--r--drivers/char/sysrq.c4
-rw-r--r--drivers/char/tty_io.c594
-rw-r--r--drivers/char/vc_screen.c5
-rw-r--r--drivers/isdn/avmb1/capiutil.c6
-rw-r--r--drivers/net/8390.c1
-rw-r--r--drivers/net/Config.in22
-rw-r--r--drivers/net/Makefile18
-rw-r--r--drivers/net/README.wanpipe12
-rw-r--r--drivers/net/Space.c15
-rw-r--r--drivers/net/cops.c1018
-rw-r--r--drivers/net/cops.h60
-rw-r--r--drivers/net/cops_ffdrv.h533
-rw-r--r--drivers/net/cops_ltdrv.h242
-rw-r--r--drivers/net/ibmtr.c11
-rw-r--r--drivers/net/lapbether.c2
-rw-r--r--drivers/net/sdla_fr.c339
-rw-r--r--drivers/net/sdla_ppp.c142
-rw-r--r--drivers/net/sdla_x25.c174
-rw-r--r--drivers/net/sdlamain.c45
-rw-r--r--drivers/net/shaper.c22
-rw-r--r--drivers/net/shaper.h61
-rw-r--r--drivers/net/strip.c1940
-rw-r--r--drivers/net/sunhme.c17
-rw-r--r--drivers/net/sunqe.c14
-rw-r--r--drivers/net/tlan.c2309
-rw-r--r--drivers/net/tlan.h485
-rw-r--r--drivers/net/tulip.c1
-rw-r--r--drivers/pci/pci.c2
-rw-r--r--drivers/pnp/parport_procfs.c3
-rw-r--r--drivers/sbus/char/bwtwo.c12
-rw-r--r--drivers/sbus/char/cgfourteen.c11
-rw-r--r--drivers/sbus/char/cgsix.c65
-rw-r--r--drivers/sbus/char/cgthree.c11
-rw-r--r--drivers/sbus/char/creator.c604
-rw-r--r--drivers/sbus/char/fb.h15
-rw-r--r--drivers/sbus/char/leo.c41
-rw-r--r--drivers/sbus/char/openprom.c10
-rw-r--r--drivers/sbus/char/suncons.c89
-rw-r--r--drivers/sbus/char/sunfb.c20
-rw-r--r--drivers/sbus/char/sunkbd.c20
-rw-r--r--drivers/sbus/char/sunmouse.c2
-rw-r--r--drivers/sbus/char/sunserial.c11
-rw-r--r--drivers/sbus/char/sunserial.h2
-rw-r--r--drivers/sbus/char/tcx.c12
-rw-r--r--drivers/sbus/char/vfc_dev.c11
-rw-r--r--drivers/sbus/char/weitek.c11
-rw-r--r--drivers/scsi/BusLogic.h3
-rw-r--r--drivers/scsi/README.in200025
-rw-r--r--drivers/scsi/advansys.h2
-rw-r--r--drivers/scsi/amiga7xx.c13
-rw-r--r--drivers/scsi/in2000.c631
-rw-r--r--drivers/scsi/in2000.h359
-rw-r--r--drivers/scsi/ppa.c2
-rw-r--r--drivers/scsi/qlogicpti.c12
-rw-r--r--drivers/scsi/scsi.c4
-rw-r--r--drivers/scsi/sg.c2
-rw-r--r--drivers/scsi/sr_ioctl.c1
-rw-r--r--drivers/scsi/tmscsim.c49
-rw-r--r--drivers/scsi/wd33c93.c201
-rw-r--r--drivers/scsi/wd33c93.h4
-rw-r--r--drivers/sound/Config.in284
-rw-r--r--drivers/sound/dev_table.h1
-rw-r--r--drivers/sound/soundcard.c5
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, &param) == 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;