summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2020-03-06 15:06:52 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2020-08-19 07:34:45 +0200
commitc6b23fc0169057c4b4ffb530a71ec3a4742a4530 (patch)
tree2ae8b2f05e9d559c58a30c8a705b44805a317ffa /net
parent5a66288569a1960d34312d8a30c93da48bee95f2 (diff)
downloadbarebox-c6b23fc0169057c4b4ffb530a71ec3a4742a4530.tar.gz
barebox-c6b23fc0169057c4b4ffb530a71ec3a4742a4530.tar.xz
net: Add a slice to struct eth_device
Add ethernet code safe for being called from a poller. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'net')
-rw-r--r--net/eth.c74
1 files changed, 72 insertions, 2 deletions
diff --git a/net/eth.c b/net/eth.c
index 25815c519b..4cd7d6119e 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,47 @@ 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 void eth_do_work(struct eth_device *edev)
{
+ struct eth_q *q, *tmp;
int ret;
+ slice_acquire(eth_device_slice(edev));
+
ret = eth_carrier_check(edev, 0);
if (ret)
- return;
+ goto out;
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);
+ }
+
+out:
+ slice_release(eth_device_slice(edev));
}
int eth_rx(void)
@@ -349,10 +403,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 +480,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);
}