|
@@ -59,6 +59,26 @@ struct glink_defer_cmd {
|
|
|
u8 data[];
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * struct glink_core_rx_intent - RX intent
|
|
|
+ * RX intent
|
|
|
+ *
|
|
|
+ * data: pointer to the data (may be NULL for zero-copy)
|
|
|
+ * id: remote or local intent ID
|
|
|
+ * size: size of the original intent (do not modify)
|
|
|
+ * reuse: To mark if the intent can be reused after first use
|
|
|
+ * in_use: To mark if intent is already in use for the channel
|
|
|
+ * offset: next write offset (initially 0)
|
|
|
+ */
|
|
|
+struct glink_core_rx_intent {
|
|
|
+ void *data;
|
|
|
+ u32 id;
|
|
|
+ size_t size;
|
|
|
+ bool reuse;
|
|
|
+ bool in_use;
|
|
|
+ u32 offset;
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* struct qcom_glink - driver context, relates to one remote subsystem
|
|
|
* @dev: reference to the associated struct device
|
|
@@ -117,6 +137,8 @@ enum {
|
|
|
* @name: unique channel name/identifier
|
|
|
* @lcid: channel id, in local space
|
|
|
* @rcid: channel id, in remote space
|
|
|
+ * @intent_lock: lock for protection of @liids
|
|
|
+ * @liids: idr of all local intents
|
|
|
* @buf: receive buffer, for gathering fragments
|
|
|
* @buf_offset: write offset in @buf
|
|
|
* @buf_size: size of current @buf
|
|
@@ -137,6 +159,9 @@ struct glink_channel {
|
|
|
unsigned int lcid;
|
|
|
unsigned int rcid;
|
|
|
|
|
|
+ spinlock_t intent_lock;
|
|
|
+ struct idr liids;
|
|
|
+
|
|
|
void *buf;
|
|
|
int buf_offset;
|
|
|
int buf_size;
|
|
@@ -154,6 +179,9 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops;
|
|
|
#define RPM_CMD_OPEN 2
|
|
|
#define RPM_CMD_CLOSE 3
|
|
|
#define RPM_CMD_OPEN_ACK 4
|
|
|
+#define RPM_CMD_INTENT 5
|
|
|
+#define RPM_CMD_RX_INTENT_REQ 7
|
|
|
+#define RPM_CMD_RX_INTENT_REQ_ACK 8
|
|
|
#define RPM_CMD_TX_DATA 9
|
|
|
#define RPM_CMD_CLOSE_ACK 11
|
|
|
#define RPM_CMD_TX_DATA_CONT 12
|
|
@@ -172,12 +200,14 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink,
|
|
|
|
|
|
/* Setup glink internal glink_channel data */
|
|
|
spin_lock_init(&channel->recv_lock);
|
|
|
+ spin_lock_init(&channel->intent_lock);
|
|
|
channel->glink = glink;
|
|
|
channel->name = kstrdup(name, GFP_KERNEL);
|
|
|
|
|
|
init_completion(&channel->open_req);
|
|
|
init_completion(&channel->open_ack);
|
|
|
|
|
|
+ idr_init(&channel->liids);
|
|
|
kref_init(&channel->refcount);
|
|
|
|
|
|
return channel;
|
|
@@ -187,6 +217,11 @@ static void qcom_glink_channel_release(struct kref *ref)
|
|
|
{
|
|
|
struct glink_channel *channel = container_of(ref, struct glink_channel,
|
|
|
refcount);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&channel->intent_lock, flags);
|
|
|
+ idr_destroy(&channel->liids);
|
|
|
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
|
|
|
|
|
|
kfree(channel->name);
|
|
|
kfree(channel);
|
|
@@ -424,6 +459,130 @@ static void qcom_glink_receive_version_ack(struct qcom_glink *glink,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * qcom_glink_send_intent_req_ack() - convert an rx intent request ack cmd to
|
|
|
+ wire format and transmit
|
|
|
+ * @glink: The transport to transmit on.
|
|
|
+ * @channel: The glink channel
|
|
|
+ * @granted: The request response to encode.
|
|
|
+ *
|
|
|
+ * Return: 0 on success or standard Linux error code.
|
|
|
+ */
|
|
|
+static int qcom_glink_send_intent_req_ack(struct qcom_glink *glink,
|
|
|
+ struct glink_channel *channel,
|
|
|
+ bool granted)
|
|
|
+{
|
|
|
+ struct glink_msg msg;
|
|
|
+
|
|
|
+ msg.cmd = cpu_to_le16(RPM_CMD_RX_INTENT_REQ_ACK);
|
|
|
+ msg.param1 = cpu_to_le16(channel->lcid);
|
|
|
+ msg.param2 = cpu_to_le32(granted);
|
|
|
+
|
|
|
+ qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qcom_glink_advertise_intent - convert an rx intent cmd to wire format and
|
|
|
+ * transmit
|
|
|
+ * @glink: The transport to transmit on.
|
|
|
+ * @channel: The local channel
|
|
|
+ * @size: The intent to pass on to remote.
|
|
|
+ *
|
|
|
+ * Return: 0 on success or standard Linux error code.
|
|
|
+ */
|
|
|
+static int qcom_glink_advertise_intent(struct qcom_glink *glink,
|
|
|
+ struct glink_channel *channel,
|
|
|
+ struct glink_core_rx_intent *intent)
|
|
|
+{
|
|
|
+ struct command {
|
|
|
+ u16 id;
|
|
|
+ u16 lcid;
|
|
|
+ u32 count;
|
|
|
+ u32 size;
|
|
|
+ u32 liid;
|
|
|
+ } __packed;
|
|
|
+ struct command cmd;
|
|
|
+
|
|
|
+ cmd.id = cpu_to_le16(RPM_CMD_INTENT);
|
|
|
+ cmd.lcid = cpu_to_le16(channel->lcid);
|
|
|
+ cmd.count = cpu_to_le32(1);
|
|
|
+ cmd.size = cpu_to_le32(intent->size);
|
|
|
+ cmd.liid = cpu_to_le32(intent->id);
|
|
|
+
|
|
|
+ qcom_glink_tx(glink, &cmd, sizeof(cmd), NULL, 0, true);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct glink_core_rx_intent *
|
|
|
+qcom_glink_alloc_intent(struct qcom_glink *glink,
|
|
|
+ struct glink_channel *channel,
|
|
|
+ size_t size,
|
|
|
+ bool reuseable)
|
|
|
+{
|
|
|
+ struct glink_core_rx_intent *intent;
|
|
|
+ int ret;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ intent = kzalloc(sizeof(*intent), GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!intent)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ intent->data = kzalloc(size, GFP_KERNEL);
|
|
|
+ if (!intent->data)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&channel->intent_lock, flags);
|
|
|
+ ret = idr_alloc_cyclic(&channel->liids, intent, 1, -1, GFP_ATOMIC);
|
|
|
+ if (ret < 0) {
|
|
|
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
|
|
|
+
|
|
|
+ intent->id = ret;
|
|
|
+ intent->size = size;
|
|
|
+ intent->reuse = reuseable;
|
|
|
+
|
|
|
+ return intent;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qcom_glink_handle_intent_req() - Receive a request for rx_intent
|
|
|
+ * from remote side
|
|
|
+ * if_ptr: Pointer to the transport interface
|
|
|
+ * rcid: Remote channel ID
|
|
|
+ * size: size of the intent
|
|
|
+ *
|
|
|
+ * The function searches for the local channel to which the request for
|
|
|
+ * rx_intent has arrived and allocates and notifies the remote back
|
|
|
+ */
|
|
|
+static void qcom_glink_handle_intent_req(struct qcom_glink *glink,
|
|
|
+ u32 cid, size_t size)
|
|
|
+{
|
|
|
+ struct glink_core_rx_intent *intent;
|
|
|
+ struct glink_channel *channel;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&glink->idr_lock, flags);
|
|
|
+ channel = idr_find(&glink->rcids, cid);
|
|
|
+ spin_unlock_irqrestore(&glink->idr_lock, flags);
|
|
|
+
|
|
|
+ if (!channel) {
|
|
|
+ pr_err("%s channel not found for cid %d\n", __func__, cid);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ intent = qcom_glink_alloc_intent(glink, channel, size, false);
|
|
|
+ if (intent)
|
|
|
+ qcom_glink_advertise_intent(glink, channel, intent);
|
|
|
+
|
|
|
+ qcom_glink_send_intent_req_ack(glink, channel, !!intent);
|
|
|
+}
|
|
|
+
|
|
|
static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
|
|
|
{
|
|
|
struct glink_defer_cmd *dcmd;
|
|
@@ -586,6 +745,7 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)
|
|
|
case RPM_CMD_VERSION_ACK:
|
|
|
case RPM_CMD_CLOSE:
|
|
|
case RPM_CMD_CLOSE_ACK:
|
|
|
+ case RPM_CMD_RX_INTENT_REQ:
|
|
|
ret = qcom_glink_rx_defer(glink, 0);
|
|
|
break;
|
|
|
case RPM_CMD_OPEN_ACK:
|
|
@@ -1002,6 +1162,9 @@ static void qcom_glink_work(struct work_struct *work)
|
|
|
case RPM_CMD_CLOSE_ACK:
|
|
|
qcom_glink_rx_close_ack(glink, param1);
|
|
|
break;
|
|
|
+ case RPM_CMD_RX_INTENT_REQ:
|
|
|
+ qcom_glink_handle_intent_req(glink, param1, param2);
|
|
|
+ break;
|
|
|
default:
|
|
|
WARN(1, "Unknown defer object %d\n", cmd);
|
|
|
break;
|
|
@@ -1014,7 +1177,8 @@ static void qcom_glink_work(struct work_struct *work)
|
|
|
struct qcom_glink *qcom_glink_native_probe(struct device *dev,
|
|
|
unsigned long features,
|
|
|
struct qcom_glink_pipe *rx,
|
|
|
- struct qcom_glink_pipe *tx)
|
|
|
+ struct qcom_glink_pipe *tx,
|
|
|
+ bool intentless)
|
|
|
{
|
|
|
int irq;
|
|
|
int ret;
|
|
@@ -1029,6 +1193,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
|
|
|
glink->rx_pipe = rx;
|
|
|
|
|
|
glink->features = features;
|
|
|
+ glink->intentless = intentless;
|
|
|
|
|
|
mutex_init(&glink->tx_lock);
|
|
|
spin_lock_init(&glink->rx_lock);
|