diff options
Diffstat (limited to 'drivers/net/arcnet/arcnet.c')
-rw-r--r-- | drivers/net/arcnet/arcnet.c | 107 |
1 files changed, 99 insertions, 8 deletions
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index e41dd36fe832..6ea963e3b89a 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -52,6 +52,8 @@ #include <linux/init.h> #include <linux/jiffies.h> +#include <linux/leds.h> + #include "arcdevice.h" #include "com9026.h" @@ -189,6 +191,71 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum, #endif +/* Trigger a LED event in response to a ARCNET device event */ +void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event) +{ + struct arcnet_local *lp = netdev_priv(dev); + unsigned long led_delay = 350; + unsigned long tx_delay = 50; + + switch (event) { + case ARCNET_LED_EVENT_RECON: + led_trigger_blink_oneshot(lp->recon_led_trig, + &led_delay, &led_delay, 0); + break; + case ARCNET_LED_EVENT_OPEN: + led_trigger_event(lp->tx_led_trig, LED_OFF); + led_trigger_event(lp->recon_led_trig, LED_OFF); + break; + case ARCNET_LED_EVENT_STOP: + led_trigger_event(lp->tx_led_trig, LED_OFF); + led_trigger_event(lp->recon_led_trig, LED_OFF); + break; + case ARCNET_LED_EVENT_TX: + led_trigger_blink_oneshot(lp->tx_led_trig, + &tx_delay, &tx_delay, 0); + break; + } +} +EXPORT_SYMBOL_GPL(arcnet_led_event); + +static void arcnet_led_release(struct device *gendev, void *res) +{ + struct arcnet_local *lp = netdev_priv(to_net_dev(gendev)); + + led_trigger_unregister_simple(lp->tx_led_trig); + led_trigger_unregister_simple(lp->recon_led_trig); +} + +/* Register ARCNET LED triggers for a arcnet device + * + * This is normally called from a driver's probe function + */ +void devm_arcnet_led_init(struct net_device *netdev, int index, int subid) +{ + struct arcnet_local *lp = netdev_priv(netdev); + void *res; + + res = devres_alloc(arcnet_led_release, 0, GFP_KERNEL); + if (!res) { + netdev_err(netdev, "cannot register LED triggers\n"); + return; + } + + snprintf(lp->tx_led_trig_name, sizeof(lp->tx_led_trig_name), + "arc%d-%d-tx", index, subid); + snprintf(lp->recon_led_trig_name, sizeof(lp->recon_led_trig_name), + "arc%d-%d-recon", index, subid); + + led_trigger_register_simple(lp->tx_led_trig_name, + &lp->tx_led_trig); + led_trigger_register_simple(lp->recon_led_trig_name, + &lp->recon_led_trig); + + devres_add(&netdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_arcnet_led_init); + /* Unregister a protocol driver from the arc_proto_map. Protocol drivers * are responsible for registering themselves, but the unregister routine * is pretty generic so we'll do it here. @@ -314,6 +381,16 @@ static void arcdev_setup(struct net_device *dev) dev->flags = IFF_BROADCAST; } +static void arcnet_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + + if (!netif_carrier_ok(dev)) { + netif_carrier_on(dev); + netdev_info(dev, "link up\n"); + } +} + struct net_device *alloc_arcdev(const char *name) { struct net_device *dev; @@ -325,6 +402,9 @@ struct net_device *alloc_arcdev(const char *name) struct arcnet_local *lp = netdev_priv(dev); spin_lock_init(&lp->lock); + init_timer(&lp->timer); + lp->timer.data = (unsigned long) dev; + lp->timer.function = arcnet_timer; } return dev; @@ -423,8 +503,11 @@ int arcnet_open(struct net_device *dev) lp->hw.intmask(dev, lp->intmask); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + netif_carrier_off(dev); netif_start_queue(dev); + mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000)); + arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN); return 0; out_module_put: @@ -438,7 +521,11 @@ int arcnet_close(struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); + arcnet_led_event(dev, ARCNET_LED_EVENT_STOP); + del_timer_sync(&lp->timer); + netif_stop_queue(dev); + netif_carrier_off(dev); /* flush TX and disable RX */ lp->hw.intmask(dev, 0); @@ -515,7 +602,7 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, struct ArcProto *proto; int txbuf; unsigned long flags; - int freeskb, retval; + int retval; arc_printk(D_DURING, dev, "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n", @@ -554,15 +641,13 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, * the package later - forget about it now */ dev->stats.tx_bytes += skb->len; - freeskb = 1; + dev_kfree_skb(skb); } else { /* do it the 'split' way */ lp->outgoing.proto = proto; lp->outgoing.skb = skb; lp->outgoing.pkt = pkt; - freeskb = 0; - if (proto->continue_tx && proto->continue_tx(dev, txbuf)) { arc_printk(D_NORMAL, dev, @@ -574,7 +659,6 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, lp->next_tx = txbuf; } else { retval = NETDEV_TX_BUSY; - freeskb = 0; } arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", @@ -588,10 +672,9 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", __FILE__, __LINE__, __func__, lp->hw.status(dev)); - spin_unlock_irqrestore(&lp->lock, flags); - if (freeskb) - dev_kfree_skb(skb); + arcnet_led_event(dev, ARCNET_LED_EVENT_TX); + spin_unlock_irqrestore(&lp->lock, flags); return retval; /* no need to try again */ } EXPORT_SYMBOL(arcnet_send_packet); @@ -843,6 +926,13 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n", status); + if (netif_carrier_ok(dev)) { + netif_carrier_off(dev); + netdev_info(dev, "link down\n"); + } + mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000)); + + arcnet_led_event(dev, ARCNET_LED_EVENT_RECON); /* MYRECON bit is at bit 7 of diagstatus */ if (diagstatus & 0x80) arc_printk(D_RECON, dev, "Put out that recon myself\n"); @@ -893,6 +983,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) lp->num_recons = lp->network_down = 0; arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n"); + netif_carrier_on(dev); } if (didsomething) |