diff options
Diffstat (limited to 'drivers/net/plip.c')
-rw-r--r-- | drivers/net/plip.c | 417 |
1 files changed, 263 insertions, 154 deletions
diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 9fbf8cf49..25c33ab1a 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -1,4 +1,4 @@ -/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */ +/* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ */ /* PLIP: A parallel port "network" driver for Linux. */ /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */ /* @@ -11,16 +11,9 @@ * * Modularization and ifreq/ifmap support by Alan Cox. * Rewritten by Niibe Yutaka. + * parport-sharing awareness code by Philip Blundell. * * Fixes: - * 9-Sep-95 Philip Blundell <pjb27@cam.ac.uk> - * - only claim 3 bytes of I/O space for port at 0x3bc - * - treat NULL return from register_netdev() as success in - * init_module() - * - added message if driver loaded as a module but no - * interfaces present. - * - release claimed I/O ports if malloc() fails during init. - * * Niibe Yutaka * - Module initialization. You can specify I/O addr and IRQ: * # insmod plip.o io=0x3bc irq=7 @@ -44,7 +37,7 @@ * Crynwr packet driver, Peter Bauer changed the protocol again * back to original protocol. * - * This version follows original PLIP protocol. + * This version follows original PLIP protocol. * So, this PLIP can't communicate the PLIP of Linux v1.0. */ @@ -52,7 +45,7 @@ * To use with DOS box, please do (Turn on ARP switch): * # ifconfig plip[0-2] arp */ -static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n"; +static const char *version = "NET3 PLIP version 2.2-parport gniibe@mri.co.jp\n"; /* Sources: @@ -87,7 +80,7 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n"; */ #include <linux/module.h> - +#include <linux/config.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/types.h> @@ -102,6 +95,7 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n"; #include <linux/errno.h> #include <linux/delay.h> #include <linux/lp.h> +#include <linux/init.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -114,6 +108,11 @@ static const char *version = "NET3 PLIP version 2.2 gniibe@mri.co.jp\n"; #include <asm/irq.h> #include <asm/byteorder.h> +#include <linux/parport.h> + +/* Maximum number of devices to support. */ +#define PLIP_MAX 8 + /* Use 0 for production, 1 for verification, >2 for debug */ #ifndef NET_DEBUG #define NET_DEBUG 1 @@ -143,14 +142,15 @@ static void plip_bh(struct device *dev); static void plip_interrupt(int irq, void *dev_id, struct pt_regs *regs); /* Functions for DEV methods */ -static int plip_rebuild_header(void *buff, struct device *dev, - unsigned long raddr, struct sk_buff *skb); +static int plip_rebuild_header(struct sk_buff *skb); static int plip_tx_packet(struct sk_buff *skb, struct device *dev); static int plip_open(struct device *dev); static int plip_close(struct device *dev); -static struct enet_statistics *plip_get_stats(struct device *dev); +static struct net_device_stats *plip_get_stats(struct device *dev); static int plip_config(struct device *dev, struct ifmap *map); static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd); +static int plip_preempt(void *handle); +static int plip_wakeup(void *handle); enum plip_connection_state { PLIP_CN_NONE=0, @@ -188,7 +188,7 @@ struct plip_local { unsigned char lsb; #else #error "Please fix the endianness defines in <asm/byteorder.h>" -#endif +#endif } b; unsigned short h; } length; @@ -199,67 +199,52 @@ struct plip_local { }; struct net_local { - struct enet_statistics enet_stats; + struct net_device_stats enet_stats; struct tq_struct immediate; struct tq_struct deferred; struct plip_local snd_data; struct plip_local rcv_data; + struct ppd *pardev; unsigned long trigger; unsigned long nibble; enum plip_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); + int is_deferred; + int port_owner; + int should_relinquish; + int (*orig_rebuild_header)(struct sk_buff *skb); }; /* Entry point of PLIP driver. - Probe the hardware, and register/initialize the driver. */ -int -plip_init(struct device *dev) + Probe the hardware, and register/initialize the driver. + + PLIP is rather weird, because of the way it interacts with the parport + system. It is _not_ initialised from Space.c. Instead, plip_init() + is called, and that function makes up a "struct device" for each port, and + then calls us here. + + */ +__initfunc(int +plip_init_dev(struct device *dev, struct parport *pb)) { struct net_local *nl; - int iosize = (PAR_DATA(dev) == 0x3bc) ? 3 : 8; + struct ppd *pardev; - /* Check region before the probe */ - if (check_region(PAR_DATA(dev), iosize) < 0) - return -ENODEV; + dev->irq = pb->irq; + dev->base_addr = pb->base; - /* Check that there is something at base_addr. */ - outb(0, PAR_DATA(dev)); - udelay(1000); - if (inb(PAR_DATA(dev)) != 0) + if (pb->irq == -1) { + printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name); return -ENODEV; - - printk(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); - outb(PAR_INTR_OFF, PAR_CONTROL(dev)); - outb(PAR_INTR_ON, PAR_CONTROL(dev)); - outb(PAR_INTR_OFF, PAR_CONTROL(dev)); - 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); } + + pardev = parport_register_device(pb, dev->name, plip_preempt, + plip_wakeup, + plip_interrupt, PARPORT_DEV_LURK, dev); - request_region(PAR_DATA(dev), iosize, dev->name); + printk(version); + printk("%s: Parallel port at %#3lx, using IRQ %d\n", dev->name, + dev->base_addr, dev->irq); /* Fill in the generic fields of the device structure. */ ether_setup(dev); @@ -278,7 +263,7 @@ plip_init(struct device *dev) dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL); if (dev->priv == NULL) { printk(KERN_ERR "%s: out of memory\n", dev->name); - release_region(PAR_DATA(dev), iosize); + parport_unregister_device(pardev); return -ENOMEM; } memset(dev->priv, 0, sizeof(struct net_local)); @@ -286,6 +271,9 @@ plip_init(struct device *dev) nl->orig_rebuild_header = dev->rebuild_header; dev->rebuild_header = plip_rebuild_header; + nl->pardev = pardev; + + nl->port_owner = 0; /* Initialize constants */ nl->trigger = PLIP_TRIGGER_WAIT; @@ -415,7 +403,6 @@ plip_bh_timeout_error(struct device *dev, struct net_local *nl, } rcv->state = PLIP_PK_DONE; if (rcv->skb) { - rcv->skb->free = 1; kfree_skb(rcv->skb, FREE_READ); rcv->skb = NULL; } @@ -556,7 +543,7 @@ plip_receive_packet(struct device *dev, struct net_local *nl, case PLIP_PK_DATA: lbuf = rcv->skb->data; do - if (plip_receive(nibble_timeout, status_addr, + if (plip_receive(nibble_timeout, status_addr, &rcv->nibble, &lbuf[rcv->byte])) return TIMEOUT; while (++rcv->byte < rcv->length.h); @@ -608,7 +595,7 @@ plip_receive_packet(struct device *dev, struct net_local *nl, return OK; } -/* PLIP_SEND --- send a byte (two nibbles) +/* PLIP_SEND --- send a byte (two nibbles) Returns OK on success, TIMEOUT when timeout */ inline static int plip_send(unsigned short nibble_timeout, unsigned short data_addr, @@ -628,7 +615,7 @@ plip_send(unsigned short nibble_timeout, unsigned short data_addr, data_addr++; while (1) { c0 = inb(data_addr); - if ((c0 & 0x80) == 0) + if ((c0 & 0x80) == 0) break; if (--cx == 0) return TIMEOUT; @@ -773,6 +760,10 @@ plip_connection_close(struct device *dev, struct net_local *nl, mark_bh(NET_BH); } sti(); + if (nl->should_relinquish) { + nl->should_relinquish = nl->port_owner = 0; + parport_release(nl->pardev); + } return OK; } @@ -788,6 +779,7 @@ plip_error(struct device *dev, struct net_local *nl, if (net_debug > 2) printk("%s: reset interface.\n", dev->name); nl->connection = PLIP_CN_NONE; + nl->should_relinquish = 0; dev->tbusy = 0; dev->interrupt = 0; outb(PAR_INTR_ON, PAR_CONTROL(dev)); @@ -806,8 +798,8 @@ static void plip_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct device *dev = (struct device *) irq2dev_map[irq]; - struct net_local *nl = (struct net_local *)dev->priv; - struct plip_local *rcv = &nl->rcv_data; + struct net_local *nl; + struct plip_local *rcv; unsigned char c0; if (dev == NULL) { @@ -815,6 +807,9 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs) return; } + nl = (struct net_local *)dev->priv; + rcv = &nl->rcv_data; + if (dev->interrupt) return; @@ -857,17 +852,21 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs) /* We don't need to send arp, for plip is point-to-point. */ static int -plip_rebuild_header(void *buff, struct device *dev, unsigned long dst, - struct sk_buff *skb) +plip_rebuild_header(struct sk_buff *skb) { + struct device *dev = skb->dev; struct net_local *nl = (struct net_local *)dev->priv; - struct ethhdr *eth = (struct ethhdr *)buff; + struct ethhdr *eth = (struct ethhdr *)skb->data; int i; if ((dev->flags & IFF_NOARP)==0) - return nl->orig_rebuild_header(buff, dev, dst, skb); + return nl->orig_rebuild_header(skb); - if (eth->h_proto != htons(ETH_P_IP)) { + if (eth->h_proto != __constant_htons(ETH_P_IP) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + && eth->h_proto != __constant_htons(ETH_P_IPV6) +#endif + ) { printk("plip_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; @@ -875,7 +874,15 @@ plip_rebuild_header(void *buff, struct device *dev, unsigned long dst, for (i=0; i < ETH_ALEN - sizeof(u32); i++) eth->h_dest[i] = 0xfc; +#if 0 *(u32 *)(eth->h_dest+i) = dst; +#else + /* Do not want to include net/route.h here. + * In any case, it is TOP of silliness to emulate + * hardware addresses on PtP link. --ANK + */ + *(u32 *)(eth->h_dest+i) = 0; +#endif return 0; } @@ -888,6 +895,13 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev) if (dev->tbusy) return 1; + /* We may need to grab the bus */ + if (!nl->port_owner) { + if (parport_claim(nl->pardev)) + return 1; + nl->port_owner = 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. */ @@ -938,18 +952,13 @@ plip_open(struct device *dev) struct net_local *nl = (struct net_local *)dev->priv; int i; - if (dev->irq == 0) { - printk("%s: IRQ is not set. Please set it by ifconfig.\n", dev->name); - return -EAGAIN; + /* Grab the port */ + if (!nl->port_owner) { + if (parport_claim(nl->pardev)) return -EAGAIN; + nl->port_owner = 1; } - cli(); - if (request_irq(dev->irq , plip_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; - sti(); + + nl->should_relinquish = 0; /* Clear the data port. */ outb (0x00, PAR_DATA(dev)); @@ -966,11 +975,14 @@ plip_open(struct device *dev) /* Fill in the MAC-level header. */ for (i=0; i < ETH_ALEN - sizeof(u32); i++) dev->dev_addr[i] = 0xfc; + /* Ugh, this is like death. */ *(u32 *)(dev->dev_addr+i) = dev->pa_addr; dev->interrupt = 0; dev->start = 1; dev->tbusy = 0; + irq2dev_map[dev->irq] = dev; + MOD_INC_USE_COUNT; return 0; } @@ -986,12 +998,17 @@ plip_close(struct device *dev) dev->tbusy = 1; dev->start = 0; cli(); - free_irq(dev->irq, NULL); irq2dev_map[dev->irq] = NULL; - nl->is_deferred = 0; - nl->connection = PLIP_CN_NONE; sti(); +#ifdef NOTDEF outb(0x00, PAR_DATA(dev)); +#endif + nl->is_deferred = 0; + nl->connection = PLIP_CN_NONE; + if (nl->port_owner) { + parport_release(nl->pardev); + nl->port_owner = 0; + } snd->state = PLIP_PK_DONE; if (snd->skb) { @@ -1000,22 +1017,69 @@ plip_close(struct device *dev) } rcv->state = PLIP_PK_DONE; if (rcv->skb) { - rcv->skb->free = 1; kfree_skb(rcv->skb, FREE_READ); rcv->skb = NULL; } +#ifdef NOTDEF /* Reset. */ outb(0x00, PAR_CONTROL(dev)); +#endif MOD_DEC_USE_COUNT; return 0; } -static struct enet_statistics * +static int +plip_preempt(void *handle) +{ + struct device *dev = (struct device *)handle; + struct net_local *nl = (struct net_local *)dev->priv; + + /* Stand our ground if a datagram is on the wire */ + if (nl->connection != PLIP_CN_NONE) { + nl->should_relinquish = 1; + return 1; + } + + nl->port_owner = 0; /* Remember that we released the bus */ + return 0; +} + +static int +plip_wakeup(void *handle) +{ + struct device *dev = (struct device *)handle; + struct net_local *nl = (struct net_local *)dev->priv; + + if (nl->port_owner) { + /* Why are we being woken up? */ + printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name); + if (!parport_claim(nl->pardev)) + /* bus_owner is already set (but why?) */ + printk(KERN_DEBUG "%s: I'm broken.\n", dev->name); + else + return 1; + } + + if (!(dev->flags & IFF_UP)) + /* Don't need the port when the interface is down */ + return 1; + + if (!parport_claim(nl->pardev)) { + nl->port_owner = 1; + irq2dev_map[dev->irq] = dev; + /* Clear the data port. */ + outb (0x00, PAR_DATA(dev)); + } + + return 0; +} + +static struct net_device_stats * plip_get_stats(struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; - struct enet_statistics *r = &nl->enet_stats; + struct net_device_stats *r = &nl->enet_stats; return r; } @@ -1040,7 +1104,7 @@ plip_ioctl(struct device *dev, struct ifreq *rq, int cmd) { struct net_local *nl = (struct net_local *) dev->priv; struct plipconf *pc = (struct plipconf *) &rq->ifr_data; - + switch(pc->pcmd) { case PLIP_GET_TIMEOUT: pc->trigger = nl->trigger; @@ -1056,88 +1120,133 @@ plip_ioctl(struct device *dev, struct ifreq *rq, int cmd) return 0; } -#ifdef MODULE -static int io[] = {0, 0, 0}; -static int irq[] = {0, 0, 0}; - -static struct device dev_plip[] = { - { - "plip0", - 0, 0, 0, 0, /* memory */ - 0x3BC, 5, /* base, irq */ - 0, 0, 0, NULL, plip_init - }, - { - "plip1", - 0, 0, 0, 0, /* memory */ - 0x378, 7, /* base, irq */ - 0, 0, 0, NULL, plip_init - }, - { - "plip2", - 0, 0, 0, 0, /* memory */ - 0x278, 2, /* base, irq */ - 0, 0, 0, NULL, plip_init - } -}; +static int parport[PLIP_MAX] = { -1, }; +static int timid = 0; + +MODULE_PARM(parport, "1-" __MODULE_STRING(PLIP_MAX) "i"); +MODULE_PARM(timid, "1i"); -int -init_module(void) +static struct device *dev_plip[PLIP_MAX] = { NULL, }; + +#ifdef MODULE +void +cleanup_module(void) { - int no_parameters=1; - int devices=0; int i; - /* When user feeds parameters, use them */ - for (i=0; i < 3; i++) { - int specified=0; - - if (io[i] != 0) { - dev_plip[i].base_addr = io[i]; - specified++; - } - if (irq[i] != 0) { - dev_plip[i].irq = irq[i]; - specified++; - } - if (specified) { - if (register_netdev(&dev_plip[i]) != 0) { - printk(KERN_INFO "plip%d: Not found\n", i); - return -EIO; - } - no_parameters = 0; + for (i=0; i < PLIP_MAX; i++) { + if (dev_plip[i]) { + struct net_local *nl = + (struct net_local *)dev_plip[i]->priv; + unregister_netdev(dev_plip[i]); + if (nl->port_owner) + parport_release(nl->pardev); + parport_unregister_device(nl->pardev); + kfree(dev_plip[i]->priv); + kfree(dev_plip[i]->name); + kfree(dev_plip[i]); + dev_plip[i] = NULL; } } - if (!no_parameters) - return 0; +} + +#define plip_init init_module + +#else /* !MODULE */ - /* No parameters. Default action is probing all interfaces. */ - for (i=0; i < 3; i++) { - if (register_netdev(&dev_plip[i]) == 0) - devices++; +static int parport_ptr = 0; + +void plip_setup(char *str, int *ints) +{ + /* Ugh. */ + if (!strncmp(str, "parport", 7)) { + int n = simple_strtoul(str+7, NULL, 10); + if (parport_ptr < PLIP_MAX) + parport[parport_ptr++] = n; + else + printk(KERN_INFO "plip: too many ports, %s ignored.\n", + str); + } else if (!strcmp(str, "timid")) { + timid = 1; + } else { + if (ints[0] == 0 || ints[1] == 0) { + /* disable driver on "parport=" or "parport=0" */ + parport[0] = -2; + } else { + printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n", + ints[1]); + } } - if (devices == 0) { - printk(KERN_INFO "plip: no interfaces found\n"); - return -EIO; +} + +#endif /* MODULE */ + +static int inline +plip_searchfor(int list[], int a) +{ + int i; + for (i = 0; i < 3 && list[i] != -1; i++) { + if (list[i] == a) return 1; } return 0; } -void -cleanup_module(void) +__initfunc(int +plip_init(void)) { - int i; + struct parport *pb = parport_enumerate(); + int devices=0; + int i=0; + + if (parport[0] == -2) + return 0; - for (i=0; i < 3; i++) { - if (dev_plip[i].priv) { - unregister_netdev(&dev_plip[i]); - release_region(PAR_DATA(&dev_plip[i]), (PAR_DATA(&dev_plip[i]) == 0x3bc)? 3 : 8); - kfree_s(dev_plip[i].priv, sizeof(struct net_local)); - dev_plip[i].priv = NULL; + if (parport[0] != -1 && timid) { + printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n"); + timid = 0; + } + + /* When user feeds parameters, use them */ + while (pb) { + if ((parport[0] == -1 && (!timid || !pb->devices)) || + plip_searchfor(parport, i)) { + if (i == PLIP_MAX) { + printk(KERN_ERR "plip: too many devices\n"); + break; + } + dev_plip[i] = kmalloc(sizeof(struct device), + GFP_KERNEL); + if (!dev_plip[i]) { + printk(KERN_ERR "plip: memory squeeze\n"); + break; + } + memset(dev_plip[i], 0, sizeof(struct device)); + dev_plip[i]->name = + kmalloc(strlen("plipXXX"), GFP_KERNEL); + if (!dev_plip[i]->name) { + printk(KERN_ERR "plip: memory squeeze.\n"); + kfree(dev_plip[i]); + break; + } + sprintf(dev_plip[i]->name, "plip%d", i); + dev_plip[i]->priv = pb; + if (plip_init_dev(dev_plip[i],pb) || register_netdev(dev_plip[i])) { + kfree(dev_plip[i]->name); + kfree(dev_plip[i]); + } else { + devices++; + } } + i++; + pb = pb->next; + } + + if (devices == 0) { + printk(KERN_INFO "plip: no devices registered\n"); + return -EIO; } + return 0; } -#endif /* MODULE */ /* * Local variables: |