]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/arcnet/arcnet.c
Merge remote-tracking branch 'input-current/for-linus'
[karo-tx-linux.git] / drivers / net / arcnet / arcnet.c
index e41dd36fe832f6892f0cea1a9b8257cd8fa0fe9e..6ea963e3b89a1ab1c78ccb4d63c99a16e1bfbe0b 100644 (file)
@@ -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)