Commit 1db934a5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rpmsg-v4.11' of git://github.com/andersson/remoteproc

Pull rpmsg updates from Bjorn Andersson:
 "This introduces an interface for user space to directly communicate on
  rpmsg endpoints without the implementation of specific kernel drivers,
  which is useful for e.g. debug channels:

* tag 'rpmsg-v4.11' of git://github.com/andersson/remoteproc:
  rpmsg: rpmsg_create_ept() returns NULL on error
  rpmsg: qcom: smd: Return positively when not enabled
  rpmsg: unlock on error in rpmsg_eptdev_read()
  rpmsg: char: add CONFIG_NET dependency
  rpmsg: smd: Register rpmsg user space interface for edges
  rpmsg: Driver for user space endpoint interface
  rpmsg: qcom_smd: Implement endpoint "poll"
  rpmsg: Introduce "poll" to endpoint ops
  rpmsg: qcom_smd: Add support for "label" property
parents a3919caa fa04b769
...@@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments ...@@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca> 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
0xB3 00 linux/mmc/ioctl.h 0xB3 00 linux/mmc/ioctl.h
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org> 0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
0xC0 00-0F linux/usb/iowarrior.h 0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h 0xCA 00-0F uapi/misc/cxl.h
0xCA 80-8F uapi/scsi/cxlflash_ioctl.h 0xCA 80-8F uapi/scsi/cxlflash_ioctl.h
......
...@@ -4,6 +4,15 @@ menu "Rpmsg drivers" ...@@ -4,6 +4,15 @@ menu "Rpmsg drivers"
config RPMSG config RPMSG
tristate tristate
config RPMSG_CHAR
tristate "RPMSG device interface"
depends on RPMSG
depends on NET
help
Say Y here to export rpmsg endpoints as device files, usually found
in /dev. They make it possible for user-space programs to send and
receive rpmsg packets.
config RPMSG_QCOM_SMD config RPMSG_QCOM_SMD
tristate "Qualcomm Shared Memory Driver (SMD)" tristate "Qualcomm Shared Memory Driver (SMD)"
depends on QCOM_SMEM depends on QCOM_SMEM
......
obj-$(CONFIG_RPMSG) += rpmsg_core.o obj-$(CONFIG_RPMSG) += rpmsg_core.o
obj-$(CONFIG_RPMSG_CHAR) += rpmsg_char.o
obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o
obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o
...@@ -117,6 +117,8 @@ static const struct { ...@@ -117,6 +117,8 @@ static const struct {
struct qcom_smd_edge { struct qcom_smd_edge {
struct device dev; struct device dev;
const char *name;
struct device_node *of_node; struct device_node *of_node;
unsigned edge_id; unsigned edge_id;
unsigned remote_pid; unsigned remote_pid;
...@@ -917,6 +919,21 @@ static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len) ...@@ -917,6 +919,21 @@ static int qcom_smd_trysend(struct rpmsg_endpoint *ept, void *data, int len)
return __qcom_smd_send(qsept->qsch, data, len, false); return __qcom_smd_send(qsept->qsch, data, len, false);
} }
static unsigned int qcom_smd_poll(struct rpmsg_endpoint *ept,
struct file *filp, poll_table *wait)
{
struct qcom_smd_endpoint *qsept = to_smd_endpoint(ept);
struct qcom_smd_channel *channel = qsept->qsch;
unsigned int mask = 0;
poll_wait(filp, &channel->fblockread_event, wait);
if (qcom_smd_get_tx_avail(channel) > 20)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
/* /*
* Finds the device_node for the smd child interested in this channel. * Finds the device_node for the smd child interested in this channel.
*/ */
...@@ -949,6 +966,7 @@ static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = { ...@@ -949,6 +966,7 @@ static const struct rpmsg_endpoint_ops qcom_smd_endpoint_ops = {
.destroy_ept = qcom_smd_destroy_ept, .destroy_ept = qcom_smd_destroy_ept,
.send = qcom_smd_send, .send = qcom_smd_send,
.trysend = qcom_smd_trysend, .trysend = qcom_smd_trysend,
.poll = qcom_smd_poll,
}; };
/* /*
...@@ -984,6 +1002,20 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel) ...@@ -984,6 +1002,20 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
return rpmsg_register_device(rpdev); return rpmsg_register_device(rpdev);
} }
static int qcom_smd_create_chrdev(struct qcom_smd_edge *edge)
{
struct qcom_smd_device *qsdev;
qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
if (!qsdev)
return -ENOMEM;
qsdev->edge = edge;
qsdev->rpdev.ops = &qcom_smd_device_ops;
qsdev->rpdev.dev.parent = &edge->dev;
return rpmsg_chrdev_register_device(&qsdev->rpdev);
}
/* /*
* Allocate the qcom_smd_channel object for a newly found smd channel, * Allocate the qcom_smd_channel object for a newly found smd channel,
* retrieving and validating the smem items involved. * retrieving and validating the smem items involved.
...@@ -1248,6 +1280,10 @@ static int qcom_smd_parse_edge(struct device *dev, ...@@ -1248,6 +1280,10 @@ static int qcom_smd_parse_edge(struct device *dev,
return -EINVAL; return -EINVAL;
} }
ret = of_property_read_string(node, "label", &edge->name);
if (ret < 0)
edge->name = node->name;
irq = irq_of_parse_and_map(node, 0); irq = irq_of_parse_and_map(node, 0);
if (irq < 0) { if (irq < 0) {
dev_err(dev, "required smd interrupt missing\n"); dev_err(dev, "required smd interrupt missing\n");
...@@ -1285,6 +1321,21 @@ static void qcom_smd_edge_release(struct device *dev) ...@@ -1285,6 +1321,21 @@ static void qcom_smd_edge_release(struct device *dev)
kfree(edge); kfree(edge);
} }
static ssize_t rpmsg_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qcom_smd_edge *edge = to_smd_edge(dev);
return sprintf(buf, "%s\n", edge->name);
}
static DEVICE_ATTR_RO(rpmsg_name);
static struct attribute *qcom_smd_edge_attrs[] = {
&dev_attr_rpmsg_name.attr,
NULL
};
ATTRIBUTE_GROUPS(qcom_smd_edge);
/** /**
* qcom_smd_register_edge() - register an edge based on an device_node * qcom_smd_register_edge() - register an edge based on an device_node
* @parent: parent device for the edge * @parent: parent device for the edge
...@@ -1306,6 +1357,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, ...@@ -1306,6 +1357,7 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
edge->dev.parent = parent; edge->dev.parent = parent;
edge->dev.release = qcom_smd_edge_release; edge->dev.release = qcom_smd_edge_release;
edge->dev.groups = qcom_smd_edge_groups;
dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name);
ret = device_register(&edge->dev); ret = device_register(&edge->dev);
if (ret) { if (ret) {
...@@ -1319,6 +1371,12 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, ...@@ -1319,6 +1371,12 @@ struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
goto unregister_dev; goto unregister_dev;
} }
ret = qcom_smd_create_chrdev(edge);
if (ret) {
dev_err(&edge->dev, "failed to register chrdev for edge\n");
goto unregister_dev;
}
schedule_work(&edge->scan_work); schedule_work(&edge->scan_work);
return edge; return edge;
......
This diff is collapsed.
...@@ -72,7 +72,7 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev, ...@@ -72,7 +72,7 @@ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
struct rpmsg_channel_info chinfo) struct rpmsg_channel_info chinfo)
{ {
if (WARN_ON(!rpdev)) if (WARN_ON(!rpdev))
return ERR_PTR(-EINVAL); return NULL;
return rpdev->ops->create_ept(rpdev, cb, priv, chinfo); return rpdev->ops->create_ept(rpdev, cb, priv, chinfo);
} }
...@@ -239,6 +239,26 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst) ...@@ -239,6 +239,26 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
} }
EXPORT_SYMBOL(rpmsg_trysendto); EXPORT_SYMBOL(rpmsg_trysendto);
/**
* rpmsg_poll() - poll the endpoint's send buffers
* @ept: the rpmsg endpoint
* @filp: file for poll_wait()
* @wait: poll_table for poll_wait()
*
* Returns mask representing the current state of the endpoint's send buffers
*/
unsigned int rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
poll_table *wait)
{
if (WARN_ON(!ept))
return 0;
if (!ept->ops->poll)
return 0;
return ept->ops->poll(ept, filp, wait);
}
EXPORT_SYMBOL(rpmsg_poll);
/** /**
* rpmsg_send_offchannel() - send a message using explicit src/dst addresses * rpmsg_send_offchannel() - send a message using explicit src/dst addresses
* @ept: the rpmsg endpoint * @ept: the rpmsg endpoint
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define __RPMSG_INTERNAL_H__ #define __RPMSG_INTERNAL_H__
#include <linux/rpmsg.h> #include <linux/rpmsg.h>
#include <linux/poll.h>
#define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev) #define to_rpmsg_device(d) container_of(d, struct rpmsg_device, dev)
#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) #define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv)
...@@ -70,6 +71,8 @@ struct rpmsg_endpoint_ops { ...@@ -70,6 +71,8 @@ struct rpmsg_endpoint_ops {
int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); int (*trysendto)(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst, int (*trysend_offchannel)(struct rpmsg_endpoint *ept, u32 src, u32 dst,
void *data, int len); void *data, int len);
unsigned int (*poll)(struct rpmsg_endpoint *ept, struct file *filp,
poll_table *wait);
}; };
int rpmsg_register_device(struct rpmsg_device *rpdev); int rpmsg_register_device(struct rpmsg_device *rpdev);
...@@ -79,4 +82,19 @@ int rpmsg_unregister_device(struct device *parent, ...@@ -79,4 +82,19 @@ int rpmsg_unregister_device(struct device *parent,
struct device *rpmsg_find_device(struct device *parent, struct device *rpmsg_find_device(struct device *parent,
struct rpmsg_channel_info *chinfo); struct rpmsg_channel_info *chinfo);
/**
* rpmsg_chrdev_register_device() - register chrdev device based on rpdev
* @rpdev: prepared rpdev to be used for creating endpoints
*
* This function wraps rpmsg_register_device() preparing the rpdev for use as
* basis for the rpmsg chrdev.
*/
static inline int rpmsg_chrdev_register_device(struct rpmsg_device *rpdev)
{
strcpy(rpdev->id.name, "rpmsg_chrdev");
rpdev->driver_override = "rpmsg_chrdev";
return rpmsg_register_device(rpdev);
}
#endif #endif
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/poll.h>
#define RPMSG_ADDR_ANY 0xFFFFFFFF #define RPMSG_ADDR_ANY 0xFFFFFFFF
...@@ -156,6 +157,9 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst); ...@@ -156,6 +157,9 @@ int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst);
int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
void *data, int len); void *data, int len);
unsigned int rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
poll_table *wait);
#else #else
static inline int register_rpmsg_device(struct rpmsg_device *dev) 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,6 +258,15 @@ static inline int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src,
return -ENXIO; return -ENXIO;
} }
static inline unsigned int rpmsg_poll(struct rpmsg_endpoint *ept,
struct file *filp, poll_table *wait)
{
/* This shouldn't be possible */
WARN_ON(1);
return 0;
}
#endif /* IS_ENABLED(CONFIG_RPMSG) */ #endif /* IS_ENABLED(CONFIG_RPMSG) */
/* use a macro to avoid include chaining to get THIS_MODULE */ /* use a macro to avoid include chaining to get THIS_MODULE */
......
...@@ -18,14 +18,12 @@ static inline struct qcom_smd_edge * ...@@ -18,14 +18,12 @@ static inline struct qcom_smd_edge *
qcom_smd_register_edge(struct device *parent, qcom_smd_register_edge(struct device *parent,
struct device_node *node) struct device_node *node)
{ {
return ERR_PTR(-ENXIO); return NULL;
} }
static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{ {
/* This shouldn't be possible */ return 0;
WARN_ON(1);
return -ENXIO;
} }
#endif #endif
......
/*
* Copyright (c) 2016, Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _UAPI_RPMSG_H_
#define _UAPI_RPMSG_H_
#include <linux/ioctl.h>
#include <linux/types.h>
/**
* struct rpmsg_endpoint_info - endpoint info representation
* @name: name of service
* @src: local address
* @dst: destination address
*/
struct rpmsg_endpoint_info {
char name[32];
__u32 src;
__u32 dst;
};
#define RPMSG_CREATE_EPT_IOCTL _IOW(0xb5, 0x1, struct rpmsg_endpoint_info)
#define RPMSG_DESTROY_EPT_IOCTL _IO(0xb5, 0x2)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment