|
@@ -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)
|