This lets the CPC host device drivers dequeue frames when it's
convenient for them to do so, instead of forcing each to them to
implement a queue to store pending skbs.
The callback is changed from `transmit` to `wake_tx` and let CPC core
notify these drivers when there is something to transmit.
Signed-off-by: Damien Riégel <damien.riegel@silabs.com>
---
drivers/greybus/cpc/host.c | 49 +++++++++++++++++++++++++++++++++++---
drivers/greybus/cpc/host.h | 10 ++++++--
2 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/drivers/greybus/cpc/host.c b/drivers/greybus/cpc/host.c
index 0f9aa394690..7ae5bb0666f 100644
--- a/drivers/greybus/cpc/host.c
+++ b/drivers/greybus/cpc/host.c
@@ -156,6 +156,7 @@ static struct gb_hd_driver cpc_gb_driver = {
static void cpc_hd_init(struct cpc_host_device *cpc_hd)
{
mutex_init(&cpc_hd->lock);
+ skb_queue_head_init(&cpc_hd->tx_queue);
}
struct cpc_host_device *cpc_hd_create(struct cpc_hd_driver *driver, struct device *parent)
@@ -163,7 +164,7 @@ struct cpc_host_device *cpc_hd_create(struct cpc_hd_driver *driver, struct devic
struct cpc_host_device *cpc_hd;
struct gb_host_device *hd;
- if (!driver->transmit) {
+ if (!driver->wake_tx) {
dev_err(parent, "missing mandatory callback\n");
return ERR_PTR(-EINVAL);
}
@@ -232,13 +233,55 @@ EXPORT_SYMBOL_GPL(cpc_hd_rcvd);
* @cpc_hd: Host device to send SKB over.
* @skb: SKB to send.
*/
-int cpc_hd_send_skb(struct cpc_host_device *cpc_hd, struct sk_buff *skb)
+void cpc_hd_send_skb(struct cpc_host_device *cpc_hd, struct sk_buff *skb)
{
const struct cpc_hd_driver *drv = cpc_hd->driver;
- return drv->transmit(cpc_hd, skb);
+ mutex_lock(&cpc_hd->lock);
+ skb_queue_tail(&cpc_hd->tx_queue, skb);
+ mutex_unlock(&cpc_hd->lock);
+
+ drv->wake_tx(cpc_hd);
}
+/**
+ * cpc_hd_tx_queue_empty() - Check if transmit queue is empty.
+ * @cpc_hd: CPC Host Device.
+ *
+ * Return: True if transmit queue is empty, false otherwise.
+ */
+bool cpc_hd_tx_queue_empty(struct cpc_host_device *cpc_hd)
+{
+ bool empty;
+
+ mutex_lock(&cpc_hd->lock);
+ empty = skb_queue_empty(&cpc_hd->tx_queue);
+ mutex_unlock(&cpc_hd->lock);
+
+ return empty;
+}
+EXPORT_SYMBOL_GPL(cpc_hd_tx_queue_empty);
+
+/**
+ * cpc_hd_dequeue() - Get the next SKB that was queued for transmission.
+ * @cpc_hd: CPC Host Device.
+ *
+ * Get an SKB that was previously queued by cpc_hd_send_skb().
+ *
+ * Return: An SKB, or %NULL if queue was empty.
+ */
+struct sk_buff *cpc_hd_dequeue(struct cpc_host_device *cpc_hd)
+{
+ struct sk_buff *skb;
+
+ mutex_lock(&cpc_hd->lock);
+ skb = skb_dequeue(&cpc_hd->tx_queue);
+ mutex_unlock(&cpc_hd->lock);
+
+ return skb;
+}
+EXPORT_SYMBOL_GPL(cpc_hd_dequeue);
+
MODULE_DESCRIPTION("Greybus over CPC");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Silicon Laboratories, Inc.");
diff --git a/drivers/greybus/cpc/host.h b/drivers/greybus/cpc/host.h
index 07bb4eb5fb8..2c47e167ac1 100644
--- a/drivers/greybus/cpc/host.h
+++ b/drivers/greybus/cpc/host.h
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/greybus.h>
#include <linux/mutex.h>
+#include <linux/skbuff.h>
#include <linux/types.h>
#define GB_CPC_MSG_SIZE_MAX 2048
@@ -18,7 +19,7 @@ struct cpc_cport;
struct cpc_host_device;
struct cpc_hd_driver {
- int (*transmit)(struct cpc_host_device *hd, struct sk_buff *skb);
+ int (*wake_tx)(struct cpc_host_device *cpc_hd);
};
/**
@@ -33,6 +34,8 @@ struct cpc_host_device {
const struct cpc_hd_driver *driver;
struct mutex lock; /* Synchronize access to cports */
+ struct sk_buff_head tx_queue;
+
struct cpc_cport *cports[GB_CPC_NUM_CPORTS];
};
@@ -47,6 +50,9 @@ void cpc_hd_put(struct cpc_host_device *cpc_hd);
void cpc_hd_del(struct cpc_host_device *cpc_hd);
void cpc_hd_rcvd(struct cpc_host_device *cpc_hd, struct sk_buff *skb);
-int cpc_hd_send_skb(struct cpc_host_device *cpc_hd, struct sk_buff *skb);
+void cpc_hd_send_skb(struct cpc_host_device *cpc_hd, struct sk_buff *skb);
+
+bool cpc_hd_tx_queue_empty(struct cpc_host_device *cpc_hd);
+struct sk_buff *cpc_hd_dequeue(struct cpc_host_device *cpc_hd);
#endif
--
2.49.0