diff --git a/drivers/rpmsg/glink_pkt.c b/drivers/rpmsg/glink_pkt.c index e6c9c8dead71..1449fda27978 100644 --- a/drivers/rpmsg/glink_pkt.c +++ b/drivers/rpmsg/glink_pkt.c @@ -17,6 +17,8 @@ #include #include +#include "qcom_glink_native.h" + /* Define IPC Logging Macros */ #define GLINK_PKT_IPC_LOG_PAGE_CNT 2 static void *glink_pkt_ilctxt; @@ -132,22 +134,6 @@ static ssize_t open_timeout_show(struct device *dev, static DEVICE_ATTR_RW(open_timeout); -static int glink_pkt_rpdev_probe(struct rpmsg_device *rpdev) -{ - struct device_driver *drv = rpdev->dev.driver; - struct rpmsg_driver *rpdrv = drv_to_rpdrv(drv); - struct glink_pkt_device *gpdev = rpdrv_to_gpdev(rpdrv); - - mutex_lock(&gpdev->lock); - gpdev->rpdev = rpdev; - mutex_unlock(&gpdev->lock); - - dev_set_drvdata(&rpdev->dev, gpdev); - complete_all(&gpdev->ch_open); - - return 0; -} - static int glink_pkt_rpdev_cb(struct rpmsg_device *rpdev, void *buf, int len, void *priv, u32 addr) { @@ -194,6 +180,23 @@ static int glink_pkt_rpdev_sigs(struct rpmsg_device *rpdev, void *priv, return 0; } +static int glink_pkt_rpdev_probe(struct rpmsg_device *rpdev) +{ + struct device_driver *drv = rpdev->dev.driver; + struct rpmsg_driver *rpdrv = drv_to_rpdrv(drv); + struct glink_pkt_device *gpdev = rpdrv_to_gpdev(rpdrv); + + mutex_lock(&gpdev->lock); + gpdev->rpdev = rpdev; + qcom_glink_register_signals_cb(rpdev->ept, glink_pkt_rpdev_sigs); + mutex_unlock(&gpdev->lock); + + dev_set_drvdata(&rpdev->dev, gpdev); + complete_all(&gpdev->ch_open); + + return 0; +} + static void glink_pkt_rpdev_remove(struct rpmsg_device *rpdev) { struct device_driver *drv = rpdev->dev.driver; @@ -558,7 +561,7 @@ static int glink_pkt_tiocmset(struct glink_pkt_device *gpdev, unsigned int cmd, clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_CD | TIOCM_RI; GLINK_PKT_INFO("set[0x%x] clear[0x%x]\n", set, clear); - return rpmsg_set_signals(gpdev->rpdev->ept, set, clear); + return qcom_glink_set_signals(gpdev->rpdev->ept, set, clear); } /** @@ -598,7 +601,7 @@ static long glink_pkt_ioctl(struct file *file, unsigned int cmd, gpdev->sig_change = false; spin_unlock_irqrestore(&gpdev->queue_lock, flags); - ret = rpmsg_get_signals(gpdev->rpdev->ept); + ret = qcom_glink_get_signals(gpdev->rpdev->ept); if (ret >= 0) ret = put_user(ret, (int __user *)arg); break; @@ -727,7 +730,6 @@ static int glink_pkt_init_rpmsg(struct glink_pkt_device *gpdev) rpdrv->probe = glink_pkt_rpdev_probe; rpdrv->remove = glink_pkt_rpdev_remove; rpdrv->callback = glink_pkt_rpdev_cb; - rpdrv->signals = glink_pkt_rpdev_sigs; rpdrv->id_table = match; rpdrv->drv.name = drv_name; diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index d4457bb6e83c..c6bdb5162536 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "rpmsg_internal.h" @@ -200,6 +201,9 @@ enum { * @intent_req_completed: Status of intent request completion * @intent_req_ack: Waitqueue for @intent_req_acked * @intent_req_comp: Waitqueue for @intent_req_completed + * @local_signals: local side signals + * @remote_sigalss: remote side signals + * @signals_cb: client callback for notifying signal change */ struct glink_channel { struct rpmsg_endpoint ept; @@ -235,6 +239,10 @@ struct glink_channel { atomic_t intent_req_completed; wait_queue_head_t intent_req_ack; wait_queue_head_t intent_req_comp; + + unsigned int local_signals; + unsigned int remote_signals; + int (*signals_cb)(struct rpmsg_device *dev, void *priv, u32 old, u32 new); }; #define to_glink_channel(_ept) container_of(_ept, struct glink_channel, ept) @@ -255,9 +263,15 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops; #define RPM_CMD_TX_DATA_CONT 12 #define RPM_CMD_READ_NOTIF 13 #define RPM_CMD_RX_DONE_W_REUSE 14 +#define RPM_CMD_SIGNALS 15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG BIT(31) +#define NATIVE_CTS_SIG BIT(30) +#define NATIVE_CD_SIG BIT(29) +#define NATIVE_RI_SIG BIT(28) + static void qcom_glink_rx_done_work(struct kthread_work *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -1183,6 +1197,77 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_send_signals() - convert a signal cmd to wire format and transmit + * @glink: The transport to transmit on. + * @channel: The glink channel + * @signals: The signals to encode. + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_send_signals(struct qcom_glink *glink, + struct glink_channel *channel, + u32 signals) +{ + struct glink_msg msg; + + /* convert signals from TIOCM to NATIVE */ + signals &= 0x0fff; + if (signals & TIOCM_DTR) + signals |= NATIVE_DTR_SIG; + if (signals & TIOCM_RTS) + signals |= NATIVE_CTS_SIG; + if (signals & TIOCM_CD) + signals |= NATIVE_CD_SIG; + if (signals & TIOCM_RI) + signals |= NATIVE_RI_SIG; + + msg.cmd = cpu_to_le16(RPM_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(signals); + + GLINK_INFO(glink->ilc, "signals:%d\n", signals); + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static int qcom_glink_handle_signals(struct qcom_glink *glink, + unsigned int rcid, unsigned int signals) +{ + struct glink_channel *channel; + unsigned long flags; + u32 old; + + 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 -EINVAL; + } + + old = channel->remote_signals; + + /* convert signals from NATIVE to TIOCM */ + if (signals & NATIVE_DTR_SIG) + signals |= TIOCM_DSR; + if (signals & NATIVE_CTS_SIG) + signals |= TIOCM_CTS; + if (signals & NATIVE_CD_SIG) + signals |= TIOCM_CD; + if (signals & NATIVE_RI_SIG) + signals |= TIOCM_RI; + signals &= 0x0fff; + + channel->remote_signals = signals; + + CH_INFO(channel, "old:%d new:%d\n", old, channel->remote_signals); + if (channel->signals_cb) + channel->signals_cb(channel->ept.rpdev, channel->ept.priv, + old, channel->remote_signals); + + return 0; +} + static int qcom_glink_native_rx(struct qcom_glink *glink, int iterations) { struct glink_msg msg; @@ -1263,6 +1348,10 @@ static int qcom_glink_native_rx(struct qcom_glink *glink, int iterations) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case RPM_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; @@ -1693,6 +1782,70 @@ static int qcom_glink_trysendto(struct rpmsg_endpoint *ept, void *data, int len, return __qcom_glink_send(channel, data, len, false); } +int qcom_glink_get_signals(struct rpmsg_endpoint *ept) +{ + struct glink_channel *channel; + + if (!ept) + return 0; + + channel = to_glink_channel(ept); + + return channel->remote_signals; +} +EXPORT_SYMBOL(qcom_glink_get_signals); + +int qcom_glink_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear) +{ + struct glink_channel *channel; + struct qcom_glink *glink; + u32 signals; + + if (!ept) + return -EINVAL; + + channel = to_glink_channel(ept); + glink = channel->glink; + signals = channel->local_signals; + + if (set & TIOCM_DTR) + signals |= TIOCM_DTR; + if (set & TIOCM_RTS) + signals |= TIOCM_RTS; + if (set & TIOCM_CD) + signals |= TIOCM_CD; + if (set & TIOCM_RI) + signals |= TIOCM_RI; + if (clear & TIOCM_DTR) + signals &= ~TIOCM_DTR; + if (clear & TIOCM_RTS) + signals &= ~TIOCM_RTS; + if (clear & TIOCM_CD) + signals &= ~TIOCM_CD; + if (clear & TIOCM_RI) + signals &= ~TIOCM_RI; + + channel->local_signals = signals; + + return qcom_glink_send_signals(glink, channel, signals); +} +EXPORT_SYMBOL(qcom_glink_set_signals); + +int qcom_glink_register_signals_cb(struct rpmsg_endpoint *ept, + int (*cb)(struct rpmsg_device *, void *, u32, u32)) +{ + struct glink_channel *channel; + + if (!ept || !cb) + return -EINVAL; + + channel = to_glink_channel(ept); + channel->signals_cb = cb; + + return 0; +} +EXPORT_SYMBOL(qcom_glink_register_signals_cb); + /* * Finds the device_node for the glink child interested in this channel. */ diff --git a/drivers/rpmsg/qcom_glink_native.h b/drivers/rpmsg/qcom_glink_native.h index a3be0f5cfaf4..aeb6a0bc0343 100644 --- a/drivers/rpmsg/qcom_glink_native.h +++ b/drivers/rpmsg/qcom_glink_native.h @@ -6,6 +6,8 @@ #ifndef __QCOM_GLINK_NATIVE_H__ #define __QCOM_GLINK_NATIVE_H__ +#include + #define GLINK_FEATURE_INTENT_REUSE BIT(0) #define GLINK_FEATURE_MIGRATION BIT(1) #define GLINK_FEATURE_TRACER_PKT BIT(2) @@ -38,4 +40,10 @@ int qcom_glink_native_start(struct qcom_glink *glink); void qcom_glink_native_remove(struct qcom_glink *glink); void qcom_glink_native_unregister(struct qcom_glink *glink); + +/* These operations are temporarily exposing signal interfaces */ +int qcom_glink_get_signals(struct rpmsg_endpoint *ept); +int qcom_glink_set_signals(struct rpmsg_endpoint *ept, u32 set, u32 clear); +int qcom_glink_register_signals_cb(struct rpmsg_endpoint *ept, + int (*signals_cb)(struct rpmsg_device *dev, void *priv, u32 old, u32 new)); #endif