Loading drivers/rpmsg/qcom_smd.c +141 −6 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/wait.h> #include <linux/rpmsg.h> #include <linux/rpmsg/qcom_smd.h> #include <linux/ipc_logging.h> #include "rpmsg_internal.h" Loading Loading @@ -93,6 +94,17 @@ static const struct { }, }; /* ipc logging wrapper */ #define smd_ipc(log_ctx, print, dev, x...) do { \ ipc_log_string(log_ctx, x); \ if (print) { \ if (dev) \ dev_err((dev), x); \ else \ pr_err(x); \ } \ } while (0) /** * struct qcom_smd_edge - representing a remote processor * @dev: device associated with this edge Loading Loading @@ -143,6 +155,7 @@ struct qcom_smd_edge { struct work_struct scan_work; struct work_struct state_work; void *ipc; /* ipc logging hanlder */ }; /* Loading Loading @@ -196,6 +209,9 @@ struct qcom_smd_endpoint { * @pkt_size: size of the currently handled packet * @drvdata: driver private data * @list: lite entry for @channels in qcom_smd_edge * @extended_buf: buffer for reading data greater than fifo size * @ext_buf: helper pointer for reading data greater than fifo size * @ext_pkt_size: size of data greater than fifo size */ struct qcom_smd_channel { struct qcom_smd_edge *edge; Loading Loading @@ -228,6 +244,10 @@ struct qcom_smd_channel { struct list_head list; u32 rsigs; void *extended_buf; void *ext_buf; int ext_pkt_size; }; /* Loading Loading @@ -295,7 +315,9 @@ struct smd_channel_info_word_pair { (GET_RX_CHANNEL_FLAG(channel, fDSR) ? TIOCM_DSR : 0) | \ (GET_RX_CHANNEL_FLAG(channel, fCTS) ? TIOCM_CTS : 0) | \ (GET_RX_CHANNEL_FLAG(channel, fCD) ? TIOCM_CD : 0) | \ (GET_RX_CHANNEL_FLAG(channel, fRI) ? TIOCM_RI : 0); \ (GET_RX_CHANNEL_FLAG(channel, fRI) ? TIOCM_RI : 0) | \ (GET_TX_CHANNEL_FLAG(channel, fDSR) ? TIOCM_DTR : 0) | \ (GET_TX_CHANNEL_FLAG(channel, fCTS) ? TIOCM_RTS : 0); \ }) #define SET_RX_CHANNEL_FLAG(channel, param, value) \ Loading Loading @@ -412,6 +434,8 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) SET_RX_CHANNEL_INFO(channel, tail, 0); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); channel->state = SMD_CHANNEL_CLOSED; channel->pkt_size = 0; Loading Loading @@ -442,6 +466,9 @@ static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel) head = GET_RX_CHANNEL_INFO(channel, head); tail = GET_RX_CHANNEL_INFO(channel, tail); smd_ipc(channel->edge->ipc, false, NULL, "%s: h: 0x%x t: 0x%x ch %s ed %s\n", __func__, head, tail, channel->name, channel->edge->name); return (head - tail) & (channel->fifo_size - 1); } Loading @@ -468,6 +495,8 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, channel->state = state; qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); } /* Loading Loading @@ -546,6 +575,62 @@ static void qcom_smd_channel_advance(struct qcom_smd_channel *channel, SET_RX_CHANNEL_INFO(channel, tail, tail); } /* Read the data of size greater than fifo size */ static size_t qcom_smd_channel_ext_read(struct qcom_smd_channel *channel) { struct rpmsg_endpoint *ept = &channel->qsept->ept; size_t len; void *ptr; int ret; int avail; if (!channel->extended_buf) { channel->ext_pkt_size = channel->pkt_size; channel->extended_buf = kmalloc(channel->ext_pkt_size, GFP_ATOMIC); if (!channel->extended_buf) { smd_ipc(channel->edge->ipc, true, NULL, "%s: mem alloc failed\n", __func__); return 0; } ptr = channel->extended_buf; channel->ext_buf = ptr; } else { ptr = channel->ext_buf; } if (channel->pkt_size >= channel->fifo_size) { avail = qcom_smd_channel_get_rx_avail(channel); len = qcom_smd_channel_peek(channel, ptr, avail); } else { len = qcom_smd_channel_peek(channel, ptr, channel->pkt_size); } qcom_smd_channel_advance(channel, len); channel->pkt_size = channel->pkt_size - len; channel->ext_buf = ptr + len; if (channel->pkt_size == 0) { ptr = channel->extended_buf; len = channel->ext_pkt_size; ret = ept->cb(ept->rpdev, ptr, len, ept->priv, RPMSG_ADDR_ANY); if (ret < 0) { smd_ipc(channel->edge->ipc, false, NULL, "%s: ret %d len %d ch %s\n", __func__, ret, len, channel->name); } kfree(channel->extended_buf); channel->extended_buf = NULL; channel->ext_buf = NULL; channel->ext_pkt_size = 0; } /* Indicate that we have seen and updated tail */ SET_TX_CHANNEL_FLAG(channel, fTAIL, 1); return len; } /* * Read out a single packet from the rx fifo and deliver it to the device */ Loading @@ -559,6 +644,15 @@ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) tail = GET_RX_CHANNEL_INFO(channel, tail); /* extended buffer if data size is greter than or equal to fifo size */ if ((channel->pkt_size >= channel->fifo_size) || channel->ext_pkt_size) { len = qcom_smd_channel_ext_read(channel); if (len == 0) return -ENOMEM; goto exit; } /* Use bounce buffer if the data wraps */ if (tail + channel->pkt_size >= channel->fifo_size) { ptr = channel->bounce_buffer; Loading @@ -569,14 +663,21 @@ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) } ret = ept->cb(ept->rpdev, ptr, len, ept->priv, RPMSG_ADDR_ANY); if (ret < 0) if (ret < 0) { smd_ipc(channel->edge->ipc, false, NULL, "%s: ret %d len %d ch %s\n", __func__, ret, len, channel->name); return ret; } /* Only forward the tail if the client consumed the data */ qcom_smd_channel_advance(channel, len); channel->pkt_size = 0; exit: smd_ipc(channel->edge->ipc, false, NULL, "%s: len %d ch %s ed %s\n", __func__, len, channel->name, channel->edge->name); return 0; } Loading Loading @@ -631,10 +732,18 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel) qcom_smd_channel_peek(channel, &pktlen, sizeof(pktlen)); qcom_smd_channel_advance(channel, SMD_PACKET_HEADER_LEN); channel->pkt_size = le32_to_cpu(pktlen); } else if (channel->pkt_size && avail >= channel->pkt_size) { smd_ipc(channel->edge->ipc, false, NULL, "%s: pkt_size: %d ch %s ed %s\n", __func__, channel->pkt_size, channel->name, channel->edge->name); } else if (channel->pkt_size && (avail >= channel->pkt_size || channel->pkt_size >= channel->fifo_size)) { ret = qcom_smd_channel_recv_single(channel); if (ret) if (ret) { smd_ipc(channel->edge->ipc, false, NULL, "%s: fail ret %d ch %s ed %s\n", __func__, ret, channel->name, channel->edge->name); break; } } else { break; } Loading @@ -649,6 +758,8 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel) wmb(); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); } out: Loading @@ -674,6 +785,9 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) list_for_each_entry(channel, &edge->channels, list) { spin_lock(&channel->recv_lock); kick_state |= qcom_smd_channel_intr(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: to APPS ch %s ed %s\n", __func__, channel->name, edge->name); spin_unlock(&channel->recv_lock); } spin_unlock(&edge->channels_lock); Loading Loading @@ -822,6 +936,9 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data, wmb(); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: hdr:%d len:%d ch %s ed %s\n", __func__, sizeof(hdr), len, channel->name, channel->edge->name); out_unlock: spin_unlock_irqrestore(&channel->tx_lock, flags); Loading Loading @@ -1038,6 +1155,8 @@ static int qcom_smd_set_sigs(struct rpmsg_endpoint *ept, u32 sigs) SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); return 0; } Loading Loading @@ -1136,6 +1255,8 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) rpdev->dev.parent = &edge->dev; rpdev->dev.release = qcom_smd_release_device; smd_ipc(channel->edge->ipc, false, NULL, "%s: registering ch %s\n", __func__, channel->name); return rpmsg_register_device(rpdev); } Loading Loading @@ -1363,6 +1484,8 @@ static void qcom_channel_state_worker(struct work_struct *work) strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; smd_ipc(channel->edge->ipc, false, NULL, "%s: unregistering ch %s\n", __func__, channel->name); rpmsg_unregister_device(&edge->dev, &chinfo); channel->registered = false; spin_lock_irqsave(&edge->channels_lock, flags); Loading Loading @@ -1451,8 +1574,9 @@ static int qcom_smd_parse_edge(struct device *dev, } ret = devm_request_irq(dev, irq, qcom_smd_edge_intr, IRQF_TRIGGER_RISING, node->name, edge); qcom_smd_edge_intr, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, node->name, edge); if (ret) { dev_err(dev, "failed to request smd irq\n"); goto put_node; Loading Loading @@ -1526,6 +1650,13 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, edge->dev.of_node = node; edge->dev.groups = qcom_smd_edge_groups; dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); /* ipc logging handler */ edge->ipc = ipc_log_context_create(4, dev_name(&edge->dev), 0); if (!edge->ipc) dev_info(&edge->dev, "%s: failed to create ipc log cntxt\n", __func__); ret = device_register(&edge->dev); if (ret) { pr_err("failed to register smd edge\n"); Loading @@ -1547,6 +1678,8 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, schedule_work(&edge->scan_work); smd_ipc(edge->ipc, false, NULL, "%s: %s:%s\n", __func__, dev_name(parent), node->name); return edge; unregister_dev: Loading Loading @@ -1608,6 +1741,8 @@ static int qcom_smd_remove_edge(struct device *dev, void *data) { struct qcom_smd_edge *edge = to_smd_edge(dev); ipc_log_context_destroy(edge->ipc); return qcom_smd_unregister_edge(edge); } Loading Loading
drivers/rpmsg/qcom_smd.c +141 −6 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/wait.h> #include <linux/rpmsg.h> #include <linux/rpmsg/qcom_smd.h> #include <linux/ipc_logging.h> #include "rpmsg_internal.h" Loading Loading @@ -93,6 +94,17 @@ static const struct { }, }; /* ipc logging wrapper */ #define smd_ipc(log_ctx, print, dev, x...) do { \ ipc_log_string(log_ctx, x); \ if (print) { \ if (dev) \ dev_err((dev), x); \ else \ pr_err(x); \ } \ } while (0) /** * struct qcom_smd_edge - representing a remote processor * @dev: device associated with this edge Loading Loading @@ -143,6 +155,7 @@ struct qcom_smd_edge { struct work_struct scan_work; struct work_struct state_work; void *ipc; /* ipc logging hanlder */ }; /* Loading Loading @@ -196,6 +209,9 @@ struct qcom_smd_endpoint { * @pkt_size: size of the currently handled packet * @drvdata: driver private data * @list: lite entry for @channels in qcom_smd_edge * @extended_buf: buffer for reading data greater than fifo size * @ext_buf: helper pointer for reading data greater than fifo size * @ext_pkt_size: size of data greater than fifo size */ struct qcom_smd_channel { struct qcom_smd_edge *edge; Loading Loading @@ -228,6 +244,10 @@ struct qcom_smd_channel { struct list_head list; u32 rsigs; void *extended_buf; void *ext_buf; int ext_pkt_size; }; /* Loading Loading @@ -295,7 +315,9 @@ struct smd_channel_info_word_pair { (GET_RX_CHANNEL_FLAG(channel, fDSR) ? TIOCM_DSR : 0) | \ (GET_RX_CHANNEL_FLAG(channel, fCTS) ? TIOCM_CTS : 0) | \ (GET_RX_CHANNEL_FLAG(channel, fCD) ? TIOCM_CD : 0) | \ (GET_RX_CHANNEL_FLAG(channel, fRI) ? TIOCM_RI : 0); \ (GET_RX_CHANNEL_FLAG(channel, fRI) ? TIOCM_RI : 0) | \ (GET_TX_CHANNEL_FLAG(channel, fDSR) ? TIOCM_DTR : 0) | \ (GET_TX_CHANNEL_FLAG(channel, fCTS) ? TIOCM_RTS : 0); \ }) #define SET_RX_CHANNEL_FLAG(channel, param, value) \ Loading Loading @@ -412,6 +434,8 @@ static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) SET_RX_CHANNEL_INFO(channel, tail, 0); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); channel->state = SMD_CHANNEL_CLOSED; channel->pkt_size = 0; Loading Loading @@ -442,6 +466,9 @@ static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel) head = GET_RX_CHANNEL_INFO(channel, head); tail = GET_RX_CHANNEL_INFO(channel, tail); smd_ipc(channel->edge->ipc, false, NULL, "%s: h: 0x%x t: 0x%x ch %s ed %s\n", __func__, head, tail, channel->name, channel->edge->name); return (head - tail) & (channel->fifo_size - 1); } Loading @@ -468,6 +495,8 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, channel->state = state; qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); } /* Loading Loading @@ -546,6 +575,62 @@ static void qcom_smd_channel_advance(struct qcom_smd_channel *channel, SET_RX_CHANNEL_INFO(channel, tail, tail); } /* Read the data of size greater than fifo size */ static size_t qcom_smd_channel_ext_read(struct qcom_smd_channel *channel) { struct rpmsg_endpoint *ept = &channel->qsept->ept; size_t len; void *ptr; int ret; int avail; if (!channel->extended_buf) { channel->ext_pkt_size = channel->pkt_size; channel->extended_buf = kmalloc(channel->ext_pkt_size, GFP_ATOMIC); if (!channel->extended_buf) { smd_ipc(channel->edge->ipc, true, NULL, "%s: mem alloc failed\n", __func__); return 0; } ptr = channel->extended_buf; channel->ext_buf = ptr; } else { ptr = channel->ext_buf; } if (channel->pkt_size >= channel->fifo_size) { avail = qcom_smd_channel_get_rx_avail(channel); len = qcom_smd_channel_peek(channel, ptr, avail); } else { len = qcom_smd_channel_peek(channel, ptr, channel->pkt_size); } qcom_smd_channel_advance(channel, len); channel->pkt_size = channel->pkt_size - len; channel->ext_buf = ptr + len; if (channel->pkt_size == 0) { ptr = channel->extended_buf; len = channel->ext_pkt_size; ret = ept->cb(ept->rpdev, ptr, len, ept->priv, RPMSG_ADDR_ANY); if (ret < 0) { smd_ipc(channel->edge->ipc, false, NULL, "%s: ret %d len %d ch %s\n", __func__, ret, len, channel->name); } kfree(channel->extended_buf); channel->extended_buf = NULL; channel->ext_buf = NULL; channel->ext_pkt_size = 0; } /* Indicate that we have seen and updated tail */ SET_TX_CHANNEL_FLAG(channel, fTAIL, 1); return len; } /* * Read out a single packet from the rx fifo and deliver it to the device */ Loading @@ -559,6 +644,15 @@ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) tail = GET_RX_CHANNEL_INFO(channel, tail); /* extended buffer if data size is greter than or equal to fifo size */ if ((channel->pkt_size >= channel->fifo_size) || channel->ext_pkt_size) { len = qcom_smd_channel_ext_read(channel); if (len == 0) return -ENOMEM; goto exit; } /* Use bounce buffer if the data wraps */ if (tail + channel->pkt_size >= channel->fifo_size) { ptr = channel->bounce_buffer; Loading @@ -569,14 +663,21 @@ static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) } ret = ept->cb(ept->rpdev, ptr, len, ept->priv, RPMSG_ADDR_ANY); if (ret < 0) if (ret < 0) { smd_ipc(channel->edge->ipc, false, NULL, "%s: ret %d len %d ch %s\n", __func__, ret, len, channel->name); return ret; } /* Only forward the tail if the client consumed the data */ qcom_smd_channel_advance(channel, len); channel->pkt_size = 0; exit: smd_ipc(channel->edge->ipc, false, NULL, "%s: len %d ch %s ed %s\n", __func__, len, channel->name, channel->edge->name); return 0; } Loading Loading @@ -631,10 +732,18 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel) qcom_smd_channel_peek(channel, &pktlen, sizeof(pktlen)); qcom_smd_channel_advance(channel, SMD_PACKET_HEADER_LEN); channel->pkt_size = le32_to_cpu(pktlen); } else if (channel->pkt_size && avail >= channel->pkt_size) { smd_ipc(channel->edge->ipc, false, NULL, "%s: pkt_size: %d ch %s ed %s\n", __func__, channel->pkt_size, channel->name, channel->edge->name); } else if (channel->pkt_size && (avail >= channel->pkt_size || channel->pkt_size >= channel->fifo_size)) { ret = qcom_smd_channel_recv_single(channel); if (ret) if (ret) { smd_ipc(channel->edge->ipc, false, NULL, "%s: fail ret %d ch %s ed %s\n", __func__, ret, channel->name, channel->edge->name); break; } } else { break; } Loading @@ -649,6 +758,8 @@ static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel) wmb(); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); } out: Loading @@ -674,6 +785,9 @@ static irqreturn_t qcom_smd_edge_intr(int irq, void *data) list_for_each_entry(channel, &edge->channels, list) { spin_lock(&channel->recv_lock); kick_state |= qcom_smd_channel_intr(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: to APPS ch %s ed %s\n", __func__, channel->name, edge->name); spin_unlock(&channel->recv_lock); } spin_unlock(&edge->channels_lock); Loading Loading @@ -822,6 +936,9 @@ static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data, wmb(); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: hdr:%d len:%d ch %s ed %s\n", __func__, sizeof(hdr), len, channel->name, channel->edge->name); out_unlock: spin_unlock_irqrestore(&channel->tx_lock, flags); Loading Loading @@ -1038,6 +1155,8 @@ static int qcom_smd_set_sigs(struct rpmsg_endpoint *ept, u32 sigs) SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); qcom_smd_signal_channel(channel); smd_ipc(channel->edge->ipc, false, NULL, "%s: ch %s ed %s\n", __func__, channel->name, channel->edge->name); return 0; } Loading Loading @@ -1136,6 +1255,8 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) rpdev->dev.parent = &edge->dev; rpdev->dev.release = qcom_smd_release_device; smd_ipc(channel->edge->ipc, false, NULL, "%s: registering ch %s\n", __func__, channel->name); return rpmsg_register_device(rpdev); } Loading Loading @@ -1363,6 +1484,8 @@ static void qcom_channel_state_worker(struct work_struct *work) strncpy(chinfo.name, channel->name, sizeof(chinfo.name)); chinfo.src = RPMSG_ADDR_ANY; chinfo.dst = RPMSG_ADDR_ANY; smd_ipc(channel->edge->ipc, false, NULL, "%s: unregistering ch %s\n", __func__, channel->name); rpmsg_unregister_device(&edge->dev, &chinfo); channel->registered = false; spin_lock_irqsave(&edge->channels_lock, flags); Loading Loading @@ -1451,8 +1574,9 @@ static int qcom_smd_parse_edge(struct device *dev, } ret = devm_request_irq(dev, irq, qcom_smd_edge_intr, IRQF_TRIGGER_RISING, node->name, edge); qcom_smd_edge_intr, IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND, node->name, edge); if (ret) { dev_err(dev, "failed to request smd irq\n"); goto put_node; Loading Loading @@ -1526,6 +1650,13 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, edge->dev.of_node = node; edge->dev.groups = qcom_smd_edge_groups; dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); /* ipc logging handler */ edge->ipc = ipc_log_context_create(4, dev_name(&edge->dev), 0); if (!edge->ipc) dev_info(&edge->dev, "%s: failed to create ipc log cntxt\n", __func__); ret = device_register(&edge->dev); if (ret) { pr_err("failed to register smd edge\n"); Loading @@ -1547,6 +1678,8 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, schedule_work(&edge->scan_work); smd_ipc(edge->ipc, false, NULL, "%s: %s:%s\n", __func__, dev_name(parent), node->name); return edge; unregister_dev: Loading Loading @@ -1608,6 +1741,8 @@ static int qcom_smd_remove_edge(struct device *dev, void *data) { struct qcom_smd_edge *edge = to_smd_edge(dev); ipc_log_context_destroy(edge->ipc); return qcom_smd_unregister_edge(edge); } Loading