diff options
| -rw-r--r-- | Documentation/ioctl/ioctl-number.txt | 1 | ||||
| -rw-r--r-- | drivers/rpmsg/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/rpmsg/Makefile | 1 | ||||
| -rw-r--r-- | drivers/rpmsg/qcom_smd.c | 58 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_char.c | 584 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_core.c | 22 | ||||
| -rw-r--r-- | drivers/rpmsg/rpmsg_internal.h | 18 | ||||
| -rw-r--r-- | include/linux/rpmsg.h | 13 | ||||
| -rw-r--r-- | include/linux/rpmsg/qcom_smd.h | 6 | ||||
| -rw-r--r-- | include/uapi/linux/rpmsg.h | 35 |
10 files changed, 742 insertions, 5 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 81c7f2bb7daf..08244bea5048 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
| @@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments | |||
| 321 | 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> | 321 | 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> |
| 322 | 0xB3 00 linux/mmc/ioctl.h | 322 | 0xB3 00 linux/mmc/ioctl.h |
| 323 | 0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org> | 323 | 0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org> |
| 324 | 0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org> | ||
| 324 | 0xC0 00-0F linux/usb/iowarrior.h | 325 | 0xC0 00-0F linux/usb/iowarrior.h |
| 325 | 0xCA 00-0F uapi/misc/cxl.h | 326 | 0xCA 00-0F uapi/misc/cxl.h |
| 326 | 0xCA 80-8F uapi/scsi/cxlflash_ioctl.h | 327 | 0xCA 80-8F uapi/scsi/cxlflash_ioctl.h |
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index de31c5f14dd9..f12ac0b28263 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig | |||
| @@ -4,6 +4,15 @@ menu "Rpmsg drivers" | |||
| 4 | config RPMSG | 4 | config RPMSG |
| 5 | tristate | 5 | tristate |
| 6 | 6 | ||
| 7 | config RPMSG_CHAR | ||
| 8 | tristate "RPMSG device interface" | ||
| 9 | depends on RPMSG | ||
| 10 | depends on NET | ||
| 11 | help | ||
| 12 | Say Y here to export rpmsg endpoints as device files, usually found | ||
| 13 | in /dev. They make it possible for user-space programs to send and | ||
| 14 | receive rpmsg packets. | ||
| 15 | |||
| 7 | config RPMSG_QCOM_SMD | 16 | config RPMSG_QCOM_SMD |
| 8 | tristate "Qualcomm Shared Memory Driver (SMD)" | 17 | tristate "Qualcomm Shared Memory Driver (SMD)" |
| 9 | depends on QCOM_SMEM | 18 | depends on QCOM_SMEM |
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index ae9c9132cf76..fae9a6d548fb 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | obj-$(CONFIG_RPMSG) += rpmsg_core.o | 1 | obj-$(CONFIG_RPMSG) += rpmsg_core.o |
| 2 | obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o | ||
| 2 | obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o | 3 | obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o |
| 3 | obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o | 4 | obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o |
diff --git a/drivers/rpmsg/qcom_smd.c b/drivers/rpmsg/qcom_smd.c index 0fae48116a0d..beaef5dd973e 100644 --- a/drivers/rpmsg/qcom_smd.c +++ b/drivers/rpmsg/qcom_smd.c | |||
| @@ -117,6 +117,8 @@ static const struct { | |||
| 117 | struct qcom_smd_edge { | 117 | struct qcom_smd_edge { |
| 118 | struct device dev; | 118 | struct device dev; |
| 119 | 119 | ||
| 120 | const char *name; | ||
| 121 | |||
| 120 | struct device_node *of_node; | 122 | struct device_node *of_node; |
| 121 | unsigned edge_id; | 123 | unsigned edge_id; |
| 122 | unsigned remote_pid; | 124 | unsigned remote_pid; |
| @@ -917,6 +919,21 @@ static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len) | |||
| 917 | return __qcom_smd_send(qsept->qsch, data, len, false); | 919 | return __qcom_smd_send(qsept->qsch, data, len, false); |
| 918 | } | 920 | } |
| 919 | 921 | ||
| 922 | static unsigned int qcom_smd_poll(struct rpmsg_endpoint *ept, | ||
| 923 | struct file *filp, poll_table *wait) | ||
| 924 | { | ||
| 925 | struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept); | ||
| 926 | struct qcom_smd_channel *channel = qsept->qsch; | ||
| 927 | unsigned int mask = 0; | ||
| 928 | |||
| 929 | poll_wait(filp, &channel->fblockread_event, wait); | ||
| 930 | |||
| 931 | if (qcom_smd_get_tx_avail(channel) > 20) | ||
| 932 | mask |= POLLOUT | POLLWRNORM; | ||
| 933 | |||
| 934 | return mask; | ||
| 935 | } | ||
| 936 | |||
| 920 | /* | 937 | /* |
| 921 | * Finds the device_node for the smd child interested in this channel. | 938 | * Finds the device_node for the smd child interested in this channel. |
| 922 | */ | 939 | */ |
| @@ -949,6 +966,7 @@ static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = { | |||
| 949 | .destroy_ept = qcom_smd_destroy_ept, | 966 | .destroy_ept = qcom_smd_destroy_ept, |
| 950 | .send = qcom_smd_send, | 967 | .send = qcom_smd_send, |
| 951 | .trysend = qcom_smd_trysend, | 968 | .trysend = qcom_smd_trysend, |
| 969 | .poll = qcom_smd_poll, | ||
| 952 | }; | 970 | }; |
| 953 | 971 | ||
| 954 | /* | 972 | /* |
| @@ -984,6 +1002,20 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) | |||
| 984 | return rpmsg_register_device(rpdev); | 1002 | return rpmsg_register_device(rpdev); |
| 985 | } | 1003 | } |
| 986 | 1004 | ||
| 1005 | static int qcom_smd_create_chrdev(struct qcom_smd_edge *edge) | ||
| 1006 | { | ||
| 1007 | struct qcom_smd_device *qsdev; | ||
| 1008 | |||
| 1009 | qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL); | ||
| 1010 | if (!qsdev) | ||
| 1011 | return -ENOMEM; | ||
| 1012 | |||
| 1013 | qsdev->edge = edge; | ||
| 1014 | qsdev->rpdev.ops = &qcom_smd_device_ops; | ||
| 1015 | qsdev->rpdev.dev.parent = &edge->dev; | ||
| 1016 | return rpmsg_chrdev_register_device(&qsdev->rpdev); | ||
| 1017 | } | ||
| 1018 | |||
| 987 | /* | 1019 | /* |
| 988 | * Allocate the qcom_smd_channel object for a newly found smd channel, | 1020 | * Allocate the qcom_smd_channel object for a newly found smd channel, |
| 989 | * retrieving and validating the smem items involved. | 1021 | * retrieving and validating the smem items involved. |
| @@ -1248,6 +1280,10 @@ static int qcom_smd_parse_edge(struct device *dev, | |||
| 1248 | return -EINVAL; | 1280 | return -EINVAL; |
| 1249 | } | 1281 | } |
| 1250 | 1282 | ||
| 1283 | ret = of_property_read_string(node, "label", &edge->name); | ||
| 1284 | if (ret < 0) | ||
| 1285 | edge->name = node->name; | ||
| 1286 | |||
| 1251 | irq = irq_of_parse_and_map(node, 0); | 1287 | irq = irq_of_parse_and_map(node, 0); |
| 1252 | if (irq < 0) { | 1288 | if (irq < 0) { |
| 1253 | dev_err(dev, "required smd interrupt missing\n"); | 1289 | dev_err(dev, "required smd interrupt missing\n"); |
| @@ -1285,6 +1321,21 @@ static void qcom_smd_edge_release(struct device *dev) | |||
| 1285 | kfree(edge); | 1321 | kfree(edge); |
| 1286 | } | 1322 | } |
| 1287 | 1323 | ||
| 1324 | static ssize_t rpmsg_name_show(struct device *dev, | ||
| 1325 | struct device_attribute *attr, char *buf) | ||
| 1326 | { | ||
| 1327 | struct qcom_smd_edge *edge = to_smd_edge(dev); | ||
| 1328 | |||
| 1329 | return sprintf(buf, "%s\n", edge->name); | ||
| 1330 | } | ||
| 1331 | static DEVICE_ATTR_RO(rpmsg_name); | ||
| 1332 | |||
| 1333 | static struct attribute *qcom_smd_edge_attrs[] = { | ||
| 1334 | &dev_attr_rpmsg_name.attr, | ||
| 1335 | NULL | ||
| 1336 | }; | ||
| 1337 | ATTRIBUTE_GROUPS(qcom_smd_edge); | ||
| 1338 | |||
| 1288 | /** | 1339 | /** |
| 1289 | * qcom_smd_register_edge() - register an edge based on an device_node | 1340 | * qcom_smd_register_edge() - register an edge based on an device_node |
| 1290 | * @parent: parent device for the edge | 1341 | * @parent: parent device for the edge |
| @@ -1306,6 +1357,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, | |||
| 1306 | 1357 | ||
| 1307 | edge->dev.parent = parent; | 1358 | edge->dev.parent = parent; |
| 1308 | edge->dev.release = qcom_smd_edge_release; | 1359 | edge->dev.release = qcom_smd_edge_release; |
| 1360 | edge->dev.groups = qcom_smd_edge_groups; | ||
| 1309 | dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); | 1361 | dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); |
| 1310 | ret = device_register(&edge->dev); | 1362 | ret = device_register(&edge->dev); |
| 1311 | if (ret) { | 1363 | if (ret) { |
| @@ -1319,6 +1371,12 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, | |||
| 1319 | goto unregister_dev; | 1371 | goto unregister_dev; |
| 1320 | } | 1372 | } |
| 1321 | 1373 | ||
| 1374 | ret = qcom_smd_create_chrdev(edge); | ||
| 1375 | if (ret) { | ||
| 1376 | dev_err(&edge->dev, "failed to register chrdev for edge\n"); | ||
| 1377 | goto unregister_dev; | ||
| 1378 | } | ||
| 1379 | |||
| 1322 | schedule_work(&edge->scan_work); | 1380 | schedule_work(&edge->scan_work); |
| 1323 | 1381 | ||
| 1324 | return edge; | 1382 | return edge; |
diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c new file mode 100644 index 000000000000..0ca2ccc09ca6 --- /dev/null +++ b/drivers/rpmsg/rpmsg_char.c | |||
| @@ -0,0 +1,584 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2016, Linaro Ltd. | ||
| 3 | * Copyright (c) 2012, Michal Simek <monstr@monstr.eu> | ||
| 4 | * Copyright (c) 2012, PetaLogix | ||
| 5 | * Copyright (c) 2011, Texas Instruments, Inc. | ||
| 6 | * Copyright (c) 2011, Google, Inc. | ||
| 7 | * | ||
| 8 | * Based on rpmsg performance statistics driver by Michal Simek, which in turn | ||
| 9 | * was based on TI & Google OMX rpmsg driver. | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License version 2 and | ||
| 13 | * only version 2 as published by the Free Software Foundation. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, | ||
| 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | * GNU General Public License for more details. | ||
| 19 | */ | ||
| 20 | #include <linux/cdev.h> | ||
| 21 | #include <linux/device.h> | ||
| 22 | #include <linux/fs.h> | ||
| 23 | #include <linux/idr.h> | ||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/module.h> | ||
| 26 | #include <linux/poll.h> | ||
| 27 | #include <linux/rpmsg.h> | ||
| 28 | #include <linux/skbuff.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <linux/uaccess.h> | ||
| 31 | #include <uapi/linux/rpmsg.h> | ||
| 32 | |||
| 33 | #include "rpmsg_internal.h" | ||
| 34 | |||
| 35 | #define RPMSG_DEV_MAX (MINORMASK + 1) | ||
| 36 | |||
| 37 | static dev_t rpmsg_major; | ||
| 38 | static struct class *rpmsg_class; | ||
| 39 | |||
| 40 | static DEFINE_IDA(rpmsg_ctrl_ida); | ||
| 41 | static DEFINE_IDA(rpmsg_ept_ida); | ||
| 42 | static DEFINE_IDA(rpmsg_minor_ida); | ||
| 43 | |||
| 44 | #define dev_to_eptdev(dev) container_of(dev, struct rpmsg_eptdev, dev) | ||
| 45 | #define cdev_to_eptdev(i_cdev) container_of(i_cdev, struct rpmsg_eptdev, cdev) | ||
| 46 | |||
| 47 | #define dev_to_ctrldev(dev) container_of(dev, struct rpmsg_ctrldev, dev) | ||
| 48 | #define cdev_to_ctrldev(i_cdev) container_of(i_cdev, struct rpmsg_ctrldev, cdev) | ||
| 49 | |||
| 50 | /** | ||
| 51 | * struct rpmsg_ctrldev - control device for instantiating endpoint devices | ||
| 52 | * @rpdev: underlaying rpmsg device | ||
| 53 | * @cdev: cdev for the ctrl device | ||
| 54 | * @dev: device for the ctrl device | ||
| 55 | */ | ||
| 56 | struct rpmsg_ctrldev { | ||
| 57 | struct rpmsg_device *rpdev; | ||
| 58 | struct cdev cdev; | ||
| 59 | struct device dev; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * struct rpmsg_eptdev - endpoint device context | ||
| 64 | * @dev: endpoint device | ||
| 65 | * @cdev: cdev for the endpoint device | ||
| 66 | * @rpdev: underlaying rpmsg device | ||
| 67 | * @chinfo: info used to open the endpoint | ||
| 68 | * @ept_lock: synchronization of @ept modifications | ||
| 69 | * @ept: rpmsg endpoint reference, when open | ||
| 70 | * @queue_lock: synchronization of @queue operations | ||
| 71 | * @queue: incoming message queue | ||
| 72 | * @readq: wait object for incoming queue | ||
| 73 | */ | ||
| 74 | struct rpmsg_eptdev { | ||
| 75 | struct device dev; | ||
| 76 | struct cdev cdev; | ||
| 77 | |||
| 78 | struct rpmsg_device *rpdev; | ||
| 79 | struct rpmsg_channel_info chinfo; | ||
| 80 | |||
| 81 | struct mutex ept_lock; | ||
| 82 | struct rpmsg_endpoint *ept; | ||
| 83 | |||
| 84 | spinlock_t queue_lock; | ||
| 85 | struct sk_buff_head queue; | ||
| 86 | wait_queue_head_t readq; | ||
| 87 | }; | ||
| 88 | |||
| 89 | static int rpmsg_eptdev_destroy(struct device *dev, void *data) | ||
| 90 | { | ||
| 91 | struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); | ||
| 92 | |||
| 93 | mutex_lock(&eptdev->ept_lock); | ||
| 94 | if (eptdev->ept) { | ||
| 95 | rpmsg_destroy_ept(eptdev->ept); | ||
| 96 | eptdev->ept = NULL; | ||
| 97 | } | ||
| 98 | mutex_unlock(&eptdev->ept_lock); | ||
| 99 | |||
| 100 | /* wake up any blocked readers */ | ||
| 101 | wake_up_interruptible(&eptdev->readq); | ||
| 102 | |||
| 103 | device_del(&eptdev->dev); | ||
| 104 | put_device(&eptdev->dev); | ||
| 105 | |||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, | ||
| 110 | void *priv, u32 addr) | ||
| 111 | { | ||
| 112 | struct rpmsg_eptdev *eptdev = priv; | ||
| 113 | struct sk_buff *skb; | ||
| 114 | |||
| 115 | skb = alloc_skb(len, GFP_ATOMIC); | ||
| 116 | if (!skb) | ||
| 117 | return -ENOMEM; | ||
| 118 | |||
| 119 | memcpy(skb_put(skb, len), buf, len); | ||
| 120 | |||
| 121 | spin_lock(&eptdev->queue_lock); | ||
| 122 | skb_queue_tail(&eptdev->queue, skb); | ||
| 123 | spin_unlock(&eptdev->queue_lock); | ||
| 124 | |||
| 125 | /* wake up any blocking processes, waiting for new data */ | ||
| 126 | wake_up_interruptible(&eptdev->readq); | ||
| 127 | |||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) | ||
| 132 | { | ||
| 133 | struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); | ||
| 134 | struct rpmsg_endpoint *ept; | ||
| 135 | struct rpmsg_device *rpdev = eptdev->rpdev; | ||
| 136 | struct device *dev = &eptdev->dev; | ||
| 137 | |||
| 138 | get_device(dev); | ||
| 139 | |||
| 140 | ept = rpmsg_create_ept(rpdev, rpmsg_ept_cb, eptdev, eptdev->chinfo); | ||
| 141 | if (!ept) { | ||
| 142 | dev_err(dev, "failed to open %s\n", eptdev->chinfo.name); | ||
| 143 | put_device(dev); | ||
| 144 | return -EINVAL; | ||
| 145 | } | ||
| 146 | |||
| 147 | eptdev->ept = ept; | ||
| 148 | filp->private_data = eptdev; | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) | ||
| 154 | { | ||
| 155 | struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); | ||
| 156 | struct device *dev = &eptdev->dev; | ||
| 157 | struct sk_buff *skb; | ||
| 158 | |||
| 159 | /* Close the endpoint, if it's not already destroyed by the parent */ | ||
| 160 | mutex_lock(&eptdev->ept_lock); | ||
| 161 | if (eptdev->ept) { | ||
| 162 | rpmsg_destroy_ept(eptdev->ept); | ||
| 163 | eptdev->ept = NULL; | ||
| 164 | } | ||
| 165 | mutex_unlock(&eptdev->ept_lock); | ||
| 166 | |||
| 167 | /* Discard all SKBs */ | ||
| 168 | while (!skb_queue_empty(&eptdev->queue)) { | ||
| 169 | skb = skb_dequeue(&eptdev->queue); | ||
| 170 | kfree_skb(skb); | ||
| 171 | } | ||
| 172 | |||
| 173 | put_device(dev); | ||
| 174 | |||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | |||
| 178 | static ssize_t rpmsg_eptdev_read(struct file *filp, char __user *buf, | ||
| 179 | size_t len, loff_t *f_pos) | ||
| 180 | { | ||
| 181 | struct rpmsg_eptdev *eptdev = filp->private_data; | ||
| 182 | unsigned long flags; | ||
| 183 | struct sk_buff *skb; | ||
| 184 | int use; | ||
| 185 | |||
| 186 | if (!eptdev->ept) | ||
| 187 | return -EPIPE; | ||
| 188 | |||
| 189 | spin_lock_irqsave(&eptdev->queue_lock, flags); | ||
| 190 | |||
| 191 | /* Wait for data in the queue */ | ||
| 192 | if (skb_queue_empty(&eptdev->queue)) { | ||
| 193 | spin_unlock_irqrestore(&eptdev->queue_lock, flags); | ||
| 194 | |||
| 195 | if (filp->f_flags & O_NONBLOCK) | ||
| 196 | return -EAGAIN; | ||
| 197 | |||
| 198 | /* Wait until we get data or the endpoint goes away */ | ||
| 199 | if (wait_event_interruptible(eptdev->readq, | ||
| 200 | !skb_queue_empty(&eptdev->queue) || | ||
| 201 | !eptdev->ept)) | ||
| 202 | return -ERESTARTSYS; | ||
| 203 | |||
| 204 | /* We lost the endpoint while waiting */ | ||
| 205 | if (!eptdev->ept) | ||
| 206 | return -EPIPE; | ||
| 207 | |||
| 208 | spin_lock_irqsave(&eptdev->queue_lock, flags); | ||
| 209 | } | ||
| 210 | |||
| 211 | skb = skb_dequeue(&eptdev->queue); | ||
| 212 | spin_unlock_irqrestore(&eptdev->queue_lock, flags); | ||
| 213 | if (!skb) | ||
| 214 | return -EFAULT; | ||
| 215 | |||
| 216 | use = min_t(size_t, len, skb->len); | ||
| 217 | if (copy_to_user(buf, skb->data, use)) | ||
| 218 | use = -EFAULT; | ||
| 219 | |||
| 220 | kfree_skb(skb); | ||
| 221 | |||
| 222 | return use; | ||
| 223 | } | ||
| 224 | |||
| 225 | static ssize_t rpmsg_eptdev_write(struct file *filp, const char __user *buf, | ||
| 226 | size_t len, loff_t *f_pos) | ||
| 227 | { | ||
| 228 | struct rpmsg_eptdev *eptdev = filp->private_data; | ||
| 229 | void *kbuf; | ||
| 230 | int ret; | ||
| 231 | |||
| 232 | kbuf = memdup_user(buf, len); | ||
| 233 | if (IS_ERR(kbuf)) | ||
| 234 | return PTR_ERR(kbuf); | ||
| 235 | |||
| 236 | if (mutex_lock_interruptible(&eptdev->ept_lock)) { | ||
| 237 | ret = -ERESTARTSYS; | ||
| 238 | goto free_kbuf; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (!eptdev->ept) { | ||
| 242 | ret = -EPIPE; | ||
| 243 | goto unlock_eptdev; | ||
| 244 | } | ||
| 245 | |||
| 246 | if (filp->f_flags & O_NONBLOCK) | ||
| 247 | ret = rpmsg_trysend(eptdev->ept, kbuf, len); | ||
| 248 | else | ||
| 249 | ret = rpmsg_send(eptdev->ept, kbuf, len); | ||
| 250 | |||
| 251 | unlock_eptdev: | ||
| 252 | mutex_unlock(&eptdev->ept_lock); | ||
| 253 | |||
| 254 | free_kbuf: | ||
| 255 | kfree(kbuf); | ||
| 256 | return ret < 0 ? ret : len; | ||
| 257 | } | ||
| 258 | |||
| 259 | static unsigned int rpmsg_eptdev_poll(struct file *filp, poll_table *wait) | ||
| 260 | { | ||
| 261 | struct rpmsg_eptdev *eptdev = filp->private_data; | ||
| 262 | unsigned int mask = 0; | ||
| 263 | |||
| 264 | if (!eptdev->ept) | ||
| 265 | return POLLERR; | ||
| 266 | |||
| 267 | poll_wait(filp, &eptdev->readq, wait); | ||
| 268 | |||
| 269 | if (!skb_queue_empty(&eptdev->queue)) | ||
| 270 | mask |= POLLIN | POLLRDNORM; | ||
| 271 | |||
| 272 | mask |= rpmsg_poll(eptdev->ept, filp, wait); | ||
| 273 | |||
| 274 | return mask; | ||
| 275 | } | ||
| 276 | |||
| 277 | static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, | ||
| 278 | unsigned long arg) | ||
| 279 | { | ||
| 280 | struct rpmsg_eptdev *eptdev = fp->private_data; | ||
| 281 | |||
| 282 | if (cmd != RPMSG_DESTROY_EPT_IOCTL) | ||
| 283 | return -EINVAL; | ||
| 284 | |||
| 285 | return rpmsg_eptdev_destroy(&eptdev->dev, NULL); | ||
| 286 | } | ||
| 287 | |||
| 288 | static const struct file_operations rpmsg_eptdev_fops = { | ||
| 289 | .owner = THIS_MODULE, | ||
| 290 | .open = rpmsg_eptdev_open, | ||
| 291 | .release = rpmsg_eptdev_release, | ||
| 292 | .read = rpmsg_eptdev_read, | ||
| 293 | .write = rpmsg_eptdev_write, | ||
| 294 | .poll = rpmsg_eptdev_poll, | ||
| 295 | .unlocked_ioctl = rpmsg_eptdev_ioctl, | ||
| 296 | }; | ||
| 297 | |||
| 298 | static ssize_t name_show(struct device *dev, struct device_attribute *attr, | ||
| 299 | char *buf) | ||
| 300 | { | ||
| 301 | struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); | ||
| 302 | |||
| 303 | return sprintf(buf, "%s\n", eptdev->chinfo.name); | ||
| 304 | } | ||
| 305 | static DEVICE_ATTR_RO(name); | ||
| 306 | |||
| 307 | static ssize_t src_show(struct device *dev, struct device_attribute *attr, | ||
| 308 | char *buf) | ||
| 309 | { | ||
| 310 | struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); | ||
| 311 | |||
| 312 | return sprintf(buf, "%d\n", eptdev->chinfo.src); | ||
| 313 | } | ||
| 314 | static DEVICE_ATTR_RO(src); | ||
| 315 | |||
| 316 | static ssize_t dst_show(struct device *dev, struct device_attribute *attr, | ||
| 317 | char *buf) | ||
| 318 | { | ||
| 319 | struct rpmsg_eptdev *eptdev = dev_get_drvdata(dev); | ||
| 320 | |||
| 321 | return sprintf(buf, "%d\n", eptdev->chinfo.dst); | ||
| 322 | } | ||
| 323 | static DEVICE_ATTR_RO(dst); | ||
| 324 | |||
| 325 | static struct attribute *rpmsg_eptdev_attrs[] = { | ||
| 326 | &dev_attr_name.attr, | ||
| 327 | &dev_attr_src.attr, | ||
| 328 | &dev_attr_dst.attr, | ||
| 329 | NULL | ||
| 330 | }; | ||
| 331 | ATTRIBUTE_GROUPS(rpmsg_eptdev); | ||
| 332 | |||
| 333 | static void rpmsg_eptdev_release_device(struct device *dev) | ||
| 334 | { | ||
| 335 | struct rpmsg_eptdev *eptdev = dev_to_eptdev(dev); | ||
| 336 | |||
| 337 | ida_simple_remove(&rpmsg_ept_ida, dev->id); | ||
| 338 | ida_simple_remove(&rpmsg_minor_ida, MINOR(eptdev->dev.devt)); | ||
| 339 | cdev_del(&eptdev->cdev); | ||
| 340 | kfree(eptdev); | ||
| 341 | } | ||
| 342 | |||
| 343 | static int rpmsg_eptdev_create(struct rpmsg_ctrldev *ctrldev, | ||
| 344 | struct rpmsg_channel_info chinfo) | ||
| 345 | { | ||
| 346 | struct rpmsg_device *rpdev = ctrldev->rpdev; | ||
| 347 | struct rpmsg_eptdev *eptdev; | ||
| 348 | struct device *dev; | ||
| 349 | int ret; | ||
| 350 | |||
| 351 | eptdev = kzalloc(sizeof(*eptdev), GFP_KERNEL); | ||
| 352 | if (!eptdev) | ||
| 353 | return -ENOMEM; | ||
| 354 | |||
| 355 | dev = &eptdev->dev; | ||
| 356 | eptdev->rpdev = rpdev; | ||
| 357 | eptdev->chinfo = chinfo; | ||
| 358 | |||
| 359 | mutex_init(&eptdev->ept_lock); | ||
| 360 | spin_lock_init(&eptdev->queue_lock); | ||
| 361 | skb_queue_head_init(&eptdev->queue); | ||
| 362 | init_waitqueue_head(&eptdev->readq); | ||
| 363 | |||
| 364 | device_initialize(dev); | ||
| 365 | dev->class = rpmsg_class; | ||
| 366 | dev->parent = &ctrldev->dev; | ||
| 367 | dev->groups = rpmsg_eptdev_groups; | ||
| 368 | dev_set_drvdata(dev, eptdev); | ||
| 369 | |||
| 370 | cdev_init(&eptdev->cdev, &rpmsg_eptdev_fops); | ||
| 371 | eptdev->cdev.owner = THIS_MODULE; | ||
| 372 | |||
| 373 | ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); | ||
| 374 | if (ret < 0) | ||
| 375 | goto free_eptdev; | ||
| 376 | dev->devt = MKDEV(MAJOR(rpmsg_major), ret); | ||
| 377 | |||
| 378 | ret = ida_simple_get(&rpmsg_ept_ida, 0, 0, GFP_KERNEL); | ||
| 379 | if (ret < 0) | ||
| 380 | goto free_minor_ida; | ||
| 381 | dev->id = ret; | ||
| 382 | dev_set_name(dev, "rpmsg%d", ret); | ||
| 383 | |||
| 384 | ret = cdev_add(&eptdev->cdev, dev->devt, 1); | ||
| 385 | if (ret) | ||
| 386 | goto free_ept_ida; | ||
| 387 | |||
| 388 | /* We can now rely on the release function for cleanup */ | ||
| 389 | dev->release = rpmsg_eptdev_release_device; | ||
| 390 | |||
| 391 | ret = device_add(dev); | ||
| 392 | if (ret) { | ||
| 393 | dev_err(dev, "device_register failed: %d\n", ret); | ||
| 394 | put_device(dev); | ||
| 395 | } | ||
| 396 | |||
| 397 | return ret; | ||
| 398 | |||
| 399 | free_ept_ida: | ||
| 400 | ida_simple_remove(&rpmsg_ept_ida, dev->id); | ||
| 401 | free_minor_ida: | ||
| 402 | ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); | ||
| 403 | free_eptdev: | ||
| 404 | put_device(dev); | ||
| 405 | kfree(eptdev); | ||
| 406 | |||
| 407 | return ret; | ||
| 408 | } | ||
| 409 | |||
| 410 | static int rpmsg_ctrldev_open(struct inode *inode, struct file *filp) | ||
| 411 | { | ||
| 412 | struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); | ||
| 413 | |||
| 414 | get_device(&ctrldev->dev); | ||
| 415 | filp->private_data = ctrldev; | ||
| 416 | |||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | static int rpmsg_ctrldev_release(struct inode *inode, struct file *filp) | ||
| 421 | { | ||
| 422 | struct rpmsg_ctrldev *ctrldev = cdev_to_ctrldev(inode->i_cdev); | ||
| 423 | |||
| 424 | put_device(&ctrldev->dev); | ||
| 425 | |||
| 426 | return 0; | ||
| 427 | } | ||
| 428 | |||
| 429 | static long rpmsg_ctrldev_ioctl(struct file *fp, unsigned int cmd, | ||
| 430 | unsigned long arg) | ||
| 431 | { | ||
| 432 | struct rpmsg_ctrldev *ctrldev = fp->private_data; | ||
| 433 | void __user *argp = (void __user *)arg; | ||
| 434 | struct rpmsg_endpoint_info eptinfo; | ||
| 435 | struct rpmsg_channel_info chinfo; | ||
| 436 | |||
| 437 | if (cmd != RPMSG_CREATE_EPT_IOCTL) | ||
| 438 | return -EINVAL; | ||
| 439 | |||
| 440 | if (copy_from_user(&eptinfo, argp, sizeof(eptinfo))) | ||
| 441 | return -EFAULT; | ||
| 442 | |||
| 443 | memcpy(chinfo.name, eptinfo.name, RPMSG_NAME_SIZE); | ||
| 444 | chinfo.name[RPMSG_NAME_SIZE-1] = '\0'; | ||
| 445 | chinfo.src = eptinfo.src; | ||
| 446 | chinfo.dst = eptinfo.dst; | ||
| 447 | |||
| 448 | return rpmsg_eptdev_create(ctrldev, chinfo); | ||
| 449 | }; | ||
| 450 | |||
| 451 | static const struct file_operations rpmsg_ctrldev_fops = { | ||
| 452 | .owner = THIS_MODULE, | ||
| 453 | .open = rpmsg_ctrldev_open, | ||
| 454 | .release = rpmsg_ctrldev_release, | ||
| 455 | .unlocked_ioctl = rpmsg_ctrldev_ioctl, | ||
| 456 | }; | ||
| 457 | |||
| 458 | static void rpmsg_ctrldev_release_device(struct device *dev) | ||
| 459 | { | ||
| 460 | struct rpmsg_ctrldev *ctrldev = dev_to_ctrldev(dev); | ||
| 461 | |||
| 462 | ida_simple_remove(&rpmsg_ctrl_ida, dev->id); | ||
| 463 | ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); | ||
| 464 | cdev_del(&ctrldev->cdev); | ||
| 465 | kfree(ctrldev); | ||
| 466 | } | ||
| 467 | |||
| 468 | static int rpmsg_chrdev_probe(struct rpmsg_device *rpdev) | ||
| 469 | { | ||
| 470 | struct rpmsg_ctrldev *ctrldev; | ||
| 471 | struct device *dev; | ||
| 472 | int ret; | ||
| 473 | |||
| 474 | ctrldev = kzalloc(sizeof(*ctrldev), GFP_KERNEL); | ||
| 475 | if (!ctrldev) | ||
| 476 | return -ENOMEM; | ||
| 477 | |||
| 478 | ctrldev->rpdev = rpdev; | ||
| 479 | |||
| 480 | dev = &ctrldev->dev; | ||
| 481 | device_initialize(dev); | ||
| 482 | dev->parent = &rpdev->dev; | ||
| 483 | dev->class = rpmsg_class; | ||
| 484 | |||
| 485 | cdev_init(&ctrldev->cdev, &rpmsg_ctrldev_fops); | ||
| 486 | ctrldev->cdev.owner = THIS_MODULE; | ||
| 487 | |||
| 488 | ret = ida_simple_get(&rpmsg_minor_ida, 0, RPMSG_DEV_MAX, GFP_KERNEL); | ||
| 489 | if (ret < 0) | ||
| 490 | goto free_ctrldev; | ||
| 491 | dev->devt = MKDEV(MAJOR(rpmsg_major), ret); | ||
| 492 | |||
| 493 | ret = ida_simple_get(&rpmsg_ctrl_ida, 0, 0, GFP_KERNEL); | ||
| 494 | if (ret < 0) | ||
| 495 | goto free_minor_ida; | ||
| 496 | dev->id = ret; | ||
| 497 | dev_set_name(&ctrldev->dev, "rpmsg_ctrl%d", ret); | ||
| 498 | |||
| 499 | ret = cdev_add(&ctrldev->cdev, dev->devt, 1); | ||
| 500 | if (ret) | ||
| 501 | goto free_ctrl_ida; | ||
| 502 | |||
| 503 | /* We can now rely on the release function for cleanup */ | ||
| 504 | dev->release = rpmsg_ctrldev_release_device; | ||
| 505 | |||
| 506 | ret = device_add(dev); | ||
| 507 | if (ret) { | ||
| 508 | dev_err(&rpdev->dev, "device_register failed: %d\n", ret); | ||
| 509 | put_device(dev); | ||
| 510 | } | ||
| 511 | |||
| 512 | dev_set_drvdata(&rpdev->dev, ctrldev); | ||
| 513 | |||
| 514 | return ret; | ||
| 515 | |||
| 516 | free_ctrl_ida: | ||
| 517 | ida_simple_remove(&rpmsg_ctrl_ida, dev->id); | ||
| 518 | free_minor_ida: | ||
| 519 | ida_simple_remove(&rpmsg_minor_ida, MINOR(dev->devt)); | ||
| 520 | free_ctrldev: | ||
| 521 | put_device(dev); | ||
| 522 | kfree(ctrldev); | ||
| 523 | |||
| 524 | return ret; | ||
| 525 | } | ||
| 526 | |||
| 527 | static void rpmsg_chrdev_remove(struct rpmsg_device *rpdev) | ||
| 528 | { | ||
| 529 | struct rpmsg_ctrldev *ctrldev = dev_get_drvdata(&rpdev->dev); | ||
| 530 | int ret; | ||
| 531 | |||
| 532 | /* Destroy all endpoints */ | ||
| 533 | ret = device_for_each_child(&ctrldev->dev, NULL, rpmsg_eptdev_destroy); | ||
| 534 | if (ret) | ||
| 535 | dev_warn(&rpdev->dev, "failed to nuke endpoints: %d\n", ret); | ||
| 536 | |||
| 537 | device_del(&ctrldev->dev); | ||
| 538 | put_device(&ctrldev->dev); | ||
| 539 | } | ||
| 540 | |||
| 541 | static struct rpmsg_driver rpmsg_chrdev_driver = { | ||
| 542 | .probe = rpmsg_chrdev_probe, | ||
| 543 | .remove = rpmsg_chrdev_remove, | ||
| 544 | .drv = { | ||
| 545 | .name = "rpmsg_chrdev", | ||
| 546 | }, | ||
| 547 | }; | ||
| 548 | |||
| 549 | static int rpmsg_char_init(void) | ||
| 550 | { | ||
| 551 | int ret; | ||
| 552 | |||
| 553 | ret = alloc_chrdev_region(&rpmsg_major, 0, RPMSG_DEV_MAX, "rpmsg"); | ||
| 554 | if (ret < 0) { | ||
| 555 | pr_err("rpmsg: failed to allocate char dev region\n"); | ||
| 556 | return ret; | ||
| 557 | } | ||
| 558 | |||
| 559 | rpmsg_class = class_create(THIS_MODULE, "rpmsg"); | ||
| 560 | if (IS_ERR(rpmsg_class)) { | ||
| 561 | pr_err("failed to create rpmsg class\n"); | ||
| 562 | unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); | ||
| 563 | return PTR_ERR(rpmsg_class); | ||
| 564 | } | ||
| 565 | |||
| 566 | ret = register_rpmsg_driver(&rpmsg_chrdev_driver); | ||
| 567 | if (ret < 0) { | ||
| 568 | pr_err("rpmsgchr: failed to register rpmsg driver\n"); | ||
| 569 | class_destroy(rpmsg_class); | ||
| 570 | unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); | ||
| 571 | } | ||
| 572 | |||
| 573 | return ret; | ||
| 574 | } | ||
| 575 | postcore_initcall(rpmsg_char_init); | ||
| 576 | |||
| 577 | static void rpmsg_chrdev_exit(void) | ||
| 578 | { | ||
| 579 | unregister_rpmsg_driver(&rpmsg_chrdev_driver); | ||
| 580 | class_destroy(rpmsg_class); | ||
| 581 | unregister_chrdev_region(rpmsg_major, RPMSG_DEV_MAX); | ||
| 582 | } | ||
| 583 | module_exit(rpmsg_chrdev_exit); | ||
| 584 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 1cfb775e8e82..600f5f9f7431 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c | |||
| @@ -72,7 +72,7 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, | |||
| 72 | struct rpmsg_channel_info chinfo) | 72 | struct rpmsg_channel_info chinfo) |
| 73 | { | 73 | { |
| 74 | if (WARN_ON(!rpdev)) | 74 | if (WARN_ON(!rpdev)) |
| 75 | return ERR_PTR(-EINVAL); | 75 | return NULL; |
| 76 | 76 | ||
| 77 | return rpdev->ops->create_ept(rpdev, cb, priv, chinfo); | 77 | return rpdev->ops->create_ept(rpdev, cb, priv, chinfo); |
| 78 | } | 78 | } |
| @@ -240,6 +240,26 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) | |||
| 240 | EXPORT_SYMBOL(rpmsg_trysendto); | 240 | EXPORT_SYMBOL(rpmsg_trysendto); |
| 241 | 241 | ||
| 242 | /** | 242 | /** |
| 243 | * rpmsg_poll() - poll the endpoint's send buffers | ||
| 244 | * @ept: the rpmsg endpoint | ||
| 245 | * @filp: file for poll_wait() | ||
| 246 | * @wait: poll_table for poll_wait() | ||
| 247 | * | ||
| 248 | * Returns mask representing the current state of the endpoint's send buffers | ||
| 249 | */ | ||
| 250 | unsigned int rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, | ||
| 251 | poll_table *wait) | ||
| 252 | { | ||
| 253 | if (WARN_ON(!ept)) | ||
| 254 | return 0; | ||
| 255 | if (!ept->ops->poll) | ||
| 256 | return 0; | ||
| 257 | |||
| 258 | return ept->ops->poll(ept, filp, wait); | ||
| 259 | } | ||
| 260 | EXPORT_SYMBOL(rpmsg_poll); | ||
| 261 | |||
| 262 | /** | ||
| 243 | * rpmsg_send_offchannel() - send a message using explicit src/dst addresses | 263 | * rpmsg_send_offchannel() - send a message using explicit src/dst addresses |
| 244 | * @ept: the rpmsg endpoint | 264 | * @ept: the rpmsg endpoint |
| 245 | * @src: source address | 265 | * @src: source address |
diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 8075a20f919b..0cf9c7e2ee83 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #define __RPMSG_INTERNAL_H__ | 21 | #define __RPMSG_INTERNAL_H__ |
| 22 | 22 | ||
| 23 | #include <linux/rpmsg.h> | 23 | #include <linux/rpmsg.h> |
| 24 | #include <linux/poll.h> | ||
| 24 | 25 | ||
| 25 | #define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev) | 26 | #define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev) |
| 26 | #define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) | 27 | #define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) |
| @@ -70,6 +71,8 @@ struct rpmsg_endpoint_ops { | |||
| 70 | int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); | 71 | int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); |
| 71 | int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, | 72 | int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, |
| 72 | void *data, int len); | 73 | void *data, int len); |
| 74 | unsigned int (*poll)(struct rpmsg_endpoint *ept, struct file *filp, | ||
| 75 | poll_table *wait); | ||
| 73 | }; | 76 | }; |
| 74 | 77 | ||
| 75 | int rpmsg_register_device(struct rpmsg_device *rpdev); | 78 | int rpmsg_register_device(struct rpmsg_device *rpdev); |
| @@ -79,4 +82,19 @@ int rpmsg_unregister_device(struct device *parent, | |||
| 79 | struct device *rpmsg_find_device(struct device *parent, | 82 | struct device *rpmsg_find_device(struct device *parent, |
| 80 | struct rpmsg_channel_info *chinfo); | 83 | struct rpmsg_channel_info *chinfo); |
| 81 | 84 | ||
| 85 | /** | ||
| 86 | * rpmsg_chrdev_register_device() - register chrdev device based on rpdev | ||
| 87 | * @rpdev: prepared rpdev to be used for creating endpoints | ||
| 88 | * | ||
| 89 | * This function wraps rpmsg_register_device() preparing the rpdev for use as | ||
| 90 | * basis for the rpmsg chrdev. | ||
| 91 | */ | ||
| 92 | static inline int rpmsg_chrdev_register_device(struct rpmsg_device *rpdev) | ||
| 93 | { | ||
| 94 | strcpy(rpdev->id.name, "rpmsg_chrdev"); | ||
| 95 | rpdev->driver_override = "rpmsg_chrdev"; | ||
| 96 | |||
| 97 | return rpmsg_register_device(rpdev); | ||
| 98 | } | ||
| 99 | |||
| 82 | #endif | 100 | #endif |
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 18f9e1ae4b7e..10d6ae8bbb7d 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h | |||
| @@ -41,6 +41,7 @@ | |||
| 41 | #include <linux/mod_devicetable.h> | 41 | #include <linux/mod_devicetable.h> |
| 42 | #include <linux/kref.h> | 42 | #include <linux/kref.h> |
| 43 | #include <linux/mutex.h> | 43 | #include <linux/mutex.h> |
| 44 | #include <linux/poll.h> | ||
| 44 | 45 | ||
| 45 | #define RPMSG_ADDR_ANY 0xFFFFFFFF | 46 | #define RPMSG_ADDR_ANY 0xFFFFFFFF |
| 46 | 47 | ||
| @@ -156,6 +157,9 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); | |||
| 156 | int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, | 157 | int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, |
| 157 | void *data, int len); | 158 | void *data, int len); |
| 158 | 159 | ||
| 160 | unsigned int rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, | ||
| 161 | poll_table *wait); | ||
| 162 | |||
| 159 | #else | 163 | #else |
| 160 | 164 | ||
| 161 | static inline int register_rpmsg_device(struct rpmsg_device *dev) | 165 | static inline int register_rpmsg_device(struct rpmsg_device *dev) |
| @@ -254,6 +258,15 @@ static inline int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, | |||
| 254 | return -ENXIO; | 258 | return -ENXIO; |
| 255 | } | 259 | } |
| 256 | 260 | ||
| 261 | static inline unsigned int rpmsg_poll(struct rpmsg_endpoint *ept, | ||
| 262 | struct file *filp, poll_table *wait) | ||
| 263 | { | ||
| 264 | /* This shouldn't be possible */ | ||
| 265 | WARN_ON(1); | ||
| 266 | |||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 257 | #endif /* IS_ENABLED(CONFIG_RPMSG) */ | 270 | #endif /* IS_ENABLED(CONFIG_RPMSG) */ |
| 258 | 271 | ||
| 259 | /* use a macro to avoid include chaining to get THIS_MODULE */ | 272 | /* use a macro to avoid include chaining to get THIS_MODULE */ |
diff --git a/include/linux/rpmsg/qcom_smd.h b/include/linux/rpmsg/qcom_smd.h index e674b2e3074b..8ec8b6439b25 100644 --- a/include/linux/rpmsg/qcom_smd.h +++ b/include/linux/rpmsg/qcom_smd.h | |||
| @@ -18,14 +18,12 @@ static inline struct qcom_smd_edge * | |||
| 18 | qcom_smd_register_edge(struct device *parent, | 18 | qcom_smd_register_edge(struct device *parent, |
| 19 | struct device_node *node) | 19 | struct device_node *node) |
| 20 | { | 20 | { |
| 21 | return ERR_PTR(-ENXIO); | 21 | return NULL; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) | 24 | static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) |
| 25 | { | 25 | { |
| 26 | /* This shouldn't be possible */ | 26 | return 0; |
| 27 | WARN_ON(1); | ||
| 28 | return -ENXIO; | ||
| 29 | } | 27 | } |
| 30 | 28 | ||
| 31 | #endif | 29 | #endif |
diff --git a/include/uapi/linux/rpmsg.h b/include/uapi/linux/rpmsg.h new file mode 100644 index 000000000000..dedc226e0d3f --- /dev/null +++ b/include/uapi/linux/rpmsg.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2016, Linaro Ltd. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 and | ||
| 6 | * only version 2 as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef _UAPI_RPMSG_H_ | ||
| 15 | #define _UAPI_RPMSG_H_ | ||
| 16 | |||
| 17 | #include <linux/ioctl.h> | ||
| 18 | #include <linux/types.h> | ||
| 19 | |||
| 20 | /** | ||
| 21 | * struct rpmsg_endpoint_info - endpoint info representation | ||
| 22 | * @name: name of service | ||
| 23 | * @src: local address | ||
| 24 | * @dst: destination address | ||
| 25 | */ | ||
| 26 | struct rpmsg_endpoint_info { | ||
| 27 | char name[32]; | ||
| 28 | __u32 src; | ||
| 29 | __u32 dst; | ||
| 30 | }; | ||
| 31 | |||
| 32 | #define RPMSG_CREATE_EPT_IOCTL _IOW(0xb5, 0x1, struct rpmsg_endpoint_info) | ||
| 33 | #define RPMSG_DESTROY_EPT_IOCTL _IO(0xb5, 0x2) | ||
| 34 | |||
| 35 | #endif | ||
