Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright (c) 2021, Stephan Gerhold <stephan@gerhold.net> */
0003 #include <linux/kernel.h>
0004 #include <linux/mod_devicetable.h>
0005 #include <linux/module.h>
0006 #include <linux/platform_device.h>
0007 #include <linux/rpmsg.h>
0008 #include <linux/wwan.h>
0009 
0010 struct rpmsg_wwan_dev {
0011     /* Lower level is a rpmsg dev, upper level is a wwan port */
0012     struct rpmsg_device *rpdev;
0013     struct wwan_port *wwan_port;
0014     struct rpmsg_endpoint *ept;
0015 };
0016 
0017 static int rpmsg_wwan_ctrl_callback(struct rpmsg_device *rpdev,
0018                     void *buf, int len, void *priv, u32 src)
0019 {
0020     struct rpmsg_wwan_dev *rpwwan = priv;
0021     struct sk_buff *skb;
0022 
0023     skb = alloc_skb(len, GFP_ATOMIC);
0024     if (!skb)
0025         return -ENOMEM;
0026 
0027     skb_put_data(skb, buf, len);
0028     wwan_port_rx(rpwwan->wwan_port, skb);
0029     return 0;
0030 }
0031 
0032 static int rpmsg_wwan_ctrl_start(struct wwan_port *port)
0033 {
0034     struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port);
0035     struct rpmsg_channel_info chinfo = {
0036         .src = rpwwan->rpdev->src,
0037         .dst = RPMSG_ADDR_ANY,
0038     };
0039 
0040     strncpy(chinfo.name, rpwwan->rpdev->id.name, RPMSG_NAME_SIZE);
0041     rpwwan->ept = rpmsg_create_ept(rpwwan->rpdev, rpmsg_wwan_ctrl_callback,
0042                        rpwwan, chinfo);
0043     if (!rpwwan->ept)
0044         return -EREMOTEIO;
0045 
0046     return 0;
0047 }
0048 
0049 static void rpmsg_wwan_ctrl_stop(struct wwan_port *port)
0050 {
0051     struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port);
0052 
0053     rpmsg_destroy_ept(rpwwan->ept);
0054     rpwwan->ept = NULL;
0055 }
0056 
0057 static int rpmsg_wwan_ctrl_tx(struct wwan_port *port, struct sk_buff *skb)
0058 {
0059     struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port);
0060     int ret;
0061 
0062     ret = rpmsg_trysend(rpwwan->ept, skb->data, skb->len);
0063     if (ret)
0064         return ret;
0065 
0066     consume_skb(skb);
0067     return 0;
0068 }
0069 
0070 static int rpmsg_wwan_ctrl_tx_blocking(struct wwan_port *port, struct sk_buff *skb)
0071 {
0072     struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port);
0073     int ret;
0074 
0075     ret = rpmsg_send(rpwwan->ept, skb->data, skb->len);
0076     if (ret)
0077         return ret;
0078 
0079     consume_skb(skb);
0080     return 0;
0081 }
0082 
0083 static __poll_t rpmsg_wwan_ctrl_tx_poll(struct wwan_port *port,
0084                     struct file *filp, poll_table *wait)
0085 {
0086     struct rpmsg_wwan_dev *rpwwan = wwan_port_get_drvdata(port);
0087 
0088     return rpmsg_poll(rpwwan->ept, filp, wait);
0089 }
0090 
0091 static const struct wwan_port_ops rpmsg_wwan_pops = {
0092     .start = rpmsg_wwan_ctrl_start,
0093     .stop = rpmsg_wwan_ctrl_stop,
0094     .tx = rpmsg_wwan_ctrl_tx,
0095     .tx_blocking = rpmsg_wwan_ctrl_tx_blocking,
0096     .tx_poll = rpmsg_wwan_ctrl_tx_poll,
0097 };
0098 
0099 static struct device *rpmsg_wwan_find_parent(struct device *dev)
0100 {
0101     /* Select first platform device as parent for the WWAN ports.
0102      * On Qualcomm platforms this is usually the platform device that
0103      * represents the modem remote processor. This might need to be
0104      * adjusted when adding device IDs for other platforms.
0105      */
0106     for (dev = dev->parent; dev; dev = dev->parent) {
0107         if (dev_is_platform(dev))
0108             return dev;
0109     }
0110     return NULL;
0111 }
0112 
0113 static int rpmsg_wwan_ctrl_probe(struct rpmsg_device *rpdev)
0114 {
0115     struct rpmsg_wwan_dev *rpwwan;
0116     struct wwan_port *port;
0117     struct device *parent;
0118 
0119     parent = rpmsg_wwan_find_parent(&rpdev->dev);
0120     if (!parent)
0121         return -ENODEV;
0122 
0123     rpwwan = devm_kzalloc(&rpdev->dev, sizeof(*rpwwan), GFP_KERNEL);
0124     if (!rpwwan)
0125         return -ENOMEM;
0126 
0127     rpwwan->rpdev = rpdev;
0128     dev_set_drvdata(&rpdev->dev, rpwwan);
0129 
0130     /* Register as a wwan port, id.driver_data contains wwan port type */
0131     port = wwan_create_port(parent, rpdev->id.driver_data,
0132                 &rpmsg_wwan_pops, rpwwan);
0133     if (IS_ERR(port))
0134         return PTR_ERR(port);
0135 
0136     rpwwan->wwan_port = port;
0137 
0138     return 0;
0139 };
0140 
0141 static void rpmsg_wwan_ctrl_remove(struct rpmsg_device *rpdev)
0142 {
0143     struct rpmsg_wwan_dev *rpwwan = dev_get_drvdata(&rpdev->dev);
0144 
0145     wwan_remove_port(rpwwan->wwan_port);
0146 }
0147 
0148 static const struct rpmsg_device_id rpmsg_wwan_ctrl_id_table[] = {
0149     /* RPMSG channels for Qualcomm SoCs with integrated modem */
0150     { .name = "DATA5_CNTL", .driver_data = WWAN_PORT_QMI },
0151     { .name = "DATA4", .driver_data = WWAN_PORT_AT },
0152     {},
0153 };
0154 MODULE_DEVICE_TABLE(rpmsg, rpmsg_wwan_ctrl_id_table);
0155 
0156 static struct rpmsg_driver rpmsg_wwan_ctrl_driver = {
0157     .drv.name = "rpmsg_wwan_ctrl",
0158     .id_table = rpmsg_wwan_ctrl_id_table,
0159     .probe = rpmsg_wwan_ctrl_probe,
0160     .remove = rpmsg_wwan_ctrl_remove,
0161 };
0162 module_rpmsg_driver(rpmsg_wwan_ctrl_driver);
0163 
0164 MODULE_LICENSE("GPL v2");
0165 MODULE_DESCRIPTION("RPMSG WWAN CTRL Driver");
0166 MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>");