|
|
|
|
@@ -92,6 +92,8 @@ struct glink_core_rx_intent {
|
|
|
|
|
* @rcids: idr of all channels with a known remote channel id
|
|
|
|
|
* @features: remote features
|
|
|
|
|
* @intentless: flag to indicate that there is no intent
|
|
|
|
|
* @tx_avail_notify: Waitqueue for pending tx tasks
|
|
|
|
|
* @sent_read_notify: flag to check cmd sent or not
|
|
|
|
|
*/
|
|
|
|
|
struct qcom_glink {
|
|
|
|
|
struct device *dev;
|
|
|
|
|
@@ -118,6 +120,8 @@ struct qcom_glink {
|
|
|
|
|
unsigned long features;
|
|
|
|
|
|
|
|
|
|
bool intentless;
|
|
|
|
|
wait_queue_head_t tx_avail_notify;
|
|
|
|
|
bool sent_read_notify;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
@@ -301,6 +305,20 @@ static void qcom_glink_tx_write(struct qcom_glink *glink,
|
|
|
|
|
glink->tx_pipe->write(glink->tx_pipe, hdr, hlen, data, dlen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void qcom_glink_send_read_notify(struct qcom_glink *glink)
|
|
|
|
|
{
|
|
|
|
|
struct glink_msg msg;
|
|
|
|
|
|
|
|
|
|
msg.cmd = cpu_to_le16(RPM_CMD_READ_NOTIF);
|
|
|
|
|
msg.param1 = 0;
|
|
|
|
|
msg.param2 = 0;
|
|
|
|
|
|
|
|
|
|
qcom_glink_tx_write(glink, &msg, sizeof(msg), NULL, 0);
|
|
|
|
|
|
|
|
|
|
mbox_send_message(glink->mbox_chan, NULL);
|
|
|
|
|
mbox_client_txdone(glink->mbox_chan, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int qcom_glink_tx(struct qcom_glink *glink,
|
|
|
|
|
const void *hdr, size_t hlen,
|
|
|
|
|
const void *data, size_t dlen, bool wait)
|
|
|
|
|
@@ -321,12 +339,21 @@ static int qcom_glink_tx(struct qcom_glink *glink,
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!glink->sent_read_notify) {
|
|
|
|
|
glink->sent_read_notify = true;
|
|
|
|
|
qcom_glink_send_read_notify(glink);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait without holding the tx_lock */
|
|
|
|
|
spin_unlock_irqrestore(&glink->tx_lock, flags);
|
|
|
|
|
|
|
|
|
|
usleep_range(10000, 15000);
|
|
|
|
|
wait_event_timeout(glink->tx_avail_notify,
|
|
|
|
|
qcom_glink_tx_avail(glink) >= tlen, 10 * HZ);
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&glink->tx_lock, flags);
|
|
|
|
|
|
|
|
|
|
if (qcom_glink_tx_avail(glink) >= tlen)
|
|
|
|
|
glink->sent_read_notify = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qcom_glink_tx_write(glink, hdr, hlen, data, dlen);
|
|
|
|
|
@@ -986,6 +1013,9 @@ static irqreturn_t qcom_glink_native_intr(int irq, void *data)
|
|
|
|
|
unsigned int cmd;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
/* To wakeup any blocking writers */
|
|
|
|
|
wake_up_all(&glink->tx_avail_notify);
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
avail = qcom_glink_rx_avail(glink);
|
|
|
|
|
if (avail < sizeof(msg))
|
|
|
|
|
@@ -1271,6 +1301,8 @@ static int __qcom_glink_send(struct glink_channel *channel,
|
|
|
|
|
} __packed req;
|
|
|
|
|
int ret;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
int chunk_size = len;
|
|
|
|
|
int left_size = 0;
|
|
|
|
|
|
|
|
|
|
if (!glink->intentless) {
|
|
|
|
|
while (!intent) {
|
|
|
|
|
@@ -1304,18 +1336,46 @@ static int __qcom_glink_send(struct glink_channel *channel,
|
|
|
|
|
iid = intent->id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wait && chunk_size > SZ_8K) {
|
|
|
|
|
chunk_size = SZ_8K;
|
|
|
|
|
left_size = len - chunk_size;
|
|
|
|
|
}
|
|
|
|
|
req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA);
|
|
|
|
|
req.msg.param1 = cpu_to_le16(channel->lcid);
|
|
|
|
|
req.msg.param2 = cpu_to_le32(iid);
|
|
|
|
|
req.chunk_size = cpu_to_le32(len);
|
|
|
|
|
req.left_size = cpu_to_le32(0);
|
|
|
|
|
req.chunk_size = cpu_to_le32(chunk_size);
|
|
|
|
|
req.left_size = cpu_to_le32(left_size);
|
|
|
|
|
|
|
|
|
|
ret = qcom_glink_tx(glink, &req, sizeof(req), data, len, wait);
|
|
|
|
|
ret = qcom_glink_tx(glink, &req, sizeof(req), data, chunk_size, wait);
|
|
|
|
|
|
|
|
|
|
/* Mark intent available if we failed */
|
|
|
|
|
if (ret && intent)
|
|
|
|
|
if (ret && intent) {
|
|
|
|
|
intent->in_use = false;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (left_size > 0) {
|
|
|
|
|
data = (void *)((char *)data + chunk_size);
|
|
|
|
|
chunk_size = left_size;
|
|
|
|
|
if (chunk_size > SZ_8K)
|
|
|
|
|
chunk_size = SZ_8K;
|
|
|
|
|
left_size -= chunk_size;
|
|
|
|
|
|
|
|
|
|
req.msg.cmd = cpu_to_le16(RPM_CMD_TX_DATA_CONT);
|
|
|
|
|
req.msg.param1 = cpu_to_le16(channel->lcid);
|
|
|
|
|
req.msg.param2 = cpu_to_le32(iid);
|
|
|
|
|
req.chunk_size = cpu_to_le32(chunk_size);
|
|
|
|
|
req.left_size = cpu_to_le32(left_size);
|
|
|
|
|
|
|
|
|
|
ret = qcom_glink_tx(glink, &req, sizeof(req), data,
|
|
|
|
|
chunk_size, wait);
|
|
|
|
|
|
|
|
|
|
/* Mark intent available if we failed */
|
|
|
|
|
if (ret && intent) {
|
|
|
|
|
intent->in_use = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1387,9 +1447,7 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops = {
|
|
|
|
|
static void qcom_glink_rpdev_release(struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
|
|
|
|
|
struct glink_channel *channel = to_glink_channel(rpdev->ept);
|
|
|
|
|
|
|
|
|
|
channel->rpdev = NULL;
|
|
|
|
|
kfree(rpdev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1440,7 +1498,7 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rpdev->ept = &channel->ept;
|
|
|
|
|
strncpy(rpdev->id.name, name, RPMSG_NAME_SIZE);
|
|
|
|
|
strscpy_pad(rpdev->id.name, name, RPMSG_NAME_SIZE);
|
|
|
|
|
rpdev->src = RPMSG_ADDR_ANY;
|
|
|
|
|
rpdev->dst = RPMSG_ADDR_ANY;
|
|
|
|
|
rpdev->ops = &glink_device_ops;
|
|
|
|
|
@@ -1494,6 +1552,7 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
|
|
|
|
|
|
|
|
|
|
rpmsg_unregister_device(glink->dev, &chinfo);
|
|
|
|
|
}
|
|
|
|
|
channel->rpdev = NULL;
|
|
|
|
|
|
|
|
|
|
qcom_glink_send_close_ack(glink, channel->rcid);
|
|
|
|
|
|
|
|
|
|
@@ -1507,9 +1566,13 @@ static void qcom_glink_rx_close(struct qcom_glink *glink, unsigned int rcid)
|
|
|
|
|
|
|
|
|
|
static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
|
|
|
|
|
{
|
|
|
|
|
struct rpmsg_channel_info chinfo;
|
|
|
|
|
struct glink_channel *channel;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
/* To wakeup any blocking writers */
|
|
|
|
|
wake_up_all(&glink->tx_avail_notify);
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&glink->idr_lock, flags);
|
|
|
|
|
channel = idr_find(&glink->lcids, lcid);
|
|
|
|
|
if (WARN(!channel, "close ack on unknown channel\n")) {
|
|
|
|
|
@@ -1521,6 +1584,16 @@ static void qcom_glink_rx_close_ack(struct qcom_glink *glink, unsigned int lcid)
|
|
|
|
|
channel->lcid = 0;
|
|
|
|
|
spin_unlock_irqrestore(&glink->idr_lock, flags);
|
|
|
|
|
|
|
|
|
|
/* Decouple the potential rpdev from the channel */
|
|
|
|
|
if (channel->rpdev) {
|
|
|
|
|
strscpy(chinfo.name, channel->name, sizeof(chinfo.name));
|
|
|
|
|
chinfo.src = RPMSG_ADDR_ANY;
|
|
|
|
|
chinfo.dst = RPMSG_ADDR_ANY;
|
|
|
|
|
|
|
|
|
|
rpmsg_unregister_device(glink->dev, &chinfo);
|
|
|
|
|
}
|
|
|
|
|
channel->rpdev = NULL;
|
|
|
|
|
|
|
|
|
|
kref_put(&channel->refcount, qcom_glink_channel_release);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1670,6 +1743,7 @@ struct qcom_glink *qcom_glink_native_probe(struct device *dev,
|
|
|
|
|
spin_lock_init(&glink->rx_lock);
|
|
|
|
|
INIT_LIST_HEAD(&glink->rx_queue);
|
|
|
|
|
INIT_WORK(&glink->rx_work, qcom_glink_work);
|
|
|
|
|
init_waitqueue_head(&glink->tx_avail_notify);
|
|
|
|
|
|
|
|
|
|
spin_lock_init(&glink->idr_lock);
|
|
|
|
|
idr_init(&glink->lcids);
|
|
|
|
|
|