summaryrefslogtreecommitdiffstats
path: root/net/eth.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-09-25 08:06:22 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2020-09-25 08:06:22 +0200
commitfaddac631a0e521780854ecf9a53f96481a32716 (patch)
tree292c866315fd161df41d90f4bbde1572b14415d1 /net/eth.c
parent1ab2e649dc0ccef7ee990b999e1d8f8c32e7cd24 (diff)
parent0d4d157cf0d42206491b7a1b417c9904a9abd553 (diff)
downloadbarebox-faddac631a0e521780854ecf9a53f96481a32716.tar.gz
barebox-faddac631a0e521780854ecf9a53f96481a32716.tar.xz
Merge branch 'for-next/net' into master
Diffstat (limited to 'net/eth.c')
-rw-r--r--net/eth.c88
1 files changed, 81 insertions, 7 deletions
diff --git a/net/eth.c b/net/eth.c
index e3d0d06efe..85110ef695 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -22,6 +22,7 @@
#include <init.h>
#include <dhcp.h>
#include <net.h>
+#include <dma.h>
#include <of.h>
#include <linux/phy.h>
#include <errno.h>
@@ -208,6 +209,36 @@ static int eth_carrier_check(struct eth_device *edev, int force)
return edev->phydev->link ? 0 : -ENETDOWN;
}
+struct eth_q {
+ struct eth_device *edev;
+ int length;
+ struct list_head list;
+ void *data;
+};
+
+static int eth_queue(struct eth_device *edev, void *packet, int length)
+{
+ struct eth_q *q;
+
+ q = xzalloc(sizeof(*q));
+ if (!q)
+ return -ENOMEM;
+
+ q->data = dma_alloc(length);
+ if (!q->data) {
+ free(q);
+ return -ENOMEM;
+ }
+
+ q->length = length;
+ q->edev = edev;
+
+ memcpy(q->data, packet, length);
+ list_add_tail(&q->list, &edev->send_queue);
+
+ return 0;
+}
+
int eth_send(struct eth_device *edev, void *packet, int length)
{
int ret;
@@ -215,24 +246,51 @@ int eth_send(struct eth_device *edev, void *packet, int length)
if (!edev->active)
return -ENETDOWN;
+ if (slice_acquired(eth_device_slice(edev)))
+ return eth_queue(edev, packet, length);
+
ret = eth_carrier_check(edev, 0);
if (ret)
return ret;
+ slice_acquire(eth_device_slice(edev));
+
led_trigger_network(LED_TRIGGER_NET_TX);
- return edev->send(edev, packet, length);
+ ret = edev->send(edev, packet, length);
+
+ slice_release(eth_device_slice(edev));
+
+ return ret;
}
-static int __eth_rx(struct eth_device *edev)
+static void eth_do_work(struct eth_device *edev)
{
+ struct eth_q *q, *tmp;
int ret;
- ret = eth_carrier_check(edev, 0);
- if (ret)
- return ret;
+ if (!phy_acquired(edev->phydev)) {
+ ret = eth_carrier_check(edev, 0);
+ if (ret)
+ return;
+ }
+
+ if (slice_acquired(eth_device_slice(edev)))
+ return;
+
+ slice_acquire(eth_device_slice(edev));
+
+ edev->recv(edev);
- return edev->recv(edev);
+ list_for_each_entry_safe(q, tmp, &edev->send_queue, list) {
+ led_trigger_network(LED_TRIGGER_NET_TX);
+ edev->send(edev, q->data, q->length);
+ list_del(&q->list);
+ free(q->data);
+ free(q);
+ }
+
+ slice_release(eth_device_slice(edev));
}
int eth_rx(void)
@@ -241,7 +299,7 @@ int eth_rx(void)
for_each_netdev(edev) {
if (edev->active)
- __eth_rx(edev);
+ eth_do_work(edev);
}
return 0;
@@ -349,10 +407,14 @@ int eth_register(struct eth_device *edev)
edev->dev.id = DEVICE_ID_DYNAMIC;
}
+ INIT_LIST_HEAD(&edev->send_queue);
+
ret = register_device(&edev->dev);
if (ret)
return ret;
+ slice_init(&edev->slice, dev_name(dev));
+
edev->devname = xstrdup(dev_name(&edev->dev));
dev_add_param_ip(dev, "ipaddr", NULL, NULL, &edev->ipaddr, edev);
@@ -422,15 +484,27 @@ void eth_close(struct eth_device *edev)
void eth_unregister(struct eth_device *edev)
{
+ struct eth_q *q, *tmp;
+
if (edev->active)
edev->halt(edev);
+ list_for_each_entry_safe(q, tmp, &edev->send_queue, list) {
+ if (q->edev != edev)
+ continue;
+
+ list_del(&q->list);
+ free(q->data);
+ free(q);
+ }
+
if (IS_ENABLED(CONFIG_OFDEVICE))
free(edev->nodepath);
free(edev->devname);
unregister_device(&edev->dev);
+ slice_exit(&edev->slice);
list_del(&edev->list);
}