Merge tag '6.6-rc-ksmbd' of git://git.samba.org/ksmbd
[platform/kernel/linux-rpi.git] / drivers / rpmsg / qcom_glink_native.c
index 1beb40a..82d460f 100644 (file)
@@ -200,9 +200,15 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops;
 #define GLINK_CMD_TX_DATA_CONT         12
 #define GLINK_CMD_READ_NOTIF           13
 #define GLINK_CMD_RX_DONE_W_REUSE      14
+#define GLINK_CMD_SIGNALS              15
 
 #define GLINK_FEATURE_INTENTLESS       BIT(1)
 
+#define NATIVE_DTR_SIG                 NATIVE_DSR_SIG
+#define NATIVE_DSR_SIG                 BIT(31)
+#define NATIVE_RTS_SIG                 NATIVE_CTS_SIG
+#define NATIVE_CTS_SIG                 BIT(30)
+
 static void qcom_glink_rx_done_work(struct work_struct *work);
 
 static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
@@ -221,6 +227,10 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
 
        channel->glink = glink;
        channel->name = kstrdup(name, GFP_KERNEL);
+       if (!channel->name) {
+               kfree(channel);
+               return ERR_PTR(-ENOMEM);
+       }
 
        init_completion(&channel->open_req);
        init_completion(&channel->open_ack);
@@ -1025,6 +1035,52 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)
        return 0;
 }
 
+/**
+ * qcom_glink_set_flow_control() - convert a signal cmd to wire format and transmit
+ * @ept:       Rpmsg endpoint for channel.
+ * @pause:     Pause transmission
+ * @dst:       destination address of the endpoint
+ *
+ * Return: 0 on success or standard Linux error code.
+ */
+static int qcom_glink_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst)
+{
+       struct glink_channel *channel = to_glink_channel(ept);
+       struct qcom_glink *glink = channel->glink;
+       struct glink_msg msg;
+       u32 sigs = 0;
+
+       if (pause)
+               sigs |= NATIVE_DTR_SIG | NATIVE_RTS_SIG;
+
+       msg.cmd = cpu_to_le16(GLINK_CMD_SIGNALS);
+       msg.param1 = cpu_to_le16(channel->lcid);
+       msg.param2 = cpu_to_le32(sigs);
+
+       return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
+}
+
+static void qcom_glink_handle_signals(struct qcom_glink *glink,
+                                     unsigned int rcid, unsigned int sigs)
+{
+       struct glink_channel *channel;
+       unsigned long flags;
+       bool enable;
+
+       spin_lock_irqsave(&glink->idr_lock, flags);
+       channel = idr_find(&glink->rcids, rcid);
+       spin_unlock_irqrestore(&glink->idr_lock, flags);
+       if (!channel) {
+               dev_err(glink->dev, "signal for non-existing channel\n");
+               return;
+       }
+
+       enable = sigs & NATIVE_DSR_SIG || sigs & NATIVE_CTS_SIG;
+
+       if (channel->ept.flow_cb)
+               channel->ept.flow_cb(channel->ept.rpdev, channel->ept.priv, enable);
+}
+
 void qcom_glink_native_rx(struct qcom_glink *glink)
 {
        struct glink_msg msg;
@@ -1086,6 +1142,10 @@ void qcom_glink_native_rx(struct qcom_glink *glink)
                        qcom_glink_handle_intent_req_ack(glink, param1, param2);
                        qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
                        break;
+               case GLINK_CMD_SIGNALS:
+                       qcom_glink_handle_signals(glink, param1, param2);
+                       qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8));
+                       break;
                default:
                        dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd);
                        ret = -EINVAL;
@@ -1446,6 +1506,7 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops = {
        .sendto = qcom_glink_sendto,
        .trysend = qcom_glink_trysend,
        .trysendto = qcom_glink_trysendto,
+       .set_flow_control = qcom_glink_set_flow_control,
 };
 
 static void qcom_glink_rpdev_release(struct device *dev)