@@ -439,6 +439,99 @@ static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
return of_driver_match_device(dev, drv);
}
+/**
+ * rpmsg_device_get_name_extension() - get the name extension of a rpmsg device
+ * @rpdev: the rpmsg device to work with
+ * @skip: how many characters in the extension should be skipped over
+ *
+ * With function rpmsg_id_match() allowing for extension of the base driver name
+ * in order to differentiate services, this function returns the extension part
+ * of an rpmsg device name. As such and with the following rpmsg driver device
+ * id table and rpmsg device names:
+ *
+ * static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
+ * { .name = "rpmsg-client-sample" },
+ * { },
+ * }
+ *
+ * rpdev1->id.name == "rpmsg-client-sample";
+ * rpdev2->id.name == "rpmsg-client-sample_instance0";
+ *
+ * Calling rpmsg_device_get_name_extension() will yields the following:
+ *
+ * rpmsg_device_get_name_extension(rpdev1, 0) == NULL;
+ * rpmsg_device_get_name_extension(rpdev2, 0) == "_instance0";
+ * rpmsg_device_get_name_extension(rpdev2, 1) == "instance0";
+ *
+ * Return: The name extension if found, NULL if the name of the RPMSG device
+ * equals the name of the RPMSG driver and an error if no match is
+ * found or a validation problem has occurred.
+ */
+const char *rpmsg_device_get_name_extension(struct rpmsg_device *rpdev,
+ unsigned int skip)
+{
+ const char *drv_name, *dev_name, *extension;
+ const struct rpmsg_device_id *ids;
+ struct device *dev = &rpdev->dev;
+ struct rpmsg_driver *rpdrv;
+ bool match = false;
+ unsigned int i;
+
+ if (!dev->driver)
+ return ERR_PTR(-EINVAL);
+
+ rpdrv = to_rpmsg_driver(dev->driver);
+
+ /*
+ * No point in going further if the device doesn't have name or
+ * the driver doesn't have a table to work with.
+ */
+ if (!rpdev->id.name[0] || !rpdrv->id_table)
+ return ERR_PTR(-EINVAL);
+
+ ids = rpdrv->id_table;
+ dev_name = rpdev->id.name;
+
+ /*
+ * See if any name in the driver's table match the beginning
+ * of the rpmsg device's name.
+ */
+ for (i = 0; ids[i].name[0]; i++) {
+ drv_name = ids[i].name;
+ if (strncmp(drv_name, dev_name, strlen(drv_name)) == 0) {
+ match = true;
+ break;
+ }
+ }
+
+ /*
+ * A match was not found, return an error to differentiate with cases
+ * where a match was found but the name has no extension (see below).
+ */
+ if (!match)
+ return ERR_PTR(-ENOENT);
+
+ /* No name extension to return if device and driver are the same */
+ if (strlen(dev_name) == strlen(drv_name))
+ return NULL;
+
+ /*
+ * Make sure we were not requested to skip past the end
+ * of the device name.
+ */
+ if (strlen(drv_name) + skip >= strlen(dev_name))
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * Move past the base name published by the driver and
+ * skip any extra characters if needed.
+ */
+ extension = dev_name + strlen(drv_name) + skip;
+
+ return extension;
+}
+EXPORT_SYMBOL(rpmsg_device_get_name_extension);
+
static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
@@ -135,6 +135,9 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
__poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
poll_table *wait);
+const char *rpmsg_device_get_name_extension(struct rpmsg_device *dev,
+ unsigned int skip);
+
#else
static inline int register_rpmsg_device(struct rpmsg_device *dev)
@@ -242,6 +245,16 @@ static inline __poll_t rpmsg_poll(struct rpmsg_endpoint *ept,
return 0;
}
+static inline
+const char *rpmsg_device_get_name_extension(struct rpmsg_device *dev,
+ unsigned int skip)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return NULL;
+}
+
#endif /* IS_ENABLED(CONFIG_RPMSG) */
/* use a macro to avoid include chaining to get THIS_MODULE */