VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 24
+SUBLEVEL = 25
all: Version zImage
ROOT_DEV = CURRENT
+#
+# INSTALL_PATH specifies where to place the updated kernel and system map
+# images. Uncomment if you want to place them anywhere other than root.
+
+#INSTALL_PATH=/boot
+
#
# If you want to preset the SVGA mode, uncomment the next line and
# set SVGA_MODE to whatever number you want.
dd bs=8192 if=zImage of=/dev/fd0
zlilo: $(CONFIGURE) zImage
- if [ -f /vmlinuz ]; then mv /vmlinuz /vmlinuz.old; fi
- if [ -f /zSystem.map ]; then mv /zSystem.map /zSystem.old; fi
- cat zImage > /vmlinuz
- cp zSystem.map /
+ if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
+ if [ -f $(INSTALL_PATH)/zSystem.map ]; then mv $(INSTALL_PATH)/zSystem.map $(INSTALL_PATH)/zSystem.old; fi
+ cat zImage > $(INSTALL_PATH)/vmlinuz
+ cp zSystem.map $(INSTALL_PATH)/
if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
tools/zSystem: boot/head.o init/main.o tools/version.o linuxsubdirs
# bool ' SLIP debugging on' SL_DUMP y
fi
bool 'PPP (point-to-point) support' CONFIG_PPP n
+bool 'PLIP (parallel port) support' CONFIG_PLIP n
+bool 'SK_G16 support' CONFIG_SK_G16 n
bool 'Load balancing support (experimental)' CONFIG_SLAVE_BALANCING n
bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA n
bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC n
bool 'DEPCA support' CONFIG_DEPCA n
if [ "$CONFIG_NET_ALPHA" = "y" ]; then
bool 'EtherExpress support' CONFIG_EEXPRESS n
+ bool 'AT1700 support' CONFIG_AT1700 n
fi
bool 'HP PCLAN support' CONFIG_HPLAN n
bool 'NE2000/NE1000 support' CONFIG_NE2000 n
fi
-bool 'PLIP (parallel port) support' CONFIG_PLIP n
bool 'EISA and on board controllers' CONFIG_NET_EISA n
if [ "$CONFIG_NET_ALPHA" = "y" ]; then
bool 'Ansel Communications EISA 3200 support' CONFIG_AC3200 n
- bool 'AT1700 support' CONFIG_AT1700 n
fi
bool 'Apricot Xen-II on board ethernet' CONFIG_APRICOT n
#bool 'NI52EE support' CONFIG_NI52 n
*
*/
+/*
+ *
+ * Setting up the Sony CDU31A/CDU33A drive interface card. If
+ * You have another card, you are on your own.
+ *
+ * +----------+-----------------+----------------------+
+ * | JP1 | 34 Pin Conn | |
+ * | JP2 +-----------------+ |
+ * | JP3 |
+ * | JP4 |
+ * | +--+
+ * | | +-+
+ * | | | | External
+ * | | | | Connector
+ * | | | |
+ * | | +-+
+ * | +--+
+ * | |
+ * | +--------+
+ * | |
+ * +------------------------------------------+
+ *
+ * JP1 sets the Base Address, using the following settings:
+ *
+ * Address Pin 1 Pin 2
+ * ------- ----- -----
+ * 0x320 Short Short
+ * 0x330 Short Open
+ * 0x340 Open Short
+ * 0x360 Open Open
+ *
+ * JP2 and JP3 configure the DMA channel; they must be set the same.
+ *
+ * DMA Pin 1 Pin 2 Pin 3
+ * --- ----- ----- -----
+ * 1 On Off On
+ * 2 Off On Off
+ * 3 Off Off On
+ *
+ * JP4 Configures the IRQ:
+ *
+ * IRQ Pin 1 Pin 2 Pin 3 Pin 4
+ * --- ----- ----- ----- -----
+ * 3 Off Off On Off
+ * 4 Off Off* Off On
+ * 5 On Off Off Off
+ * 6 Off On Off Off
+ *
+ * * The documentation states to set this for interrupt
+ * 4, but I think that is a mistake.
+ */
+
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
{ 0x360, -1, 0 }, /* Secondary standard Sony Interface */
{ 0x320, -1, 0 }, /* Secondary standard Sony Interface */
{ 0x330, -1, 0 }, /* Secondary standard Sony Interface */
+ { 0x634, -1, 0 }, /* Sound FX SC400 */
+ { 0x654, -1, 0 }, /* Sound FX SC400 */
{ 0 }
};
static int curr_control_reg = 0; /* Current value of the control register */
+#if 1 /* This will go away as soon as the isofs code is fixed
+ to use the fops struct. */
/*
* This routine returns 1 if the disk has been changed since the last
* check or 0 if it hasn't. Setting flag to 0 resets the changed flag.
return retval;
}
+#endif
+
+/*
+ * This routine returns 1 if the disk has been changed since the last
+ * check or 0 if it hasn't. Setting flag to 0 resets the changed flag.
+ */
+static int
+scd_disk_change(dev_t full_dev)
+{
+ int retval, target;
+
+
+ target = MINOR(full_dev);
+
+ if (target > 0) {
+ printk("Sony CD-ROM request error: invalid device.\n");
+ return 0;
+ }
+
+ retval = sony_disc_changed;
+ sony_disc_changed = 0;
+
+ return retval;
+}
static inline void
enable_interrupts(void)
NULL, /* mmap */
scd_open, /* open */
scd_release, /* release */
- NULL /* fsync */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ scd_disk_change, /* media_change */
+ NULL /* revalidate */
};
#define FLOPPY_DMA 2
#define FDC_FIFO_UNTESTED /* -bb */
+#define FDC_FIFO_BUG
+
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
static void floppy_ready(void);
static void recalibrate_floppy(void);
-static int floppy_grab_irq_and_dma(void);
-static void floppy_release_irq_and_dma(void);
+int floppy_grab_irq_and_dma(void);
+void floppy_release_irq_and_dma(void);
/*
* These are global variables, as that's the easiest way to give
printk("Unable to send byte to FDC\n");
}
+#ifdef FDC_FIFO_BUG
+
+static void output_byte_force(char byte)
+{
+ int counter;
+ unsigned char status;
+
+ if (reset)
+ return;
+ for (counter = 0 ; counter < 10000 ; counter++) {
+ status = inb_p(FD_STATUS);
+ if ((status & (STATUS_READY | STATUS_DIR)) == STATUS_READY) {
+ outb(byte,FD_DATA);
+ return;
+ }
+ if ((status & (STATUS_READY | STATUS_BUSY)) == (STATUS_READY | STATUS_BUSY)) {
+ outb(byte,FD_DATA);
+ return;
+ }
+ }
+ current_track = NO_TRACK;
+ reset = 1;
+ printk("Unable to send byte to FDC\n");
+}
+#endif /* FDC_FIFO_BUG */
+
static int result(void)
{
int i = 0, counter, status;
if (rate & 0x40) {
unsigned char r = rate & 0x03;
if (r == 0)
+#ifndef FDC_FIFO_BUG
output_byte(2); /* perpendicular, 500 kbps */
+#else
+ output_byte_force(2); /* perpendicular, 500 kbps */
+#endif
else if (r == 3)
+#ifndef FDC_FIFO_BUG
output_byte(3); /* perpendicular, 1Mbps */
+#else
+ output_byte_force(3); /* perpendicular, 1Mbps */
+#endif
else {
printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n");
reset = 1;
}
} else
+#ifndef FDC_FIFO_BUG
output_byte(0); /* conventional mode */
+#else
+ output_byte_force(0); /* conventional mode */
+#endif
} else {
if (rate & 0x40) {
printk(DEVICE_NAME ": perpendicular mode not supported by this FDC.\n");
if (need_configure && (fdc_version == FDC_TYPE_82077)) {
/* Enhanced version with FIFO & vertical recording. */
output_byte(FD_CONFIGURE);
+#ifndef FDC_FIFO_BUG
output_byte(0);
+#else
+ output_byte_force(0);
+#endif
output_byte(0x1A); /* FIFO on, polling off, 10 byte threshold */
+#ifndef FDC_FIFO_BUG
output_byte(0); /* precompensation from track 0 upwards */
+#else
+ output_byte_force(0); /* precompensation from track 0 upwards */
+#endif
need_configure = 0;
printk(DEVICE_NAME ": FIFO enabled\n");
}
static int usage_count = 0;
-static int floppy_grab_irq_and_dma(void)
+int floppy_grab_irq_and_dma(void)
{
if (usage_count++)
return 0;
return 0;
}
-static void floppy_release_irq_and_dma(void)
+void floppy_release_irq_and_dma(void)
{
if (--usage_count)
return;
(Jon Tombs <jon@robots.ox.ac.uk>)
0.3.3 Added more #defines and mcd_setup()
(Jon Tombs <jon@gtex02.us.es>)
+
+ October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
+ Braunschweig, Germany: Total rework to speed up data read operation.
+ Also enabled definition of irq and address from bootstrap, using the
+ environment. linux/init/main.c must be patched to export the env.
+ November 93 added code for FX001 S,D (single & double speed).
+ February 94 added code for broken M 5/6 series of 16-bit single speed.
*/
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
/* #define REALLY_SLOW_IO */
#include <asm/system.h>
static int mcdPresent = 0;
-static char mcd_buf[2048]; /* buffer for block size conversion */
-static int mcd_bn = -1;
+#if 0
+#define TEST1 /* <int-..> */
+#define TEST2 /* do_mcd_req */
+#define TEST3 */ /* MCD_S_state */
+#define TEST4 /* QUICK_LOOP-counter */
+#define TEST5 */ /* port(1) state */
+#endif
+
+#if 1
+#define QUICK_LOOP_DELAY udelay(45) /* use udelay */
+#define QUICK_LOOP_COUNT 20
+#else
+#define QUICK_LOOP_DELAY
+#define QUICK_LOOP_COUNT 140 /* better wait constant time */
+#endif
+/* #define DOUBLE_QUICK_ONLY */
+
+#define CURRENT_VALID \
+ (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \
+ && CURRENT -> sector != -1)
+#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
+#define MCD_BUF_SIZ 16
+static volatile int mcd_transfer_is_active;
+static char mcd_buf[2048*MCD_BUF_SIZ]; /* buffer for block size conversion */
+static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
+static volatile int mcd_buf_in, mcd_buf_out = -1;
+static volatile int mcd_error;
+static int mcd_open_count;
+enum mcd_state_e {
+ MCD_S_IDLE, /* 0 */
+ MCD_S_START, /* 1 */
+ MCD_S_MODE, /* 2 */
+ MCD_S_READ, /* 3 */
+ MCD_S_DATA, /* 4 */
+ MCD_S_STOP, /* 5 */
+ MCD_S_STOPPING /* 6 */
+};
+static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
+static int mcd_mode = -1;
+static int MCMD_DATA_READ= MCMD_PLAY_READ;
+#define READ_TIMEOUT 3000
+#define WORK_AROUND_MITSUMI_BUG_92
+#define WORK_AROUND_MITSUMI_BUG_93
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+int mitsumi_bug_93_wait = 0;
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
static short mcd_port = MCD_BASE_ADDR;
static int mcd_irq = MCD_INTR_NR;
static char mcdVersion;
static void mcd_transfer(void);
-static void mcd_start(void);
-static void mcd_status(void);
-static void mcd_read_cmd(void);
-static void mcd_data(void);
+static void mcd_poll(void);
+static void mcd_invalidate_buffers(void);
static void do_mcd_request(void);
static void hsg2msf(long hsg, struct msf *msf);
static void bin2bcd(unsigned char *p);
mcd_port = ints[1];
if (ints[0] > 1)
mcd_irq = ints[2];
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+ if (ints[0] > 2)
+ mitsumi_bug_93_wait = ints[3];
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
}
static void
mcd_transfer(void)
{
- long offs;
-
- while (CURRENT -> nr_sectors > 0 && mcd_bn == CURRENT -> sector / 4)
- {
- offs = (CURRENT -> sector & 3) * 512;
- memcpy(CURRENT -> buffer, mcd_buf + offs, 512);
- CURRENT -> nr_sectors--;
- CURRENT -> sector++;
- CURRENT -> buffer += 512;
+ if (CURRENT_VALID) {
+ while (CURRENT -> nr_sectors) {
+ int bn = CURRENT -> sector / 4;
+ int i;
+ for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i)
+ ;
+ if (i < MCD_BUF_SIZ) {
+ int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+ int nr_sectors = 4 - (CURRENT -> sector & 3);
+ if (mcd_buf_out != i) {
+ mcd_buf_out = i;
+ if (mcd_buf_bn[i] != bn) {
+ mcd_buf_out = -1;
+ continue;
+ }
}
+ if (nr_sectors > CURRENT -> nr_sectors)
+ nr_sectors = CURRENT -> nr_sectors;
+ memcpy(CURRENT -> buffer, mcd_buf + offs, nr_sectors * 512);
+ CURRENT -> nr_sectors -= nr_sectors;
+ CURRENT -> sector += nr_sectors;
+ CURRENT -> buffer += nr_sectors * 512;
+ } else {
+ mcd_buf_out = -1;
+ break;
+ }
+ }
+ }
}
int st;
st = inb(MCDPORT(1)) & 0xFF;
- if (st != 0xFF)
+#ifdef TEST1
+ printk("<int1-%02X>", st);
+#endif
+ if (!(st & MFL_STATUS))
{
st = inb(MCDPORT(0)) & 0xFF;
-#if 0
- printk("<int-%02X>", st);
+#ifdef TEST1
+ printk("<int0-%02X>", st);
#endif
+ if ((st & 0xFF) != 0xFF)
+ mcd_error = st ? st & 0xFF : -1;
}
}
-/*
- * I/O request routine called from Linux kernel.
- */
-
static void
do_mcd_request(void)
{
- unsigned int block,dev;
- unsigned int nsect;
+#ifdef TEST2
+ printk(" do_mcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+ mcd_transfer_is_active = 1;
+ while (CURRENT_VALID) {
+ if (CURRENT->bh) {
+ if (!CURRENT->bh->b_lock)
+ panic(DEVICE_NAME ": block not locked");
+ }
+ mcd_transfer();
+ if (CURRENT -> nr_sectors == 0) {
+ end_request(1);
+ } else {
+ mcd_buf_out = -1; /* Want to read a block not in buffer */
+ if (mcd_state == MCD_S_IDLE) {
+ if (!tocUpToDate) {
+ if (updateToc() < 0) {
+ while (CURRENT_VALID)
+ end_request(0);
+ break;
+ }
+ }
+ mcd_state = MCD_S_START;
+ McdTries = 5;
+ SET_TIMER(mcd_poll, 1);
+ }
+ break;
+ }
+ }
+ mcd_transfer_is_active = 0;
+#ifdef TEST2
+ printk(" do_mcd_request ends\n");
+#endif
+}
-repeat:
- if (!(CURRENT) || CURRENT->dev < 0) return;
- INIT_REQUEST;
- dev = MINOR(CURRENT->dev);
- block = CURRENT->sector;
- nsect = CURRENT->nr_sectors;
- if (CURRENT == NULL || CURRENT -> sector == -1)
- return;
- if (CURRENT -> cmd != READ)
- {
- printk("mcd: bad cmd %d\n", CURRENT -> cmd);
- end_request(0);
- goto repeat;
+static void
+mcd_poll(void)
+{
+ int st;
+
+
+ if (mcd_error) {
+ if (mcd_error & 0xA5) {
+ printk("mcd: I/O error 0x%02x", mcd_error);
+ if (mcd_error & 0x80)
+ printk(" (Door open)");
+ if (mcd_error & 0x20)
+ printk(" (Disk changed)");
+ if (mcd_error & 0x04)
+ printk(" (Read error)");
+ printk("\n");
+ mcd_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+ if (McdTries == 5)
+ printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+ if (!McdTries--) {
+ printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+ if (mcd_transfer_is_active) {
+ McdTries = 0;
+ goto ret;
}
+ if (CURRENT_VALID)
+ end_request(0);
+ McdTries = 5;
+ }
+ }
+ mcd_error = 0;
+ mcd_state = MCD_S_STOP;
+ }
- mcd_transfer();
- /* if we satisfied the request from the buffer, we're done. */
- if (CURRENT -> nr_sectors == 0)
- {
- end_request(1);
- goto repeat;
- }
+ immediatly:
+ switch (mcd_state) {
- McdTries = MCD_RETRY_ATTEMPTS;
- mcd_start();
-}
-/*
- * Start the I/O for the cdrom. Handle retry count.
- */
+ case MCD_S_IDLE:
+#ifdef TEST3
+ printk("MCD_S_IDLE\n");
+#endif
+ return;
-static void
-mcd_start()
-{
- if (McdTries == 0)
- {
- printk("mcd: read failed after %d tries\n", MCD_RETRY_ATTEMPTS);
- end_request(0);
- SET_TIMER(do_mcd_request, 1); /* wait a bit, try again */
- return;
- }
- McdTries--;
- outb(0x40, MCDPORT(0)); /* get status */
- McdTimeout = MCD_STATUS_DELAY;
- SET_TIMER(mcd_status, 1);
-}
+ case MCD_S_START:
+#ifdef TEST3
+ printk("MCD_S_START\n");
+#endif
-/*
- * Called from the timer to check the results of the get-status cmd.
- * On success, send the set-mode command.
- */
+ outb(MCMD_GET_STATUS, MCDPORT(0));
+ mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
+ McdTimeout = 3000;
+ break;
-static void
-mcd_status()
-{
- int st;
- McdTimeout--;
- st = mcdStatus();
- if (st == -1)
- {
- if (McdTimeout == 0)
- {
- printk("mcd: status timed out\n");
- SET_TIMER(mcd_start, 1); /* wait a bit, try again */
- return;
- }
- SET_TIMER(mcd_status, 1);
- return;
- }
+ case MCD_S_MODE:
+#ifdef TEST3
+ printk("MCD_S_MODE\n");
+#endif
- if (st & MST_DSK_CHG)
- {
- mcdDiskChanged = 1;
- }
-
- if ((st & MST_READY) == 0)
- {
- printk("mcd: disk removed\n");
- mcdDiskChanged = 1;
- end_request(0);
- do_mcd_request();
- return;
+ if ((st = mcdStatus()) != -1) {
+
+ if (st & MST_DSK_CHG) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ mcd_invalidate_buffers();
+ }
+
+ set_mode_immediatly:
+
+ if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ if (mcd_transfer_is_active) {
+ mcd_state = MCD_S_START;
+ goto immediatly;
}
+ printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+ mcd_state = MCD_S_IDLE;
+ while (CURRENT_VALID)
+ end_request(0);
+ return;
+ }
- outb(0x50, MCDPORT(0)); /* set mode */
- outb(0x01, MCDPORT(0)); /* mode = cooked data */
- McdTimeout = 100;
- SET_TIMER(mcd_read_cmd, 1);
-}
+ outb(MCMD_SET_MODE, MCDPORT(0));
+ outb(1, MCDPORT(0));
+ mcd_mode = 1;
+ mcd_state = MCD_S_READ;
+ McdTimeout = 3000;
+ }
+ break;
-/*
- * Check the result of the set-mode command. On success, send the
- * read-data command.
- */
-static void
-mcd_read_cmd()
-{
- int st;
- long block;
- struct mcd_Play_msf mcdcmd;
- McdTimeout--;
- st = mcdStatus();
+ case MCD_S_READ:
+#ifdef TEST3
+ printk("MCD_S_READ\n");
+#endif
- if (st & MST_DSK_CHG)
- {
- mcdDiskChanged = 1;
+ if ((st = mcdStatus()) != -1) {
+
+ if (st & MST_DSK_CHG) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ mcd_invalidate_buffers();
+ }
+
+ read_immediatly:
+
+ if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ if (mcd_transfer_is_active) {
+ mcd_state = MCD_S_START;
+ goto immediatly;
}
-
- if (st == -1)
- {
- if (McdTimeout == 0)
- {
- printk("mcd: set mode timed out\n");
- SET_TIMER(mcd_start, 1); /* wait a bit, try again */
- return;
- }
+ printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+ mcd_state = MCD_S_IDLE;
+ while (CURRENT_VALID)
+ end_request(0);
+ return;
+ }
+
+ if (CURRENT_VALID) {
+ struct mcd_Play_msf msf;
+ mcd_next_bn = CURRENT -> sector / 4;
+ hsg2msf(mcd_next_bn, &msf.start);
+ msf.end.min = ~0;
+ msf.end.sec = ~0;
+ msf.end.frame = ~0;
+ sendMcdCmd(MCMD_DATA_READ, &msf);
+ mcd_state = MCD_S_DATA;
+ McdTimeout = READ_TIMEOUT;
+ } else {
+ mcd_state = MCD_S_STOP;
+ goto immediatly;
+ }
+
+ }
+ break;
+
+
+ case MCD_S_DATA:
+#ifdef TEST3
+ printk("MCD_S_DATA\n");
+#endif
- SET_TIMER(mcd_read_cmd, 1);
- return;
+ st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
+ data_immediatly:
+#ifdef TEST5
+ printk("Status %02x\n",st);
+#endif
+ switch (st) {
+
+ case MFL_DATA:
+#ifdef WARN_IF_READ_FAILURE
+ if (McdTries == 5)
+ printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+ if (!McdTries--) {
+ printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+ if (mcd_transfer_is_active) {
+ McdTries = 0;
+ break;
+ }
+ if (CURRENT_VALID)
+ end_request(0);
+ McdTries = 5;
+ }
+ mcd_state = MCD_S_START;
+ McdTimeout = READ_TIMEOUT;
+ goto immediatly;
+
+ case MFL_STATUSorDATA:
+ break;
+
+ default:
+ McdTries = 5;
+ if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
+ mcd_state = MCD_S_STOP;
+ goto immediatly;
+ }
+ mcd_buf_bn[mcd_buf_in] = -1;
+ READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048);
+ mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
+ if (mcd_buf_out == -1)
+ mcd_buf_out = mcd_buf_in;
+ mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
+ if (!mcd_transfer_is_active) {
+ while (CURRENT_VALID) {
+ mcd_transfer();
+ if (CURRENT -> nr_sectors == 0)
+ end_request(1);
+ else
+ break;
}
+ }
+
+ if (CURRENT_VALID
+ && (CURRENT -> sector / 4 < mcd_next_bn ||
+ CURRENT -> sector / 4 > mcd_next_bn + 16)) {
+ mcd_state = MCD_S_STOP;
+ goto immediatly;
+ }
+ McdTimeout = READ_TIMEOUT;
+#ifdef DOUBLE_QUICK_ONLY
+ if (MCMD_DATA_READ != MCMD_PLAY_READ)
+#endif
+ {
+ int count= QUICK_LOOP_COUNT;
+ while (count--) {
+ QUICK_LOOP_DELAY;
+ if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
+# ifdef TEST4
+/* printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */
+ printk(" %d ",QUICK_LOOP_COUNT-count);
+# endif
+ goto data_immediatly;
+ }
+ }
+# ifdef TEST4
+/* printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */
+ printk("ended ");
+# endif
+ }
+ break;
+ }
+ break;
- mcd_bn = -1; /* purge our buffer */
- block = CURRENT -> sector / 4;
- hsg2msf(block, &mcdcmd.start); /* cvt to msf format */
- mcdcmd.end.min = 0;
- mcdcmd.end.sec = 0;
- mcdcmd.end.frame = 1;
- sendMcdCmd(MCMD_PLAY_READ, &mcdcmd); /* read command */
- McdTimeout = 200;
- SET_TIMER(mcd_data, 1);
-}
+ case MCD_S_STOP:
+#ifdef TEST3
+ printk("MCD_S_STOP\n");
+#endif
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+ if (!mitsumi_bug_93_wait)
+ goto do_not_work_around_mitsumi_bug_93_1;
+
+ McdTimeout = mitsumi_bug_93_wait;
+ mcd_state = 9+3+1;
+ break;
+
+ case 9+3+1:
+ if (McdTimeout)
+ break;
+
+ do_not_work_around_mitsumi_bug_93_1:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+ outb(MCMD_STOP, MCDPORT(0));
+
+#ifdef WORK_AROUND_MITSUMI_BUG_92
+ if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+ int i = 4096;
+ do {
+ inb(MCDPORT(0));
+ } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+ outb(MCMD_STOP, MCDPORT(0));
+ if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+ i = 4096;
+ do {
+ inb(MCDPORT(0));
+ } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+ outb(MCMD_STOP, MCDPORT(0));
+ }
+ }
+#endif /* WORK_AROUND_MITSUMI_BUG_92 */
+
+ mcd_state = MCD_S_STOPPING;
+ McdTimeout = 1000;
+ break;
+
+ case MCD_S_STOPPING:
+#ifdef TEST3
+ printk("MCD_S_STOPPING\n");
+#endif
-/*
- * Check the completion of the read-data command. On success, read
- * the 2048 bytes of data from the disk into our buffer.
- */
+ if ((st = mcdStatus()) == -1 && McdTimeout)
+ break;
-static void
-mcd_data()
-{
- int i;
+ if ((st != -1) && (st & MST_DSK_CHG)) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ mcd_invalidate_buffers();
+ }
- McdTimeout--;
- cli();
- i =inb(MCDPORT(1)) & (MFL_STATUS | MFL_DATA);
- if (i == MFL_DATA)
- {
- printk("mcd: read failed\n");
-#ifdef MCD_DEBUG
- printk("got 0xB %02X\n", inb(MCDPORT(0)) & 0xFF);
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+ if (!mitsumi_bug_93_wait)
+ goto do_not_work_around_mitsumi_bug_93_2;
+
+ McdTimeout = mitsumi_bug_93_wait;
+ mcd_state = 9+3+2;
+ break;
+
+ case 9+3+2:
+ if (McdTimeout)
+ break;
+
+ st = -1;
+
+ do_not_work_around_mitsumi_bug_93_2:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+#ifdef TEST3
+ printk("CURRENT_VALID %d mcd_mode %d\n",
+ CURRENT_VALID, mcd_mode);
#endif
- SET_TIMER(mcd_start, 1);
- sti();
- return;
- }
-
- if (i == (MFL_STATUS | MFL_DATA))
- {
- if (McdTimeout == 0)
- {
- printk("mcd: data timeout, retrying\n");
- SET_TIMER(mcd_start, 1);
- }
-
- else
- SET_TIMER(mcd_data, 1);
-
- sti();
- return;
- }
- CLEAR_TIMER;
- READ_DATA(MCDPORT(0), &mcd_buf[0], 2048);
- sti();
+ if (CURRENT_VALID) {
+ if (st != -1) {
+ if (mcd_mode == 1)
+ goto read_immediatly;
+ else
+ goto set_mode_immediatly;
+ } else {
+ mcd_state = MCD_S_START;
+ McdTimeout = 1;
+ }
+ } else {
+ mcd_state = MCD_S_IDLE;
+ return;
+ }
+ break;
+
+ default:
+ printk("mcd: invalid state %d\n", mcd_state);
+ return;
+ }
+
+ ret:
+ if (!McdTimeout--) {
+ printk("mcd: timeout in state %d\n", mcd_state);
+ mcd_state = MCD_S_STOP;
+ }
+
+ SET_TIMER(mcd_poll, 1);
+}
+
- mcd_bn = CURRENT -> sector / 4;
- mcd_transfer();
- end_request(1);
- SET_TIMER(do_mcd_request, 1);
+
+static void
+mcd_invalidate_buffers(void)
+{
+ int i;
+ for (i = 0; i < MCD_BUF_SIZ; ++i)
+ mcd_buf_bn[i] = -1;
+ mcd_buf_out = -1;
}
if (mcdPresent == 0)
return -ENXIO; /* no hardware */
+ if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
+
+ mcd_invalidate_buffers();
+
st = statusCmd(); /* check drive status */
if (st == -1)
return -EIO; /* drive doesn't respond */
if (updateToc() < 0)
return -EIO;
+ }
+ ++mcd_open_count;
+
return 0;
}
static void
mcd_release(struct inode * inode, struct file * file)
{
- mcd_bn = -1;
+ if (!--mcd_open_count) {
+ mcd_invalidate_buffers();
sync_dev(inode->i_rdev);
invalidate_buffers(inode -> i_rdev);
+ }
}
mcd_ioctl, /* ioctl */
NULL, /* mmap */
mcd_open, /* open */
- mcd_release /* release */
+ mcd_release, /* release */
+ NULL /* fsync */
};
int count;
unsigned char result[3];
+ if (mcd_port <= 0 || mcd_irq <= 0) {
+ printk("skip mcd_init\n");
+ return mem_start;
+ }
+
+ printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
+
if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
{
- printk("mcd: Unable to get major %d for Mitsumi CD-ROM\n",
+ printk("Unable to get major %d for Mitsumi CD-ROM\n",
MAJOR_NR);
return mem_start;
}
if (check_region(mcd_port, 4)) {
- printk("mcd: Init failed, I/O port (%X) already in use\n",
+ printk("Init failed, I/O port (%X) already in use\n",
mcd_port);
return mem_start;
}
/* check for card */
outb(0, MCDPORT(1)); /* send reset */
- for (count = 0; count < 1000000; count++)
+ for (count = 0; count < 2000000; count++)
(void) inb(MCDPORT(1)); /* delay a bit */
outb(0x40, MCDPORT(0)); /* send get-stat cmd */
- for (count = 0; count < 1000000; count++)
+ for (count = 0; count < 2000000; count++)
if (!(inb(MCDPORT(1)) & MFL_STATUS))
break;
- if (count >= 1000000) {
- printk("mcd: Init failed. No mcd device at 0x%x irq %d\n",
+ if (count >= 2000000) {
+ printk("Init failed. No mcd device at 0x%x irq %d\n",
mcd_port, mcd_irq);
return mem_start;
}
outb(MCMD_GET_VERSION,MCDPORT(0));
for(count=0;count<3;count++)
if(getValue(result+count)) {
- printk("mcd: mitsumi get version failed at 0x%d\n",
+ printk("mitsumi get version failed at 0x%d\n",
mcd_port);
return mem_start;
}
if (result[0] == result[1] && result[1] == result[2])
return mem_start;
- printk("mcd: Mitsumi version : %02X %c %x\n",
+ printk("Mitsumi status, type and version : %02X %c %x\n",
result[0],result[1],result[2]);
+ if (result[1] == 'D') MCMD_DATA_READ= 0xC1;
mcdVersion=result[2];
if (irqaction(mcd_irq, &mcd_sigaction))
{
- printk("mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
+ printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
return mem_start;
}
snarf_region(mcd_port, 4);
+
+ outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
+ outb(0x02,MCDPORT(0));
+ outb(0x00,MCDPORT(0));
+ getValue(result);
+
+ outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
+ outb(0x10,MCDPORT(0));
+ outb(0x04,MCDPORT(0));
+ getValue(result);
+
+ mcd_invalidate_buffers();
mcdPresent = 1;
- printk("mcd: Mitsumi CD-ROM Drive present at addr %x, irq %d\n",
- mcd_port, mcd_irq);
+ printk("\n");
return mem_start;
}
{
outb(MCMD_SET_MODE, MCDPORT(0));
outb(0x05, MCDPORT(0)); /* mode: toc */
+ mcd_mode = 0x05;
if (getMcdStatus(MCD_STATUS_DELAY) != -1)
break;
}
{
outb(MCMD_SET_MODE, MCDPORT(0));
outb(0x01, MCDPORT(0));
+ mcd_mode = 1;
if (getMcdStatus(MCD_STATUS_DELAY) != -1)
break;
}
return(length);
}
-/*
- * If the root device is the RAM disk, try to load it.
- * In order to do this, the root device is originally set to the
- * floppy, and we later change it to be RAM disk.
- */
-void rd_load(void)
+static void do_load(void)
{
struct buffer_head *bh;
struct minix_super_block s;
int nblocks;
char *cp;
- /* If no RAM disk specified, give up early. */
- if (!rd_length) return;
- printk("RAMDISK: %d bytes, starting at 0x%x\n",
- rd_length, (int) rd_start);
-
- /* If we are doing a diskette boot, we might have to pre-load it. */
- if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
-
/*
* Check for a super block on the diskette.
* The old-style boot/root diskettes had their RAM image
return;
}
}
+
+int floppy_grab_irq_and_dma(void);
+void floppy_release_irq_and_dma(void);
+
+/*
+ * If the root device is the RAM disk, try to load it.
+ * In order to do this, the root device is originally set to the
+ * floppy, and we later change it to be RAM disk.
+ */
+void rd_load(void)
+{
+ /* If no RAM disk specified, give up early. */
+ if (!rd_length)
+ return;
+ printk("RAMDISK: %d bytes, starting at 0x%x\n",
+ rd_length, (int) rd_start);
+
+ /* If we are doing a diskette boot, we might have to pre-load it. */
+ if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR)
+ return;
+
+/* ugly, ugly */
+ if (floppy_grab_irq_and_dma()) {
+ printk("Unable to gram floppy IRQ/DMA for loading ramdisk image\n");
+ return;
+ }
+ do_load();
+ floppy_release_irq_and_dma();
+}
* Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
* By Craig Southeren
*
- * 3c505.c This module implements an interface to the 3Com
+ * elplus.c This module implements an interface to the 3Com
* Etherlink Plus (3c505) ethernet card. Linux device
* driver interface reverse engineered from the Linux 3C509
* device drivers. Vital 3C505 information gleaned from
* the Crynwr packet driver
*
- * Version: @(#)3c505.c 0.1 23/09/93
+ * Version: @(#)elplus.c 0.5 11-Jun-94
*
* Authors: Linux 3c505 device driver by:
* Craig Southeren, <geoffw@extro.ucc.su.oz.au>
+ * Final debugging by:
+ * Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ * Auto irq, auto detect, cleanup and v1.1.4+ kernel mods by:
+ * Juha Laiho, <jlaiho@ichaos.nullnet.fi>
* Linux 3C509 driver by:
* Donald Becker, <becker@super.org>
* Crynwr packet driver by
* modified by Warren Van Houten and krus@diku.dk.
* 3C505 technical information provided by
* Terry Murphy, of 3Com Network Adapter Division
- * Special thanks to Juha Laiho, <jlaiho@ichaos.nullnet.fi>
*
*/
+
+/*********************************************************
+ *
+ * set ELP_KERNEL_TYPE to the following values depending upon
+ * the kernel type:
+ * 0 = 0.99pl14 or earlier
+ * 1 = 0.99pl15 through 1.1.3
+ * 2 = 1.1.4 through 1.1.11
+ * 3 = 1.1.12 through 1.1.19
+ * 4 = 1.1.20
+ *
+ *********************************************************/
+
+#define ELP_KERNEL_TYPE 4
+
+/*********************************************************
+ *
+ * set ELP_NEED_HARD_RESET to 1, if having problems with
+ * "warm resets" from DOS. Bootup will then take some more
+ * time, as the adapter will perform self-test upon hard
+ * reset. This misbehaviour is reported to happen at least
+ * after use of Windows real-mode NDIS drivers.
+ *
+ *********************************************************/
+
+#define ELP_NEED_HARD_RESET 0
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
#include <asm/io.h>
-#ifndef port_read
-#include "iow.h"
-#endif
+#if (ELP_KERNEL_TYPE < 2)
+#include "dev.h"
+#include "eth.h"
+#include "skbuff.h"
+#include "arp.h"
+#else
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#endif
+
+#ifndef port_read
+#include "iow.h"
+#endif
#include "3c505.h"
-#ifdef ELP_DEBUG
-static int elp_debug = ELP_DEBUG;
+#define ELPLUS_DEBUG 0
+
+/*********************************************************
+ *
+ * define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+static char * filename = __FILE__;
+
+static char * null_msg = "*** NULL at %s(%d) ***\n";
+#define CHECK_NULL(p) if (!p) printk(null_msg, filename, __LINE__)
+
+static char * timeout_msg = "*** timeout at %s(%d) ***\n";
+#define TIMEOUT_MSG() printk(timeout_msg, filename,__LINE__)
+
+static char * invalid_pcb_msg = "*** invalid pcb length %d at %s(%d) ***\n";
+
+static char * search_msg = "%s: Looking for 3c505 adapter at address 0x%x...";
+
+static char * stilllooking_msg = "still looking...";
+
+static char * found_msg = "found.\n";
+
+static char * notfound_msg = "not found (reason = %d)\n";
+
+static char * couldnot_msg = "%s: 3c505 not found\n";
+
+/*********************************************************
+ *
+ * various other debug stuff
+ *
+ *********************************************************/
+
+#ifdef ELPLUS_DEBUG
+static int elp_debug = ELPLUS_DEBUG;
#else
static int elp_debug = 0;
#endif
+#if (ELP_KERNEL_TYPE < 2)
+extern void skb_check(struct sk_buff *skb,int, char *);
+#ifndef IS_SKB
+#define IS_SKB(skb) skb_check((skb),__LINE__,filename)
+#endif
+#else
+#ifndef IS_SKB
+#define IS_SKB(skb) skb_check((skb),0,__LINE__,filename)
+#endif
+#endif
+
+
/*
* 0 = no messages
* 1 = messages when high level commands performed
* 3 = messages when interrupts received
*/
-#define ELP_VERSION "0.1.0"
-
-extern struct device *irq2dev_map[16];
+#define ELPLUS_VERSION "0.4.0"
/*****************************************************************
*
*
*****************************************************************/
-#define INB(port) inb((unsigned short)port)
-#define OUTB(val,port) outb((unsigned char)val,(unsigned short)port);
+/*
+ * kernels before pl15 used an unobvious method for accessing
+ * the skb data area
+ */
+#if (ELP_KERNEL_TYPE < 1)
+#define SKB_DATA (skb+1)
+#else
+#define SKB_DATA (skb->data)
+#endif
+
+/*
+ * not all kernels before 1.1.4 had an alloc_skb function (apparently!!)
+ */
+#if (ELP_KERNEL_TYPE < 2)
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#endif
+#endif
+
+#define INB(port) inb((unsigned short)(port))
+#define OUTB(val,port) outb((unsigned char)(val),(unsigned short)(port));
#ifndef TRUE
#define TRUE 1
#endif
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+const int addr_list[]={0x300,0x280,0x310,0};
+
/*****************************************************************
*
* PCB structure
*
*****************************************************************/
-typedef struct {
- unsigned char command; /* PCB command code */
- unsigned char length; /* PCB data length */
- unsigned char data[MAX_PCB_DATA]; /* PCB data */
-} pcb_struct;
-
+#include "3c505dta.h"
/*****************************************************************
*
typedef struct {
int io_addr; /* base I/O address */
- short got_configure; /* set to TRUE when configure response received */
+ char * name; /* used for debug output */
+ short got[NUM_TRANSMIT_CMDS]; /* flags for command completion */
pcb_struct tx_pcb; /* PCB for foreground sending */
pcb_struct rx_pcb; /* PCB for foreground receiving */
pcb_struct itx_pcb; /* PCB for background sending */
static int get_status (elp_device * adapter)
{
+ int timeout = jiffies + TIMEOUT;
register int stat1;
do {
stat1 = INB(adapter->io_addr+PORT_STATUS);
- } while (stat1 != INB(adapter->io_addr+PORT_STATUS));
+ } while (stat1 != INB(adapter->io_addr+PORT_STATUS) && jiffies < timeout);
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
return stat1;
}
while(((INB(adapter->io_addr+PORT_STATUS)&STATUS_HCRE)==0) &&
(jiffies <= timeout))
;
- if (jiffies > timeout) {
- printk("elp0: timeout waiting for HCRE\n");
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG();
return FALSE;
}
return TRUE;
int retry = 0;
int timeout;
+ CHECK_NULL(pcb);
+ CHECK_NULL(adapter);
+
+ if (pcb->length > MAX_PCB_DATA)
+ printk(invalid_pcb_msg, pcb->length, filename, __LINE__);
+
while (1) {
cont = 1;
SET_HSF(0);
OUTB(pcb->command, (adapter->io_addr)+PORT_COMMAND);
cont = WAIT_HCRE(5);
- /* SET_HSF(0); */
if (cont) {
OUTB(pcb->length, (adapter->io_addr)+PORT_COMMAND);
}
for (i = 0; cont && (i < pcb->length); i++) {
- OUTB(pcb->data[i], (adapter->io_addr)+PORT_COMMAND);
+ OUTB(pcb->data.raw[i], (adapter->io_addr)+PORT_COMMAND);
cont = WAIT_HCRE(2);
}
(i == ASF_PCB_NAK))
break;
}
+
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+
if (i == ASF_PCB_ACK) {
SET_HSF(0);
return TRUE;
- } else if (i = ASF_PCB_NAK) {
+ } else if (i == ASF_PCB_NAK) {
SET_HSF(0);
- printk("elp0: PCB send was NAKed\n");
+ printk("%s: PCB send was NAKed\n", adapter->name);
{
int to = jiffies + 5;
while (jiffies < to)
}
}
- if (elp_debug >= 6)
- printk("elp0: NAK/timeout on send PCB\n");
+ if (elp_debug >= 2)
+ printk("%s: NAK/timeout on send PCB\n", adapter->name);
if ((retry++ & 7) == 0)
- printk("elp0: retry #%i on send PCB\n", retry);
+ printk("%s: retry #%i on send PCB\n", adapter->name, retry);
}
+
+ return FALSE;
}
/*****************************************************************
int i;
int total_length;
int stat;
+ int timeout;
+
+ CHECK_NULL(pcb);
+ CHECK_NULL(adapter);
/* get the command code */
- while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+ timeout = jiffies + TIMEOUT;
+ while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+
SET_HSF(0);
pcb->command = INB(adapter->io_addr+PORT_COMMAND);
if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
/* read the data length */
- while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+ timeout = jiffies + TIMEOUT;
+ while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
pcb->length = INB(adapter->io_addr+PORT_COMMAND);
+
+ if (pcb->length > MAX_PCB_DATA)
+ printk(invalid_pcb_msg, pcb->length, filename,__LINE__);
+
if ((stat & ASF_PCB_MASK) != ASF_PCB_END) {
/* read the data */
i = 0;
+ timeout = jiffies + TIMEOUT;
do {
- while (((stat = GET_STATUS())&STATUS_ACRF) == 0)
+ while (((stat = GET_STATUS())&STATUS_ACRF) == 0 && jiffies < timeout)
;
- pcb->data[i++] = INB(adapter->io_addr+PORT_COMMAND);
- } while ((stat & ASF_PCB_MASK) != ASF_PCB_END);
-
+ pcb->data.raw[i++] = INB(adapter->io_addr+PORT_COMMAND);
+ if (i > MAX_PCB_DATA)
+ printk(invalid_pcb_msg, i, filename, __LINE__);
+ } while ((stat & ASF_PCB_MASK) != ASF_PCB_END && jiffies < timeout);
+
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+
/* woops, the last "data" byte was really the length! */
- total_length = pcb->data[--i];
+ total_length = pcb->data.raw[--i];
/* safety check total length vs data length */
if (total_length != (pcb->length + 2)) {
- if (elp_debug >= 6)
- printk("elp0: mangled PCB received\n");
+ if (elp_debug >= 2)
+ printk("%s: mangled PCB received\n", adapter->name);
SET_HSF(HSF_PCB_NAK);
return FALSE;
}
+
SET_HSF(HSF_PCB_ACK);
return TRUE;
}
return FALSE;
}
+#if ELP_NEED_HARD_RESET
+
static void adapter_hard_reset(elp_device * adapter)
{
int timeout;
+ CHECK_NULL(adapter);
+
/*
* take FLSH and ATTN high
*/
break;
}
-static void adapter_reset(elp_device * adapter)
+#endif /* ELP_NEED_HARD_RESET */
+static void adapter_reset(elp_device * adapter)
{
int timeout;
+ CHECK_NULL(adapter);
+
cli();
OUTB(CONTROL_ATTN|INB(adapter->io_addr+PORT_CONTROL), adapter->io_addr+PORT_CONTROL);
sti();
*/
for (timeout = jiffies + 20; jiffies <= timeout; )
;
-
+
cli();
OUTB(INB(adapter->io_addr+PORT_CONTROL)&~(CONTROL_ATTN), adapter->io_addr+PORT_CONTROL);
sti();
+
}
/******************************************************
static int start_receive(elp_device * adapter, pcb_struct * tx_pcb)
{
- if (elp_debug > 3)
- printk("elp0: restarting receiver\n");
+ CHECK_NULL(adapter);
+ CHECK_NULL(tx_pcb);
+
+ if (elp_debug >= 3)
+ printk("%s: restarting receiver\n", adapter->name);
tx_pcb->command = CMD_RECEIVE_PACKET;
- tx_pcb->length = 8;
- tx_pcb->data[4] = 1600 & 0xff;
- tx_pcb->data[5] = 1600 >> 8;
- tx_pcb->data[6] = 0; /* set timeout to zero */
- tx_pcb->data[7] = 0;
+ tx_pcb->length = sizeof(struct Rcv_pkt);
+ tx_pcb->data.rcv_pkt.buf_seg = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+ tx_pcb->data.rcv_pkt.buf_len = 1600;
+ tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */
return send_pcb(adapter, tx_pcb);
}
register int i;
unsigned short * ptr;
short d;
+ int timeout;
+ int rlen;
+ struct sk_buff *skb;
/*
* allocate a buffer to put the packet into.
+ * (for kernels prior to 1.1.4 only)
*/
- struct sk_buff *skb;
- skb = alloc_skb(len+3, GFP_ATOMIC);
+#if (ELP_KERNEL_TYPE < 2)
+ int sksize = sizeof(struct sk_buff) + len + 4;
+#endif
+
+ CHECK_NULL(dev);
+ CHECK_NULL(adapter);
+
+ if (len <= 0 || ((len & ~1) != len))
+ if (elp_debug >= 3)
+ printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+
+ rlen = (len+1) & ~1;
+
+#if (ELP_KERNEL_TYPE < 2)
+ skb = alloc_skb(sksize, GFP_ATOMIC);
+#else
+ skb = alloc_skb(rlen, GFP_ATOMIC);
+#endif
/*
* make sure the data register is going the right way
* if buffer could not be allocated, swallow it
*/
if (skb == NULL) {
- for (i = 0; i < (len/2); i++) {
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0)
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = jiffies + TIMEOUT;
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 &&
+ jiffies < timeout)
;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+
d = inw(adapter->io_addr+PORT_DATA);
}
adapter->stats.rx_dropped++;
} else {
+ skb->lock = 0;
+ skb->len = rlen;
+ skb->dev = dev;
+
+/*
+ * for kernels before 1.1.4, the driver allocated the buffer
+ */
+#if (ELP_KERNEL_TYPE < 2)
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+#endif
/*
* now read the data from the adapter
*/
- ptr = (unsigned short *)(skb->data);
- for (i = 0; i < (len/2); i++) {
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0) {
- ;
- }
+ ptr = (unsigned short *)SKB_DATA;
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = jiffies + TIMEOUT;
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 &&
+ jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ {
+ printk("*** timeout at %s(%d) reading word %d of %d ***\n",
+ filename,__LINE__, i, rlen/2);
+#if (ELP_KERNEL_TYPE < 2)
+ kfree_s(skb, sksize);
+#else
+ kfree_s(skb, rlen);
+#endif
+ return;
+ }
+
*ptr = inw(adapter->io_addr+PORT_DATA);
ptr++;
}
* protocol chain. If it returns 0, we can assume the packet was
* swallowed up. If not, then we are responsible for freeing memory
*/
- if (dev_rint((unsigned char *)skb, len, IN_SKBUFF, dev) != 0) {
+
+ IS_SKB(skb);
+
+/*
+ * for kernels before 1.1.4, the driver allocated the buffer, so it had
+ * to free it
+ */
+#if (ELP_KERNEL_TYPE < 2)
+ if (dev_rint((unsigned char *)skb, rlen, IN_SKBUFF, dev) != 0) {
printk("%s: receive buffers full.\n", dev->name);
- kfree_skb(skb, FREE_READ);
+ kfree_s(skb, sksize);
}
+#else
+ netif_rx(skb);
+#endif
}
OUTB(INB(adapter->io_addr+PORT_CONTROL)&(~CONTROL_DIR), adapter->io_addr+PORT_CONTROL);
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev;
elp_device * adapter;
+ int timeout;
if (irq < 0 || irq > 15) {
- printk ("elp0: illegal IRQ number found in interrupt routine (%i)\n", irq);
+ printk ("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
return;
}
- if (irq != 0xc) {
- printk ("elp0: warning - interrupt routine has incorrect IRQ of %i\n", irq);
+/* FIXME: How do I do this kind of check without a fixed IRQ? */
+#if 0
+ if (irq != ELP_IRQ) {
+ printk ("elp_interrupt(): - interrupt routine has incorrect IRQ of %i\n", irq);
return;
}
+#endif
dev = irq2dev_map[irq];
- adapter = (elp_device *) dev->priv;
if (dev == NULL) {
printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
return;
}
+ adapter = (elp_device *) dev->priv;
+
+ CHECK_NULL(adapter);
+
if (dev->interrupt)
- if (elp_debug >= 3)
+ if (elp_debug >= 2)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
dev->interrupt = 1;
/*
* receive a PCB from the adapter
*/
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0) {
+ timeout = jiffies + TIMEOUT;
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0 &&
+ jiffies < timeout) {
if (receive_pcb(adapter, &adapter->irx_pcb)) {
* 82586 configured correctly
*/
case CMD_CONFIGURE_82586_RESPONSE:
- adapter->got_configure = 1;
+ adapter->got[CMD_CONFIGURE_82586] = 1;
if (elp_debug >= 3)
printk("%s: interrupt - configure response received\n", dev->name);
break;
+ /*
+ * Adapter memory configuration
+ */
+ case CMD_CONFIGURE_ADAPTER_RESPONSE:
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Adapter memory configuration %s.",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Multicast list loading
+ */
+ case CMD_LOAD_MULTICAST_RESPONSE:
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Multicast address list loading %s.",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Station address setting
+ */
+ case CMD_SET_ADDRESS_RESPONSE:
+ adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Ethernet address setting %s.",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+
+ /*
+ * received board statistics
+ */
+ case CMD_NETWORK_STATISTICS_RESPONSE:
+ adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
+ adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
+ adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
+ adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
+ adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
+ adapter->got[CMD_NETWORK_STATISTICS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - statistics response received\n", dev->name);
+ break;
+
/*
* received a packet
*/
case CMD_RECEIVE_PACKET_COMPLETE:
- len = adapter->irx_pcb.data[6] + (adapter->irx_pcb.data[7] << 8);
- dlen = adapter->irx_pcb.data[4] + (adapter->irx_pcb.data[5] << 8);
- if (adapter->irx_pcb.data[8] != 0) {
+ /* if the device isn't open, don't pass packets up the stack */
+ if (dev->start == 0)
+ break;
+ len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+ dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+ if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
printk("%s: interrupt - packet not received correctly\n", dev->name);
} else {
if (elp_debug >= 3)
receive_packet(dev, adapter, dlen);
if (elp_debug >= 3)
printk("%s: packet received\n", dev->name);
- adapter->stats.rx_packets++;
}
if (dev->start && !start_receive(adapter, &adapter->itx_pcb))
if (elp_debug >= 2)
case CMD_TRANSMIT_PACKET_COMPLETE:
if (elp_debug >= 3)
printk("%s: interrupt - packet sent\n", dev->name);
- if (adapter->irx_pcb.data[4] != 0)
+ if (dev->start == 0)
+ break;
+ if (adapter->irx_pcb.data.xmit_resp.c_stat != 0)
if (elp_debug >= 2)
- printk("%s: interrupt - error sending packet %4.4x\n", dev->name,
- adapter->irx_pcb.data[4] + (adapter->irx_pcb.data[5] << 8));
+ printk("%s: interrupt - error sending packet %4.4x\n",
+ dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
dev->tbusy = 0;
+#if (ELP_KERNEL_TYPE < 3)
mark_bh(INET_BH);
- adapter->stats.tx_packets++;
+#else
+ mark_bh(NET_BH);
+#endif
break;
/*
} else
printk("%s: failed to read PCB on interrupt\n", dev->name);
}
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
/*
* indicate no longer in interrupt routine
{
elp_device * adapter = (elp_device *) dev->priv;
- if (elp_debug >= 1)
+ CHECK_NULL(dev);
+
+ if (elp_debug >= 3)
printk("%s: request to open device\n", dev->name);
/*
return -EAGAIN;
}
+ /*
+ * disable interrupts on the board
+ */
+ OUTB(0x00, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * clear any pending interrupts
+ */
+ INB(adapter->io_addr+PORT_COMMAND);
+
/*
* interrupt routine not entered
*/
*/
dev->start = 1;
+ /*
+ * configure adapter memory: we need 10 multicast addresses, default==0
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 3c505 memory configuration command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+ adapter->tx_pcb.data.memconf.cmd_q = 10;
+ adapter->tx_pcb.data.memconf.rcv_q = 20;
+ adapter->tx_pcb.data.memconf.mcast = 10;
+ adapter->tx_pcb.data.memconf.frame = 20;
+ adapter->tx_pcb.data.memconf.rcv_b = 20;
+ adapter->tx_pcb.data.memconf.progs = 0;
+ adapter->tx_pcb.length = sizeof(struct Memconf);
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+ if (!send_pcb(adapter, &adapter->tx_pcb))
+ printk("%s: couldn't send memory configuration command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+ }
+
+
/*
* configure adapter to receive broadcast messages and wait for response
*/
- if (elp_debug >= 2)
+ if (elp_debug >= 3)
printk("%s: sending 82586 configure command\n", dev->name);
adapter->tx_pcb.command = CMD_CONFIGURE_82586;
- adapter->tx_pcb.data[0] = 1;
- adapter->tx_pcb.data[1] = 0;
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
adapter->tx_pcb.length = 2;
- adapter->got_configure = 0;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
if (!send_pcb(adapter, &adapter->tx_pcb))
printk("%s: couldn't send 82586 configure command\n", dev->name);
- else
- while (adapter->got_configure == 0)
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+ }
/*
- * queue a receive command to start things rolling
+ * queue receive commands to provide buffering
*/
if (!start_receive(adapter, &adapter->tx_pcb))
printk("%s: start receive command failed \n", dev->name);
- if (elp_debug >= 2)
+ if (elp_debug >= 3)
printk("%s: start receive command sent\n", dev->name);
return 0; /* Always succeed */
}
-/******************************************************
- *
- * close the board
- *
- ******************************************************/
-
-static int elp_close (struct device *dev)
-
-{
- elp_device * adapter = (elp_device *) dev->priv;
-
- if (elp_debug >= 1)
- printk("%s: request to close device\n", dev->name);
-
- /*
- * disable interrupts on the board
- */
- OUTB(0x00, adapter->io_addr+PORT_CONTROL);
-
- /*
- * flag transmitter as busy (i.e. not available)
- */
- dev->tbusy = 1;
-
- /*
- * indicate device is closed
- */
- dev->start = 0;
-
- /*
- * release the IRQ
- */
- free_irq(dev->irq);
-
- /*
- * and we no longer have to map irq to dev either
- */
- irq2dev_map[dev->irq] = 0;
-
- return 0;
-}
-
/******************************************************
*
*/
unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
+ CHECK_NULL(adapter);
+ CHECK_NULL(ptr);
+
+ if (nlen < len)
+ printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
+
/*
* send the adapter a transmit packet command. Ignore segment and offset
* and make sure the length is even
*/
adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
- adapter->tx_pcb.length = 6;
- adapter->tx_pcb.data[4] = nlen & 0xff;
- adapter->tx_pcb.data[5] = nlen >> 8;
+ adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+ adapter->tx_pcb.data.xmit_pkt.buf_ofs = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+ adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
if (!send_pcb(adapter, &adapter->tx_pcb))
return FALSE;
* write data to the adapter
*/
for (i = 0; i < (nlen/2);i++) {
- while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0)
+ int timeout = jiffies + TIMEOUT;
+ while ((INB(adapter->io_addr+PORT_STATUS)&STATUS_HRDY) == 0 && jiffies < timeout)
;
+ if (jiffies >= timeout) {
+ printk("*** timeout at %s(%d) writing word %d of %d ***\n",
+ filename,__LINE__, i, nlen/2);
+ return FALSE;
+ }
+
outw(*(short *)ptr, adapter->io_addr+PORT_DATA);
ptr +=2;
}
{
elp_device * adapter = (elp_device *) dev->priv;
+ CHECK_NULL(dev);
+
/*
- * not sure what this does, but the 3c609 driver does it, so...
+ * not sure what this does, but the 3c509 driver does it, so...
*/
if (skb == NULL) {
dev_tint(dev);
return 0;
}
+ /*
+ * Fill in the ethernet header
+ * (for kernels prior to 1.1.4 only)
+ */
+#if (ELP_KERNEL_TYPE < 2)
+ IS_SKB(skb);
+ if (!skb->arp && dev->rebuild_header(SKB_DATA, dev)) {
+ skb->dev = dev;
+ IS_SKB(skb);
+ arp_queue (skb);
+ return 0;
+ }
+#endif
+
/*
* if we ended up with a munged length, don't send it
*/
if (skb->len <= 0)
return 0;
- if (elp_debug >= 1)
- printk("%s: request to send packet of length %i\n", dev->name, skb->len);
+ if (elp_debug >= 3)
+ printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len);
/*
* if the transmitter is still busy, we have a transmit timeout...
*/
if (dev->tbusy) {
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < 500)
+ if (tickssofar < 200) /* was 500, AJT */
return 1;
printk("%s: transmit timed out, resetting adapter\n", dev->name);
if ((INB(adapter->io_addr+PORT_STATUS)&STATUS_ACRF) != 0)
}
/*
- * send the packet at (void *)(skb+1) for skb->len
+ * send the packet at skb->data for skb->len
*/
- if (!send_packet(adapter, (unsigned char *)(skb->data), skb->len)) {
+ if (!send_packet(adapter, (unsigned char *)SKB_DATA, skb->len)) {
printk("%s: send packet PCB failed\n", dev->name);
return 1;
}
- if (elp_debug >= 2)
- printk("%s: packet of length %i sent\n", dev->name, skb->len);
+ if (elp_debug >= 3)
+ printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
/*
/*
* if we have been asked to free the buffer, do so
*/
+#if (ELP_KERNEL_TYPE < 4)
+ if (skb->free)
+ {
+ IS_SKB(skb);
+ kfree_skb(skb, FREE_WRITE);
+ }
+#else
dev_kfree_skb(skb, FREE_WRITE);
+#endif
return 0;
}
static struct enet_statistics * elp_get_stats(struct device *dev)
{
- if (elp_debug >= 1)
+ elp_device *adapter = (elp_device *) dev->priv;
+
+ if (elp_debug >= 3)
printk("%s: request for stats\n", dev->name);
- elp_device * adapter = (elp_device *) dev->priv;
+ /* If the device is closed, just return the latest stats we have,
+ - we cannot ask from the adapter without interrupts */
+ if (!dev->start)
+ return &adapter->stats;
+
+ /* send a get statistics command to the board */
+ adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+ adapter->tx_pcb.length = 0;
+ adapter->got[CMD_NETWORK_STATISTICS] = 0;
+ if (!send_pcb(adapter, &adapter->tx_pcb))
+ printk("%s: couldn't send get statistics command\n", dev->name);
+ else
+ {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG();
+ return &adapter->stats;
+ }
+ }
+
+ /* statistics are now up to date */
return &adapter->stats;
}
/******************************************************
*
- * initialise Etherlink Pus board
+ * close the board
*
******************************************************/
-static void elp_init(struct device *dev)
+static int elp_close (struct device *dev)
{
+ elp_device * adapter = (elp_device *) dev->priv;
+
+ CHECK_NULL(dev);
+ CHECK_NULL(adapter);
+
+ if (elp_debug >= 3)
+ printk("%s: request to close device\n", dev->name);
+
+ /* Someone may request the device statistic information even when
+ * the interface is closed. The following will update the statistics
+ * structure in the driver, so we'll be able to give current statistics.
+ */
+ (void) elp_get_stats(dev);
+
+ /*
+ * disable interrupts on the board
+ */
+ OUTB(0x00, adapter->io_addr+PORT_CONTROL);
+
+ /*
+ * flag transmitter as busy (i.e. not available)
+ */
+ dev->tbusy = 1;
+
+ /*
+ * indicate device is closed
+ */
+ dev->start = 0;
+
+ /*
+ * release the IRQ
+ */
+ free_irq(dev->irq);
+
+ /*
+ * and we no longer have to map irq to dev either
+ */
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void elp_set_mc_list(struct device *dev, int num_addrs, void *addrs)
+{
+ elp_device *adapter = (elp_device *) dev->priv;
int i;
+
+ if (elp_debug >= 3)
+ printk("%s: request to set multicast list\n", dev->name);
+
+ if (num_addrs != -1) {
+ /* send a "load multicast list" command to the board, max 10 addrs/cmd */
+ /* if num_addrs==0 the list will be cleared */
+ adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+ adapter->tx_pcb.length = 6*num_addrs;
+ for (i=0;i<num_addrs;i++)
+ memcpy(adapter->tx_pcb.data.multicast[i], addrs+6*i,6);
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+ if (!send_pcb(adapter, &adapter->tx_pcb))
+ printk("%s: couldn't send set_multicast command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG();
+ }
+ }
+ if (num_addrs)
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
+ else /* num_addrs == 0 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ } else /* num_addrs == -1 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_ALL;
+ /*
+ * configure adapter to receive messages (as specified above)
+ * and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(adapter, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG();
+ }
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Plus board
+ *
+ ******************************************************/
+
+static void elp_init(struct device *dev)
+
+{
elp_device * adapter;
+ CHECK_NULL(dev);
+
/*
* NULL out buffer pointers
+ * (kernels prior to 1.1.4 only)
*/
- for (i = 0; i < DEV_NUMBUFFS; i++)
- dev->buffs[i] = NULL;
+#if (ELP_KERNEL_TYPE < 2)
+ {
+ int i;
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ dev->buffs[i] = NULL;
+ }
+#endif
/*
* set ptrs to various functions
dev->stop = elp_close; /* local */
dev->get_stats = elp_get_stats; /* local */
dev->hard_start_xmit = elp_start_xmit; /* local */
+ dev->set_multicast_list = elp_set_mc_list; /* local */
+#if (ELP_KERNEL_TYPE < 2)
dev->hard_header = eth_header; /* eth.c */
dev->add_arp = eth_add_arp; /* eth.c */
dev->rebuild_header = eth_rebuild_header; /* eth.c */
dev->type_trans = eth_type_trans; /* eth.c */
-
dev->queue_xmit = dev_queue_xmit; /* dev.c */
+#else
+ /* Setup the generic properties */
+ ether_setup(dev);
+#endif
/*
* setup ptr to adapter specific information
*/
adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+ CHECK_NULL(adapter);
adapter->io_addr = dev->base_addr;
+ adapter->name = dev->name;
memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
/*
* Ethernet information
+ * (for kernels prior to 1.1.4 only)
*/
+#if (ELP_KERNEL_TYPE < 2)
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = 1500; /* eth_mtu */
dev->addr_len = ETH_ALEN;
- for (i = 0; i < dev->addr_len; i++)
- dev->broadcast[i] = 0xff;
+ {
+ int i;
+ for (i = 0; i < dev->addr_len; i++)
+ dev->broadcast[i] = 0xff;
+ }
/*
* New-style flags.
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
+#endif
/*
* memory information
*/
dev->mem_start = dev->mem_end = dev->rmem_end = dev->mem_start = 0;
+
+#if ELP_NEED_HARD_RESET
+ adapter_hard_reset(adapter);
+#else
+ adapter_reset(adapter);
+#endif
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int elp_sense(int addr)
+{
+ int timeout;
+ byte orig_HCR=INB(addr+PORT_CONTROL),
+ orig_HSR=INB(addr+PORT_STATUS);
+
+ if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
+ ( (orig_HCR & CONTROL_DIR) != (orig_HSR & STATUS_DIR) ) )
+ return 1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+
+ /* Wait for a while; the adapter may still be booting up */
+ if (elp_debug > 0)
+ printk(stilllooking_msg);
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if ((INB(addr+PORT_STATUS) & ASF_PCB_MASK) != ASF_PCB_END)
+ break;
+
+ if (orig_HCR & CONTROL_DIR) {
+ /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+ OUTB(orig_HCR & ~CONTROL_DIR,addr+PORT_CONTROL);
+ timeout = jiffies+30;
+ while (jiffies < timeout)
+ ;
+ if (INB(addr+PORT_STATUS) & STATUS_DIR) {
+ OUTB(orig_HCR,addr+PORT_CONTROL);
+ return 2;
+ }
+ } else {
+ /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+ OUTB(orig_HCR | CONTROL_DIR,addr+PORT_CONTROL);
+ timeout = jiffies+300;
+ while (jiffies < timeout)
+ ;
+ if (!(INB(addr+PORT_STATUS) & STATUS_DIR)) {
+ OUTB(orig_HCR,addr+PORT_CONTROL);
+ return 3;
+ }
+ }
+ return 0; /* It certainly looks like a 3c505. */
}
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int elp_autodetect(struct device * dev)
+{
+ int idx=0, addr;
+
+ /* if base address set, then only check that address
+ otherwise, run through the table */
+ if ( (addr=dev->base_addr) ) { /* dev->base_addr == 0 ==> plain autodetect */
+ if (elp_debug > 0)
+ printk(search_msg, dev->name, addr);
+ if (elp_sense(addr) == 0)
+ {
+ if (elp_debug > 0)
+ printk(found_msg);
+ return addr;
+ } else if (elp_debug > 0)
+ printk(notfound_msg);
+ } else while ( (addr=addr_list[idx++]) ) {
+ if (elp_debug > 0)
+ printk(search_msg, dev->name, addr);
+ if (elp_sense(addr) == 0) {
+ if (elp_debug > 0)
+ printk(found_msg);
+ return addr;
+ } else if (elp_debug > 0)
+ printk(notfound_msg);
+ }
+
+ /* could not find an adapter */
+ if (elp_debug == 0)
+ printk(couldnot_msg, dev->name);
+ return 0; /* Because of this, the layer above will return -ENODEV */
+}
/******************************************************
*
* probe for an Etherlink Plus board at the specified address
- * by attempting to get the ethernet address.
*
******************************************************/
-int elp_probe(struct device *dev)
+int elplus_probe(struct device *dev)
{
elp_device adapter;
int i;
+ CHECK_NULL(dev);
+
/*
* setup adapter structure
*/
- adapter.io_addr = dev->base_addr;
- printk ("%s: probing for 3c505...", dev->name);
+ adapter.io_addr = dev->base_addr = elp_autodetect(dev);
+ if ( !adapter.io_addr )
+ return -ENODEV;
/*
- * get the adapter's undivided attention (if it's there!)
+ * As we enter here from bootup, the adapter should have IRQs enabled,
+ * but we can as well enable them anyway.
*/
- adapter_hard_reset(&adapter);
+ OUTB(INB(dev->base_addr+PORT_CONTROL) | CONTROL_CMDE,
+ dev->base_addr+PORT_CONTROL);
+ autoirq_setup(0);
/*
* use ethernet address command to probe for board in polled mode
+ * (this also makes us the IRQ that we need for automatic detection)
*/
adapter.tx_pcb.command = CMD_STATION_ADDRESS;
adapter.tx_pcb.length = 0;
!receive_pcb(&adapter, &adapter.rx_pcb) ||
(adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
(adapter.rx_pcb.length != 6)) {
- printk("not found\n");
+ printk("%s: not responding to first PCB\n", dev->name);
return -ENODEV;
}
-
+ if (dev->irq) { /* Is there a preset IRQ? */
+ if (dev->irq != autoirq_report(0)) {
+ printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
+ return -ENODEV;
+ }
+ /* if dev->irq == autoirq_report(0), all is well */
+ } else /* No preset IRQ; just use what we can detect */
+ dev->irq=autoirq_report(0);
+ switch (dev->irq) { /* Legal, sane? */
+ case 0: printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
+ printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
+ return -ENODEV;
+ case 1:
+ case 6:
+ case 8:
+ case 13:
+ printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+ dev->name,
+ dev->irq);
+ return -ENODEV;
+ }
+ /*
+ * Now we have the IRQ number so we can disable the interrupts from
+ * the board until the board is opened.
+ */
+ OUTB(INB(dev->base_addr+PORT_CONTROL) & ~CONTROL_CMDE,
+ dev->base_addr+PORT_CONTROL);
+
+ /*
+ * copy ethernet address into structure
+ */
for (i = 0; i < 6; i++)
- dev->dev_addr[i] = adapter.rx_pcb.data[i];
+ dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
- printk("found at port 0x%x, address = %s\n", dev->base_addr, eth_print(dev->dev_addr));
+ /*
+ * print remainder of startup message
+ */
+#if (ELP_KERNEL_TYPE < 2)
+ printk("%s: 3c505 card found at I/O 0x%x using IRQ%d has address %s\n",
+ dev->name, dev->base_addr, dev->irq, eth_print(dev->dev_addr));
+#else
+ printk("%s: 3c505 card found at I/O 0x%x using IRQ%d has address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->base_addr, dev->irq,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+#endif
+ /*
+ * initialise the device
+ */
elp_init(dev);
return 0;
}
-
*
*****************************************************************/
-#define TIMEOUT 10000
+#define TIMEOUT 300
/*****************************************************************
*
CMD_SELF_TEST = 0x0f,
CMD_SET_STATION_ADDRESS = 0x10,
CMD_ADAPTER_INFO = 0x11,
+ NUM_TRANSMIT_CMDS,
/*
* adapter PCB commands
--- /dev/null
+/* This header file defines some data structures used by the 3c505 driver */
+
+/* Data units */
+typedef unsigned char byte;
+typedef unsigned short int word;
+typedef unsigned long int dword;
+
+
+/* Data structures */
+struct Memconf {
+ word cmd_q,
+ rcv_q,
+ mcast,
+ frame,
+ rcv_b,
+ progs;
+};
+
+struct Rcv_pkt {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ timeout;
+};
+
+struct Xmit_pkt {
+ word buf_ofs,
+ buf_seg,
+ pkt_len;
+};
+
+struct Rcv_resp {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ pkt_len,
+ timeout,
+ status;
+ dword timetag;
+};
+
+struct Xmit_resp {
+ word buf_ofs,
+ buf_seg,
+ c_stat,
+ status;
+};
+
+
+struct Netstat {
+ dword tot_recv,
+ tot_xmit;
+ word err_CRC,
+ err_align,
+ err_res,
+ err_ovrrun;
+};
+
+
+struct Selftest {
+ word error;
+ union {
+ word ROM_cksum;
+ struct {
+ word ofs, seg;
+ } RAM;
+ word i82586;
+ } failure;
+};
+
+struct Info {
+ byte minor_vers,
+ major_vers;
+ word ROM_cksum,
+ RAM_sz,
+ free_ofs,
+ free_seg;
+};
+
+struct Memdump {
+ word size,
+ off,
+ seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the ethernet
+data, which has different packaging.)
+*/
+typedef struct {
+ byte command;
+ byte length;
+ union {
+ struct Memconf memconf;
+ word configure;
+ struct Rcv_pkt rcv_pkt;
+ struct Xmit_pkt xmit_pkt;
+ byte multicast[10][6];
+ byte eth_addr[6];
+ byte failed;
+ struct Rcv_resp rcv_resp;
+ struct Xmit_resp xmit_resp;
+ struct Netstat netstat;
+ struct Selftest selftest;
+ struct Info info;
+ struct Memdump memdump;
+ byte raw[62];
+ } data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION 0x00
+#define RECV_BROAD 0x01
+#define RECV_MULTI 0x02
+#define RECV_ALL 0x04
+#define NO_LOOPBACK 0x00
+#define INT_LOOPBACK 0x08
+#define EXT_LOOPBACK 0x10
net_init.o: ../../include/linux/autoconf.h
+ifdef CONFIG_SK_G16
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(sk_g16.o)
+endif
+
ifdef CONFIG_WD80x3
NETDRV_OBJS := $(NETDRV_OBJS) net.a(wd.o)
CONFIG_8390 = CONFIG_8390
$(CC) $(CPPFLAGS) $(CFLAGS) $(HP_OPTS) -c $<
endif
+ifdef CONFIG_HPLAN_PLUS
+NETDRV_OBJS := $(NETDRV_OBJS) net.a(hp-plus.o)
+CONFIG_8390 = CONFIG_8390
+endif
+
ifdef CONFIG_ULTRA
NETDRV_OBJS := $(NETDRV_OBJS) net.a(smc-ultra.o)
CONFIG_8390 = CONFIG_8390
--- /dev/null
+The address and IRQ used by the 3c505 driver can be configured at boot
+time by typing 'ether=eth0,15,0x300' (replace IRQ and base address with
+ones that tell how your adapter is jumpered).
+
+If no base address is given at the boot time, the driver will look for
+a 3c505 adapter at addresses 0x300, 0x280 and 0x310 in this order,
+possibly messing up any other hardware residing in these addresses.
+If a base address is given, it will be verified.
+
+There's two #defines one may need to change in the 3c505 driver:
+ELP_KERNEL_TYPE
+ this exists just to adapt the driver with pretty wide range of kernels.
+ See 3c505.c for exact information.
+
+ELP_NEED_HARD_RESET
+ some DOS drivers seem to get the adapter to some irrecoverable state
+ if the machine is "warm booted" from DOS to Linux. If you experience
+ problems when warm booting, but "cold boot" works, #defining this
+ to 1 may help.
+
+
+Known problems:
+ when 'ifconfig up' is run for the first time after bootup, the driver
+ complains:
+elp_interrupt(): irq 15 for unknown device.
+ ^^
+ There should be the IRQ the ELPlus adapter is using. IF the IRQ doesn't
+ match, something is seriously wrong.
+
+Authors:
+ The driver is mainly written by Craig Southeren, email c/o
+ <geoffw@extro.ucc.su.OZ.AU>.
+ Parts of the driver (adapting the driver to 1.1.4+ kernels,
+ IRQ/address detection, minor changes) and this (lousy) 'readme'
+ by Juha Laiho <jlaiho@ichaos.nullnet.fi>.
extern int el2_probe(struct device *dev);
extern int ne_probe(struct device *dev);
extern int hp_probe(struct device *dev);
+extern int hp_plus_probe(struct device *dev);
extern int znet_probe(struct device *);
extern int express_probe(struct device *);
extern int el3_probe(struct device *);
extern int elplus_probe(struct device *);
extern int ac3200_probe(struct device *);
extern int e2100_probe(struct device *);
+extern int SK_init(struct device *dev);
/* Detachable devices ("pocket adaptors" and special PCMCIA drivers). */
extern int atp_init(struct device *);
#if defined(CONFIG_HPLAN) || defined(HPLAN)
&& hp_probe(dev)
#endif
+#if defined(CONFIG_HPLAN_PLUS)
+ && hp_plus_probe(dev)
+#endif
#ifdef CONFIG_AT1500
&& at1500_probe(dev)
#endif
#endif
#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */
&& de620_probe(dev)
+#endif
+#if defined(CONFIG_SK_G16)
+ && SK_init(dev)
#endif
&& 1 ) {
return 1; /* -ENODEV or -EAGAIN would be more accurate. */
--- /dev/null
+/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */
+/*
+ Written 1993, 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
+ Adapter. The programming information is from the users manual, as related
+ by glee@ardnassak.math.clemson.edu.
+ */
+
+static char *version =
+ "ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include "8390.h"
+
+/* Offsets from the base address. */
+#define AC_NIC_BASE 0x00
+#define AC_SA_PROM 0x16 /* The station address PROM. */
+#define AC_ADDR0 0x00 /* Prefix station address values. */
+#define AC_ADDR1 0x40 /* !!!!These are just guesses!!!! */
+#define AC_ADDR2 0x90
+#define AC_ID_PORT 0xC80
+#define AC_EISA_ID 0x0110d305
+#define AC_RESET_PORT 0xC84
+#define AC_RESET 0x00
+#define AC_ENABLE 0x01
+#define AC_CONFIG 0xC90 /* The configuration port. */
+
+/* Decoding of the configuration register. */
+static unsigned char config2irqmap[8] = {15, 12, 11, 10, 9, 7, 5, 3};
+static int addrmap[8] =
+{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000, 0xD0000, 0 };
+static char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};
+
+#define config2irq(configval) config2irqmap[((configval) >> 3) & 7]
+#define config2mem(configval) addrmap[(configval) & 7]
+#define config2name(configval) port_name[((configval) >> 6) & 3]
+
+/* First and last 8390 pages. */
+#define AC_START_PG 0x00 /* First page of 8390 TX buffer */
+#define AC_STOP_PG 0x80 /* Last page +1 of the 8390 RX ring */
+
+int ac3200_probe(struct device *dev);
+static int ac_probe1(int ioaddr, struct device *dev);
+
+static int ac_open(struct device *dev);
+static void ac_reset_8390(struct device *dev);
+static int ac_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void ac_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static int ac_close_card(struct device *dev);
+\f
+
+/* Probe for the AC3200.
+
+ The AC3200 can be identified by either the EISA configuration registers,
+ or the unique value in the station address PROM.
+ */
+
+int ac3200_probe(struct device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return ac_probe1(ioaddr, dev);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ /* If you have a pre-pl15 machine you should delete this line. */
+ if ( ! EISA_bus)
+ return ENXIO;
+
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000)
+ if (ac_probe1(ioaddr, dev) == 0)
+ return 0;
+
+ return ENODEV;
+}
+
+static int ac_probe1(int ioaddr, struct device *dev)
+{
+ int i;
+
+#ifndef final_version
+ printk("AC3200 ethercard probe at %#3x:", ioaddr);
+
+ for(i = 0; i < 6; i++)
+ printk(" %02x", inb(ioaddr + AC_SA_PROM + i));
+#endif
+
+ /* !!!!The values of AC_ADDRn (see above) should be corrected when we
+ find out the correct station address prefix!!!! */
+ if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+ || inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+ || inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+#ifndef final_version
+ printk(" not found (invalid prefix).\n");
+#endif
+ return ENODEV;
+ }
+
+ /* The correct probe method is to check the EISA ID. */
+ for (i = 0; i < 4; i++)
+ if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
+ printk("EISA ID mismatch, %8x vs %8x.\n",
+ inl(ioaddr + AC_EISA_ID), AC_EISA_ID);
+ return ENODEV;
+ }
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+
+#ifndef final_version
+ printk("\nAC3200 ethercard configuration register is %#02x,"
+ " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+ inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+ inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#endif
+
+ /* Assign and snarf the interrupt now. */
+ if (dev->irq == 0)
+ dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (irqaction (dev->irq, &ei_sigaction)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return 0;
+ }
+
+ dev->base_addr = ioaddr;
+
+#ifdef notyet
+ if (dev->mem_start) { /* Override the value from the board. */
+ for (i = 0; i < 7; i++)
+ if (addrmap[i] == dev->mem_start)
+ break;
+ if (i >= 7)
+ i = 0;
+ outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG);
+ }
+#endif
+
+ dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
+ dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start
+ + (AC_STOP_PG - AC_START_PG)*256;
+
+ ethdev_init(dev);
+
+ ei_status.name = "AC3200";
+ ei_status.tx_start_page = AC_START_PG;
+ ei_status.rx_start_page = AC_START_PG + TX_PAGES;
+ ei_status.stop_page = AC_STOP_PG;
+ ei_status.word16 = 1;
+
+ printk("\n%s: AC3200 at %#x, IRQ %d, %s port, shared memory %#lx-%#lx.\n",
+ dev->name, ioaddr, dev->irq, port_name[dev->if_port],
+ dev->mem_start, dev->mem_end-1);
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &ac_reset_8390;
+ ei_status.block_input = &ac_block_input;
+ ei_status.block_output = &ac_block_output;
+
+ dev->open = &ac_open;
+ dev->stop = &ac_close_card;
+ NS8390_init(dev, 0);
+ return 0;
+}
+
+static int ac_open(struct device *dev)
+{
+#ifdef notyet
+ /* Someday we may enable the IRQ and shared memory here. */
+ int ioaddr = dev->base_addr;
+
+ if (irqaction(dev->irq, &ei_sigaction))
+ return -EAGAIN;
+#endif
+
+ return ei_open(dev);
+}
+
+static void ac_reset_8390(struct device *dev)
+{
+ ushort ioaddr = dev->base_addr;
+
+ outb(AC_RESET, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("resetting AC3200, t=%ld...", jiffies);
+
+ ei_status.txing = 0;
+ outb(AC_ENABLE, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+
+ return;
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static int ac_block_input(struct device *dev, int count, char *buf,
+ int ring_offset)
+{
+ long xfer_start = dev->mem_start + ring_offset - (AC_START_PG<<8);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy(buf, (char*)xfer_start, semi_count);
+ count -= semi_count;
+ memcpy(buf + semi_count, (char *)dev->rmem_start, count);
+ return dev->rmem_start + count;
+ }
+ memcpy(buf, (char*)xfer_start, count);
+
+ return ring_offset + count;
+}
+
+static void ac_block_output(struct device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ long shmem = dev->mem_start + ((start_page - AC_START_PG)<<8);
+
+ memcpy((unsigned char *)shmem, buf, count);
+}
+
+static int ac_close_card(struct device *dev)
+{
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+#ifdef notyet
+ /* We should someday disable shared memory and interrupts. */
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ac3200.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
--- /dev/null
+/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
+/*
+ Written 1994 by Donald Becker.
+
+ This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+ These cards are sold under several model numbers, usually 2724*.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ As is often the case, a great deal of credit is owed to Russ Nelson.
+ The Crynwr packet driver was my primary source of HP-specific
+ programming information.
+*/
+
+static char *version =
+"hp-plus.c:v0.04 6/16/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/config.h>
+#include <linux/string.h> /* Important -- this inlines word moves. */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#ifdef start_bh_atomic /* This checks for kernels >= 1.1.0. */
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#else
+#include "dev.h"
+#endif
+
+#include "8390.h"
+
+/*
+ The HP EtherTwist chip implementation is a fairly routine DP8390
+ implementation. It allows both shared memory and programmed-I/O buffer
+ access, using a custom interface for both. The programed-I/O mode is
+ entirely implemented in the HP EtherTwist chip, bypassing the problem
+ ridden built-in 8390 facilities used on NE2000 designs. The shared
+ memory mode is likewise special, with an offset register used to make
+ packets appear at the shared memory base. Both modes use a base and bounds
+ page register to hide the Rx ring buffer wrap -- a packet that spans the
+ end of physical buffer memory appears continuous to the driver. (c.f. the
+ 3c503 and Cabletron E2100)
+
+ A special note: the internal buffer of the board is only 8 bits wide.
+ This lays several nasty traps for the unaware:
+ - the 8390 must be programmed for byte-wide operations
+ - all I/O and memory operations must work on whole words (the access
+ latches are serially preloaded and have no byte-swapping ability).
+
+ This board is laid out in I/O space much like the earlier HP boards:
+ the first 16 locations are for the board registers, and the second 16 are
+ for the 8390. The board is easy to identify, with both a dedicated 16 bit
+ ID register and a constant 0x530* value in the upper bits of the paging
+ register.
+*/
+
+#define HPP_PROBE_LIST {0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0}
+
+#define HP_IO_EXTENT 32
+
+#define HP_ID 0x00 /* ID register, always 0x4850. */
+#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
+#define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option. */
+#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page. */
+#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page. */
+#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page. */
+#define NIC_OFFSET 0x10 /* Offset to the 8390 registers. */
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+ Perf_Page = 0, /* Normal operation. */
+ MAC_Page = 1, /* The ethernet address (+checksum). */
+ HW_Page = 2, /* EEPROM-loaded hardware parameters. */
+ LAN_Page = 4, /* Transceiver selection, testing, etc. */
+ ID_Page = 6 };
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+ NICReset = 1, ChipReset = 2, /* Active low, really UNreset. */
+ EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
+ MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
+
+int hpplus_probe(struct device *dev);
+int hpp_probe1(struct device *dev, int ioaddr);
+
+static void hpp_reset_8390(struct device *dev);
+static int hpp_open(struct device *dev);
+static int hpp_close(struct device *dev);
+static int hpp_mem_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void hpp_mem_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int hpp_io_block_input(struct device *dev, int count,
+ char *buf, int ring_offset);
+static void hpp_io_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+
+\f
+/* Probe a list of addresses for an HP LAN+ adaptor.
+ This routine is almost boilerplate. */
+
+int hp_plus_probe(struct device *dev)
+{
+ int *port, ports[] = HPP_PROBE_LIST;
+ short ioaddr = dev->base_addr;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return hpp_probe1(dev, ioaddr);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = &ports[0]; *port; port++) {
+ if (check_region(*port, HP_IO_EXTENT))
+ continue;
+ if (hpp_probe1(dev, *port) == 0) {
+ return 0;
+ }
+ }
+ return ENODEV;
+}
+
+/* Do the interesting part of the probe at a single address. */
+int hpp_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ unsigned char checksum = 0;
+ char *name = "HP-PC-LAN+";
+ unsigned char *station_addr = dev->dev_addr;
+ int mem_start;
+
+ /* Check for the HP+ signature, 50 48 0x 53. */
+ if (inw(ioaddr + HP_ID) != 0x4850
+ || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
+ return ENODEV;
+
+ /* OK, we think that we have it. Get and checksum the physical address. */
+ printk("%s: %s at %#3x,", dev->name, name, ioaddr);
+
+ /* Retrieve and checksum the station address. */
+ outw(MAC_Page, ioaddr + HP_PAGING);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ unsigned char inval = inb(ioaddr + 8 + i);
+ station_addr[i] = inval;
+ checksum += inval;
+ printk(" %2.2x", inval);
+ }
+ checksum += inb(ioaddr + 14);
+
+ if (checksum != 0xff) {
+ printk(" bad checksum %2.2x.\n", checksum);
+ return ENODEV;
+ } else {
+ /* Point at the Software Configuration Flags. */
+ outw(ID_Page, ioaddr + HP_PAGING);
+ printk(" ID %4.4x", inw(ioaddr + 12));
+ }
+
+ /* Grab the region so we can find another board if something fails. */
+ snarf_region(ioaddr, HP_IO_EXTENT);
+
+ /* Read the IRQ line. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ {
+ int irq = inb(ioaddr + 13) & 0x0f;
+ int option = inw(ioaddr + HPP_OPTION);
+
+ dev->irq = irq;
+ if (option & MemEnable) {
+ mem_start = inw(ioaddr + 9) << 8;
+ printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
+ } else {
+ mem_start = 0;
+ printk(", IRQ %d, programmed-I/O mode.\n", irq);
+ }
+ }
+
+ printk( "%s%s", KERN_INFO, version);
+
+ /* Set the wrap registers for string I/O reads. */
+ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+
+ ethdev_init(dev);
+
+ dev->open = &hpp_open;
+ dev->stop = &hpp_close;
+
+ ei_status.name = name;
+ ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_2X_PAGES;
+ ei_status.stop_page = HP_STOP_PG;
+
+ ei_status.reset_8390 = &hpp_reset_8390;
+ ei_status.block_input = &hpp_io_block_input;
+ ei_status.block_output = &hpp_io_block_output;
+
+ /* Check if the memory_enable flag is set in the option register. */
+ if (mem_start) {
+ ei_status.block_input = &hpp_mem_block_input;
+ ei_status.block_output = &hpp_mem_block_output;
+ dev->mem_start = mem_start;
+ dev->rmem_start = dev->mem_start + TX_2X_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
+ }
+
+ outw(Perf_Page, ioaddr + HP_PAGING);
+ NS8390_init(dev, 0);
+ /* Leave the 8390 and HP chip reset. */
+ outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
+
+ return 0;
+}
+
+static int
+hpp_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg;
+
+ if (request_irq(dev->irq, &ei_interrupt)) {
+ return -EAGAIN;
+ }
+
+ /* Reset the 8390 and HP chip. */
+ option_reg = inw(ioaddr + HPP_OPTION);
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+ /* Unreset the board and enable interrupts. */
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ /* Set the wrap registers for programmed-I/O operation. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Select the operational page. */
+ outw(Perf_Page, ioaddr + HP_PAGING);
+
+ return ei_open(dev);
+}
+
+static int
+hpp_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ NS8390_init(dev, 0);
+ outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
+ ioaddr + HPP_OPTION);
+
+ return 0;
+}
+
+static void
+hpp_reset_8390(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ /* Pause a few cycles for the hardware reset to take place. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ ei_status.txing = 0;
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+
+
+ if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+ return;
+}
+
+/* Block input and output, similar to the Crynwr packet driver.
+ Note that transfer with the EtherTwist+ must be on word boundaries. */
+
+static int
+hpp_io_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, buf, count>>1);
+ if (count & 0x01)
+ buf[count-1] = inw(ioaddr + HP_DATAPORT);
+ return ring_offset + count;
+}
+
+static int
+hpp_mem_block_input(struct device *dev, int count, char *buf, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ /* Caution: this relies on 8390.c rounding up allocations! */
+ memcpy(buf, (char*)dev->mem_start, (count + 3) & ~3);
+ outw(option_reg, ioaddr + HPP_OPTION);
+
+ return ring_offset + count;
+}
+
+/* A special note: we *must* always transfer >=16 bit words.
+ It's always safe to round up, so we do. */
+static void
+hpp_io_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
+ return;
+}
+
+static void
+hpp_mem_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy((char *)dev->mem_start, buf, (count + 3) & ~3);
+ outw(option_reg, ioaddr + HPP_OPTION);
+
+ return;
+}
+
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp-plus.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
--- /dev/null
+/*-
+ * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.c
+ *
+ * Version : $Revision: 1.1 $
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/26
+ * Last Updated : $Date: 1994/06/30 16:25:15 $
+ *
+ * Description : Schneider & Koch G16 Ethernet Device Driver for
+ * Linux Kernel >= 1.1.22
+ * Update History :
+ *
+-*/
+
+static char *rcsid = "$Id: sk_g16.c,v 1.1 1994/06/30 16:25:15 root Exp $";
+
+/*
+ * The Schneider & Koch (SK) G16 Network device driver is based
+ * on the 'ni6510' driver from Michael Hipp which can be found at
+ * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz
+ *
+ * Sources: 1) ni6510.c by M. Hipp
+ * 2) depca.c by D.C. Davies
+ * 3) skeleton.c by D. Becker
+ * 4) Am7990 Local Area Network Controller for Ethernet (LANCE),
+ * AMD, Pub. #05698, June 1989
+ *
+ * Many Thanks for helping me to get things working to:
+ *
+ * A. Cox (A.Cox@swansea.ac.uk)
+ * M. Hipp (mhipp@student.uni-tuebingen.de)
+ * R. Bolz (Schneider & Koch, Germany)
+ *
+ * See README.sk_g16 for details about limitations and bugs for the
+ * current version.
+ *
+ * To Do:
+ * - Support of SK_G8 and other SK Network Cards.
+ * - Autoset memory mapped RAM. Check for free memory and then
+ * configure RAM correctly.
+ * - SK_close should really set card in to initial state.
+ * - Test if IRQ 3 is not switched off. Use autoirq() functionality.
+ * (as in /drivers/net/skeleton.c)
+ * - Implement Multicast addressing. At minimum something like
+ * in depca.c.
+ * - Redo the statistics part.
+ * - Try to find out if the board is in 8 Bit or 16 Bit slot.
+ * If in 8 Bit mode don't use IRQ 11.
+ * - (Try to make it slightly faster.)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sk_g16.h"
+
+/*
+ * Schneider & Koch Card Definitions
+ * =================================
+ */
+
+#define SK_NAME "SK_G16"
+
+/*
+ * SK_G16 Configuration
+ * --------------------
+ */
+
+/*
+ * Abbreviations
+ * -------------
+ *
+ * RAM - used for the 16KB shared memory
+ * Boot_ROM, ROM - are used for referencing the BootEPROM
+ *
+ * SK_BOOT_ROM and SK_ADDR are symbolic constants used to configure
+ * the behaviour of the driver and the SK_G16.
+ *
+ * ! See sk_g16.install on how to install and configure the driver !
+ *
+ * SK_BOOT_ROM defines if the Boot_ROM should be switched off or not.
+ *
+ * SK_ADDR defines the address where the RAM will be mapped into the real
+ * host memory.
+ * valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps.
+ */
+
+#define SK_BOOT_ROM 1 /* 1=BootROM on 0=off */
+
+#define SK_ADDR 0xcc000
+
+/*
+ * In POS3 are bits A14-A19 of the address bus. These bits can be set
+ * to choose the RAM address. Thats why we only can choose the RAM address
+ * in 16KB steps.
+ */
+
+#define POS_ADDR (rom_addr>>14) /* Do not change this line */
+
+/*
+ * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations
+ * ----------------------------------------------
+ */
+
+/*
+ * As nearly every card has also SK_G16 a specified I/O Port region and
+ * only a few possible IRQ's.
+ * In the Installation Guide from Schneider & Koch is listed a possible
+ * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt
+ * controllers. So we use in SK_IRQS IRQ9.
+ */
+
+/* Don't touch any of the following #defines. */
+
+#define SK_IO_PORTS { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 }
+
+#define SK_IRQS { 3, 5, 9, 11, 0 }
+
+#define SK_BOOT_ROM_LOCATIONS { 0xc0000, 0xc4000, 0xc8000, 0xcc000, 0xd0000, 0xd4000, 0xd8000, 0xdc000, 0 }
+
+#define SK_BOOT_ROM_ID { 0x55, 0xaa, 0x10, 0x50, 0x06, 0x33 }
+
+/*
+ * SK_G16 POS REGISTERS
+ * --------------------
+ */
+
+/*
+ * SK_G16 has a Programmable Option Select (POS) Register.
+ * The POS is composed of 8 separate registers (POS0-7) which
+ * are I/O mapped on an address set by the W1 switch.
+ *
+ */
+
+#define SK_POS_SIZE 8 /* 8 I/O Ports are used by SK_G16 */
+
+#define SK_POS0 ioaddr /* Card-ID Low (R) */
+#define SK_POS1 ioaddr+1 /* Card-ID High (R) */
+#define SK_POS2 ioaddr+2 /* Card-Enable, Boot-ROM Disable (RW) */
+#define SK_POS3 ioaddr+3 /* Base address of RAM */
+#define SK_POS4 ioaddr+4 /* IRQ */
+
+/* POS5 - POS7 are unused */
+
+/*
+ * SK_G16 MAC PREFIX
+ * -----------------
+ */
+
+/*
+ * Scheider & Koch manufactorer code (00:00:a5).
+ * This must be checked, that we are sure it is a SK card.
+ */
+
+#define SK_MAC0 0x00
+#define SK_MAC1 0x00
+#define SK_MAC2 0x5a
+
+/*
+ * SK_G16 ID
+ * ---------
+ */
+
+/*
+ * If POS0,POS1 contain the following ID, then we know
+ * at which I/O Port Address we are.
+ */
+
+#define SK_IDLOW 0xfd
+#define SK_IDHIGH 0x6a
+
+
+/*
+ * LANCE POS Bit definitions
+ * -------------------------
+ */
+
+#define SK_ROM_RAM_ON (POS2_CARD)
+#define SK_ROM_RAM_OFF (POS2_EPROM)
+#define SK_ROM_ON (inb(SK_POS2) & POS2_CARD)
+#define SK_ROM_OFF (inb(SK_POS2) | POS2_EPROM)
+#define SK_RAM_ON (inb(SK_POS2) | POS2_CARD)
+#define SK_RAM_OFF (inb(SK_POS2) & POS2_EPROM)
+
+#define POS2_CARD 0x0001 /* 1 = SK_G16 on 0 = off */
+#define POS2_EPROM 0x0002 /* 1 = Boot EPROM off 0 = on */
+
+/*
+ * SK_G16 Memory mapped Registers
+ * ------------------------------
+ *
+ */
+
+#define SK_IOREG (board->ioreg) /* LANCE data registers. */
+#define SK_PORT (board->port) /* Control, Status register */
+#define SK_IOCOM (board->iocom) /* I/O Command */
+
+/*
+ * SK_G16 Status/Control Register bits
+ * -----------------------------------
+ *
+ * (C) Controlreg (S) Statusreg
+ */
+
+/*
+ * Register transfer: 0 = no transfer
+ * 1 = transfering data between LANCE and I/O reg
+ */
+#define SK_IORUN 0x20
+
+/*
+ * LANCE interrupt: 0 = LANCE interrupt occured
+ * 1 = no LANCE interrupt occured
+ */
+#define SK_IRQ 0x10
+
+#define SK_RESET 0x08 /* Reset SK_CARD: 0 = RESET 1 = normal */
+#define SK_RW 0x02 /* 0 = write to 1 = read from */
+#define SK_ADR 0x01 /* 0 = REG DataPort 1 = RAP Reg addr port */
+
+
+#define SK_RREG SK_RW /* Transferdirection to read from lance */
+#define SK_WREG 0 /* Transferdirection to write to lance */
+#define SK_RAP SK_ADR /* Destination Register RAP */
+#define SK_RDATA 0 /* Destination Register REG DataPort */
+
+/*
+ * SK_G16 I/O Command
+ * ------------------
+ */
+
+/*
+ * Any bitcombination sets the internal I/O bit (transfer will start)
+ * when written to I/O Command
+ */
+
+#define SK_DOIO 0x80 /* Do Transfer */
+
+/*
+ * LANCE RAP (Register Adress Port).
+ * ---------------------------------
+ */
+
+/*
+ * The LANCE internal registers are selected through the RAP.
+ * The Registers are:
+ *
+ * CSR0 - Status and Control flags
+ * CSR1 - Low order bits of initialize block (bits 15:00)
+ * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved)
+ * CSR3 - Allows redifinition of the Bus Master Interface.
+ * This register must be set to 0x0002, which means BSWAP = 0,
+ * ACON = 1, BCON = 0;
+ *
+ */
+
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+/*
+ * General Definitions
+ * ===================
+ */
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * We have 16KB RAM which can be accessed by the LANCE. In the
+ * memory are not only the buffers but also the ring descriptors and
+ * the initialize block.
+ * Don't change anything unless you really know what you do.
+ */
+
+#define LC_LOG_TX_BUFFERS 1 /* (2 == 2^^1) 2 Transmit buffers */
+#define LC_LOG_RX_BUFFERS 3 /* (8 == 2^^3) 8 Receive buffers */
+
+/* Descriptor ring sizes */
+
+#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */
+#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */
+
+/* Define Mask for setting RMD, TMD length in the LANCE init_block */
+
+#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29)
+#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29)
+
+/*
+ * Data Buffer size is set to maximum packet length.
+ */
+
+#define PKT_BUF_SZ 1518
+
+/*
+ * The number of low I/O ports used by the ethercard.
+ */
+
+#define ETHERCARD_TOTAL_SIZE SK_POS_SIZE
+
+/*
+ * Portreserve is there to mark the Card I/O Port region as used.
+ * Check_region is to check if the region at ioaddr with the size "size"
+ * is free or not.
+ * Snarf_region allocates the I/O Port region.
+ */
+
+#ifndef HAVE_PORTRESERVE
+
+#define check_region(ioaddr, size) 0
+#define snarf_region(ioaddr, size); do ; while (0)
+
+#endif
+
+/*
+ * SK_DEBUG
+ *
+ * Here you can choose what level of debugging wanted.
+ *
+ * If SK_DEBUG and SK_DEBUG2 are undefined, then only the
+ * necessary messages will be printed.
+ *
+ * If SK_DEBUG is defined, there will be many debugging prints
+ * which can help to find some mistakes in configuration or even
+ * in the driver code.
+ *
+ * If SK_DEBUG2 is defined, many many messages will be printed
+ * which normally you don't need. I used this to check the interrupt
+ * routine.
+ *
+ * (If you define only SK_DEBUG2 then only the messages for
+ * checking interrupts will be printed!)
+ *
+ * Normal way of live is:
+ *
+ * For the whole thing get going let both symbolic constants
+ * undefined. If you face any problems and you know whats going
+ * on (you know something about the card and you can interpret some
+ * hex LANCE register output) then define SK_DEBUG
+ *
+ */
+
+#undef SK_DEBUG /* debugging */
+#undef SK_DEBUG2 /* debugging with more verbose report */
+
+#ifdef SK_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x) /**/
+#endif
+
+#ifdef SK_DEBUG2
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x) /**/
+#endif
+
+/*
+ * SK_G16 RAM
+ *
+ * The components are memory mapped and can be set in a region from
+ * 0x00000 through 0xfc000 in 16KB steps.
+ *
+ * The Network components are: dual ported RAM, Prom, I/O Reg, Status-,
+ * Controlregister and I/O Command.
+ *
+ * dual ported RAM: This is the only memory region which the LANCE chip
+ * has access to. From the Lance it is addressed from 0x0000 to
+ * 0x3fbf. The host accesses it normaly.
+ *
+ * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a
+ * 8-Bit PROM, this means only the 16 even addresses are used of the
+ * 32 Byte Address region. Access to a odd address results in invalid
+ * data.
+ *
+ * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write,
+ * Hi-Byte Write, Low-Byte Read, Hi-Byte Read.
+ * Transfer from or to the LANCE is always in 16Bit so Low and High
+ * registers are always relevant.
+ *
+ * The Data from the Readregister is not the data in the Writeregister!!
+ *
+ * Port: Status- and Controlregister.
+ * Two different registers which share the same address, Status is
+ * read-only, Control is write-only.
+ *
+ * I/O Command:
+ * Any bitcombination written in here starts the transmission between
+ * Host and LANCE.
+ */
+
+typedef struct
+{
+ unsigned char ram[0x3fc0]; /* 16KB dual ported ram */
+ unsigned char rom[0x0020]; /* 32Byte PROM containing 6Byte MAC */
+ unsigned char res1[0x0010]; /* reserved */
+ unsigned volatile short ioreg;/* LANCE I/O Register */
+ unsigned volatile char port; /* Statusregister and Controlregister */
+ unsigned char iocom; /* I/O Command Register */
+} SK_RAM;
+
+/* struct */
+
+/*
+ * This is the structure for the dual ported ram. We
+ * have exactly 16 320 Bytes. In here there must be:
+ *
+ * - Initialize Block (starting at a word boundary)
+ * - Receive and Transmit Descriptor Rings (quadword boundary)
+ * - Data Buffers (arbitrary boundary)
+ *
+ * This is because LANCE has on SK_G16 only access to the dual ported
+ * RAM and nowhere else.
+ */
+
+struct SK_ram
+{
+ struct init_block ib;
+ struct tmd tmde[TMDNUM];
+ struct rmd rmde[RMDNUM];
+ char tmdbuf[TMDNUM][PKT_BUF_SZ];
+ char rmdbuf[RMDNUM][PKT_BUF_SZ];
+};
+
+/*
+ * Structure where all necessary information is for ring buffer
+ * management and statistics.
+ */
+
+struct priv
+{
+ struct SK_ram *ram; /* dual ported ram structure */
+ struct rmd *rmdhead; /* start of receive ring descriptors */
+ struct tmd *tmdhead; /* start of transmit ring descriptors */
+ int rmdnum; /* actual used ring descriptor */
+ int tmdnum; /* actual transmit descriptor for transmitting data */
+ int tmdlast; /* last sent descriptor used for error handling, etc */
+ void *rmdbufs[RMDNUM]; /* pointer to the receive buffers */
+ void *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */
+ struct enet_statistics stats; /* Device driver statistics */
+};
+
+/* global variable declaration */
+
+/* IRQ map used to reserve a IRQ (see SK_open()) */
+
+extern void *irq2dev_map[16];
+
+/* static variables */
+
+static SK_RAM *board; /* pointer to our memory mapped board components */
+
+/* Macros */
+
+\f
+/* Function Prototypes */
+
+/*
+ * Device Driver functions
+ * -----------------------
+ * See for short explanation of each function its definitions header.
+ */
+
+int SK_init(struct device *dev);
+static int SK_probe(struct device *dev, short ioaddr);
+
+static int SK_open(struct device *dev);
+static int SK_send_packet(struct sk_buff *skb, struct device *dev);
+static void SK_interrupt(int reg_ptr);
+static void SK_rxintr(struct device *dev);
+static void SK_txintr(struct device *dev);
+static int SK_close(struct device *dev);
+
+static struct enet_statistics *SK_get_stats(struct device *dev);
+
+unsigned int SK_rom_addr(void);
+
+#ifdef HAVE_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
+
+/*
+ * LANCE Functions
+ * ---------------
+ */
+
+static int SK_lance_init(struct device *dev, unsigned short mode);
+void SK_reset_board(void);
+void SK_set_RAP(int reg_number);
+int SK_read_reg(int reg_number);
+int SK_rread_reg(void);
+void SK_write_reg(int reg_number, int value);
+
+/*
+ * Debuging functions
+ * ------------------
+ */
+
+void SK_print_pos(struct device *dev, char *text);
+void SK_print_dev(struct device *dev, char *text);
+void SK_print_ram(struct device *dev);
+
+\f
+/*-
+ * Function : SK_init
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Check for a SK_G16 network adaptor and initialize it.
+ * This function gets called by dev_init which initializes
+ * all Network devices.
+ *
+ * Parameters : I : struct device *dev - structure preconfigured
+ * from Space.c
+ * Return Value : 0 = Driver Found and initialized
+ * Errors : ENODEV - no device found
+ * ENXIO - not probed
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, alloate space for the device and return success
+ * (detachable devices only).
+ */
+
+int SK_init(struct device *dev)
+{
+ int ioaddr = 0; /* I/O port address used for POS regs */
+ int *port, ports[] = SK_IO_PORTS; /* SK_G16 supported ports */
+
+ /* get preconfigured base_addr from dev which is done in Space.c */
+ int base_addr = dev->base_addr;
+
+ PRINTK(("%s: %s", SK_NAME, rcsid));
+ rcsid = NULL; /* We do not want to use this further */
+
+ if (base_addr > 0x0ff) /* Check a single specified address */
+ {
+ /* Check if on specified address is a SK_G16 */
+
+ if ( (inb(SK_POS0) == SK_IDLOW) ||
+ (inb(SK_POS1) == SK_IDHIGH) )
+ {
+ return SK_probe(dev, base_addr);
+ }
+
+ return ENODEV; /* Sorry, but on specified address NO SK_G16 */
+ }
+ else if (base_addr > 0) /* Don't probe at all */
+ {
+ return ENXIO;
+ }
+
+ /* Autoprobe base_addr */
+
+ for (port = &ports[0]; *port; port++)
+ {
+ ioaddr = *port; /* we need ioaddr for accessing POS regs */
+
+ /* Check if I/O Port region is used by another board */
+
+ if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
+ {
+ continue; /* Try next Port address */
+ }
+
+ /* Check if at ioaddr is a SK_G16 */
+
+ if ( !(inb(SK_POS0) == SK_IDLOW) ||
+ !(inb(SK_POS1) == SK_IDHIGH) )
+ {
+ continue; /* Try next Port address */
+ }
+
+ dev->base_addr = ioaddr; /* Set I/O Port Address */
+
+ if (SK_probe(dev, ioaddr) == 0)
+ {
+ return 0; /* Card found and initialized */
+ }
+ }
+
+ dev->base_addr = base_addr; /* Write back original base_addr */
+
+ return ENODEV; /* Failed to find or init driver */
+
+} /* End of SK_init */
+
+\f
+/*-
+ * Function : SK_probe
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function is called by SK_init and
+ * does the main part of initialization.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : short ioaddr - I/O Port address where POS is.
+ * Return Value : 0 = Initilization done
+ * Errors : ENODEV - No SK_G16 found
+ * -1 - Configuration problem
+ * Globals : irq2dev_map - Which device uses which IRQ
+ * : board - pointer to SK_RAM
+ * Update History :
+ * YY/MM/DD uid Description
+ * 94/06/30 pwe SK_ADDR now checked and at the correct place
+-*/
+
+int SK_probe(struct device *dev, short ioaddr)
+{
+ int i,j; /* Counters */
+ int sk_addr_flag = 0; /* SK ADDR correct? 1 - no, 0 - yes */
+ unsigned int rom_addr; /* used to store RAM address used for POS_ADDR */
+
+ struct priv *p; /* SK_G16 private structure */
+
+ if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000)
+ {
+
+ sk_addr_flag = 1;
+
+ /*
+ * Now here we could use a routine which searches for a free
+ * place in the ram and set SK_ADDR if found. TODO.
+ */
+ }
+
+ if (SK_BOOT_ROM) /* Shall we keep Boot_ROM on ? */
+ {
+ PRINTK(("## %s: SK_BOOT_ROM is set.\n", SK_NAME));
+
+ rom_addr = SK_rom_addr();
+
+ if (rom_addr == 0) /* No Boot_ROM found */
+ {
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR; /* assign predefined address */
+
+ PRINTK(("## %s: NO Bootrom found \n", SK_NAME));
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+ else if (rom_addr == SK_ADDR)
+ {
+ printk("%s: RAM + ROM are set to the same address %#08x\n"
+ " Check configuration. Now switching off Boot_ROM\n",
+ SK_NAME, rom_addr);
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off*/
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+ else
+ {
+ PRINTK(("## %s: Found ROM at %#08x\n", SK_NAME, rom_addr));
+ PRINTK(("## %s: Keeping Boot_ROM on\n", SK_NAME));
+
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_ROM_RAM_ON, SK_POS2); /* RAM on, BOOT_ROM on */
+ }
+ }
+ else /* Don't keep Boot_ROM */
+ {
+ PRINTK(("## %s: SK_BOOT_ROM is not set.\n", SK_NAME));
+
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_rom_addr(); /* Try to find a Boot_ROM */
+
+ /* IF we find a Boot_ROM disable it */
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+
+ /* We found a Boot_ROM and its gone. Set RAM address on
+ * Boot_ROM address.
+ */
+
+ if (rom_addr)
+ {
+ printk("%s: We found Boot_ROM at %#08x. Now setting RAM on"
+ "that address\n", SK_NAME, rom_addr);
+
+ outb(POS_ADDR, SK_POS3); /* Set RAM on Boot_ROM address */
+ }
+ else /* We did not find a Boot_ROM, use predefined SK_ADDR for ram */
+ {
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ }
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "POS registers after ROM, RAM config");
+#endif
+
+ board = (SK_RAM *) rom_addr;
+
+ /* Read in station address */
+ for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2)
+ {
+ dev->dev_addr[i] = board->rom[j];
+ }
+
+ /* Check for manufactorer code */
+ if (!(dev->dev_addr[0] == SK_MAC0 &&
+ dev->dev_addr[1] == SK_MAC1 &&
+ dev->dev_addr[2] == SK_MAC2) )
+ {
+ PRINTK(("## %s: We did not find SK_G16 at RAM location.\n",
+ SK_NAME));
+ return ENODEV; /* NO SK_G16 found */
+ }
+
+ printk("%s: %s found at %#3x, HW addr: %#04x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name,
+ "Schneider & Koch Netcard",
+ (unsigned int) dev->base_addr,
+ dev->dev_addr[0],
+ dev->dev_addr[1],
+ dev->dev_addr[2],
+ dev->dev_addr[3],
+ dev->dev_addr[4],
+ dev->dev_addr[5]);
+
+ /* Grap the I/O Port region */
+ snarf_region(ioaddr, ETHERCARD_TOTAL_SIZE);
+
+ /* Initialize device structure */
+
+ /* Allocate memory for private structure */
+ p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL);
+ memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */
+
+ /* Assign our Device Driver functions */
+
+ dev->open = &SK_open;
+ dev->stop = &SK_close;
+ dev->hard_start_xmit = &SK_send_packet;
+ dev->get_stats = &SK_get_stats;
+
+#ifdef HAVE_MULTICAST
+ dev->set_multicast_list = &set_multicast_list;
+#endif
+
+
+ /* Set the generic fields of the device structure */
+
+ ether_setup(dev);
+
+ /* Initialize private structure */
+
+ p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */
+ p->tmdhead = &(p->ram)->tmde[0]; /* Set TMD head */
+ p->rmdhead = &(p->ram)->rmde[0]; /* Set RMD head */
+
+ /* Initialize buffer pointers */
+
+ for (i = 0; i < TMDNUM; i++)
+ {
+ p->tmdbufs[i] = &(p->ram)->tmdbuf[i];
+ }
+
+ for (i = 0; i < RMDNUM; i++)
+ {
+ p->rmdbufs[i] = &(p->ram)->rmdbuf[i];
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "End of SK_probe");
+ SK_print_ram(dev);
+#endif
+
+ return 0; /* Initialization done */
+
+} /* End of SK_probe() */
+
+\f
+/*-
+ * Function : SK_open
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function is called sometimes after booting
+ * when ifconfig programm is run.
+ *
+ * This function requests an IRQ, sets the correct
+ * IRQ in the card. Then calls SK_lance_init() to
+ * init and start the LANCE chip. Then if everthing is
+ * ok returns with 0 (OK), which means SK_G16 is now
+ * opened and operational.
+ *
+ * (Called by dev_open() /net/inet/dev.c)
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : 0 - Device opened
+ * Errors : -EAGAIN - Open failed
+ * Globals : irq2dev_map - which device uses which irq
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_open(struct device *dev)
+{
+ int i = 0;
+ int irqval = 0;
+ int ioaddr = dev->base_addr;
+
+ int irqtab[] = SK_IRQS;
+
+ struct priv *p = (struct priv *)dev->priv;
+
+ PRINTK(("## %s: At beginning of SK_open(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ if (dev->irq == 0) /* Autoirq */
+ {
+ i = 0;
+
+ /*
+ * Check if one IRQ out of SK_IRQS is free and install
+ * interrupt handler.
+ * Most done by request_irq().
+ * irqval: 0 - interrupt handler installed for IRQ irqtab[i]
+ * -EBUSY - interrupt busy
+ * -EINVAL - irq > 15 or handler = NULL
+ */
+
+ do
+ {
+ irqval = request_irq(irqtab[i], &SK_interrupt);
+ i++;
+ } while (irqval && irqtab[i]);
+
+ if (irqval) /* We tried every possible IRQ but no success */
+ {
+ printk("%s: unable to get an IRQ\n", dev->name);
+ return -EAGAIN;
+ }
+
+ dev->irq = irqtab[--i];
+
+ outb(i<<2, SK_POS4); /* Set Card on probed IRQ */
+
+ }
+ else if (dev->irq == 2) /* IRQ2 is always IRQ9 */
+ {
+ if (request_irq(9, &SK_interrupt))
+ {
+ printk("%s: unable to get IRQ 9\n", dev->name);
+ return -EAGAIN;
+ }
+ dev->irq = 9;
+
+ /*
+ * Now we set card on IRQ2.
+ * This can be confusing, but remember that IRQ2 on the network
+ * card is in reality IRQ9
+ */
+ outb(0x08, SK_POS4); /* set card to IRQ2 */
+
+ }
+ else /* Check IRQ as defined in Space.c */
+ {
+ int i = 0;
+
+ /* check if IRQ free and valid. Then install Interrupt handler */
+
+ if (request_irq(dev->irq, &SK_interrupt))
+ {
+ printk("%s: unable to get selected IRQ\n", dev->name);
+ return -EAGAIN;
+ }
+
+ switch(dev->irq)
+ {
+ case 3: i = 0;
+ break;
+ case 5: i = 1;
+ break;
+ case 2: i = 2;
+ break;
+ case 11:i = 3;
+ break;
+ default:
+ printk("%s: Preselected IRQ %d is invalid for %s boards",
+ dev->name,
+ dev->irq,
+ SK_NAME);
+ return -EAGAIN;
+ }
+
+ outb(i<<2, SK_POS4); /* Set IRQ on card */
+ }
+
+ irq2dev_map[dev->irq] = dev; /* Set IRQ as used by us */
+
+ printk("%s: Schneider & Koch G16 at %#3x, IRQ %d, shared mem at %#08x\n",
+ dev->name, (unsigned int)dev->base_addr,
+ (int) dev->irq, (unsigned int) p->ram);
+
+ if (!(i = SK_lance_init(dev, 0))) /* LANCE init OK? */
+ {
+
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef SK_DEBUG
+
+ /*
+ * This debug block tries to stop LANCE,
+ * reinit LANCE with transmitter and receiver disabled,
+ * then stop again and reinit with NORMAL_MODE
+ */
+
+ printk("## %s: After lance init. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printk("## %s: LANCE stopped. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(dev, MODE_DTX | MODE_DRX);
+ printk("## %s: Reinit with DTX + DRX off. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printk("## %s: LANCE stopped. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(dev, MODE_NORMAL);
+ printk("## %s: LANCE back to normal mode. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_print_pos(dev, "POS regs before returning OK");
+
+#endif /* SK_DEBUG */
+
+ return 0; /* SK_open() is successful */
+ }
+ else /* LANCE init failed */
+ {
+
+ PRINTK(("## %s: LANCE init failed: CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ dev->start = 0; /* Device not ready */
+ return -EAGAIN;
+ }
+
+} /* End of SK_open() */
+
+\f
+/*-
+ * Function : SK_lance_init
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Reset LANCE chip, fill RMD, TMD structures with
+ * start values and Start LANCE.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : int mode - put LANCE into "mode" see data-sheet for
+ * more info.
+ * Return Value : 0 - Init done
+ * Errors : -1 - Init failed
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_lance_init(struct device *dev, unsigned short mode)
+{
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+ struct rmd *rmdp;
+
+ PRINTK(("## %s: At beginning of LANCE init. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Reset LANCE */
+ SK_reset_board();
+
+ /* Initialize TMD's with start values */
+ p->tmdnum = 0; /* First descriptor for transmitting */
+ p->tmdlast = 0; /* First descriptor for reading stats */
+
+ for (i = 0; i < TMDNUM; i++) /* Init all TMD's */
+ {
+ tmdp = p->tmdhead + i;
+
+ tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */
+
+ /* Mark TMD as start and end of packet */
+ tmdp->u.s.status = TX_STP | TX_ENP;
+ }
+
+
+ /* Initialize RMD's with start values */
+
+ p->rmdnum = 0; /* First RMD which will be used */
+
+ for (i = 0; i < RMDNUM; i++) /* Init all RMD's */
+ {
+ rmdp = p->rmdhead + i;
+
+
+ rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */
+
+ /*
+ * LANCE must be owner at beginning so that he can fill in
+ * receiving packets, set status and release RMD
+ */
+
+ rmdp->u.s.status = RX_OWN;
+
+ rmdp->blen = -PKT_BUF_SZ; /* Buffer Size in a two's comliment */
+
+ rmdp->mlen = 0; /* init message length */
+
+ }
+
+ /* Fill LANCE Initialize Block */
+
+ (p->ram)->ib.mode = mode; /* Set operation mode */
+
+ for (i = 0; i < ETH_ALEN; i++) /* Set physical address */
+ {
+ (p->ram)->ib.paddr[i] = dev->dev_addr[i];
+ }
+
+ for (i = 0; i < 8; i++) /* Set multicast, logical adress */
+ {
+ (p->ram)->ib.laddr[i] = 0; /* We do not use logical addressing */
+ }
+
+ /* Set ring descriptor pointers and set number of descriptors */
+
+ (p->ram)->ib.rdrp = (int) p->rmdhead | RMDNUMMASK;
+ (p->ram)->ib.tdrp = (int) p->tmdhead | TMDNUMMASK;
+
+ /* Prepare LANCE Controll and Status Registers */
+
+ cli();
+
+ SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */
+
+ /*
+ * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to
+ * PC Memory locations.
+ *
+ * In structure SK_ram is defined that the first thing in ram
+ * is the initalization block. So his address is for LANCE always
+ * 0x0000
+ *
+ * CSR1 contains low order bits 15:0 of initialization block address
+ * CSR2 is built of:
+ * 7:0 High order bits 23:16 of initialization block address
+ * 15:8 reserved, must be 0
+ */
+
+ /* Set initialization block address (must be on word boundary) */
+ SK_write_reg(CSR1, 0); /* Set low order bits 15:0 */
+ SK_write_reg(CSR2, 0); /* Set high order bits 23:16 */
+
+
+ PRINTK(("## %s: After setting CSR1-3. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Initialize LANCE */
+
+ /*
+ * INIT = Initialize, when set, cuases the LANCE to begin the
+ * initialization procedure and access the Init Block.
+ */
+
+ SK_write_reg(CSR0, CSR0_INIT);
+
+ sti();
+
+ /* Wait until LANCE finished initialization */
+
+ SK_set_RAP(CSR0); /* Register Address Pointer to CSR0 */
+
+ for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++)
+ ; /* Wait until init done or go ahead if problems (i>=100) */
+
+ if (i >= 100) /* Something is wrong ! */
+ {
+ printk("%s: can't init am7990, status: %04x "
+ "init_block: %#08x\n",
+ dev->name, (int) SK_read_reg(CSR0),
+ (unsigned int) &(p->ram)->ib);
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "LANCE INIT failed");
+ SK_print_dev(dev,"Device Structure:");
+#endif
+
+ return -1; /* LANCE init failed */
+ }
+
+ PRINTK(("## %s: init done after %d ticks\n", SK_NAME, i));
+
+ /* Clear Initialize done, enable Interrupts, start LANCE */
+
+ SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT);
+
+ PRINTK(("## %s: LANCE started. CSR0: %#06x\n", SK_NAME,
+ SK_read_reg(CSR0)));
+
+ return 0; /* LANCE is up and running */
+
+} /* End of SK_lance_init() */
+
+
+\f
+/*-
+ * Function : SK_send_packet
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : Writes an socket buffer into a transmit descriptor
+ * and starts transmission.
+ *
+ * Parameters : I : struct sk_buff *skb - packet to transfer
+ * I : struct device *dev - SK_G16 device structure
+ * Return Value : 0 - OK
+ * 1 - Could not transmit (dev_queue_xmit will queue it)
+ * and try to sent it later
+ * Globals : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+
+ if (dev->tbusy)
+ {
+ /* if Transmitter more than 150ms busy -> time_out */
+
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 15)
+ {
+ return 1; /* We have to try transmit later */
+ }
+
+ printk("%s: xmitter timed out, try to restart!\n", dev->name);
+
+ SK_lance_init(dev, MODE_NORMAL); /* Reinit LANCE */
+
+ dev->tbusy = 0; /* Clear Transmitter flag */
+
+ dev->trans_start = jiffies; /* Mark Start of transmission */
+
+ }
+
+ /*
+ * If some upper Layer thinks we missed a transmit done interrupt
+ * we are passed NULL.
+ * (dev_queue_xmit net/inet/dev.c
+ */
+
+ if (skb == NULL)
+ {
+ /*
+ * Dequeue packets from transmit queue and send them.
+ */
+ dev_tint(dev);
+
+ return 0;
+ }
+
+ PRINTK2(("## %s: SK_send_packet() called, CSR0 %#04x.\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ * This means check if we are already in.
+ */
+
+ if (set_bit(0, (void *) &dev->tbusy) != 0) /* dev->tbusy already set ? */
+ {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ }
+ else
+ {
+ /* Evaluate Packet length */
+ short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+ tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */
+
+ /* Fill in Transmit Message Descriptor */
+
+ /* Copy data into dual ported ram */
+
+ memcpy((char *) (tmdp->u.buffer & 0x00ffffff), (char *)skb->data,
+ skb->len);
+
+ tmdp->blen = -len; /* set length to transmit */
+
+ /*
+ * Packet start and end is always set because we use the maximum
+ * packet length as buffer length.
+ * Relinquish ownership to LANCE
+ */
+
+ tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP;
+
+ /* Start Demand Transmission */
+ SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA);
+
+ dev->trans_start = jiffies; /* Mark start of transmission */
+
+ /* Set pointer to next transmit buffer */
+ p->tmdnum++;
+ p->tmdnum &= TMDNUM-1;
+
+ /* Do we own the next transmit buffer ? */
+ if (! ((p->tmdhead + p->tmdnum)->u.s.status & TX_OWN) )
+ {
+ /*
+ * We own next buffer and are ready to transmit, so
+ * clear busy flag
+ */
+ dev->tbusy = 0;
+ }
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+} /* End of SK_send_packet */
+
+\f
+/*-
+ * Function : SK_interrupt
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : SK_G16 interrupt handler which checks for LANCE
+ * Errors, handles transmit and receive interrupts
+ *
+ * Parameters : I : int reg_ptr -
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_interrupt(int reg_ptr)
+{
+ int irq = - (((struct pt_regs *)reg_ptr)->orig_eax+2);
+ int csr0;
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct priv *p = (struct priv *) dev->priv;
+
+
+ PRINTK2(("## %s: SK_interrupt(). status: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ if (dev == NULL)
+ {
+ printk("SK_interrupt(): IRQ %d for unknown device.\n", irq);
+ }
+
+
+ if (dev->interrupt)
+ {
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ }
+
+ csr0 = SK_read_reg(CSR0); /* store register for checking */
+
+ dev->interrupt = 1; /* We are handling an interrupt */
+
+ /*
+ * Acknowledge all of the current interrupt sources, disable
+ * Interrupts (INEA = 0)
+ */
+
+ SK_write_reg(CSR0, csr0 & CSR0_CLRALL);
+
+ if (csr0 & CSR0_ERR) /* LANCE Error */
+ {
+ printk("%s: error: %04x\n", dev->name, csr0);
+
+ if (csr0 & CSR0_MISS) /* No place to store packet ? */
+ {
+ p->stats.rx_dropped++;
+ }
+ }
+
+ if (csr0 & CSR0_RINT) /* Receive Interrupt (packet arrived) */
+ {
+ SK_rxintr(dev);
+ }
+
+ if (csr0 & CSR0_TINT) /* Transmit interrupt (packet sent) */
+ {
+ SK_txintr(dev);
+ }
+
+ SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */
+
+ dev->interrupt = 0; /* We are out */
+} /* End of SK_interrupt() */
+
+\f
+/*-
+ * Function : SK_txintr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : After sending a packet we check status, update
+ * statistics and relinquish ownership of transmit
+ * descriptor ring.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_txintr(struct device *dev)
+{
+ int tmdstat;
+ struct tmd *tmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+
+ PRINTK2(("## %s: SK_txintr() status: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ tmdp = p->tmdhead + p->tmdlast; /* Which buffer we sent at last ? */
+
+ /* Set next buffer */
+ p->tmdlast++;
+ p->tmdlast &= TMDNUM-1;
+
+ tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */
+
+ /*
+ * We check status of transmitted packet.
+ * see LANCE data-sheet for error explanation
+ */
+ if (tmdstat & TX_ERR) /* Error occured */
+ {
+ printk("%s: TX error: %04x %04x\n", dev->name, (int) tmdstat,
+ (int) tmdp->status2);
+
+ if (tmdp->status2 & TX_TDR) /* TDR problems? */
+ {
+ printk("%s: tdr-problems \n", dev->name);
+ }
+
+ if (tmdp->status2 & TX_RTRY) /* Failed in 16 attempts to transmit ? */
+ p->stats.tx_aborted_errors++;
+ if (tmdp->status2 & TX_LCOL) /* Late collision ? */
+ p->stats.tx_window_errors++;
+ if (tmdp->status2 & TX_LCAR) /* Loss of Carrier ? */
+ p->stats.tx_carrier_errors++;
+ if (tmdp->status2 & TX_UFLO) /* Underflow error ? */
+ {
+ p->stats.tx_fifo_errors++;
+
+ /*
+ * If UFLO error occurs it will turn tranmitter of.
+ * So we must reinit LANCE
+ */
+
+ SK_lance_init(dev, MODE_NORMAL);
+ }
+
+ p->stats.tx_errors++;
+
+ tmdp->status2 = 0; /* Clear error flags */
+ }
+ else if (tmdstat & TX_MORE) /* Collisions occured ? */
+ {
+ /*
+ * Here I have a problem.
+ * I only know that there must be one or up to 15 collisions.
+ * Thats why TX_MORE is set, because after 16 attempts TX_RTRY
+ * will be set which means couldn't send packet aborted transfer.
+ *
+ * First I did not have this in but then I thought at minimum
+ * we see that something was not ok.
+ * If anyone knows something better than this to handle this
+ * please report it. (see Email addresses in the README file)
+ */
+
+ p->stats.collisions++;
+ }
+ else /* Packet sent without any problems */
+ {
+ p->stats.tx_packets++;
+ }
+
+ /*
+ * We mark transmitter not busy anymore, because now we have a free
+ * transmit descriptor which can be filled by SK_send_packet and
+ * afterwards sent by the LANCE
+ */
+
+ dev->tbusy = 0;
+
+ /*
+ * mark_bh(NET_BH);
+ * This will cause net_bh() to run after this interrupt handler.
+ *
+ * The function which do handle slow IRQ parts is do_bottom_half()
+ * which runs at normal kernel priority, that means all interrupt are
+ * enabled. (see kernel/irq.c)
+ *
+ * net_bh does something like this:
+ * - check if already in net_bh
+ * - try to transmit something from the send queue
+ * - if something is in the receive queue send it up to higher
+ * levels if it is a known protocol
+ * - try to transmit something from the send queue
+ */
+
+ mark_bh(NET_BH);
+
+} /* End of SK_txintr() */
+
+\f
+/*-
+ * Function : SK_rxintr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : Buffer sent, check for errors, relinquish ownership
+ * of the receive message descriptor.
+ *
+ * Parameters : I : SK_G16 device structure
+ * Return Value : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_rxintr(struct device *dev)
+{
+
+ struct rmd *rmdp;
+ int rmdstat;
+ struct priv *p = (struct priv *) dev->priv;
+
+ PRINTK2(("## %s: SK_rxintr(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ rmdp = p->rmdhead + p->rmdnum;
+
+ /* As long as we own the next entry, check status and send
+ * it up to higher layer
+ */
+
+ while (!( (rmdstat = rmdp->u.s.status) & RX_OWN))
+ {
+ /*
+ * Start and end of packet must be set, because we use
+ * the ethernet maximum packet length (1518) as buffer size.
+ *
+ * Because our buffers are at maximum OFLO and BUFF errors are
+ * not to be concerned (see Data sheet)
+ */
+
+ if (rmdstat & (RX_STP | RX_ENP) != (RX_STP | RX_ENP))
+ {
+ /* Start of a frame > 1518 Bytes ? */
+
+ if (rmdstat & RX_STP)
+ {
+ p->stats.rx_errors++; /* bad packet received */
+ p->stats.rx_length_errors++; /* packet to long */
+
+ printk("%s: packet too long\n", dev->name);
+ }
+
+ /*
+ * All other packets will be ignored until a new frame with
+ * start (RX_STP) set follows.
+ *
+ * What we do is just give descriptor free for new incoming
+ * packets.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else if (rmdstat & RX_ERR) /* Receive Error ? */
+ {
+ printk("%s: RX error: %04x\n", dev->name, (int) rmdstat);
+
+ p->stats.rx_errors++;
+
+ if (rmdstat & RX_FRAM) p->stats.rx_frame_errors++;
+ if (rmdstat & RX_CRC) p->stats.rx_crc_errors++;
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else /* We have a packet which can be queued for the upper layers */
+ {
+
+ int len = (rmdp->mlen & 0x0fff); /* extract message length from receive buffer */
+ struct sk_buff *skb;
+
+ skb = alloc_skb(len, GFP_ATOMIC); /* allocate socket buffer */
+
+ if (skb == NULL) /* Could not get mem ? */
+ {
+
+ /*
+ * Couldn't allocate sk_buffer so we give descriptor back
+ * to Lance, update statistics and go ahead.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Reliquish ownershipt to LANCE */
+ printk("%s: Couldn't allocate sk_buff, deferring packet.\n",
+ dev->name);
+ p->stats.rx_dropped++;
+
+ break; /* Jump out */
+ }
+
+ /* Prepare sk_buff to queue for upper layers */
+
+ skb->len = len;
+ skb->dev = dev;
+
+ /*
+ * Copy data out of our receive descriptor into sk_buff.
+ *
+ * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and
+ * ignore status fields)
+ */
+
+ memcpy(skb->data, (unsigned char *) (rmdp->u.buffer & 0x00ffffff),
+ len);
+
+
+ /*
+ * Notify the upper protocol layers that there is another packet
+ * to handle
+ *
+ * netif_rx() always succeeds. see /net/inet/dev.c for more.
+ */
+
+ netif_rx(skb); /* queue packet and mark it for processing */
+
+ /*
+ * Packet is queued and marked for processing so we
+ * free our descriptor and update statistics
+ */
+
+ rmdp->u.s.status = RX_OWN;
+ p->stats.rx_packets++;
+
+
+ p->rmdnum++;
+ p->rmdnum %= RMDNUM;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+ }
+} /* End of SK_rxintr() */
+
+\f
+/*-
+ * Function : SK_close
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : close gets called from dev_close() and should
+ * deinstall the card (free_irq, mem etc).
+ *
+ * Parameters : I : struct device *dev - our device structure
+ * Return Value : 0 - closed device driver
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+/* I have tried to set BOOT_ROM on and RAM off but then, after a 'ifconfig
+ * down' the system stops. So I don't shut set card to init state.
+ */
+
+static int SK_close(struct device *dev)
+{
+
+ PRINTK(("## %s: SK_close(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ dev->tbusy = 1; /* Transmitter busy */
+ dev->start = 0; /* Card down */
+
+ printk("%s: Shuting %s down CSR0 %#06x\n", dev->name, SK_NAME,
+ (int) SK_read_reg(CSR0));
+
+ SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */
+
+ free_irq(dev->irq); /* Free IRQ */
+ irq2dev_map[dev->irq] = 0; /* Mark IRQ as unused */
+
+ return 0; /* always succed */
+
+} /* End of SK_close() */
+
+\f
+/*-
+ * Function : SK_get_stats
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Return current status structure to upper layers.
+ * It is called by sprintf_stats (dev.c).
+ *
+ * Parameters : I : struct device *dev - our device structure
+ * Return Value : struct enet_statistics * - our current statistics
+ * Errors : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static struct enet_statistics *SK_get_stats(struct device *dev)
+{
+
+ struct priv *p = (struct priv *) dev->priv;
+
+ PRINTK(("## %s: SK_get_stats(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ return &p->stats; /* Return Device status */
+
+} /* End of SK_get_stats() */
+
+#ifdef HAVE_MULTICAST
+\f
+/*-
+ * Function : set_multicast_list
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function gets called when a programm performs
+ * a SIOCSIFFLAGS call. Ifconfig does this if you call
+ * 'ifconfig [-]allmultie' which enables or disables the
+ * Promiscous mode.
+ * Promiscous mode is when the Network card accepts all
+ * packets, not only the packets which match our MAC
+ * Address. It is useful for writing a network monitor,
+ * but it is also a security problem. You have to remeber
+ * that all information on the net is not encrypted.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device Structure
+ * I : int num_addrs - explanation further down
+ * I : void *addrs -
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+
+/* Set or clear the multicast filter for SK_G16.
+ *
+ * num_addrs == -1 Promiscous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets
+ */
+
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+
+ if (num_addrs == -1)
+ {
+ /* Reinitialize LANCE with MODE_PROM set */
+ SK_lance_init(dev, MODE_PROM);
+ }
+ else if (num_addrs == 0)
+ {
+ /* Reinitialize LANCE without MODE_PROM */
+ SK_lance_init(dev, MODE_NORMAL);
+ }
+ else
+ {
+ /* Multicast with logical address filter on */
+
+ /* Not implemented yet. */
+ }
+} /* End of set_multicast_list() */
+
+#endif
+
+
+\f
+/*-
+ * Function : SK_rom_addr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/06/01
+ *
+ * Description : Try to find a Boot_ROM at all possible locations
+ *
+ * Parameters : None
+ * Return Value : Address where Boot_ROM is
+ * Errors : 0 - Did not find Boot_ROM
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+unsigned int SK_rom_addr(void)
+{
+ int i,j;
+ int rom_found = 0;
+ unsigned int rom_location[] = SK_BOOT_ROM_LOCATIONS;
+ unsigned char rom_id[] = SK_BOOT_ROM_ID;
+ unsigned char *test_byte;
+
+ /* Autodetect Boot_ROM */
+ PRINTK(("## %s: Autodetection of Boot_ROM\n", SK_NAME));
+
+ for (i = 0; (rom_location[i] != 0) && (rom_found == 0); i++)
+ {
+
+ PRINTK(("## Trying ROM location %#08x", rom_location[i]));
+
+ rom_found = 1;
+ for (j = 0; j < 6; j++)
+ {
+ test_byte = (unsigned char *) (rom_location[i]+j);
+ PRINTK((" %02x ", *test_byte));
+
+ if(!(*test_byte == rom_id[j]))
+ {
+ rom_found = 0;
+ }
+ }
+ PRINTK(("\n"));
+ }
+
+ if (rom_found == 1)
+ {
+ PRINTK(("## %s: Boot_ROM found at %#08x\n",
+ SK_NAME, rom_location[(i-1)]));
+
+ return (rom_location[--i]);
+ }
+ else
+ {
+ PRINTK(("%s: No Boot_ROM found\n", SK_NAME));
+ return 0;
+ }
+} /* End of SK_rom_addr() */
+
+
+\f
+/* LANCE access functions
+ *
+ * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set !
+ */
+
+
+/*-
+ * Function : SK_reset_board
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : This function resets SK_G16 and all components, but
+ * POS registers are not changed
+ *
+ * Parameters : None
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ *
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_reset_board(void)
+{
+ int i;
+
+ SK_PORT = 0x00; /* Reset aktiv */
+ for (i = 0; i < 10 ; i++) /* Delay min 5ms */
+ ;
+ SK_PORT = SK_RESET; /* Set back to normal operation */
+
+} /* End of SK_reset_board() */
+
+\f
+/*-
+ * Function : SK_set_RAP
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : Set LANCE Register Address Port to register
+ * for later data trasfer.
+ *
+ * Parameters : I : reg_number - which CSR to read/write from/to
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_set_RAP(int reg_number)
+{
+ SK_IOREG = reg_number;
+ SK_PORT = SK_RESET | SK_RAP | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_set_RAP() */
+
+\f
+/*-
+ * Function : SK_read_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : Set RAP and read data from a LANCE CSR register
+ *
+ * Parameters : I : reg_number - which CSR to read from
+ * Return Value : Register contents
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+int SK_read_reg(int reg_number)
+{
+ SK_set_RAP(reg_number);
+
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_read_reg() */
+
+\f
+/*-
+ * Function : SK_rread_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/28
+ *
+ * Description : Read data from preseted register.
+ * This function requires that you know which
+ * Register is actually set. Be aware that CSR1-3
+ * can only be accessed when in CSR0 STOP is set.
+ *
+ * Return Value : Register contents
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+int SK_rread_reg(void)
+{
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_rread_reg() */
+
+\f
+/*-
+ * Function : SK_write_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function sets the RAP then fills in the
+ * LANCE I/O Reg and starts Transfer to LANCE.
+ * It waits until transfer has ended which is max. 7 ms
+ * and then it returns.
+ *
+ * Parameters : I : reg_number - which CSR to write to
+ * I : value - what value to fill into register
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_write_reg(int reg_number, int value)
+{
+ SK_set_RAP(reg_number);
+
+ SK_IOREG = value;
+ SK_PORT = SK_RESET | SK_RDATA | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_write_reg */
+
+\f
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+/*-
+ * Function : SK_print_pos
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function prints out the 4 POS (Programmable
+ * Option Select) Registers. Used mainly to debug operation.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : char * - Text which will be printed as title
+ * Return Value : None
+ * Errors : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_pos(struct device *dev, char *text)
+{
+ int ioaddr = dev->base_addr;
+
+ unsigned char pos0 = inb(SK_POS0),
+ pos1 = inb(SK_POS1),
+ pos2 = inb(SK_POS2),
+ pos3 = inb(SK_POS3),
+ pos4 = inb(SK_POS4);
+
+
+ printk("## %s: %s.\n"
+ "## pos0=%#4x pos1=%#4x pos2=%#04x pos3=%#08x pos4=%#04x\n",
+ SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4);
+
+} /* End of SK_print_pos() */
+
+
+\f
+/*-
+ * Function : SK_print_dev
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function simply prints out the important fields
+ * of the device structure.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : char *text - Title for printing
+ * Return Value : None
+ * Errors : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_dev(struct device *dev, char *text)
+{
+ if (dev == NULL)
+ {
+ printk("## %s: Device Structure. %s\n", SK_NAME, text);
+ printk("## DEVICE == NULL\n");
+ }
+ else
+ {
+ printk("## %s: Device Structure. %s\n", SK_NAME, text);
+ printk("## Device Name: %s Base Address: %#06x IRQ: %d\n",
+ dev->name, dev->base_addr, dev->irq);
+
+ printk("## FLAGS: start: %d tbusy: %d int: %d\n",
+ dev->start, dev->tbusy, dev->interrupt);
+
+ printk("## next device: %#08x init function: %#08x\n",
+ (int) dev->next, (int) dev->init);
+ }
+
+} /* End of SK_print_dev() */
+
+
+\f
+/*-
+ * Function : SK_print_ram
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/06/02
+ *
+ * Description : This function is used to check how are things set up
+ * in the 16KB RAM. Also the pointers to the receive and
+ * transmit descriptor rings and rx und tx buffers locations.
+ * It contains a minor bug in printing, but has no effect to the values
+ * only newlines are not correct.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_ram(struct device *dev)
+{
+
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+
+ printk("## %s: RAM Details.\n"
+ "## RAM at %#08x tmdhead: %#08x rmdhead: %#08x initblock: %#08x\n",
+ SK_NAME,
+ (unsigned int) p->ram,
+ (unsigned int) p->tmdhead,
+ (unsigned int) p->rmdhead,
+ (unsigned int) &(p->ram)->ib);
+
+ printk("## ");
+
+ for(i = 0; i < TMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printk("\n## ");
+ }
+ printk("tmdbufs%d: %#08x ", (i+1), (int) p->tmdbufs[i]);
+ }
+ printk("## ");
+
+ for(i = 0; i < RMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printk("\n## ");
+ }
+ printk("rmdbufs%d: %#08x ", (i+1), (int) p->rmdbufs[i]);
+ }
+ printk("\n");
+
+} /* End of SK_print_ram() */
+
--- /dev/null
+/*-
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.h
+ * Version : $Revision$
+ *
+ * Author : M.Hipp (mhipp@student.uni-tuebingen.de)
+ * changes by : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : In here are all necessary definitions of
+ * the am7990 (LANCE) chip used for writing a
+ * network device driver which uses this chip
+ *
+ * $Log$
+-*/
+
+#ifndef SK_G16_H
+
+#define SK_G16_H
+
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ *
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Reciever Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+
+/*
+ * Control and Status Register 3 (CSR3) bit definitions
+ *
+ */
+
+#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */
+#define CSR3_ACON 0x0002 /* ALE Control (RW) */
+#define CSR3_BCON 0x0001 /* Byte Control (RW) */
+
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define MODE_PROM 0x8000 /* Promiscuous Mode */
+#define MODE_INTL 0x0040 /* Internal Loopback */
+#define MODE_DRTY 0x0020 /* Disable Retry */
+#define MODE_COLL 0x0010 /* Force Collision */
+#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */
+#define MODE_LOOP 0x0004 /* Loopback */
+#define MODE_DTX 0x0002 /* Disable the Transmitter */
+#define MODE_DRX 0x0001 /* Disable the Reciever */
+
+#define MODE_NORMAL 0x0000 /* Normal operation mode */
+
+/*
+ * Receive message descriptor status bit definitions.
+ */
+
+#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define RX_ERR 0x40 /* Error Summary */
+#define RX_FRAM 0x20 /* Framing Error */
+#define RX_OFLO 0x10 /* Overflow Error */
+#define RX_CRC 0x08 /* CRC Error */
+#define RX_BUFF 0x04 /* Buffer Error */
+#define RX_STP 0x02 /* Start of Packet */
+#define RX_ENP 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor status bit definitions.
+ */
+
+#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define TX_ERR 0x40 /* Error Summary */
+#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */
+#define TX_ONE 0x08 /* One retry needed to Xmit */
+#define TX_DEF 0x04 /* Deferred */
+#define TX_STP 0x02 /* Start of Packet */
+#define TX_ENP 0x01 /* End of Packet */
+
+/*
+ * Transmit status (2) (valid if TX_ERR == 1)
+ */
+
+#define TX_BUFF 0x8000 /* Buffering error (no ENP) */
+#define TX_UFLO 0x4000 /* Underflow (late memory) */
+#define TX_LCOL 0x1000 /* Late collision */
+#define TX_LCAR 0x0400 /* Loss of Carrier */
+#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */
+#define TX_TDR 0x003f /* Time-domain-reflectometer-value */
+
+
+/*
+ * Structures used for Communication with the LANCE
+ */
+
+/* LANCE Initialize Block */
+
+struct init_block
+{
+ unsigned short mode; /* Mode Register */
+ unsigned char paddr[6]; /* Physical Address (MAC) */
+ unsigned char laddr[8]; /* Logical Filter Address (not used) */
+ unsigned int rdrp; /* Receive Descriptor Ring pointer */
+ unsigned int tdrp; /* Transmit Descriptor Ring pointer */
+};
+
+
+/* Receive Message Descriptor Entry */
+
+struct rmd
+{
+ union
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ volatile short blen; /* Buffer Length (two's complement) */
+ unsigned short mlen; /* Message Byte Count */
+};
+
+
+/* Transmit Message Descriptor Entry */
+
+struct tmd
+{
+ union
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ unsigned short blen; /* Buffer Length (two's complement) */
+ unsigned volatile short status2; /* Error Status Bits */
+};
+
+#endif /* End of SK_G16_H */
* allow zero or one slots
* separate routines
* status display
+ * - Jul 1994 Dmitry Gorodchanin
+ * Fixes for memory leaks.
*
*
* This module is a difficult issue. Its clearly inet code but its also clearly
(struct cstate *)kmalloc(rslots * sizeof(struct cstate),
GFP_KERNEL);
if (! comp->rstate)
+ {
+ kfree((unsigned char *)comp);
return NULL;
+ }
memset(comp->rstate, 0, rslots * sizeof(struct cstate));
comp->rslot_limit = rslots - 1;
}
(struct cstate *)kmalloc(tslots * sizeof(struct cstate),
GFP_KERNEL);
if (! comp->tstate)
+ {
+ kfree((unsigned char *)comp->rstate);
+ kfree((unsigned char *)comp);
return NULL;
+ }
memset(comp->tstate, 0, rslots * sizeof(struct cstate));
comp->tslot_limit = tslots - 1;
}
* Alan Cox : Corrected non-IP cases of the above.
* Alan Cox : Now uses hardware type as per FvK.
* Alan Cox : Default to 192.168.0.0 (RFC 1597)
+ * A.N.Kuznetsov : dev_tint() recursion fix.
+ * Dmitry Gorodchanin : SLIP memory leaks
*
*
* FIXME: This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count);
if (actual == count) {
sl_unlock(sl);
- dev_tint(sl->dev);
+ mark_bh(NET_BH);
} else {
sl->xhead = sl->xbuff + count;
sl->xtail = sl->xbuff + actual;
tty->flags &= ~TTY_DO_WRITE_WAKEUP;
sl_unlock(sl);
- dev_tint(sl->dev);
+ mark_bh(NET_BH);
} else {
sl->xtail += answer;
}
p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
if (p == NULL) {
- kfree_s((void *)sl->dev->mem_start,l+4);
+ kfree((unsigned char *)sl->dev->mem_start);
return(-ENOMEM);
}
sl->dev->rmem_start = (unsigned long) p;
p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
if (p == NULL) {
kfree((unsigned char *)sl->dev->mem_start);
+ kfree((unsigned char *)sl->dev->rmem_start);
return(-ENOMEM);
}
sl->cbuff = p;
if (sl->slcomp == NULL) {
kfree((unsigned char *)sl->dev->mem_start);
kfree((unsigned char *)sl->dev->rmem_start);
- kfree(sl->cbuff);
+ kfree((unsigned char *)sl->cbuff);
return(-ENOMEM);
}
#endif
--- /dev/null
+/* znet.c: An Zenith Z-Note ethernet driver for linux. */
+
+static char *version = "znet.c:v1.01 7/1/94 becker@cesdis.gsfc.nasa.gov\n";
+
+/*
+ Written by Donald Becker.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov.
+ This driver is based on the Linux skeleton driver. The copyright of the
+ skeleton driver is held by the United States Government, as represented
+ by DIRNSA, and it is released under the GPL.
+
+ Thanks to Mike Hollick for alpha testing and suggestions.
+
+ References:
+ The Crynwr packet driver.
+
+ "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
+ Intel Microcommunications Databook, Vol. 1, 1990.
+ As usual with Intel, the documentation is incomplete and inaccurate.
+ I had to read the Crynwr packet driver to figure out how to actually
+ use the i82593, and guess at what register bits matched the loosely
+ related i82586.
+
+ Theory of Operation
+
+ The i82593 used in the Zenith Z-Note series operates using two(!) slave
+ DMA channels, one interrupt, and one 8-bit I/O port.
+
+ While there several ways to configure '593 DMA system, I chose the one
+ that seemed commesurate with the highest system performance in the face
+ of moderate interrupt latency: Both DMA channels are configued as
+ recirculating ring buffers, with one channel (#0) dedicated to Rx and
+ the other channel (#1) to Tx and configuration. (Note that this is
+ different than the Crynwr driver, where the Tx DMA channel is initialized
+ before each operation. That approach simplifies operation and Tx error
+ recovery, but requires additional I/O in normal operation and precludes
+ transmit buffer chaining.)
+
+ Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides
+ a reasonable ring size for Rx, while simplifying DMA buffer allocation --
+ DMA buffers must not cross a 128K boundary. (In truth the size selection
+ was influenced by my lack of '593 documentation. I thus was constrained
+ to use the Crynwr '593 initialization table, which sets the Rx ring size
+ to 8K.)
+
+ Despite my usual low opinion about Intel-designed parts, I must admit
+ that the bulk data handling of the i82593 is a good design for
+ an integrated system, like a laptop, where using two slave DMA channels
+ doesn't pose a problem. I still take issue with using only a single I/O
+ port. In the same controlled environment there are essentially no
+ limitations on I/O space, and using multiple locations would eliminate
+ the need for multiple operations when looking at status registers,
+ setting the Rx ring boundary, or switching to promiscuous mode.
+
+ I also question Zenith's selection of the '593: one of the advertised
+ advantages of earlier Intel parts was that if you figured out the magic
+ initialization incantation you could use the same part on many different
+ network types. Zenith's use of the "FriendlyNet" (sic) connector rather
+ than an on-board transceiver leads me to believe that they were planning
+ to take advantage of this. But, uhmmm, the '593 omits all but ethernet
+ functionality from the serial subsystem.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#ifndef ZNET_DEBUG
+#define ZNET_DEBUG 3
+#endif
+static unsigned int znet_debug = ZNET_DEBUG;
+
+/* The DMA modes we need aren't in <dma.h>. */
+#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */
+#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+#define DMA_BUF_SIZE 8192
+#define RX_BUF_SIZE 8192
+#define TX_BUF_SIZE 8192
+
+/* Commands to the i82593 channel 0. */
+#define CMD0_CHNL_0 0x00
+#define CMD0_CHNL_1 0x10 /* Switch to channel 1. */
+#define CMD0_NOP (CMD0_CHNL_0)
+#define CMD0_PORT_1 CMD0_CHNL_1
+#define CMD1_PORT_0 1
+#define CMD0_IA_SETUP 1
+#define CMD0_CONFIGURE 2
+#define CMD0_MULTICAST_LIST 3
+#define CMD0_TRANSMIT 4
+#define CMD0_DUMP 6
+#define CMD0_DIAGNOSE 7
+#define CMD0_Rx_ENABLE 8
+#define CMD0_Rx_DISABLE 10
+#define CMD0_Rx_STOP 11
+#define CMD0_RETRANSMIT 12
+#define CMD0_ABORT 13
+#define CMD0_RESET 14
+
+#define CMD0_ACK 0x80
+
+#define CMD0_STAT0 (0 << 5)
+#define CMD0_STAT1 (1 << 5)
+#define CMD0_STAT2 (2 << 5)
+#define CMD0_STAT3 (3 << 5)
+
+#define net_local znet_private
+struct znet_private {
+ int rx_dma, tx_dma;
+ struct enet_statistics stats;
+ /* The starting, current, and end pointers for the packet buffers. */
+ ushort *rx_start, *rx_cur, *rx_end;
+ ushort *tx_start, *tx_cur, *tx_end;
+ ushort tx_buf_len; /* Tx buffer lenght, in words. */
+};
+
+/* Only one can be built-in;-> */
+static struct znet_private zn;
+static ushort dma_buffer1[DMA_BUF_SIZE/2];
+static ushort dma_buffer2[DMA_BUF_SIZE/2];
+static ushort dma_buffer3[DMA_BUF_SIZE/2 + 8];
+
+/* The configuration block. What an undocumented nightmare. The first
+ set of values are those suggested (without explaination) for ethernet
+ in the Intel 82586 databook. The rest appear to be completely undocumented,
+ except for cryptic notes in the Crynwr packet driver. This driver uses
+ the Crynwr values verbatim. */
+
+static unsigned char i593_init[] = {
+ 0xAA, /* 0: 16-byte input & 80-byte output FIFO. */
+ /* threshhold, 96-byte FIFO, 82593 mode. */
+ 0x88, /* 1: Continuous w/interrupts, 128-clock DMA.*/
+ 0x2E, /* 2: 8-byte preamble, NO address insertion, */
+ /* 6-byte Ethernet address, loopback off.*/
+ 0x00, /* 3: Default priorities & backoff methods. */
+ 0x60, /* 4: 96-bit interframe spacing. */
+ 0x00, /* 5: 512-bit slot time (low-order). */
+ 0xF2, /* 6: Slot time (high-order), 15 COLL retries. */
+ 0x00, /* 7: Promisc-off, broadcast-on, default CRC. */
+ 0x00, /* 8: Default carrier-sense, collision-detect. */
+ 0x40, /* 9: 64-byte minimum frame length. */
+ 0x5F, /* A: Type/length checks OFF, no CRC input,
+ "jabber" termination, etc. */
+ 0x00, /* B: Full-duplex disabled. */
+ 0x3F, /* C: Default multicast addresses & backoff. */
+ 0x07, /* D: Default IFS retriggering. */
+ 0x31, /* E: Internal retransmit, drop "runt" packets,
+ synchr. DRQ deassertion, 6 status bytes. */
+ 0x22, /* F: Receive ring-buffer size (8K),
+ receive-stop register enable. */
+};
+
+struct netidblk {
+ char magic[8]; /* The magic number (string) "NETIDBLK" */
+ unsigned char netid[8]; /* The physical station address */
+ char nettype, globalopt;
+ char vendor[8]; /* The machine vendor and product name. */
+ char product[8];
+ char irq1, irq2; /* Interrupts, only one is currently used. */
+ char dma1, dma2;
+ short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */
+ short iobase1, iosize1;
+ short iobase2, iosize2; /* Second iobase unused. */
+ char driver_options; /* Misc. bits */
+ char pad;
+};
+
+int znet_probe(struct device *dev);
+static int znet_open(struct device *dev);
+static int znet_send_packet(struct sk_buff *skb, struct device *dev);
+static void znet_interrupt(int reg_ptr);
+static void znet_rx(struct device *dev);
+static int znet_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+static void hardware_init(struct device *dev);
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
+
+#ifdef notdef
+static struct sigaction znet_sigaction = { &znet_interrupt, 0, 0, NULL, };
+#endif
+
+\f
+/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe
+ BIOS area. We just scan for the signature, and pull the vital parameters
+ out of the structure. */
+
+int znet_probe(struct device *dev)
+{
+ int i;
+ struct netidblk *netinfo;
+ char *p;
+
+ /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
+ for(p = (char *)0xf0000; p < (char *)0x100000; p++)
+ if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0)
+ break;
+
+ if (p >= (char *)0x100000) {
+ if (znet_debug > 1)
+ printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
+ return ENODEV;
+ }
+ netinfo = (struct netidblk *)p;
+ dev->base_addr = netinfo->iobase1;
+ dev->irq = netinfo->irq1;
+
+ printk(KERN_INFO "%s: ZNET at %#3x,", dev->name, dev->base_addr);
+
+ /* The station address is in the "netidblk" at 0x0f0000. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = netinfo->netid[i]);
+
+ printk(", using IRQ %d DMA %d and %d.\n", dev->irq, netinfo->dma1,
+ netinfo->dma2);
+
+ if (znet_debug > 1) {
+ printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
+ dev->name, netinfo->vendor,
+ netinfo->irq1, netinfo->irq2,
+ netinfo->dma1, netinfo->dma2);
+ printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
+ dev->name, netinfo->iobase1, netinfo->iosize1,
+ netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
+ }
+
+ if (znet_debug > 0)
+ printk("%s%s", KERN_INFO, version);
+
+ dev->priv = (void *) &zn;
+ zn.rx_dma = netinfo->dma1;
+ zn.tx_dma = netinfo->dma2;
+
+ /* These should never fail. You can't add devices to a sealed box! */
+ if (request_irq(dev->irq, &znet_interrupt)
+ || request_dma(zn.rx_dma)
+ || request_dma(zn.tx_dma)) {
+ printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
+ return EBUSY;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Allocate buffer memory. We can cross a 128K boundary, so we
+ must be careful about the allocation. It's easiest to waste 8K. */
+ if (dma_page_eq(dma_buffer1, &dma_buffer1[RX_BUF_SIZE/2-1]))
+ zn.rx_start = dma_buffer1;
+ else
+ zn.rx_start = dma_buffer2;
+
+ if (dma_page_eq(dma_buffer3, &dma_buffer3[RX_BUF_SIZE/2-1]))
+ zn.tx_start = dma_buffer3;
+ else
+ zn.tx_start = dma_buffer2;
+ zn.rx_end = zn.rx_start + RX_BUF_SIZE/2;
+ zn.tx_buf_len = TX_BUF_SIZE/2;
+ zn.tx_end = zn.tx_start + zn.tx_buf_len;
+
+ /* The ZNET-specific entries in the device structure. */
+ dev->open = &znet_open;
+ dev->hard_start_xmit = &znet_send_packet;
+ dev->stop = &znet_close;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the 'dev' with ethernet-generic values. */
+ ether_setup(dev);
+
+ return 0;
+}
+
+\f
+static int znet_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 2)
+ printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name);
+
+ /* Turn on the 82501 SIA, using zenith-specific magic. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) | 0x84, 0xe7); /* Turn on LAN power (bit 2). */
+ /* According to the Crynwr driver we should wait 50 msec. for the
+ LAN clock to stabilize. My experiments indicates that the '593 can
+ be initialized immediately. The delay is probably needed for the
+ DC-to-DC converter to come up to full voltage, and for the oscillator
+ to be spot-on at 20Mhz before transmitting.
+ Until this proves to be a problem we rely on the higher layers for the
+ delay and save allocating a timer entry. */
+
+ /* This follows the packet driver's lead, and checks for success. */
+ if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
+ printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n",
+ dev->name);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ hardware_init(dev);
+ dev->start = 1;
+
+ return 0;
+}
+
+static int znet_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: ZNet_send_packet(%d).\n", dev->name, dev->tbusy);
+
+ /* Transmitter timeout, likely just recovery after suspending the machine. */
+ if (dev->tbusy) {
+ ushort event, tx_status, rx_offset, state;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ outb(CMD0_STAT0, ioaddr); event = inb(ioaddr);
+ outb(CMD0_STAT1, ioaddr); tx_status = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr); rx_offset = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr); state = inb(ioaddr);
+ printk(KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
+ " resetting.\n", dev->name, event, tx_status, rx_offset, state);
+ if (tx_status == 0x0400)
+ printk(KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n",
+ dev->name);
+ outb(CMD0_RESET, ioaddr);
+ hardware_init(dev);
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Check that the part hasn't reset itself, probably from suspend. */
+ outb(CMD0_STAT0, ioaddr);
+ if (inw(ioaddr) == 0x0010
+ && inw(ioaddr) == 0x0000
+ && inw(ioaddr) == 0x0010)
+ hardware_init(dev);
+
+ /* 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 (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = (void *)(skb+1);
+ ushort *tx_link = zn.tx_cur - 1;
+ ushort rnd_len = (length + 1)>>1;
+
+ {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ addr <<= 1;
+ if (((int)zn.tx_cur & 0x1ffff) != addr)
+ printk(KERN_WARNING "Address mismatch at Tx: %#x vs %#x.\n",
+ (int)zn.tx_cur & 0xffff, addr);
+ zn.tx_cur = (ushort *)(((int)zn.tx_cur & 0xfe0000) | addr);
+ }
+
+ if (zn.tx_cur >= zn.tx_end)
+ zn.tx_cur = zn.tx_start;
+ *zn.tx_cur++ = length;
+ if (zn.tx_cur + rnd_len + 1 > zn.tx_end) {
+ int semi_cnt = (zn.tx_end - zn.tx_cur)<<1; /* Cvrt to byte cnt. */
+ memcpy(zn.tx_cur, buf, semi_cnt);
+ rnd_len -= semi_cnt>>1;
+ memcpy(zn.tx_start, buf + semi_cnt, length - semi_cnt);
+ zn.tx_cur = zn.tx_start + rnd_len;
+ } else {
+ memcpy(zn.tx_cur, buf, skb->len);
+ zn.tx_cur += rnd_len;
+ }
+ *zn.tx_cur++ = 0;
+ cli(); {
+ *tx_link = CMD0_TRANSMIT + CMD0_CHNL_1;
+ /* Is this always safe to do? */
+ outb(CMD0_TRANSMIT + CMD0_CHNL_1,ioaddr);
+ } sti();
+
+ dev->trans_start = jiffies;
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length);
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+/* The ZNET interrupt handler. */
+static void znet_interrupt(int reg_ptr)
+{
+ int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
+ struct device *dev = irq2dev_map[irq];
+ int ioaddr;
+ int boguscnt = 20;
+
+ if (dev == NULL) {
+ printk(KERN_WARNING "znet_interrupt(): IRQ %d for unknown device.\n", irq);
+ return;
+ }
+
+ dev->interrupt = 1;
+ ioaddr = dev->base_addr;
+
+ outb(CMD0_STAT0, ioaddr);
+ do {
+ ushort status = inb(ioaddr);
+ if (znet_debug > 5) {
+ ushort result, rx_ptr, running;
+ outb(CMD0_STAT1, ioaddr);
+ result = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr);
+ rx_ptr = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr);
+ running = inb(ioaddr);
+ printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
+ dev->name, status, result, rx_ptr, running, boguscnt);
+ }
+ if ((status & 0x80) == 0)
+ break;
+
+ if ((status & 0x0F) == 4) { /* Transmit done. */
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int tx_status;
+ outb(CMD0_STAT1, ioaddr);
+ tx_status = inw(ioaddr);
+ /* It's undocumented, but tx_status seems to match the i82586. */
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ } else {
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ /* ...and the catch-all. */
+ if (tx_status | 0x0760 != 0x0760)
+ lp->stats.tx_errors++;
+ }
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+
+ if ((status & 0x40)
+ || (status & 0x0f) == 11) {
+ znet_rx(dev);
+ }
+ /* Clear the interrupts we've handled. */
+ outb(CMD0_ACK,ioaddr);
+ } while (boguscnt--);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static void znet_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 1;
+ short next_frame_end_offset = 0; /* Offset of next frame start. */
+ short *cur_frame_end;
+ short cur_frame_end_offset;
+
+ outb(CMD0_STAT2, ioaddr);
+ cur_frame_end_offset = inw(ioaddr);
+
+ if (cur_frame_end_offset == zn.rx_cur - zn.rx_start) {
+ printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n",
+ dev->name, cur_frame_end_offset);
+ return;
+ }
+
+ /* Use same method as the Crynwr driver: construct a forward list in
+ the same area of the backwards links we now have. This allows us to
+ pass packets to the upper layers in the order they were received --
+ important for fast-path sequential operations. */
+ while (zn.rx_start + cur_frame_end_offset != zn.rx_cur
+ && ++boguscount < 5) {
+ unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
+ int count, status;
+
+ if (cur_frame_end_offset < 4) {
+ /* Oh no, we have a special case: the frame trailer wraps around
+ the end of the ring buffer. We've saved space at the end of
+ the ring buffer for just this problem. */
+ memcpy(zn.rx_end, zn.rx_start, 8);
+ cur_frame_end_offset += (RX_BUF_SIZE/2);
+ }
+ cur_frame_end = zn.rx_start + cur_frame_end_offset - 4;
+
+ lo_status = *cur_frame_end++;
+ hi_status = *cur_frame_end++;
+ status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
+ lo_cnt = *cur_frame_end++;
+ hi_cnt = *cur_frame_end++;
+ count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x"
+ " count %#x status %04x.\n",
+ cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
+ count, status);
+ cur_frame_end[-4] = status;
+ cur_frame_end[-3] = next_frame_end_offset;
+ cur_frame_end[-2] = count;
+ next_frame_end_offset = cur_frame_end_offset;
+ cur_frame_end_offset -= ((count + 1)>>1) + 3;
+ if (cur_frame_end_offset < 0)
+ cur_frame_end_offset += RX_BUF_SIZE/2;
+ };
+
+ /* Now step forward through the list. */
+ do {
+ ushort *this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ int status = this_rfp_ptr[-4];
+ int pkt_len = this_rfp_ptr[-2];
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x"
+ " next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
+ this_rfp_ptr[-3]<<1);
+ /* Once again we must assume that the i82586 docs apply. */
+ if ( ! (status & 0x2000)) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x0800) lp->stats.rx_crc_errors++;
+ if (status & 0x0400) lp->stats.rx_frame_errors++;
+ if (status & 0x0200) lp->stats.rx_over_errors++; /* Wrong. */
+ if (status & 0x0100) lp->stats.rx_fifo_errors++;
+ if (status & 0x0080) lp->stats.rx_length_errors++;
+ } else if (pkt_len > 1536) {
+ lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ int sksize = sizeof(struct sk_buff) + pkt_len;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sksize, GFP_ATOMIC);
+ if (skb == NULL) {
+ if (znet_debug)
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->mem_len = sksize;
+ skb->mem_addr = skb;
+ skb->len = pkt_len;
+ skb->dev = dev;
+
+ if (&zn.rx_cur[(pkt_len+1)>>1] > zn.rx_end) {
+ int semi_cnt = (zn.rx_end - zn.rx_cur)<<1;
+ memcpy((unsigned char *) (skb + 1), zn.rx_cur, semi_cnt);
+ memcpy((unsigned char *) (skb + 1) + semi_cnt, zn.rx_start,
+ pkt_len - semi_cnt);
+ } else {
+ memcpy((unsigned char *) (skb + 1), zn.rx_cur, pkt_len);
+ if (znet_debug > 6) {
+ unsigned int *packet = (unsigned int *) (skb + 1);
+ printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
+ packet[1], packet[2], packet[3]);
+ }
+ }
+
+#ifdef HAVE_NETIF_RX
+ netif_rx(skb);
+#else
+ skb->lock = 0;
+ if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
+ kfree_s(skb, sksize);
+ lp->stats.rx_dropped++;
+ break;
+ }
+#endif
+ lp->stats.rx_packets++;
+ }
+ zn.rx_cur = this_rfp_ptr;
+ if (zn.rx_cur >= zn.rx_end)
+ zn.rx_cur -= RX_BUF_SIZE/2;
+ update_stop_hit(ioaddr, (zn.rx_cur - zn.rx_start)<<1);
+ next_frame_end_offset = this_rfp_ptr[-3];
+ if (next_frame_end_offset == 0) /* Read all the frames? */
+ break; /* Done for now */
+ this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ } while (--boguscount);
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* The inverse routine to znet_open(). */
+static int znet_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(CMD0_RESET, ioaddr); /* CMD0_RESET */
+
+ disable_dma(zn.rx_dma);
+ disable_dma(zn.tx_dma);
+
+ free_irq(dev->irq);
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+ /* Turn off transceiver power. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) & ~0x84, 0xe7); /* Turn on LAN power (bit 2). */
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+#ifdef HAVE_MULTICAST
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ As a side effect this routine must also initialize the device parameters.
+ This is taken advantage of in open().
+
+ N.B. that we change i593_init[] in place. This (properly) makes the
+ mode change persistent, but must be changed if this code is moved to
+ a multiple adaptor environment.
+ */
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ short ioaddr = dev->base_addr;
+
+ if (num_addrs < 0) {
+ /* Enable promiscuous mode */
+ i593_init[7] &= ~3; i593_init[7] |= 1;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else if (num_addrs > 0) {
+ /* Enable accept-all-multicast mode */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else { /* Enable normal mode. */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 0;
+ }
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+#ifdef not_tested
+ if (num_addrs > 0) {
+ int addrs_len = 6*num_addrs;
+ *zn.tx_cur++ = addrs_len;
+ memcpy(zn.tx_cur, addrs, addrs_len);
+ outb(CMD0_MULTICAST_LIST+CMD0_CHNL_1, ioaddr);
+ zn.tx_cur += addrs_len>>1;
+ }
+#endif
+}
+#endif
+
+void show_dma(void)
+{
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Addr: %04x cnt:%3x...", addr<<1, get_dma_residue(zn.tx_dma));
+}
+
+/* Initialize the hardware. We have to do this when the board is open()ed
+ or when we come out of suspend mode. */
+static void hardware_init(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ zn.rx_cur = zn.rx_start;
+ zn.tx_cur = zn.tx_start;
+
+ /* Reset the chip, and start it up. */
+ outb(CMD0_RESET, ioaddr);
+
+ cli(); { /* Protect against a DMA flip-flop */
+ disable_dma(zn.rx_dma); /* reset by an interrupting task. */
+ clear_dma_ff(zn.rx_dma);
+ set_dma_mode(zn.rx_dma, DMA_RX_MODE);
+ set_dma_addr(zn.rx_dma, (unsigned int) zn.rx_start);
+ set_dma_count(zn.rx_dma, RX_BUF_SIZE);
+ enable_dma(zn.rx_dma);
+ /* Now set up the Tx channel. */
+ disable_dma(zn.tx_dma);
+ clear_dma_ff(zn.tx_dma);
+ set_dma_mode(zn.tx_dma, DMA_TX_MODE);
+ set_dma_addr(zn.tx_dma, (unsigned int) zn.tx_start);
+ set_dma_count(zn.tx_dma, zn.tx_buf_len<<1);
+ enable_dma(zn.tx_dma);
+ } sti();
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Initializing the i82593, tx buf %p... ", dev->name,
+ zn.tx_start);
+ /* Do an empty configure command, just like the Crynwr driver. This
+ resets to chip to its default values. */
+ *zn.tx_cur++ = 0;
+ *zn.tx_cur++ = 0;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = 6;
+ memcpy(zn.tx_cur, dev->dev_addr, 6);
+ zn.tx_cur += 3;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr);
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+
+ update_stop_hit(ioaddr, 8192);
+ if (znet_debug > 1) printk("enabling Rx.\n");
+ outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr);
+ dev->tbusy = 0;
+}
+
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+{
+ outb(CMD0_PORT_1, ioaddr);
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Updating stop hit with value %02x.\n",
+ (rx_stop_offset >> 6) | 0x80);
+ outb((rx_stop_offset >> 6) | 0x80, ioaddr);
+ outb(CMD1_PORT_0, ioaddr);
+}
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c znet.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
+Wed Jul 6 05:45:02 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.25 released.
+
+ * sd.c, sr.c: Use new check_media_change and revalidate
+ file_operations fields.
+
+ * st.c, st.h: Add changes from Kai Makisara, dated Jun 22.
+
+ * hosts.h: Change SG_ALL back to 0xff. Apparently soft error
+ in /dev/brain resulted in having this bumped up.
+ Change first parameter in bios_param function to be Disk * instead
+ of index into rscsi_disks.
+
+ * sd_ioctl.c: Pass pointer to rscsi_disks element instead of index
+ to array.
+
+ * sd.h: Add struct name "scsi_disk" to typedef for Scsi_Disk.
+
+ * scsi.c: Remove redundant Maxtor XT8760S from blacklist.
+ In scsi_reset, add printk when DEBUG defined.
+
+ * All low level drivers: Modify definitions of bios_param in
+ appropriate way.
+
+Thu Jun 16 10:31:59 1994 Eric Youngdale (eric@esp22)
+
+ * Linux 1.1.20 released.
+
+ * scsi_ioctl.c: Only pass down the actual number of characters
+ required to scsi_do_cmd, not the one rounded up to a even number
+ of sectors.
+
+ * ultrastor.c: Changes from Caleb Epstein for 24f cards. Support
+ larger SG lists.
+
+ * ultrastor.c: Changes from me - use scsi_register to register
+ host. Add some consistency checking,
+
Wed Jun 1 21:12:13 1994 Eric Youngdale (eric@esp22)
+ * Linux 1.1.19 released.
+
* scsi.h: Add new return code for reset() function:
SCSI_RESET_PUNT.
This file contains brief information about the SCSI tape driver.
-Last modified: Thu May 5 00:13:24 1994 by root@kai.home
+Last modified: Wed Jun 22 23:38:47 1994 by root@kai.home
BASICS
(minor equals device number) and non-rewind devices (minor is 128 +
device number) are implemented.
+By default the driver writes one filemark when the device is closed after
+writing. Two filemarks can be optionally written. In both cases end
+of data is signified by returning zero bytes for two consecutive reads.
BUFFERING
MTWEOF Write count filemarks.
MTREW Rewind tape.
MTOFFL Set device off line (often rewind plus eject).
-MTNOP Do nothing.
+MTNOP Do nothing except flush the buffers.
MTRETEN Retension tape.
MTEOM Space to end of recorded data.
MTERASE Erase tape.
MTSEEK Seek to tape block count. Uses Tandberg-compatible seek (QFA)
- for SCSI-1 drives and SCSI-2 seek for SCSI-2 drives.
+ for SCSI-1 drives and SCSI-2 seek for SCSI-2 drives. The file and
+ block numbers in the status are not valid after a seek.
MTSETBLK Set the drive block size. Setting to zero sets the drive into
variable block mode (if applicable).
MTSETDENSITY Sets the drive density code to arg. See drive
Sets the buffering options. The bits are the new states
(enabled/disabled) of the write buffering (MT_ST_BUFFER_WRITES),
asynchronous writes (MT_ST_ASYNC_WRITES), read ahead
- (MT_ST_READ_AHEAD) and debugging (MT_ST_DEBUGGING;
- (debugging must be compiled into the driver).
+ (MT_ST_READ_AHEAD), writing of two filemark (ST_TWO_FM), and
+ debugging (MT_ST_DEBUGGING ; debugging must be compiled into the
+ driver).
MT_ST_WRITE_THRESHOLD
Sets the write threshold for this device to kilobytes
specified by the lowest bits.
The current block size and the density code are stored in the field
mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
MT_ST_DENSITY_SHIFT).
+ The write protect bit in mt_gstat is set if the tape is write
+ protected.
The other fields are empty.
* General Public License for more details.
*
- * $Id: aha152x.c,v 1.0 1994/03/25 12:52:00 root Exp $
+ * $Id: aha152x.c,v 1.2 1994/07/03 12:56:36 root Exp $
*
* $Log: aha152x.c,v $
+ * Revision 1.2 1994/07/03 12:56:36 root
+ * - cleaned up debugging code
+ * - more tweaking on reset delays
+ * - updated abort/reset code (pretty untested...)
+ *
+ * Revision 1.1 1994/05/28 21:18:49 root
+ * - update for mid-level interface change (abort-reset)
+ * - delays after resets adjusted for some slow devices
+ *
* Revision 1.0 1994/03/25 12:52:00 root
* - Fixed "more data than expected" problem
* - added new BIOS signatures
**************************************************************************/
-#include "aha152x.h"
-
#include <linux/sched.h>
#include <asm/io.h>
#include "../block/blk.h"
#include "scsi.h"
+#include "sd.h" /* Reqd for biosparam definition */
#include "hosts.h"
#include "constants.h"
#include <asm/system.h>
#include <linux/wait.h>
#include <linux/ioport.h>
+#include "aha152x.h"
+
/* DEFINES */
struct sigaction sa;
int interrupt_level;
-#if defined(DEBUG_RACE)
- enter_driver("detect");
-#endif
-
if(setup_called)
{
printk("aha152x: processing commandline: ");
(int) signatures[j].sig_length);
if(!ok)
- {
-#if defined(DEBUG_RACE)
- leave_driver("(1) detect");
-#endif
- printk("failed\n");
- return 0;
- }
+ return 0;
+
printk("aha152x: BIOS test: passed, ");
#else
printk("aha152x: ");
if(i==PORT_COUNT)
{
printk("failed\n");
-#if defined(DEBUG_RACE)
- leave_driver("(2) detect");
-#endif
return 0;
}
else
/* RESET OUT */
SETBITS(SCSISEQ, SCSIRSTO );
- do_pause(5);
+ do_pause(30);
CLRBITS(SCSISEQ, SCSIRSTO );
- do_pause(10);
+ do_pause(60);
aha152x_reset(NULL);
SETPORT(SIMODE0, 0);
SETPORT(SIMODE1, 0);
-#if defined(DEBUG_RACE)
- leave_driver("(3) detect");
-#endif
-
SETBITS( DMACNTRL0, INTEN);
return 1;
}
}
sti();
+#if defined(DEBUG_RACE)
+ leave_driver("queue");
+#endif
+
return 0;
}
printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt );
#endif
+#if defined(DEBUG_ABORT)
show_queues();
+#endif
/* look for command in issue queue */
for( ptr=issue_SC, prev=NULL;
return SCSI_ABORT_SUCCESS;
}
- /* Fail abortion, if we're on the bus */
if (current_SC)
- {
+ if( TESTLO(SSTAT1, BUSFREE) ) {
+ /* fail abortion, if current command is on the bus */
sti();
return SCSI_ABORT_BUSY;
}
+ else
+ {
+ /* target entered bus free before COMMAND COMPLETE, nothing to abort */
+ sti();
+ current_SC->result = DID_ERROR << 16;
+ current_SC->done(current_SC);
+ current_SC = (Scsi_Cmnd *) NULL;
+ return SCSI_ABORT_SUCCESS;
+ }
/* look for command in disconnected queue */
for( ptr=disconnected_SC, prev=NULL;
if(prev)
prev->host_scribble = ptr->host_scribble;
else
- issue_SC = (Scsi_Cmnd *) ptr->host_scribble;
+ disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
/* set command current and initiate selection,
let the interrupt routine take care of the abortion */
/*
* Reset registers, reset a hanging bus and
- * kill active and disconnected commands
+ * kill active and disconnected commands for target w/o soft reset
*/
int aha152x_reset(Scsi_Cmnd * __unused)
{
- Scsi_Cmnd *ptr;
+ Scsi_Cmnd *ptr, *prev, *next;
aha152x_reset_ports();
printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
#endif
+#if defined( DEBUG_RESET )
show_queues();
+#endif
- /* FIXME - if the device implements soft resets, the command will still
- be running after the bus reset. In this case we should do nothing
- and let the command continue. -ERY */
-
- if(current_SC)
+ if(current_SC && !current_SC->device->soft_reset)
{
current_SC->host_scribble = NULL;
current_SC->result = DID_RESET << 16;
current_SC=NULL;
}
- while(disconnected_SC)
- {
- ptr = disconnected_SC;
- disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
- ptr->host_scribble = NULL;
- ptr->result = DID_RESET << 16;
- ptr->done(ptr);
- }
+ cli();
+ prev=NULL; ptr=disconnected_SC;
+ while(ptr)
+ {
+ if(!ptr->device->soft_reset)
+ {
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ disconnected_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ next = (Scsi_Cmnd *) ptr->host_scribble;
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->done(ptr);
+
+ ptr = next;
+ }
+ else
+ {
+ prev=ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble;
+ }
+ }
+ sti();
+
+#if defined( DEBUG_RESET )
+ printk("commands on targets w/ soft-resets:\n");
+ show_queues();
+#endif
/* RESET OUT */
SETPORT(SCSISEQ, SCSIRSTO);
- do_pause(5);
+ do_pause(30);
SETPORT(SCSISEQ, 0);
- do_pause(10);
+ do_pause(60);
- SETPORT(SIMODE0, 0 );
+ SETPORT(SIMODE0, disconnected_SC ? ENSELDI : 0 );
SETPORT(SIMODE1, issue_SC ? ENBUSFREE : 0);
SETPORT( DMACNTRL0, INTEN );
/*
* Return the "logical geometry"
*/
-int aha152x_biosparam( int size, int dev, int *info_array )
+int aha152x_biosparam(Scsi_Disk * disk, int dev, int *info_array )
{
+ int size = disk->capacity;
#if defined(DEBUG_BIOSPARAM)
printk("aha152x_biosparam: dev=%x, size=%d, ", dev, size);
#endif
#define _AHA152X_H
/*
- * $Id: aha152x.h,v 1.0 1994/03/25 12:52:00 root Exp $
+ * $Id: aha152x.h,v 1.2 1994/07/03 13:01:47 root Exp $
*/
#include "../block/blk.h"
int aha152x_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int aha152x_abort(Scsi_Cmnd *);
int aha152x_reset(Scsi_Cmnd *);
-int aha152x_biosparam(int, int, int*);
+int aha152x_biosparam(Disk *, int, int*);
/* number of queueable commands
(unless we support more than 1 cmd_per_lun this should do) */
#define AHA152X_MAXQUEUE 7
-#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.0 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.2 $"
/* Initial value of Scsi_Host entry */
#define AHA152X { /* name */ AHA152X_REVID, \
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ioport.h>
-#include <linux/config.h>
#include <linux/sched.h>
#include <asm/dma.h>
return SCSI_RESET_PUNT;
}
-#ifdef CONFIG_BLK_DEV_SD
#include "sd.h"
-#endif
-int aha1542_biosparam(int size, int dev, int * ip)
+int aha1542_biosparam(Scsi_Disk * disk, int dev, int * ip)
{
int translation_algorithm;
-#ifdef CONFIG_BLK_DEV_SD
- Scsi_Device *disk;
+ int size = disk->capacity;
- disk = rscsi_disks[MINOR(dev) >> 4].device;
- translation_algorithm = HOSTDATA(disk->host)->bios_translation;
+ translation_algorithm = HOSTDATA(disk->device->host)->bios_translation;
/* Should this be > 1024, or >= 1024? Enquiring minds want to know. */
if((size>>11) > 1024 && translation_algorithm == 2) {
/* Please verify that this is the same as what DOS returns */
ip[2] = size >> 11;
};
/* if (ip[2] >= 1024) ip[2] = 1024; */
-#endif
return 0;
}
int aha1542_abort(Scsi_Cmnd *);
const char *aha1542_info(void);
int aha1542_reset(Scsi_Cmnd *);
-int aha1542_biosparam(int, int, int*);
+int aha1542_biosparam(Disk *, int, int*);
#define AHA1542_MAILBOXES 8
#define AHA1542_SCATTER 16
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
+#include "sd.h"
#include "aha1740.h"
return SCSI_RESET_PUNT;
}
-int aha1740_biosparam(int size, int dev, int* ip)
+int aha1740_biosparam(Disk * disk, int dev, int* ip)
{
+ int size = disk->capacity;
DEB(printk("aha1740_biosparam\n"));
ip[0] = 64;
ip[1] = 32;
int aha1740_abort(Scsi_Cmnd *);
const char *aha1740_info(void);
int aha1740_reset(Scsi_Cmnd *);
-int aha1740_biosparam(int, int, int*);
+int aha1740_biosparam(Disk *, int, int*);
#define AHA1740_ECBS 32
#define AHA1740_SCATTER 16
* unfinished, questionable, or wrong.
*/
-#include <linux/config.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
-#ifdef CONFIG_BLK_DEV_SD
# include "sd.h"
-#endif
#define BUSLOGIC_PRIVATE_H /* Get the "private" stuff */
#include "buslogic.h"
return SCSI_RESET_PUNT;
}
-int buslogic_biosparam(int size, int dev, int *ip)
+int buslogic_biosparam(Disk * disk, int dev, int *ip)
{
- /* ??? This is wrong if disk is configured for > 1G mapping.
- Unfortunately, unlike UltraStor, I see know way of determining whether
- > 1G mapping has been enabled. */
-#ifdef CONFIG_BLK_DEV_SD
- int translation_algorithm;
- Scsi_Device *disk;
-
- disk = rscsi_disks[MINOR(dev) >> 4].device;
- translation_algorithm = HOSTDATA(disk->host)->bios_translation;
+ int size = disk->capacity;
+ int translation_algorithm;
+
+ /* ??? This is wrong if disk is configured for > 1G mapping.
+ Unfortunately, unlike UltraStor, I see know way of determining whether
+ > 1G mapping has been enabled. */
+
+
+ translation_algorithm = HOSTDATA(disk->device->host)->bios_translation;
/* ??? Should this be > 1024, or >= 1024? Enquiring minds want to know. */
if ((size >> 11) > 1024
&& translation_algorithm == BIOS_TRANSLATION_25563) {
}
/* if (ip[2] > 1024)
ip[2] = 1024; */
-#endif
return 0;
}
int buslogic_abort(Scsi_Cmnd *);
const char *buslogic_info(void);
int buslogic_reset(Scsi_Cmnd *);
-int buslogic_biosparam(int, int, int *);
+int buslogic_biosparam(Disk *, int, int *);
#define BUSLOGIC_CMDLUN 1 /* ??? */
**************************************************************************/
-#include <linux/config.h>
#include <linux/sched.h>
#include <asm/io.h>
#include "../block/blk.h"
return SCSI_RESET_WAKEUP;
}
-#ifdef CONFIG_BLK_DEV_SD
-
#include "sd.h"
#include "scsi_ioctl.h"
-int fdomain_16x0_biosparam( int size, int dev, int *info_array )
+int fdomain_16x0_biosparam(Scsi_Disk * disk, int dev, int *info_array )
{
int drive;
+ int size = disk->capacity;
unsigned char buf[512 + sizeof( int ) * 2];
int *sizes = (int *)buf;
unsigned char *data = (unsigned char *)(sizes + 2);
unsigned char do_read[] = { READ_6, 0, 0, 0, 1, 0 };
int retcode;
- Scsi_Device *disk;
struct drive_info {
unsigned short cylinders;
unsigned char heads;
*/
drive = MINOR(dev) / 16;
- disk = rscsi_disks[ drive ].device;
if (bios_major == 2) {
i = (struct drive_info *)( (char *)bios_base + 0x1f31 + drive * 25 );
sizes[0] = 0; /* zero bytes out */
sizes[1] = 512; /* one sector in */
memcpy( data, do_read, sizeof( do_read ) );
- retcode = kernel_scsi_ioctl( disk,
+ retcode = kernel_scsi_ioctl( disk->device,
SCSI_IOCTL_SEND_COMMAND,
(void *)buf );
if (!retcode /* SCSI command ok */
return 0;
}
-
-#endif /* CONFIG_BLK_DEV_SD */
int fdomain_16x0_reset( Scsi_Cmnd * );
int fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
-#ifdef CONFIG_BLK_DEV_SD
-int fdomain_16x0_biosparam( int, int, int * );
-#else
-#define fdomain_16x0_biosparam NULL
-#endif
+int fdomain_16x0_biosparam(Disk *, int, int * );
#define FDOMAIN_16X0 { "Future Domain TMC-16x0", \
fdomain_16x0_detect, \
*/
+/* It is senseless to set SG_ALL any higher than this - the performance
+ does not get any better, and it wastes memory */
#define SG_NONE 0
-#define SG_ALL 0x7fff
+#define SG_ALL 0xff
#define DISABLE_CLUSTERING 0
#define ENABLE_CLUSTERING 1
type of host adapter that is supported on the system.
*/
+typedef struct scsi_disk Disk;
+
typedef struct
{
/*
size, device number, list (heads, sectors, cylinders)
*/
- int (* bios_param)(int, int, int []);
+ int (* bios_param)(Disk *, int, int []);
/*
This determines if we will use a non-interrupt driven
#define AUTOPROBE_IRQ
#include "NCR5380.h"
#include "constants.h"
-
+#include "sd.h"
int scsi_irq_translate[] =
}
/*
- * Function : int pas16_biosparam(int size, int dev, int *ip)
+ * Function : int pas16_biosparam(Disk *disk, int dev, int *ip)
*
* Purpose : Generates a BIOS / DOS compatable H-C-S mapping for
* the specified device / size.
* and matching the H_C_S coordinates to what DOS uses.
*/
-int pas16_biosparam(int size, int dev, int * ip)
+int pas16_biosparam(Disk * disk, int dev, int * ip)
{
+ int size = disk->capacity;
ip[0] = 64;
ip[1] = 32;
ip[2] = size >> 11;
#ifndef ASM
int pas16_abort(Scsi_Cmnd *);
-int pas16_biosparam(int, int, int*);
+int pas16_biosparam(Disk *, int, int*);
int pas16_detect(int);
const char *pas16_info(void);
int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
{"MAXTOR","XT-4380S","B3C"}, /* Locks-up when LUN>0 polled. */
{"MAXTOR","MXT-1240S","I1.2"}, /* Locks up when LUN > 0 polled */
{"MAXTOR","XT-4170S","B5A"}, /* Locks-up sometimes when LUN>0 polled. */
- {"MAXTOR","XT-8760S","B6B"}, /* Locks-up when LUN > 0 is polled */
{"MAXTOR","XT-8760S","B7B"}, /* guess what? */
{"NEC","CD-ROM DRIVE:841","1.0"}, /* Locks-up when LUN>0 polled. */
{"RODIME","RO3000S","2.33"}, /* Locks up if polled for lun != 0 */
host->host_busy--;
}
+#ifdef DEBUG
+ printk("scsi reset function returned %d\n", temp);
+#endif
switch(temp) {
case SCSI_RESET_SUCCESS:
cli();
#include "scsi.h"
#include "hosts.h"
+#include "sd.h"
+
/* A few options that we want selected */
/* Do not attempt to use a timer to simulate a real disk with latency */
return 0;
}
-int scsi_debug_biosparam(int size, int dev, int* info){
+int scsi_debug_biosparam(Disk * disk, int dev, int* info){
+ int size = disk->capacity;
info[0] = 32;
info[1] = 64;
info[2] = (size + 2047) >> 11;
int scsi_debug_command(Scsi_Cmnd *);
int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int scsi_debug_abort(Scsi_Cmnd *, int);
-int scsi_debug_biosparam(int, int, int[]);
+int scsi_debug_biosparam(Disk *, int, int[]);
const char *scsi_debug_info(void);
int scsi_debug_reset(Scsi_Cmnd *);
extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+static int check_scsidisk_media_change(dev_t);
+static int fop_revalidate_scsidisk(dev_t);
+
static sd_init_onedisk(int);
static void requeue_sd_request (Scsi_Cmnd * SCpnt);
NULL, /* mmap */
sd_open, /* open code */
sd_release, /* release */
- block_fsync /* fsync */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_scsidisk_media_change, /* Disk change */
+ fop_revalidate_scsidisk /* revalidate */
};
static struct gendisk sd_gendisk = {
MAX_RETRIES);
}
-int check_scsidisk_media_change(int full_dev, int flag){
+static int check_scsidisk_media_change(dev_t full_dev){
int retval;
int target;
struct inode inode;
+ int flag = 0;
target = DEVICE_NR(MINOR(full_dev));
return 0;
}
+static int fop_revalidate_scsidisk(dev_t dev){
+ return revalidate_scsidisk(dev, 0);
+}
+
extern struct hd_struct * sd;
-typedef struct {
+typedef struct scsi_disk {
unsigned capacity; /* size in blocks */
unsigned sector_size; /* size in bytes */
Scsi_Device *device;
diskinfo[1] = 0;
diskinfo[2] = 0;
if(host->hostt->bios_param != NULL)
- host->hostt->bios_param(rscsi_disks[MINOR(dev) >> 4].capacity,
+ host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
dev,
&diskinfo[0]);
put_fs_byte(diskinfo[0],
return SCSI_RESET_PENDING;
}
-#ifdef CONFIG_BLK_DEV_SD
-
#include <asm/segment.h>
#include "sd.h"
#include "scsi_ioctl.h"
-int seagate_st0x_biosparam(int size, int dev, int* ip) {
+int seagate_st0x_biosparam(Disk * disk, int dev, int* ip) {
unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page;
int *sizes, result, formatted_sectors, total_sectors;
int cylinders, heads, sectors;
- Scsi_Device *disk;
-
- disk = rscsi_disks[MINOR(dev) >> 4].device;
-
/*
* Only SCSI-I CCS drives and later implement the necessary mode sense
* pages.
*/
- if (disk->scsi_level < 2)
+ if (disk->device->scsi_level < 2)
return -1;
sizes = (int *) buf;
data = (unsigned char *) (sizes + 2);
cmd[0] = MODE_SENSE;
- cmd[1] = (disk->lun << 5) & 0xe5;
+ cmd[1] = (disk->device->lun << 5) & 0xe5;
cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
cmd[3] = 0;
cmd[4] = 255;
memcpy (data, cmd, 6);
- if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
/*
* The mode page lies beyond the MODE SENSE header, with length 4, and
* the BLOCK DESCRIPTOR, with length header[3].
cmd[2] = 0x03; /* Read page 3, format page current values */
memcpy (data, cmd, 6);
- if (!(result = kernel_scsi_ioctl (disk, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
page = data + 4 + data[3];
sectors = (page[10] << 8) | page[11];
return result;
}
-#endif /* CONFIG_BLK_DEV_SD */
-
#endif /* defined(CONFIG_SCSI_SEGATE) */
#define NULL 0
#endif
-#ifdef CONFIG_BLK_DEV_SD
-int seagate_st0x_biosparam(int, int, int*);
-#else
-#define seagate_st0x_biosparam NULL
-#endif
+int seagate_st0x_biosparam(Disk *, int, int*);
#define SEAGATE_ST0X {"Seagate ST-01/ST-02", seagate_st0x_detect, \
seagate_st0x_info, seagate_st0x_command, \
extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
void requeue_sr_request (Scsi_Cmnd * SCpnt);
+static int check_cdrom_media_change(dev_t);
static void sr_release(struct inode * inode, struct file * file)
{
NULL, /* select */
sr_ioctl, /* ioctl */
NULL, /* mmap */
- sr_open, /* no special open code */
+ sr_open, /* special open code */
sr_release, /* release */
- NULL /* fsync */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ check_cdrom_media_change, /* Disk change */
+ NULL /* revalidate */
};
/*
* an inode for that to work, and we do not always have one.
*/
-int check_cdrom_media_change(int full_dev, int flag){
+int check_cdrom_media_change(dev_t full_dev){
int retval, target;
struct inode inode;
+ int flag = 0;
target = MINOR(full_dev);
Copyright 1992, 1993, 1994 Kai Makisara
email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
- Last modified: Wed May 4 20:29:42 1994 by root@kai.home
+ Last modified: Wed Jun 22 23:37:10 1994 by root@kai.home
*/
#include <linux/fs.h>
/* #define ST_RECOVERED_WRITE_FATAL */
+#define ST_TWO_FM 0
+
#define ST_BUFFER_WRITES 1
#define ST_ASYNC_WRITES 1
#define MAX_READY_RETRIES 5
#define NO_TAPE NOT_READY
-#define ST_TIMEOUT 9000
+#define ST_TIMEOUT 27000
#define ST_LONG_TIMEOUT 200000
static int st_nbr_buffers;
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
SCpnt->request.dev = -1;
+ if ((STp->buffer)->last_result != 0) {
+ printk("st%d: Backing over filemark failed.\n", dev);
+ (STp->mt_status)->mt_fileno += 1;
+ (STp->mt_status)->mt_blkno = 0;
+ }
return (STp->buffer)->last_result_fatal;
}
(STp->buffer)->buffer_blocks);
#endif
- if ((STp->buffer)->b_data[2] & 0x80) {
+ STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
+ if (STp->drv_write_prot) {
STp->write_prot = 1;
#ifdef DEBUG
if (debugging)
SCpnt->sense_buffer[0] = 0;
memset(cmd, 0, 10);
cmd[0] = WRITE_FILEMARKS;
- cmd[4] = 1;
+ cmd[4] = 1 + STp->two_fm;
SCpnt->request.dev = dev;
scsi_do_cmd( SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data,
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
- if ((STp->buffer)->last_result_fatal != 0)
+ if ((STp->buffer)->last_result_fatal != 0) {
+ SCpnt->request.dev = -1; /* Mark as not busy */
printk("st%d: Error on write filemark.\n", dev);
+ }
else {
+ SCpnt->request.dev = -1; /* Mark as not busy */
(STp->mt_status)->mt_fileno++ ;
STp->drv_block = 0;
+ if (STp->two_fm)
+ back_over_eof(dev);
}
- SCpnt->request.dev = -1; /* Mark as not busy */
}
#ifdef DEBUG
if (debugging)
- printk("st%d: Buffer flushed, EOF written\n", dev);
+ printk("st%d: Buffer flushed, %d EOF(s) written\n", dev, cmd[4]);
#endif
}
else if (!rewind) {
STp->rw = ST_WRITING;
}
+ if (STp->moves_after_eof < 255)
+ STp->moves_after_eof++;
+
if ((STp->buffer)->writing) {
write_behind_check(dev);
if ((STp->buffer)->last_result_fatal) {
return transfer;
STp->rw = ST_READING;
}
+ if (STp->moves_after_eof < 255)
+ STp->moves_after_eof++;
#ifdef DEBUG
if (debugging && STp->eof != ST_NOEOF)
"st%d: EOF detected (%d bytes read, transferred %d bytes).\n",
dev, (STp->buffer)->buffer_bytes, total);
#endif
- } /* end of EOF, EOM, ILI test */
- }
+ }
+ } /* end of EOF, EOM, ILI test */
else { /* nonzero sense key */
#ifdef DEBUG
if (debugging)
STp->drv_block = (-1);
if (total)
return total;
+ else if (STp->moves_after_eof == 1 &&
+ (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
+#ifdef DEBUG
+ if (debugging)
+ printk("st%d: Zero returned for first BLANK CHECK after EOF.\n",
+ dev);
+#endif
+ return 0; /* First BLANK_CHECK after EOF */
+ }
else
return -EIO;
}
- }
+ } /* End of extended sense test */
else {
transfer = (STp->buffer)->last_result_fatal;
SCpnt->request.dev = -1; /* Mark as not busy */
return transfer;
}
- }
+ } /* End of error handling */
else /* Read successful */
(STp->buffer)->buffer_bytes = bytes;
if (total == 0 && STp->eof == ST_FM) {
STp->eof = 0;
STp->drv_block = 0;
+ if (STp->moves_after_eof > 1)
+ STp->moves_after_eof = 0;
(STp->mt_status)->mt_fileno++;
}
if (total == 0 && STp->eof == ST_EOM_OK)
STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
STp->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
STp->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
+ STp->two_fm = (options & MT_ST_TWO_FM) != 0;
#ifdef DEBUG
debugging = (options & MT_ST_DEBUGGING) != 0;
printk(
"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
dev, STp->do_buffer_writes, STp->do_async_writes,
STp->do_read_ahead);
- printk(" debugging: %d\n", debugging);
+ printk(" two FMs: %d, debugging: %d\n", STp->two_fm,
+ debugging);
#endif
}
else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
SCpnt->request.dev = -1; /* Mark as not busy */
if (!ioctl_result) {
- STp->drv_block = blkno;
- (STp->mt_status)->mt_fileno = fileno;
+ if (cmd_in != MTSEEK) {
+ STp->drv_block = blkno;
+ (STp->mt_status)->mt_fileno = fileno;
+ }
+ else
+ STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
+ if (cmd_in == MTFSF)
+ STp->moves_after_eof = 0;
+ else
+ STp->moves_after_eof = 1;
if (cmd_in == MTBSFM)
ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
else if (cmd_in == MTFSFM)
(STp->buffer)->read_pointer = 0;
}
else if (cmd_in == MTSETDRVBUFFER)
- STp->drv_buffer = arg;
+ STp->drv_buffer = (arg & 7);
else if (cmd_in == MTSETDENSITY)
STp->density = arg;
- else if (cmd_in == MTEOM || cmd_in == MTWEOF) {
+ else if (cmd_in == MTEOM) {
STp->eof = ST_EOM_OK;
STp->eof_hit = 0;
}
memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
- i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
+ i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK ||
mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
if (i < 0)
(STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
STp->block_size - 1) / STp->block_size;
}
+ (STp->mt_status)->mt_gstat = 0;
+ if (STp->drv_write_prot)
+ (STp->mt_status)->mt_gstat |= GMT_WR_PROT(0xffffffff);
memcpy_tofs((char *)arg, (char *)(STp->mt_status),
sizeof(struct mtget));
STp->do_buffer_writes = ST_BUFFER_WRITES;
STp->do_async_writes = ST_ASYNC_WRITES;
STp->do_read_ahead = ST_READ_AHEAD;
+ STp->two_fm = ST_TWO_FM;
STp->write_threshold = st_write_threshold;
STp->drv_block = 0;
+ STp->moves_after_eof = 1;
STp->mt_status = (struct mtget *) mem_start;
mem_start += sizeof(struct mtget);
/* Initialize status */
unsigned capacity;
struct wait_queue * waiting;
Scsi_Device* device;
- unsigned dirty:1;
- unsigned rw:2;
- unsigned eof:2;
- unsigned write_prot:1;
- unsigned in_use:1;
- unsigned eof_hit:1;
- unsigned drv_buffer:3;
- unsigned do_buffer_writes:1;
- unsigned do_async_writes:1;
- unsigned do_read_ahead:1;
+ unsigned char dirty;
+ unsigned char rw;
+ unsigned char eof;
+ unsigned char write_prot;
+ unsigned char drv_write_prot;
+ unsigned char in_use;
+ unsigned char eof_hit;
+ unsigned char drv_buffer;
+ unsigned char do_buffer_writes;
+ unsigned char do_async_writes;
+ unsigned char do_read_ahead;
+ unsigned char two_fm;
unsigned char density;
ST_buffer * buffer;
int block_size;
int write_threshold;
int recover_count;
int drv_block; /* The block where the drive head is */
+ unsigned char moves_after_eof;
struct mtget * mt_status;
Scsi_Cmnd SCpnt;
} Scsi_Tape;
#define AUTOPROBE_IRQ
#include "NCR5380.h"
#include "constants.h"
-
+#include "sd.h"
}
/*
- * Function : int t128_biosparam(int size, int dev, int *ip)
+ * Function : int t128_biosparam(Disk * disk, int dev, int *ip)
*
* Purpose : Generates a BIOS / DOS compatable H-C-S mapping for
* the specified device / size.
* and matching the H_C_S coordinates to what DOS uses.
*/
-int t128_biosparam(int size, int dev, int * ip)
+int t128_biosparam(Disk * disk, int dev, int * ip)
{
+ int size = disk->capacity;
ip[0] = 64;
ip[1] = 32;
ip[2] = size >> 11;
#ifndef ASM
int t128_abort(Scsi_Cmnd *);
-int t128_biosparam(int, int, int*);
+int t128_biosparam(Disk *, int, int*);
int t128_detect(int);
const char *t128_info(void);
int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
#include "scsi.h"
#include "hosts.h"
#include "ultrastor.h"
+#include "sd.h"
#define FALSE 0
#define TRUE 1
}
-int ultrastor_biosparam(int size, int dev, int * dkinfo)
+int ultrastor_biosparam(Disk * disk, int dev, int * dkinfo)
{
+ int size = disk->capacity;
unsigned int s = config.heads * config.sectors;
dkinfo[0] = config.heads;
int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int ultrastor_abort(Scsi_Cmnd *);
int ultrastor_reset(Scsi_Cmnd *);
-int ultrastor_biosparam(int, int, int *);
+int ultrastor_biosparam(Disk *, int, int *);
#define ULTRASTOR_14F_MAX_SG 16
#define ULTRASTOR_24F_MAX_SG 33
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
+#include "sd.h"
#define ANY2SCSI_INLINE /* undef this to use old macros */
#undef DEBUG
* this way, so I think it will work OK. Someone who is ambitious can
* borrow a newer or more complete version from another driver.
*/
-int wd7000_biosparam(int size, int dev, int* ip)
+int wd7000_biosparam(Disk * disk, int dev, int* ip)
{
+ int size = disk->capacity;
ip[0] = 64;
ip[1] = 32;
ip[2] = size >> 11;
int wd7000_abort(Scsi_Cmnd *);
const char *wd7000_info(void);
int wd7000_reset(Scsi_Cmnd *);
-int wd7000_biosparam(int, int, int*);
+int wd7000_biosparam(Disk *, int, int*);
#ifndef NULL
#define NULL 0L
wait_on_buffer(bh);
if (bh->b_dev == dev && bh->b_size != size) {
- bh->b_uptodate = bh->b_dirt =
+ bh->b_uptodate = bh->b_dirt = bh->b_req =
bh->b_flushtime = 0;
};
remove_from_hash_queue(bh);
#include <linux/fcntl.h>
#include <linux/errno.h>
+#include <linux/config.h>
+
/*
* Ugly. We'll fix this once all the drivers use the f_ops->check_media_change()
* stuff instead..
*/
-#ifdef CONFIG_SCSI
-#ifdef CONFIG_BLK_DEV_SR
-extern int check_cdrom_media_change(int, int);
-#endif
-#ifdef CONFIG_BLK_DEV_SD
-extern int check_scsidisk_media_change(int, int);
-extern int revalidate_scsidisk(int, int);
-#endif
-#endif
-#ifdef CONFIG_CDU31A
-extern int check_cdu31a_media_change(int, int);
-#endif
#ifdef CONFIG_MCD
extern int check_mcd_media_change(int, int);
#endif
* People changing diskettes in the middle of an operation deserve
* to loose :-)
*/
-void check_disk_change(dev_t dev)
+int check_disk_change(dev_t dev)
{
int i;
struct file_operations * fops;
i = MAJOR(dev);
if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL)
- return;
+ return 0;
if (fops->check_media_change != NULL) {
if (!fops->check_media_change(dev))
- return;
+ return 0;
}
#if 1 /* this will go soon.. */
else switch(MAJOR(dev)){
-#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
- case SCSI_DISK_MAJOR:
- if (!check_scsidisk_media_change(dev, 0))
- return;
- break;
-#endif
-
-#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
- case SCSI_CDROM_MAJOR:
- if (!check_cdrom_media_change(dev, 0))
- return;
- break;
-#endif
-
-#if defined(CONFIG_CDU31A)
- case CDU31A_CDROM_MAJOR:
- if (!check_cdu31a_media_change(dev, 0))
- return;
- break;
-#endif
#if defined(CONFIG_MCD)
case MITSUMI_CDROM_MAJOR:
if (!check_mcd_media_change(dev, 0))
- return;
+ return 0;
break;
#endif
#if defined(CONFIG_SBPCD)
case MATSUSHITA_CDROM_MAJOR:
if (!check_sbpcd_media_change(dev, 0))
- return;
+ return 0;
break;
#endif
default:
- return;
+ return 0;
}
#endif /* will go away */
if (fops->revalidate)
fops->revalidate(dev);
-
-#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
-/* This is trickier for a removable hardisk, because we have to invalidate
- all of the partitions that lie on the disk. */
- if (MAJOR(dev) == SCSI_DISK_MAJOR)
- revalidate_scsidisk(dev, 0);
-#endif
+ return 1;
}
/*
printk("get root inode failed\n");
return NULL;
}
-#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
- if (MAJOR(s->s_dev) == SCSI_CDROM_MAJOR) {
- /* Check this one more time. */
- if(check_cdrom_media_change(s->s_dev, 0))
- goto out;
- }
-#endif
-#if defined(CONFIG_CDU31A)
- if (MAJOR(s->s_dev) == CDU31A_CDROM_MAJOR) {
- /* Check this one more time. */
- if(check_cdu31a_media_change(s->s_dev, 0))
- goto out;
- }
-#endif
-#if defined(CONFIG_MCD)
- if (MAJOR(s->s_dev) == MITSUMI_CDROM_MAJOR) {
- /* Check this one more time. */
- if(check_mcd_media_change(s->s_dev, 0))
- goto out;
- }
-#endif
-#if defined(CONFIG_SBPCD)
- if (MAJOR(s->s_dev) == MATSUSHITA_CDROM_MAJOR) {
- if (check_sbpcd_media_change(s->s_dev,0))
- goto out;
- };
-#endif CONFIG_SBPCD
- return s;
+ if(!check_disk_change(s->s_dev)) return s;
out: /* Kick out for various error conditions */
brelse(bh);
s->s_dev = 0;
if (!filp->f_inode) /* just in case */
return 0;
- if (!S_ISREG(filp->f_inode->i_mode))
- return 0;
if (l->l_type != F_UNLCK && l->l_type != F_RDLCK && l->l_type != F_WRLCK
&& l->l_type != F_SHLCK && l->l_type != F_EXLCK)
return 0;
if (! added) {
if (caller->fl_type == F_UNLCK)
- return -EINVAL;
+ return 0;
if (! (caller = alloc_lock(before, caller, fd)))
return -ENOLCK;
}
* Copyright (C) 1992, 1993 Rick Sladkey
*
* low-level nfs remote procedure call interface
+ *
+ * FIXES
+ *
+ * 2/7/94 James Bottomley and Jon Peatfield DAMTP, Cambridge University
+ *
+ * An xid mismatch no longer causes the request to be trashed.
+ *
*/
#include <linux/config.h>
#include <asm/segment.h>
#include <linux/in.h>
#include <linux/net.h>
+#include <linux/mm.h>
+
+/* JEJB/JSP 2/7/94
+ * this must match the value of NFS_SLACK_SPACE in linux/fs/nfs/proc.c
+ * ***FIXME*** should probably put this in nfs_fs.h */
+#define NFS_SLACK_SPACE 1024
extern struct socket *socki_lookup(struct inode *inode);
int n;
int addrlen;
unsigned long old_mask;
+ /* JEJB/JSP 2/7/94
+ * This is for a 4 byte recv of the xid only */
+ int recv_xid;
xid = start[0];
len = ((char *) end) - ((char *) start);
current->state = TASK_INTERRUPTIBLE;
if (!select(inode, file, SEL_IN, &wait_table)
&& !select(inode, file, SEL_IN, NULL)) {
- if (timeout > max_timeout)
- timeout = max_timeout;
+ if (timeout > max_timeout) {
+ /* JEJB/JSP 2/7/94
+ * This is useful to see if the system is
+ * hanging */
+ printk("NFS max timeout reached on %s\n",
+ server_name);
+ timeout = max_timeout;
+ }
current->timeout = jiffies + timeout;
schedule();
remove_wait_queue(entry.wait_address, &entry.wait);
timeout = init_timeout;
init_timeout <<= 1;
if (!major_timeout_seen) {
- printk("NFS server %s not responding, "
- "still trying\n", server_name);
+ printk("NFS server %s not responding, "
+ "still trying\n", server_name);
}
major_timeout_seen = 1;
continue;
remove_wait_queue(entry.wait_address, &entry.wait);
current->state = TASK_RUNNING;
addrlen = 0;
- result = sock->ops->recvfrom(sock, (void *) start, PAGE_SIZE, 1, 0,
- NULL, &addrlen);
+ /* JEJB/JSP 2/7/94
+ * Get the xid from the next packet using a peek, so keep it
+ * on the recv queue. If it is wrong, it will be some reply
+ * we don't now need, so discard it */
+ result = sock->ops->recvfrom(sock, (void *)&recv_xid,
+ sizeof(recv_xid), 1, MSG_PEEK,
+ NULL, &addrlen);
if (result < 0) {
if (result == -EAGAIN) {
#if 0
}
break;
}
- if (*start == xid) {
+ if (recv_xid == xid) {
if (major_timeout_seen)
printk("NFS server %s OK\n", server_name);
break;
}
+ /* JEJB/JSP 2/7/94
+ * we have xid mismatch, so discard the packet and start
+ * again. What a hack! but I can't call recvfrom with
+ * a null buffer yet. */
+ (void)sock->ops->recvfrom(sock, (void *)&recv_xid,
+ sizeof(recv_xid), 1, 0, NULL,
+ &addrlen);
#if 0
printk("nfs_rpc_call: XID mismatch\n");
#endif
}
+ /* JEJB/JSP 2/7/94
+ *
+ * we have the correct xid, so read into the correct place and
+ * return it
+ *
+ * Here we need to know the size given to alloc, server->wsize for
+ * writes or server->rsize for reads. In practice these are the
+ * same.
+ *
+ * If they are not the same then a reply to a write request will be
+ * a small acknowledgement, so even if wsize < rsize we should never
+ * cause data to be written past the end of the buffer (unless some
+ * brain damaged implementation sends out a large write acknowledge).
+ *
+ * FIXME: I should really know how big a packet was alloc'd --
+ * should pass it to do_nfs_rpc. */
+ result=sock->ops->recvfrom(sock, (void *)start,
+ server->rsize + NFS_SLACK_SPACE, 1, 0, NULL,
+ &addrlen);
current->blocked = old_mask;
set_fs(fs);
return result;
return 0;
}
inode = filp->f_inode;
- if (inode && S_ISREG(inode->i_mode))
+ if (inode)
fcntl_remove_locks(current, filp, fd);
if (filp->f_count > 1) {
filp->f_count--;
}
-extern void check_disk_change(dev_t dev);
+extern int check_disk_change(dev_t dev);
extern void invalidate_inodes(dev_t dev);
extern void invalidate_buffers(dev_t dev);
extern void sync_inodes(dev_t dev);
#define MCD_INTR_NR 10
/* Increase this if you get lots of timeouts */
-#define MCD_STATUS_DELAY 100
+#define MCD_STATUS_DELAY 200
/* number of times to retry a command before giving up */
#define MCD_RETRY_ATTEMPTS 5
#define MT_ST_ASYNC_WRITES 0x2
#define MT_ST_READ_AHEAD 0x4
#define MT_ST_DEBUGGING 0x8
+#define MT_ST_TWO_FM 0x10
#endif /* _LINUX_MTIO_H */
#include <linux/sys.h>
#include <linux/utsname.h>
#include <linux/interrupt.h>
+#include <linux/timer.h>
#include <linux/binfmts.h>
#include <linux/personality.h>
#include <linux/module.h>
X(disable_irq),
X(bh_active),
X(bh_mask),
+ X(add_timer),
+ X(del_timer),
/* dma handling */
X(request_dma),
X(xtime),
/* misc */
+ X(panic),
X(printk),
X(sprintf),
X(vsprintf),
* Florian La Roche, <flla@stud.uni-sb.de>
* Alan Cox, <A.Cox@swansea.ac.uk>
*
+ * Changes (see also sock.c)
+ *
+ * A.N.Kuznetsov : Socket death error in accept().
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
{
err = -sk2->err;
sk2->err=0;
+ sk2->dead=1; /* ANK */
destroy_sock(sk2);
newsock->data = NULL;
return(err);
skb2->free = 1;
/* Build Layer 2-3 headers for message back to source */
- offset = ip_build_header(skb2, daddr, saddr, &ndev,
+ offset = ip_build_header(skb2, daddr, dev->pa_addr, &ndev,
IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
if (offset < 0)
{
icmp_statistics.IcmpInMsgs++;
- if (ip_chk_addr(daddr) == IS_BROADCAST)
- {
- icmp_statistics.IcmpInErrors++;
- kfree_skb(skb1, FREE_READ);
- return(0);
- }
/*
* Grab the packet as an icmp object
* Parse the ICMP message
*/
+ if (ip_chk_addr(daddr) == IS_BROADCAST && icmph->type != ICMP_ECHO)
+ {
+ icmp_statistics.IcmpInErrors++;
+ kfree_skb(skb1, FREE_READ);
+ return(0);
+ }
+
switch(icmph->type)
{
case ICMP_TIME_EXCEEDED: