diff options
Diffstat (limited to 'drivers/net/cops.c')
-rw-r--r-- | drivers/net/cops.c | 360 |
1 files changed, 225 insertions, 135 deletions
diff --git a/drivers/net/cops.c b/drivers/net/cops.c index 1231d6b36..1c0635c62 100644 --- a/drivers/net/cops.c +++ b/drivers/net/cops.c @@ -24,10 +24,16 @@ * Hooks for cops_setup routine * (not yet implemented). * 19971101 Jay Schulist Fixes for multiple lt* devices. + * 19980607 Steven Hirsch Fixed the badly broken support + * for Tangent type cards. Only + * tested on Daystar LT200. Some + * cleanup of formatting and program + * logic. Added emacs 'local-vars' + * setup for Jay's brace style. */ static const char *version = - "cops.c:v0.02 3/17/97 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n"; +"cops.c:v0.04 6/7/98 Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n"; /* * Sources: * COPS Localtalk SDK. This provides almost all of the information @@ -118,17 +124,27 @@ static int irq = 0; /* Default IRQ */ * * This driver should support: * TANGENT driver mode: - * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200 + * Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200, + * COPS LT-1 * DAYNA driver mode: * Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, * Farallon PhoneNET PC III, Farallon PhoneNET PC II * Other cards possibly supported mode unkown though: - * Dayna DL2000 (Full length) + * Dayna DL2000 (Full length), COPS LT/M (Micro-Channel) * * Cards NOT supported by this driver but supported by the ltpc.c * driver written by Bradford W. Johnson <johns393@maroon.tc.umn.edu> * Farallon PhoneNET PC * Original Apple LocalTalk PC card + * + * N.B. + * + * The Daystar Digital LT200 boards do not support interrupt-driven + * IO. You must specify 'io=0xff' as a module parameter to invoke + * polled mode. I also believe that the port probing logic is quite + * dangerous at best and certainly hopeless for a polled card. Best to + * specify both. - Steve H. + * */ /* @@ -149,7 +165,9 @@ static int cops_irqlist[] = { 5, 4, 3, 0 }; -/* use 0 for production, 1 for verification, 2 for debug, 3 for very verbose debug */ +static struct timer_list cops_timer; + +/* use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */ #ifndef COPS_DEBUG #define COPS_DEBUG 1 #endif @@ -181,11 +199,13 @@ static void cops_load (struct device *dev); static int cops_nodeid (struct device *dev, int nodeid); static void cops_interrupt (int irq, void *dev_id, struct pt_regs *regs); +static void cops_poll (unsigned long ltdev); static void cops_rx (struct device *dev); static int cops_send_packet (struct sk_buff *skb, struct device *dev); static void set_multicast_list (struct device *dev); static int cops_hard_header (struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len); + unsigned short type, void *daddr, void *saddr, + unsigned len); static int cops_ioctl (struct device *dev, struct ifreq *rq, int cmd); static int cops_close (struct device *dev); @@ -193,11 +213,10 @@ static struct enet_statistics *cops_get_stats (struct device *dev); /* - * Check for a network adaptor of this type, and return '0' iff one exists. + * Check for a network adaptor of this type, and return '0' iff one exists. * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). + * If dev->base_addr in [1..0x1ff], always return failure. + * otherwise go with what we pass in. */ __initfunc(int cops_probe(struct device *dev)) { @@ -212,6 +231,11 @@ __initfunc(int cops_probe(struct device *dev)) else if(base_addr != 0) /* Don't probe at all. */ return -ENXIO; + /* FIXME Does this really work for cards which generate irq? + * It's definitely N.G. for polled Tangent. sh + * Dayna cards don't autoprobe well at all, but if your card is + * at IRQ 5 & IO 0x240 we find it every time. ;) JS + */ for(i=0; cops_portlist[i]; i++) { int ioaddr = cops_portlist[i]; if(check_region(ioaddr, COPS_IO_EXTENT)) @@ -232,17 +256,12 @@ __initfunc(static int cops_probe1(struct device *dev, int ioaddr)) { struct cops_local *lp; static unsigned version_printed = 0; - int irqaddr = 0; - int irqval; int board = board_type; if(cops_debug && version_printed++ == 0) printk("%s", version); - /* Fill in the 'dev' fields. */ - dev->base_addr = ioaddr; - /* * Since this board has jumpered interrupts, allocate the interrupt * vector now. There is no point in waiting since no other device @@ -250,37 +269,47 @@ __initfunc(static int cops_probe1(struct device *dev, int ioaddr)) * interrupts are typically not reported by the boards, and we must * used AutoIRQ to find them. */ - - if(dev->irq < 2 && irq) - dev->irq = irq; - - if(dev->irq < 2) + switch (dev->irq) { - irqaddr = cops_irq(ioaddr, board); /* COPS AutoIRQ routine */ - if(irqaddr == 0) - return -EAGAIN; /* No IRQ found on this port */ - else - dev->irq = irqaddr; - } - else if(dev->irq == 2) - /* - * Fixup for users that don't know that IRQ 2 is really + case 0: + /* COPS AutoIRQ routine */ + dev->irq = cops_irq(ioaddr, board); + if(!dev->irq) + return -EINVAL; /* No IRQ found on this port */ + break; + + case 1: + return -EINVAL; + break; + + /* Fixup for users that don't know that IRQ 2 is really * IRQ 9, or don't know which one to set. */ - dev->irq = 9; + case 2: + dev->irq = 9; + break; - /* Snarf the interrupt now. */ - irqval = request_irq(dev->irq, &cops_interrupt, 0, cardname, dev); + /* Polled operation requested. Although irq of zero passed as + * a parameter tells the init routines to probe, we'll + * overload it to denote polled operation at runtime. + */ + case 0xff: + dev->irq = 0; + break; - /* If its in use set it to 0 and disallow open() calls.. users can still - ifconfig the irq one day */ + default: + break; + } - if(irqval) - dev->irq = 0; + /* Reserve any actual interrupt. */ + if(dev->irq && request_irq(dev->irq, &cops_interrupt, 0, cardname, dev)) + return -EINVAL; - dev->hard_start_xmit = &cops_send_packet; + /* Grab the region so no one else tries to probe our ioports. */ + request_region(ioaddr, COPS_IO_EXTENT, cardname); + dev->base_addr = ioaddr; - /* Initialize the device structure. */ + /* Initialize the private device structure. */ dev->priv = kmalloc(sizeof(struct cops_local), GFP_KERNEL); if(dev->priv == NULL) return -ENOMEM; @@ -291,20 +320,10 @@ __initfunc(static int cops_probe1(struct device *dev, int ioaddr)) /* Copy local board variable to lp struct. */ lp->board = board; - /* Tell the user where the card is and what mode were in. */ - if(board==DAYNA) - printk("%s: %s found at %#3x, using IRQ %d, in Dayna mode.\n", - dev->name, cardname, ioaddr, dev->irq); - if(board==TANGENT) - printk("%s: %s found at %#3x, using IRQ %d, in Tangent mode.\n", - dev->name, cardname, ioaddr, dev->irq); - - /* Grab the region so no one else tries to probe our ioports. */ - request_region(ioaddr, COPS_IO_EXTENT, cardname); - /* Fill in the fields of the device structure with LocalTalk values. */ ltalk_setup(dev); + dev->hard_start_xmit = &cops_send_packet; dev->hard_header = cops_hard_header; dev->get_stats = cops_get_stats; dev->open = cops_open; @@ -313,13 +332,26 @@ __initfunc(static int cops_probe1(struct device *dev, int ioaddr)) dev->set_multicast_list = &set_multicast_list; dev->mc_list = NULL; + /* Tell the user where the card is and what mode we're in. */ + if(board==DAYNA) + printk("%s: %s at %#3x, using IRQ %d, in Dayna mode.\n", + dev->name, cardname, ioaddr, dev->irq); + if(board==TANGENT) { + if(dev->irq) + printk("%s: %s at %#3x, IRQ %d, in Tangent mode\n", + dev->name, cardname, ioaddr, dev->irq); + else + printk("%s: %s at %#3x, using polled IO, in Tangent mode.\n", + dev->name, cardname, ioaddr); + + } return 0; } __initfunc(static int cops_irq (int ioaddr, int board)) { /* * This does not use the IRQ to determine where the IRQ is. We just - * assume that when we get a correct status response that is the IRQ then. + * assume that when we get a correct status response that it's the IRQ. * This really just verifies the IO port but since we only have access * to such a small number of IRQs (5, 4, 3) this is not bad. * This will probably not work for more than one card. @@ -367,10 +399,27 @@ __initfunc(static int cops_irq (int ioaddr, int board)) */ static int cops_open(struct device *dev) { + struct cops_local *lp = (struct cops_local *)dev->priv; + if(dev->irq==0) { - printk(KERN_WARNING "%s: No irq line set.\n", dev->name); - return -EAGAIN; + /* + * I don't know if the Dayna-style boards support polled + * operation. For now, only allow it for Tangent. + */ + if(lp->board==TANGENT) /* Poll 20 times per second */ + { + init_timer(&cops_timer); + cops_timer.function = cops_poll; + cops_timer.data = (unsigned long)dev; + cops_timer.expires = jiffies + 5; + add_timer(&cops_timer); + } + else + { + printk(KERN_WARNING "%s: No irq line set\n", dev->name); + return -EAGAIN; + } } cops_jumpstart(dev); /* Start the card up. */ @@ -397,7 +446,7 @@ static int cops_jumpstart(struct device *dev) * Once the card has the firmware loaded and has acquired * the nodeid, if it is reset it will lose it all. */ - cops_reset(dev,1); /* Need to reset card before load firmware. */ + cops_reset(dev,1); /* Need to reset card before load firmware. */ cops_load(dev); /* Load the firmware. */ /* @@ -412,14 +461,12 @@ static int cops_jumpstart(struct device *dev) return 0; } -static int tangent_wait_reset(int ioaddr) +static void tangent_wait_reset(int ioaddr) { - int timeout=0; + int timeout=0; - while(timeout < 5000 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) - udelay(1000); /* Wait 1000 useconds */ - - return 0; + while(timeout++ < 5 && (inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0) + mdelay(1); /* Wait 1 second */ } /* @@ -432,23 +479,11 @@ static void cops_reset(struct device *dev, int sleep) if(lp->board==TANGENT) { - inb(ioaddr); /* Clear request latch. */ - outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ + inb(ioaddr); /* Clear request latch. */ + outb(0,ioaddr); /* Clear the TANG_TX_READY flop. */ outb(0, ioaddr+TANG_RESET); /* Reset the adapter. */ - /* Can take 5 seconds max - youch! */ - if(sleep) - { - long snapt=jiffies; - while(jiffies-snapt<5*HZ) - { - if(inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY) - break; - schedule(); - } - } - else - tangent_wait_reset(ioaddr); + tangent_wait_reset(ioaddr); outb(0, ioaddr+TANG_CLEAR_INT); } if(lp->board==DAYNA) @@ -543,12 +578,13 @@ static void cops_load (struct device *dev) } if(cops_debug > 1) - printk(KERN_DEBUG "%s: Uploaded firmware - %d bytes of %d bytes.\n", dev->name, i, ltf->length); + printk("%s: Uploaded firmware - %d bytes of %d bytes.\n", + dev->name, i, ltf->length); - if(lp->board==DAYNA) - outb(1, ioaddr+DAYNA_INT_CARD); /* Tell Dayna to run the firmware code. */ - else - inb(ioaddr); /* Tell Tang to run the firmware code. */ + if(lp->board==DAYNA) /* Tell Dayna to run the firmware code. */ + outb(1, ioaddr+DAYNA_INT_CARD); + else /* Tell Tang to run the firmware code. */ + inb(ioaddr); if(lp->board==TANGENT) { @@ -575,16 +611,16 @@ static int cops_nodeid (struct device *dev, int nodeid) /* Empty any pending adapter responses. */ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0) { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupts. */ if((inb(ioaddr+DAYNA_CARD_STATUS)&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); /* Kick out any packet waiting. */ + cops_rx(dev); /* Kick any packets waiting. */ schedule(); } - outb(2, ioaddr); /* Output command packet length as 2. */ + outb(2, ioaddr); /* Output command packet length as 2. */ outb(0, ioaddr); - outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ - outb(nodeid, ioaddr); /* Suggest node address. */ + outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + outb(nodeid, ioaddr); /* Suggest node address. */ } if(lp->board == TANGENT) @@ -592,19 +628,19 @@ static int cops_nodeid (struct device *dev, int nodeid) /* Empty any pending adapter responses. */ while(inb(ioaddr+TANG_CARD_STATUS)&TANG_RX_READY) { - outb(0, ioaddr+COPS_CLEAR_INT); /* Clear any interrupt. */ - cops_rx(dev); /* Kick out any packet waiting. */ + outb(0, ioaddr+COPS_CLEAR_INT); /* Clear interrupt. */ + cops_rx(dev); /* Kick out packets waiting. */ schedule(); } - /* Not sure what Tangent does if random nodeid we picked is already used. */ + /* Not sure what Tangent does if nodeid picked is used. */ if(nodeid == 0) /* Seed. */ - nodeid = jiffies&0xFF; /* Get a random try .*/ - outb(2, ioaddr); /* Command length LSB. */ - outb(0, ioaddr); /* Command length MSB. */ - outb(LAP_INIT, ioaddr); /* Send LAP_INIT command byte. */ + nodeid = jiffies&0xFF; /* Get a random try */ + outb(2, ioaddr); /* Command length LSB */ + outb(0, ioaddr); /* Command length MSB */ + outb(LAP_INIT, ioaddr); /* Send LAP_INIT byte */ outb(nodeid, ioaddr); /* LAP address hint. */ - outb(0xFF, ioaddr); /* Interrupt level to use (NONE). */ + outb(0xFF, ioaddr); /* Int. level to use */ } lp->node_acquire=0; /* Set nodeid holder to 0. */ @@ -626,7 +662,8 @@ static int cops_nodeid (struct device *dev, int nodeid) } if(cops_debug > 1) - printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", dev->name, lp->node_acquire); + printk(KERN_DEBUG "%s: Node ID %d has been acquired.\n", + dev->name, lp->node_acquire); lp->nodeid=1; /* Set got nodeid to 1. */ @@ -634,6 +671,37 @@ static int cops_nodeid (struct device *dev, int nodeid) } /* + * Poll the Tangent type cards to see if we have work. + */ +static void cops_poll(unsigned long ltdev) +{ + int ioaddr, status; + int boguscount = 0; + + struct device *dev = (struct device *)ltdev; + + del_timer(&cops_timer); + + if(dev == NULL) + return; /* We've been downed */ + + ioaddr = dev->base_addr; + do { + status=inb(ioaddr+TANG_CARD_STATUS); + if(status & TANG_RX_READY) + cops_rx(dev); + if(status & TANG_TX_READY) + dev->tbusy = 0; + status = inb(ioaddr+TANG_CARD_STATUS); + } while((++boguscount < 20) && (status&(TANG_RX_READY|TANG_TX_READY))); + + cops_timer.expires = jiffies+5; + add_timer(&cops_timer); + + return; +} + +/* * The typical workload of the driver: * Handle the network interface interrupts. */ @@ -646,7 +714,8 @@ static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs) if(dev == NULL) { - printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + cardname, irq); return; } dev->interrupt = 1; @@ -654,29 +723,31 @@ static void cops_interrupt(int irq, void *dev_id, struct pt_regs * regs) ioaddr = dev->base_addr; lp = (struct cops_local *)dev->priv; - do - { - /* Clear any interrupt. */ - outb(0, ioaddr + COPS_CLEAR_INT); - - if(lp->board==DAYNA) - { - status=inb(ioaddr+DAYNA_CARD_STATUS); - if((status&0x03)==DAYNA_RX_REQUEST) - cops_rx(dev); - } - else - { - status=inb(ioaddr+TANG_CARD_STATUS); - if(status&TANG_RX_READY) - cops_rx(dev); - } + if(lp->board==DAYNA) + { + do { + outb(0, ioaddr + COPS_CLEAR_INT); + status=inb(ioaddr+DAYNA_CARD_STATUS); + if((status&0x03)==DAYNA_RX_REQUEST) + cops_rx(dev); + dev->tbusy = 0; + mark_bh(NET_BH); + } while(++boguscount < 20); + } + else + { + do { + status=inb(ioaddr+TANG_CARD_STATUS); + if(status & TANG_RX_READY) + cops_rx(dev); + if(status & TANG_TX_READY) + dev->tbusy = 0; + status=inb(ioaddr+TANG_CARD_STATUS); + } while((++boguscount < 20) && + (status&(TANG_RX_READY|TANG_TX_READY))); + } - dev->tbusy = 0; - mark_bh(NET_BH); - } while (++boguscount < 20 ); dev->interrupt = 0; - return; } @@ -715,7 +786,10 @@ static void cops_rx(struct device *dev) } /* Get response length. */ - pkt_len = inb(ioaddr) & 0xFF; + if(lp->board==DAYNA) + pkt_len = inb(ioaddr) & 0xFF; + else + pkt_len = inb(ioaddr) & 0x00FF; pkt_len |= (inb(ioaddr) << 8); /* Input IO code. */ rsp_type=inb(ioaddr); @@ -724,7 +798,8 @@ static void cops_rx(struct device *dev) skb = dev_alloc_skb(pkt_len); if(skb == NULL) { - printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", + dev->name); lp->stats.rx_dropped++; while(pkt_len--) /* Discard packet */ inb(ioaddr); @@ -737,14 +812,15 @@ static void cops_rx(struct device *dev) insb(ioaddr, skb->data, pkt_len); /* Eat the Data */ if(lp->board==DAYNA) - outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card. */ + outb(1, ioaddr+DAYNA_INT_CARD); /* Interrupt the card */ sti(); /* Restore interrupts. */ /* Check for bad response length */ if(pkt_len < 0 || pkt_len > MAX_LLAP_SIZE) { - printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", dev->name, pkt_len); + printk(KERN_NOTICE "%s: Bad packet length of %d bytes.\n", + dev->name, pkt_len); lp->stats.tx_errors++; kfree_skb(skb); return; @@ -752,8 +828,8 @@ static void cops_rx(struct device *dev) /* Set nodeid and then get out. */ if(rsp_type == LAP_INIT_RSP) - { - lp->node_acquire = skb->data[0]; /* Nodeid taken from received packet. */ + { /* Nodeid taken from received packet. */ + lp->node_acquire = skb->data[0]; kfree_skb(skb); return; } @@ -769,7 +845,7 @@ static void cops_rx(struct device *dev) skb->mac.raw = skb->data; /* Point to entire packet. */ skb_pull(skb,3); - skb->h.raw = skb->data; /* Point to just the data (Skip header). */ + skb->h.raw = skb->data; /* Point to data (Skip header). */ /* Update the counters. */ lp->stats.rx_packets++; @@ -815,26 +891,22 @@ static int cops_send_packet(struct sk_buff *skb, struct device *dev) * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if(test_and_set_bit(0, (void*) &dev->tbusy) != 0) - printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); + printk(KERN_WARNING "%s: Transmitter access conflict.\n", + dev->name); else { cli(); /* Disable interrupts. */ - if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ + if(lp->board == DAYNA) /* Wait for adapter transmit buffer. */ while((inb(ioaddr+DAYNA_CARD_STATUS)&DAYNA_TX_READY)==0); - if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ + if(lp->board == TANGENT) /* Wait for adapter transmit buffer. */ while((inb(ioaddr+TANG_CARD_STATUS)&TANG_TX_READY)==0); /* Output IO length. */ + outb(skb->len, ioaddr); if(lp->board == DAYNA) - { - outb(skb->len, ioaddr); outb(skb->len >> 8, ioaddr); - } else - { - outb(skb->len&0x0FF, ioaddr); outb((skb->len >> 8)&0x0FF, ioaddr); - } /* Output IO code. */ outb(LAP_WRITE, ioaddr); @@ -844,7 +916,7 @@ static int cops_send_packet(struct sk_buff *skb, struct device *dev) outsb(ioaddr, skb->data, skb->len); /* Send out the data. */ - if(lp->board==DAYNA) /* The Dayna requires you kick the card. */ + if(lp->board==DAYNA) /* Dayna requires you kick the card */ outb(1, ioaddr+DAYNA_INT_CARD); sti(); /* Restore interrupts. */ @@ -868,7 +940,7 @@ static int cops_send_packet(struct sk_buff *skb, struct device *dev) static void set_multicast_list(struct device *dev) { if(cops_debug >= 3) - printk("%s: set_mulicast_list executed. NeatO.\n", dev->name); + printk("%s: set_multicast_list executed\n", dev->name); } /* @@ -876,7 +948,8 @@ static void set_multicast_list(struct device *dev) */ static int cops_hard_header(struct sk_buff *skb, struct device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) + unsigned short type, void *daddr, void *saddr, + unsigned len) { if(cops_debug >= 3) printk("%s: cops_hard_header executed. Wow!\n", dev->name); @@ -925,6 +998,13 @@ static int cops_ioctl(struct device *dev, struct ifreq *ifr, int cmd) static int cops_close(struct device *dev) { + struct cops_local *lp = (struct cops_local *)dev->priv; + + /* If we were running polled, yank the timer. + */ + if(lp->board==TANGENT && dev->irq==0) + del_timer(&cops_timer); + dev->tbusy = 1; dev->start = 0; @@ -966,7 +1046,8 @@ int init_module(void) int result, err; if(io == 0) - printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", cardname); + printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n", + cardname); /* Copy the parameters from insmod into the device structure. */ cops0_dev.base_addr = io; @@ -988,7 +1069,16 @@ void cleanup_module(void) unregister_netdev(&cops0_dev); if(cops0_dev.priv) kfree_s(cops0_dev.priv, sizeof(struct cops_local)); - free_irq(cops0_dev.irq, &cops0_dev); + if(cops0_dev.irq) + free_irq(cops0_dev.irq, &cops0_dev); release_region(cops0_dev.base_addr, COPS_IO_EXTENT); } #endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O2 -c cops.c" + * c-basic-offset: 4 + * c-file-offsets: ((substatement-open . 0)) + * End: + */ |