patch-1.3.86 linux/drivers/net/wic.c
Next file: linux/drivers/scsi/Config.in
Previous file: linux/drivers/net/eepro.c
Back to the patch index
Back to the overall index
- Lines: 1455
- Date:
Tue Apr 9 14:36:30 1996
- Orig file:
v1.3.85/linux/drivers/net/wic.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.85/linux/drivers/net/wic.c linux/drivers/net/wic.c
@@ -0,0 +1,1454 @@
+/* $Id: wic.c,v 1.0 1995/02/11 10:26:05 hayes Exp $ */
+/* WIC: A parallel port "network" driver for Linux. */
+/* based on the plip network driver */
+
+char *version = "NET3 WIC version 0.9 hayes@netplumbing.com";
+
+/*
+ Sources:
+ Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
+ "parallel.asm" parallel port packet driver and from the plip.c
+ parallel networking linux driver from the 1.2.13 Linux
+ distribution.
+
+ The packet is encapsulated as if it were ethernet.
+
+*/
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/if_ether.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/lp.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_wic.h>
+
+#include <linux/tqueue.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <string.h>
+
+#define NET_DEBUG 1
+/* Use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+unsigned int net_debug = NET_DEBUG;
+
+/* Connection time out = WIC_TRIGGER_WAIT * WIC_DELAY_UNIT usec */
+#define WIC_TRIGGER_WAIT 500
+
+/* Nibble time out = WIC_NIBBLE_WAIT * WIC_DELAY_UNIT usec */
+#define WIC_NIBBLE_WAIT 3000
+
+#define PAR_DATA(dev) ((dev)->base_addr+0)
+#define PAR_STATUS(dev) ((dev)->base_addr+1)
+#define PAR_CONTROL(dev) ((dev)->base_addr+2)
+
+/* Bottom halfs */
+void wic_kick_bh(struct device *dev);
+void wic_bh(struct device *dev);
+
+/* Interrupt handler */
+void wic_interrupt(int irq, void *dev_ptr, struct pt_regs *regs);
+
+/* Functions for DEV methods */
+int wic_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
+int wic_tx_packet(struct sk_buff *skb, struct device *dev);
+int wic_open(struct device *dev);
+int wic_close(struct device *dev);
+struct enet_statistics *wic_get_stats(struct device *dev);
+int wic_config(struct device *dev, struct ifmap *map);
+int wic_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+int send_cmd(struct device *dev, unsigned char *cmd, char len);
+int recv_cmd_resp(struct device *dev, unsigned char *cmd);
+int send_byte(struct device *dev, unsigned char c);
+int get_byte(struct device *dev, unsigned char *c);
+int ack_resp(struct device *dev);
+int check_bfr(struct device *dev);
+void wic_reset(struct device *dev);
+void wic_set_multicast_list(struct device *dev, int num_addrs,
+ void *addrs);
+
+#define LOOPCNT 30000
+unsigned char tog = 3;
+unsigned char save = 0;
+
+enum wic_connection_state {
+ WIC_CN_NONE=0,
+ WIC_CN_RECEIVE,
+ WIC_CN_SEND,
+ WIC_CN_CLOSING,
+ WIC_CN_ERROR
+};
+
+enum wic_packet_state {
+ WIC_PK_DONE=0,
+ WIC_PK_TRIGGER,
+ WIC_PK_LENGTH_LSB,
+ WIC_PK_LENGTH_MSB,
+ WIC_PK_DATA,
+ WIC_PK_CHECKSUM
+};
+
+enum wic_nibble_state {
+ WIC_NB_BEGIN,
+ WIC_NB_1,
+ WIC_NB_2,
+};
+
+struct wic_local {
+ enum wic_packet_state state;
+ enum wic_nibble_state nibble;
+ union {
+ struct {
+#if defined(LITTLE_ENDIAN)
+ unsigned char lsb;
+ unsigned char msb;
+#elif defined(BIG_ENDIAN)
+ unsigned char msb;
+ unsigned char lsb;
+#else
+#error "Please fix the endianness defines in <asm/byteorder.h>"
+#endif
+ } b;
+ unsigned short h;
+ } length;
+ unsigned short byte;
+ unsigned char checksum;
+ unsigned char data;
+ struct sk_buff *skb;
+};
+
+struct net_local {
+ struct enet_statistics enet_stats;
+ struct tq_struct immediate;
+ struct tq_struct deferred;
+ struct wic_local snd_data;
+ struct wic_local rcv_data;
+ unsigned long trigger;
+ unsigned long nibble;
+ enum wic_connection_state connection;
+ unsigned short timeout_count;
+ char is_deferred;
+ int (*orig_rebuild_header)(void *eth, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
+};
+
+/* Entry point of WIC driver.
+ Probe the hardware, and register/initialize the driver. */
+int
+wic_init(struct device *dev)
+{
+ struct net_local *nl;
+ struct wicconf wc;
+ int i;
+
+ /* Check region before the probe */
+ if (check_region(PAR_DATA(dev), 3) < 0)
+ return -ENODEV;
+
+ /* Check that there is something at base_addr. */
+ outb(0, PAR_DATA(dev));
+ udelay(1000);
+ if (inb(PAR_DATA(dev)) != 0)
+ return -ENODEV;
+
+ printk("%s\n",version);
+ printk("%s: Parallel port at %#3lx, ", dev->name, dev->base_addr);
+ if (dev->irq) {
+ printk("using assigned IRQ %d.\n", dev->irq);
+ } else {
+ int irq = 0;
+#ifdef MODULE
+ /* dev->irq==0 means autoprobe, but we don't try to do so
+ with module. We can change it by ifconfig */
+#else
+ unsigned int irqs = probe_irq_on();
+
+ outb(0x00, PAR_CONTROL(dev));
+ udelay(1000);
+ udelay(1000);
+ irq = probe_irq_off(irqs);
+#endif
+ if (irq > 0) {
+ dev->irq = irq;
+ printk("using probed IRQ %d.\n", dev->irq);
+ } else
+ printk("failed to detect IRQ(%d) --"
+ " Please set IRQ by ifconfig.\n", irq);
+ }
+
+ request_region(PAR_DATA(dev), 3, dev->name);
+
+ /* Fill in the generic fields of the device structure. */
+ ether_setup(dev);
+
+ /* Then, override parts of it */
+ dev->hard_start_xmit = wic_tx_packet;
+ dev->open = wic_open;
+ dev->stop = wic_close;
+ dev->get_stats = wic_get_stats;
+ dev->set_config = wic_config;
+ dev->do_ioctl = wic_ioctl;
+ dev->mtu = 1514;
+ dev->set_multicast_list = wic_set_multicast_list;
+ dev->flags = IFF_BROADCAST | IFF_RUNNING | IFF_NOTRAILERS;
+
+ /* get the MAC address from the controller */
+ wc.len = 1;
+ wc.pcmd = WIC_GETNET;
+ check_bfr(dev);
+ send_cmd(dev, (unsigned char *)&wc, 1);
+ wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+ while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */
+ wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+
+ printk("%s:MAC address: ",dev->name);
+ for (i=0; i < ETH_ALEN ; i++) {
+ dev->dev_addr[i] = wc.data[i];
+ printk("%2x ",dev->dev_addr[i]);
+ }
+ printk("\n");
+
+ /* Set the private structure */
+ dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return EAGAIN;
+ memset(dev->priv, 0, sizeof(struct net_local));
+ nl = (struct net_local *) dev->priv;
+
+ nl->orig_rebuild_header = dev->rebuild_header;
+ dev->rebuild_header = wic_rebuild_header;
+
+ /* Initialize constants */
+ nl->trigger = WIC_TRIGGER_WAIT;
+ nl->nibble = WIC_NIBBLE_WAIT;
+
+ /* Initialize task queue structures */
+ nl->immediate.next = &tq_last;
+ nl->immediate.sync = 0;
+ nl->immediate.routine = (void *)(void *)wic_bh;
+ nl->immediate.data = dev;
+
+ nl->deferred.next = &tq_last;
+ nl->deferred.sync = 0;
+ nl->deferred.routine = (void *)(void *)wic_kick_bh;
+ nl->deferred.data = dev;
+
+ return 0;
+}
+
+/* Bottom half handler for the delayed request.
+ This routine is kicked by do_timer().
+ Request `wic_bh' to be invoked. */
+void
+wic_kick_bh(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+
+ if (nl->is_deferred) {
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+}
+
+/* Forward declarations of internal routines */
+int wic_none(struct device *, struct net_local *,
+ struct wic_local *, struct wic_local *);
+int wic_receive_packet(struct device *, struct net_local *,
+ struct wic_local *, struct wic_local *);
+int wic_send_packet(struct device *, struct net_local *,
+ struct wic_local *, struct wic_local *);
+int wic_connection_close(struct device *, struct net_local *,
+ struct wic_local *, struct wic_local *);
+int wic_error(struct device *, struct net_local *,
+ struct wic_local *, struct wic_local *);
+int wic_bh_timeout_error(struct device *dev, struct net_local *nl,
+ struct wic_local *snd,
+ struct wic_local *rcv,
+ int error);
+
+#define OK 0
+#define TIMEOUT 1
+#define ERROR 2
+
+typedef int (*wic_func)(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv);
+
+wic_func connection_state_table[] =
+{
+ wic_none,
+ wic_receive_packet,
+ wic_send_packet,
+ wic_connection_close,
+ wic_error
+};
+
+void
+wic_set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+{
+ struct wicconf wc;
+ struct wic_net *wn;
+
+ disable_irq(dev->irq);
+ save &= 0xef; /* disable */
+ outb(save, PAR_CONTROL(dev));
+
+ wc.len = 1;
+ wc.pcmd = WIC_GETNET;
+ check_bfr(dev);
+ tog = 3;
+ send_cmd(dev, (unsigned char *)&wc, 1);
+ wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+ while ((wc.len == 1) && (wc.data[0] == 0x7)) /* controller int */
+ wc.len = recv_cmd_resp(dev, (unsigned char *)&wc.data);
+ wn = (struct wic_net *)&wc.data;
+ switch (num_addrs) {
+ case -1: /* promiscuous mode */
+ wn->mode |= (NET_MODE_ME | NET_MODE_BCAST |
+ NET_MODE_MCAST | NET_MODE_PROM);
+ printk("%s: Setting promiscuous mode\n", dev->name);
+ break;
+ default: /* my address and bcast addresses */
+ wn->mode &= ~(NET_MODE_PROM | NET_MODE_MCAST);
+ wn->mode |= (NET_MODE_ME | NET_MODE_BCAST);
+ /* break; */
+ }
+ wc.len = 23;
+ wc.pcmd = WIC_SETNET;
+ check_bfr(dev);
+ tog = 3;
+ send_cmd(dev, (unsigned char *)&wc, wc.len);
+
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return;
+}
+
+/* Bottom half handler of WIC. */
+void
+wic_bh(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct wic_local *snd = &nl->snd_data;
+ struct wic_local *rcv = &nl->rcv_data;
+ wic_func f;
+ int r;
+
+ nl->is_deferred = 0;
+ f = connection_state_table[nl->connection];
+ if ((r = (*f)(dev, nl, snd, rcv)) != OK
+ && (r = wic_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
+ nl->is_deferred = 1;
+ queue_task(&nl->deferred, &tq_timer);
+ }
+}
+
+int
+wic_bh_timeout_error(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv,
+ int error)
+{
+ unsigned char c0;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (nl->connection == WIC_CN_SEND) {
+
+ if (error != ERROR) { /* Timeout */
+ nl->timeout_count++;
+ if ((snd->state == WIC_PK_TRIGGER
+ && nl->timeout_count <= 10)
+ || nl->timeout_count <= 3) {
+ restore_flags(flags);
+ /* Try again later */
+ return TIMEOUT;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ printk("%s: transmit timeout(%d,%02x)\n",
+ dev->name, snd->state, c0);
+ }
+ nl->enet_stats.tx_errors++;
+ nl->enet_stats.tx_aborted_errors++;
+ } else if (nl->connection == WIC_CN_RECEIVE) {
+ if (rcv->state == WIC_PK_TRIGGER) {
+ /* Transmission was interrupted. */
+ restore_flags(flags);
+ return OK;
+ }
+ if (error != ERROR) { /* Timeout */
+ if (++nl->timeout_count <= 3) {
+ restore_flags(flags);
+ /* Try again later */
+ return TIMEOUT;
+ }
+ c0 = inb(PAR_STATUS(dev));
+ printk("%s: receive timeout(%d,%02x)\n",
+ dev->name, rcv->state, c0);
+ }
+ nl->enet_stats.rx_dropped++;
+ }
+ rcv->state = WIC_PK_DONE;
+ if (rcv->skb) {
+ rcv->skb->free = 1;
+ kfree_skb(rcv->skb, FREE_READ);
+ rcv->skb = NULL;
+ }
+ snd->state = WIC_PK_DONE;
+ if (snd->skb) {
+ snd->skb->free = 1;
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ snd->skb = NULL;
+ }
+#if (0)
+ disable_irq(dev->irq);
+ save &= 0xef; /* disable */
+ outb(save, PAR_CONTROL(dev));
+ dev->tbusy = 1;
+ outb(0x00, PAR_DATA(dev));
+#endif /* (0) */
+ nl->connection = WIC_CN_ERROR;
+ restore_flags(flags);
+
+ return TIMEOUT;
+}
+
+int
+wic_none(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv)
+{
+ return OK;
+}
+
+/* WIC_RECEIVE --- receive a byte(two nibbles)
+ Returns OK on success, TIMEOUT on timeout */
+inline int
+wic_receive(unsigned short nibble_timeout, unsigned short status_addr,
+ enum wic_nibble_state *ns_p, unsigned char *data_p)
+{
+unsigned int cx;
+
+ cx = LOOPCNT;
+ while ((inb(status_addr) & 0x08) != ((tog<<3) & 0x08)) {
+ if (--cx == 0) {
+ return TIMEOUT;
+ }
+ }
+ *data_p = inb(status_addr-1);
+ tog ^= 0x01;
+ outb(tog| save, status_addr+1);
+ return OK;
+}
+
+/* WIC_RECEIVE_PACKET --- receive a packet */
+int
+wic_receive_packet(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv)
+{
+ unsigned short status_addr = PAR_STATUS(dev);
+ unsigned short nibble_timeout = nl->nibble;
+ unsigned char *lbuf;
+ unsigned char junk;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ switch (rcv->state) {
+ case WIC_PK_TRIGGER:
+ disable_irq(dev->irq);
+ save &= 0xef; /* disable */
+ outb(save, PAR_CONTROL(dev));
+
+ dev->interrupt = 0;
+
+ tog &= 0xfe;
+ ack_resp(dev);
+ if (net_debug > 2)
+ printk("%s: receive start\n", dev->name);
+ rcv->state = WIC_PK_LENGTH_LSB;
+ rcv->nibble = WIC_NB_BEGIN;
+
+ case WIC_PK_LENGTH_LSB:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_LENGTH_LSB\n", dev->name);
+ if (snd->state != WIC_PK_DONE) {
+ if (wic_receive(nl->trigger, status_addr,
+ &rcv->nibble, &rcv->length.b.lsb)) {
+ /* collision, here dev->tbusy == 1 */
+ rcv->state = WIC_PK_DONE;
+ nl->is_deferred = 1;
+ nl->connection = WIC_CN_SEND;
+ restore_flags(flags);
+ queue_task(&nl->deferred, &tq_timer);
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ } else {
+ if (wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &rcv->length.b.lsb)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ }
+ rcv->state = WIC_PK_LENGTH_MSB;
+
+ case WIC_PK_LENGTH_MSB:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_LENGTH_MSB\n", dev->name);
+ if (wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &rcv->length.b.msb)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ if (rcv->length.h > dev->mtu || rcv->length.h < 8) {
+ printk("%s: bad packet size %d.\n", dev->name, rcv->length.h);
+ restore_flags(flags);
+ return ERROR;
+ }
+ /* Malloc up new buffer. */
+ rcv->skb = dev_alloc_skb(rcv->length.h);
+ if (rcv->skb == NULL) {
+ printk("%s: Memory squeeze.\n", dev->name);
+ restore_flags(flags);
+ return ERROR;
+ }
+ skb_put(rcv->skb,rcv->length.h);
+ rcv->skb->dev = dev;
+
+ rcv->state = WIC_PK_DATA;
+ rcv->byte = 0;
+ rcv->checksum = 0;
+
+ /* sequence numbers */
+ if (net_debug > 2)
+ printk("%s: WIC_PK_SEQ\n", dev->name);
+ if (wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &junk)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ if (wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &junk)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+
+ case WIC_PK_DATA:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_DATA: length %i\n", dev->name,
+ rcv->length.h);
+ lbuf = rcv->skb->data;
+ do {
+ if (wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &lbuf[rcv->byte])) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ } while (++rcv->byte < (rcv->length.h - 4));
+
+ /* receive pad byte */
+ if (rcv->length.h & 0x01)
+ wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &lbuf[rcv->byte]);
+
+ do {
+ rcv->checksum += lbuf[--rcv->byte];
+ } while (rcv->byte);
+
+ rcv->state = WIC_PK_CHECKSUM;
+
+ case WIC_PK_CHECKSUM:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_CHECKSUM\n", dev->name);
+ if (wic_receive(nibble_timeout, status_addr,
+ &rcv->nibble, &junk)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ outb(0, PAR_DATA(dev));
+ rcv->state = WIC_PK_DONE;
+
+ case WIC_PK_DONE:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_DONE\n", dev->name);
+ /* Inform the upper layer for the arrival of a packet. */
+ netif_rx(rcv->skb);
+ nl->enet_stats.rx_packets++;
+ rcv->skb = NULL;
+ if (net_debug > 2)
+ printk("%s: receive end\n", dev->name);
+
+ /* Close the connection. */
+ if (snd->state != WIC_PK_DONE) {
+ nl->connection = WIC_CN_SEND;
+ restore_flags(flags);
+ queue_task(&nl->immediate, &tq_immediate);
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ } else {
+ nl->connection = WIC_CN_NONE;
+ restore_flags(flags);
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ }
+ restore_flags(flags);
+ return OK;
+}
+
+/* WIC_SEND --- send a byte (two nibbles)
+ Returns OK on success, TIMEOUT when timeout */
+inline int
+wic_send(unsigned short nibble_timeout, unsigned short data_addr,
+ enum wic_nibble_state *ns_p, unsigned char data)
+{
+unsigned int cx;
+
+ cx = LOOPCNT;
+ while ((inb(data_addr+1) & 0x80) == ((tog<<7) & 0x80)) {
+ if (--cx == 0) {
+ return -TIMEOUT;
+ }
+ }
+ outb(data, data_addr);
+ outb(tog | save, data_addr+2);
+ tog ^= 0x01;
+ return OK;
+}
+
+/* WIC_SEND_PACKET --- send a packet */
+int
+wic_send_packet(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv)
+{
+ unsigned short data_addr = PAR_DATA(dev);
+ unsigned short nibble_timeout = nl->nibble;
+ unsigned char *lbuf;
+ unsigned int cx;
+ unsigned int pad = 2;
+ unsigned long flags;
+
+ if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
+ printk("%s: send skb lost\n", dev->name);
+ snd->state = WIC_PK_DONE;
+ snd->skb = NULL;
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return ERROR;
+ }
+
+ save_flags(flags);
+ cli();
+ switch (snd->state) {
+ case WIC_PK_TRIGGER:
+
+ if (nl->connection == WIC_CN_RECEIVE) {
+ /* interrupted */
+ nl->enet_stats.collisions++;
+ restore_flags(flags);
+ if (net_debug > 1)
+ printk("%s: collision.\n", dev->name);
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+
+ disable_irq(dev->irq);
+ save &= 0xef; /* disable */
+ outb(save, PAR_CONTROL(dev));
+
+ /* interrupt controller */
+ tog = 3;
+ outb(0x06 | save, PAR_CONTROL(dev));
+
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) {
+ if (--cx == 0) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ if (cx == 10)
+ outb(0x02, PAR_CONTROL(dev));
+ }
+
+ if (net_debug > 2)
+ printk("%s: send start\n", dev->name);
+ snd->state = WIC_PK_LENGTH_LSB;
+ snd->nibble = WIC_NB_BEGIN;
+ nl->timeout_count = 0;
+
+ case WIC_PK_LENGTH_LSB:
+ if (snd->length.h & 0x01)
+ pad = 3;
+ else
+ pad = 2;
+ snd->length.h += (4 + pad); /* len + seq + data + pad */
+ if (net_debug > 2)
+ printk("%s: WIC_PK_LENGTH_LSB: length = %i\n",
+ dev->name, snd->length.h);
+
+ if (wic_send(nibble_timeout, data_addr,
+ &snd->nibble, snd->length.b.lsb)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ snd->state = WIC_PK_LENGTH_MSB;
+
+ case WIC_PK_LENGTH_MSB:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_LENGTH_MSB\n", dev->name);
+ if (wic_send(nibble_timeout, data_addr,
+ &snd->nibble, snd->length.b.msb)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ snd->state = WIC_PK_DATA;
+ snd->byte = 0;
+ snd->checksum = 0;
+
+ case WIC_PK_DATA:
+ /* adjust length back to data only */
+ snd->length.h -= (4 + pad); /* len + seq + data + pad */
+ /* send 2 byte sequence number */
+ if (net_debug > 2)
+ printk("%s: WIC_SEQ\n", dev->name);
+ if (wic_send(nibble_timeout, data_addr,
+ &snd->nibble, 0)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ if (wic_send(nibble_timeout, data_addr,
+ &snd->nibble, 0)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ if (net_debug > 2)
+ printk("%s: WIC_PK_DATA\n", dev->name);
+
+ do {
+ if (wic_send(nibble_timeout, data_addr,
+ &snd->nibble, lbuf[snd->byte])) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ }
+ while (++snd->byte < snd->length.h);
+
+ do
+ snd->checksum += lbuf[--snd->byte];
+ while (snd->byte);
+
+ snd->state = WIC_PK_CHECKSUM;
+
+ case WIC_PK_CHECKSUM:
+ /* send pad bytes */
+ if (net_debug > 2)
+ printk("%s: WIC_PK_PAD: %i bytes\n",
+ dev->name, pad);
+ while(pad--)
+ if (wic_send(nibble_timeout, data_addr,
+ &snd->nibble, 0)) {
+ restore_flags(flags);
+ return TIMEOUT;
+ }
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ nl->enet_stats.tx_packets++;
+ snd->state = WIC_PK_DONE;
+
+ case WIC_PK_DONE:
+ if (net_debug > 2)
+ printk("%s: WIC_PK_DONE\n", dev->name);
+ /* Close the connection */
+ outb (0x00, PAR_DATA(dev));
+ outb(save, PAR_CONTROL(dev));
+
+ snd->skb = NULL;
+ if (net_debug > 2)
+ printk("%s: send end\n", dev->name);
+ nl->connection = WIC_CN_CLOSING;
+ nl->is_deferred = 1;
+ restore_flags(flags);
+ queue_task(&nl->deferred, &tq_timer);
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ return OK;
+ }
+ restore_flags(flags);
+ return OK;
+}
+
+int
+wic_connection_close(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv)
+{
+unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (nl->connection == WIC_CN_CLOSING) {
+ nl->connection = WIC_CN_NONE;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ restore_flags(flags);
+ return OK;
+}
+
+/* WIC_ERROR --- wait till other end settled */
+int
+wic_error(struct device *dev, struct net_local *nl,
+ struct wic_local *snd, struct wic_local *rcv)
+{
+ unsigned char status;
+
+ status = inb(PAR_STATUS(dev));
+ if ((status & 0xf8) == 0x80) {
+ if (net_debug > 2)
+ printk("%s: reset interface.\n", dev->name);
+ nl->connection = WIC_CN_NONE;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ mark_bh(NET_BH);
+ } else {
+ nl->is_deferred = 1;
+ queue_task(&nl->deferred, &tq_timer);
+ }
+
+ return OK;
+}
+
+/* Handle the parallel port interrupts. */
+void
+wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct wic_local *rcv = &nl->rcv_data;
+ unsigned long flags;
+
+ if (dev == NULL) {
+ printk ("wic_interrupt: irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt) {
+ return;
+ }
+
+ if (check_bfr(dev) < 0) {
+ return;
+ }
+
+ dev->interrupt = 1;
+ if (net_debug > 3)
+ printk("%s: interrupt.\n", dev->name);
+
+ save_flags(flags);
+ cli();
+ switch (nl->connection) {
+ case WIC_CN_CLOSING:
+ dev->tbusy = 0;
+ case WIC_CN_NONE:
+ case WIC_CN_SEND:
+ dev->last_rx = jiffies;
+ rcv->state = WIC_PK_TRIGGER;
+ nl->connection = WIC_CN_RECEIVE;
+ nl->timeout_count = 0;
+ restore_flags(flags);
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ break;
+
+ case WIC_CN_RECEIVE:
+ printk("%s: receive interrupt when receiving packet\n", dev->name);
+ restore_flags(flags);
+ break;
+
+ case WIC_CN_ERROR:
+ printk("%s: receive interrupt in error state\n", dev->name);
+ restore_flags(flags);
+ break;
+ }
+}
+
+int
+wic_rebuild_header(void *buff, struct device *dev, unsigned long dst,
+ struct sk_buff *skb)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct ethhdr *eth = (struct ethhdr *)buff;
+ int i;
+
+ if ((dev->flags & IFF_NOARP)==0)
+ return nl->orig_rebuild_header(buff, dev, dst, skb);
+
+ if (eth->h_proto != htons(ETH_P_IP)) {
+ printk("wic_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
+ }
+
+ for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++)
+ eth->h_dest[i] = 0xfc;
+ memcpy(&(eth->h_dest[i]), &dst, sizeof(unsigned long));
+ return 0;
+}
+
+int
+wic_tx_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct wic_local *snd = &nl->snd_data;
+ unsigned long flags;
+
+ if (dev->tbusy)
+ return 1;
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ if (skb->len > dev->mtu) {
+ printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
+ dev->tbusy = 0;
+ return 0;
+ }
+
+ if (net_debug > 2)
+ printk("%s: send request\n", dev->name);
+
+ save_flags(flags);
+ cli();
+ dev->trans_start = jiffies;
+ snd->skb = skb;
+ snd->length.h = skb->len;
+ snd->state = WIC_PK_TRIGGER;
+ if (nl->connection == WIC_CN_NONE) {
+ nl->connection = WIC_CN_SEND;
+ nl->timeout_count = 0;
+ }
+ restore_flags(flags);
+ queue_task(&nl->immediate, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+
+ return 0;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine gets exclusive access to the parallel port by allocating
+ its IRQ line.
+ */
+int
+wic_open(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ unsigned long flags;
+
+ if (dev->irq == 0) {
+ printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name);
+ return -EAGAIN;
+ }
+ save_flags(flags);
+ cli();
+ check_bfr(dev);
+ if (request_irq(dev->irq , wic_interrupt, 0, dev->name, NULL) != 0) {
+ sti();
+ printk("%s: couldn't get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ irq2dev_map[dev->irq] = dev;
+ restore_flags(flags);
+
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ /* Initialize the state machine. */
+ nl->rcv_data.state = nl->snd_data.state = WIC_PK_DONE;
+ nl->rcv_data.skb = nl->snd_data.skb = NULL;
+ nl->connection = WIC_CN_NONE;
+ nl->is_deferred = 0;
+
+ dev->interrupt = 0;
+ dev->start = 1;
+ dev->tbusy = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* The inverse routine to wic_open (). */
+int
+wic_close(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct wic_local *snd = &nl->snd_data;
+ struct wic_local *rcv = &nl->rcv_data;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+ cli();
+ free_irq(dev->irq, NULL);
+ irq2dev_map[dev->irq] = NULL;
+ nl->is_deferred = 0;
+ nl->connection = WIC_CN_NONE;
+ sti();
+ outb(0x00, PAR_DATA(dev));
+
+ snd->state = WIC_PK_DONE;
+ if (snd->skb) {
+ snd->skb->free = 1;
+ dev_kfree_skb(snd->skb, FREE_WRITE);
+ snd->skb = NULL;
+ }
+ rcv->state = WIC_PK_DONE;
+ if (rcv->skb) {
+ rcv->skb->free = 1;
+ kfree_skb(rcv->skb, FREE_READ);
+ rcv->skb = NULL;
+ }
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+struct enet_statistics *
+wic_get_stats(struct device *dev)
+{
+ struct net_local *nl = (struct net_local *)dev->priv;
+ struct enet_statistics *r = &nl->enet_stats;
+
+ return r;
+}
+
+int
+wic_config(struct device *dev, struct ifmap *map)
+{
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (map->base_addr != (unsigned long)-1
+ && map->base_addr != dev->base_addr)
+ printk("%s: You cannot change base_addr of this interface (ignored).\n", dev->name);
+
+ if (map->irq != (unsigned char)-1)
+ dev->irq = map->irq;
+ return 0;
+}
+
+int
+wic_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct wicconf wc;
+ int err;
+ char len = 0;
+ unsigned long flags;
+
+ err=verify_area(VERIFY_WRITE, rq->ifr_data, sizeof(struct wicconf));
+ if (err)
+ return err;
+ memcpy_fromfs(&wc, rq->ifr_data, sizeof(struct wicconf));
+ switch(wc.pcmd) {
+ case WIC_AYT:
+ strcpy(wc.data, version);
+ wc.len = strlen(wc.data);
+ memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf));
+ /* return 0; */
+ break;
+ case WIC_RESET:
+ wic_reset(dev);
+ return(0);
+ /* break; */
+ case WIC_SETSN:
+ len = 17;
+ break;
+ case WIC_SETPS:
+ len = 3;
+ break;
+ case WIC_SETAF:
+ case WIC_SETGPF:
+ len = 2;
+ break;
+ case WIC_SETNET:
+ len = 23;
+ break;
+ case WIC_SETSYS:
+ len = 15;
+ break;
+ case WIC_GETVERH:
+ case WIC_GETNL:
+ case WIC_GETSN:
+ case WIC_CLRSTATS:
+ case WIC_GETSTATS:
+ case WIC_GETVERM:
+ case WIC_GETNET:
+ case WIC_GETSYS:
+ len = 1;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Wait for lock to free */
+ while (set_bit(0, (void *)&dev->tbusy) != 0);
+ save_flags(flags);
+ cli();
+
+ disable_irq(dev->irq);
+ save &= 0xef; /* disable */
+ outb(save, PAR_CONTROL(dev));
+ err = check_bfr(dev);
+ tog = 3;
+ err = send_cmd(dev, (unsigned char *)&wc, len);
+
+ if (wc.pcmd & 0x40) { /* response */
+ len = (char)recv_cmd_resp(dev, wc.data);
+ while ((len == 1) && (wc.data[0] == 0x7)) { /* controller int */
+ len = (char)recv_cmd_resp(dev, wc.data);
+ }
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ wc.len = (len <0) ? 0 : len;
+ memcpy_tofs(rq->ifr_data, &wc, sizeof(struct wicconf));
+ } else {
+ save |= 0x10; /* enable */
+ outb(save, PAR_CONTROL(dev));
+ enable_irq(dev->irq);
+ }
+ restore_flags(flags);
+
+ outb(0, PAR_DATA(dev));
+ dev->tbusy = 0;
+ return 0;
+}
+
+int
+get_byte(struct device *dev, unsigned char *c)
+{
+unsigned int cx;
+
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0x08) != ((tog << 3)&0x08)) {
+ if (--cx == 0) {
+ return(-TIMEOUT);
+ }
+ }
+ /* receive a byte of data */
+ *c = inb(PAR_DATA(dev));
+ tog ^= 0x01;
+ /* ack reception of data */
+ outb(tog| save, PAR_CONTROL(dev));
+ return OK;
+}
+
+int
+ack_resp(struct device *dev)
+{
+unsigned int cx;
+
+ outb(save | 0x27, PAR_CONTROL(dev));
+
+ /* wait for controller to remove interrupt [Ack(low), Busy(low)] */
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0xc0) != 0x80) {
+ if (--cx == 0) {
+ return -TIMEOUT;
+ }
+ }
+
+ outb(save | 0x22, PAR_CONTROL(dev));
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0x08) == 0x08) {
+ if (--cx == 0) {
+ return TIMEOUT;
+ }
+ }
+ tog |= 0x20;
+ tog &= 0xfe;
+ return OK;
+}
+
+void
+wic_reset(struct device *dev)
+{
+unsigned char stat;
+
+ stat = inb(PAR_CONTROL(dev));
+ outb(0, PAR_DATA(dev));
+ outb(stat | 0x08, PAR_CONTROL(dev));
+ outb(stat & 0xf7, PAR_CONTROL(dev));
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ tog = 3;
+ save = 0;
+ return;
+}
+
+int
+check_bfr(struct device *dev)
+{
+unsigned char c0, l;
+
+ if ((inb(PAR_STATUS(dev)) & 0xc8) == 0x48) {
+ save |= 0x80;
+ outb(0x23| save, PAR_CONTROL(dev));
+ ack_resp(dev);
+ get_byte(dev, &l); /* len */
+ while (l--) {
+ get_byte(dev, &c0);
+ }
+ get_byte(dev, &c0);
+ save &=0x7f;
+ outb(0, PAR_DATA(dev));
+ return -l;
+ } else
+ return (0);
+}
+
+
+int
+recv_cmd_resp(struct device *dev, unsigned char *buf)
+{
+unsigned char cksum = 0;
+int err;
+unsigned char c0 = 0;
+int len;
+int savelen;
+unsigned int cx;
+int i;
+
+ tog &= 0xfe;
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0xc8) != 0x48) {
+ if (--cx == 0) {
+ /* clear Busy */
+ outb(0, PAR_DATA(dev));
+ printk("rcv_cmd_resp: timeout\n");
+ return -TIMEOUT;
+ }
+ }
+
+ /* acknowledge the interrupt */
+ i = ack_resp(dev);
+
+ /* get length */
+ err = get_byte(dev, &c0);
+ if (err < 0) {
+ printk("get_byte1: failed\n");
+ return(err);
+ }
+ len = c0;
+ savelen = len;
+
+ /* get data */
+ while(len--) {
+ err = get_byte(dev, &c0);
+ if (err < 0) {
+ printk("get_byte2: failed\n");
+ return(err);
+ }
+ outb(0, PAR_DATA(dev));
+ *buf = c0;
+ cksum += c0;
+ buf++;
+ }
+ /* get cksum */
+ err = get_byte(dev, &c0);
+ if (err < 0) {
+ printk("get_byte3: failed\n");
+ return(err);
+ }
+ if (cksum != c0) {
+ printk("cksum failed\n");
+ return(-3);
+ }
+ /* get trailing byte, if any... */
+ get_byte(dev, &c0);
+ return(savelen);
+}
+
+int
+send_byte(struct device *dev, unsigned char c)
+{
+unsigned int cx;
+
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog<<7) & 0x80)) {
+ if (--cx == 0) {
+ return(-TIMEOUT);
+ }
+ }
+ outb(c, PAR_DATA(dev));
+ outb(save |tog, PAR_CONTROL(dev));
+ tog ^= 0x01;
+ return OK;
+}
+
+
+int
+send_cmd(struct device *dev, unsigned char *cmd, char len)
+{
+unsigned char cksum = 0;
+int err = 0;
+unsigned int cx;
+
+ /* interrupt controller */
+ outb(save | 0x04, PAR_CONTROL(dev));
+ /* wait for ACK */
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0xe8) != 0xc0) {
+ if (--cx == 0)
+ return -TIMEOUT;
+ if (cx == 10)
+ outb(0x02, PAR_CONTROL(dev));
+ }
+ /* cmd coming... */
+ outb(save | 0x02, PAR_CONTROL(dev));
+ /* send length byte */
+ err = send_byte(dev, (unsigned char)len);
+
+ /* send data */
+ while (len--) {
+ err = send_byte(dev, *cmd);
+ if (err < 0) {
+ return err;
+ }
+ cksum += *cmd;
+ cmd++;
+ }
+
+ /* send cksum byte */
+ err = send_byte(dev, cksum);
+ if (err < 0)
+ return err;
+
+ cx = LOOPCNT;
+ while ((inb(PAR_STATUS(dev)) & 0x80) == ((tog <<7)&0x80)) {
+ if (--cx == 0)
+ return -TIMEOUT;
+ }
+ save |= 0x80;
+ outb(save | 0x23, PAR_CONTROL(dev));
+ outb(0, PAR_DATA(dev));
+ return OK;
+}
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+
+struct device dev_wic0 =
+{
+ "wic0" /*"wic"*/,
+ 0, 0, 0, 0, /* memory */
+ 0x3BC, 5, /* base, irq */
+ 0, 0, 0, NULL, wic_init
+};
+
+struct device dev_wic1 =
+{
+ "wic1" /*"wic"*/,
+ 0, 0, 0, 0, /* memory */
+ 0x378, 7, /* base, irq */
+ 0, 0, 0, NULL, wic_init
+};
+
+struct device dev_wic2 =
+{
+ "wic2" /*"wic"*/,
+ 0, 0, 0, 0, /* memory */
+ 0x278, 2, /* base, irq */
+ 0, 0, 0, NULL, wic_init
+};
+
+int
+init_module(void)
+{
+ int devices=0;
+
+ if (register_netdev(&dev_wic0) != 0)
+ devices++;
+ if (register_netdev(&dev_wic1) != 0)
+ devices++;
+ if (register_netdev(&dev_wic2) != 0)
+ devices++;
+ if (devices == 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ if (dev_wic0.priv) {
+ unregister_netdev(&dev_wic0);
+ release_region(PAR_DATA(&dev_wic0), 3);
+ kfree_s(dev_wic0.priv, sizeof(struct net_local));
+ dev_wic0.priv = NULL;
+ }
+ if (dev_wic1.priv) {
+ unregister_netdev(&dev_wic1);
+ release_region(PAR_DATA(&dev_wic1), 3);
+ kfree_s(dev_wic1.priv, sizeof(struct net_local));
+ dev_wic1.priv = NULL;
+ }
+ if (dev_wic2.priv) {
+ unregister_netdev(&dev_wic2);
+ release_region(PAR_DATA(&dev_wic2), 3);
+ kfree_s(dev_wic2.priv, sizeof(struct net_local));
+ dev_wic2.priv = NULL;
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DMODULE -DCONFIG_MODVERSIONS -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -g -fomit-frame-pointer -pipe -m486 -c wic.c"
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this