linux/drivers/rpmsg/rpmsg_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * remote processor messaging bus
   4 *
   5 * Copyright (C) 2011 Texas Instruments, Inc.
   6 * Copyright (C) 2011 Google, Inc.
   7 *
   8 * Ohad Ben-Cohen <ohad@wizery.com>
   9 * Brian Swetland <swetland@google.com>
  10 */
  11
  12#define pr_fmt(fmt) "%s: " fmt, __func__
  13
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/rpmsg.h>
  17#include <linux/of_device.h>
  18#include <linux/pm_domain.h>
  19#include <linux/slab.h>
  20
  21#include "rpmsg_internal.h"
  22
  23/**
  24 * rpmsg_create_channel() - create a new rpmsg channel
  25 * using its name and address info.
  26 * @rpdev: rpmsg device
  27 * @chinfo: channel_info to bind
  28 *
  29 * Returns a pointer to the new rpmsg device on success, or NULL on error.
  30 */
  31struct rpmsg_device *rpmsg_create_channel(struct rpmsg_device *rpdev,
  32                                          struct rpmsg_channel_info *chinfo)
  33{
  34        if (WARN_ON(!rpdev))
  35                return NULL;
  36        if (!rpdev->ops || !rpdev->ops->create_channel) {
  37                dev_err(&rpdev->dev, "no create_channel ops found\n");
  38                return NULL;
  39        }
  40
  41        return rpdev->ops->create_channel(rpdev, chinfo);
  42}
  43EXPORT_SYMBOL(rpmsg_create_channel);
  44
  45/**
  46 * rpmsg_release_channel() - release a rpmsg channel
  47 * using its name and address info.
  48 * @rpdev: rpmsg device
  49 * @chinfo: channel_info to bind
  50 *
  51 * Returns 0 on success or an appropriate error value.
  52 */
  53int rpmsg_release_channel(struct rpmsg_device *rpdev,
  54                          struct rpmsg_channel_info *chinfo)
  55{
  56        if (WARN_ON(!rpdev))
  57                return -EINVAL;
  58        if (!rpdev->ops || !rpdev->ops->release_channel) {
  59                dev_err(&rpdev->dev, "no release_channel ops found\n");
  60                return -ENXIO;
  61        }
  62
  63        return rpdev->ops->release_channel(rpdev, chinfo);
  64}
  65EXPORT_SYMBOL(rpmsg_release_channel);
  66
  67/**
  68 * rpmsg_create_ept() - create a new rpmsg_endpoint
  69 * @rpdev: rpmsg channel device
  70 * @cb: rx callback handler
  71 * @priv: private data for the driver's use
  72 * @chinfo: channel_info with the local rpmsg address to bind with @cb
  73 *
  74 * Every rpmsg address in the system is bound to an rx callback (so when
  75 * inbound messages arrive, they are dispatched by the rpmsg bus using the
  76 * appropriate callback handler) by means of an rpmsg_endpoint struct.
  77 *
  78 * This function allows drivers to create such an endpoint, and by that,
  79 * bind a callback, and possibly some private data too, to an rpmsg address
  80 * (either one that is known in advance, or one that will be dynamically
  81 * assigned for them).
  82 *
  83 * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint
  84 * is already created for them when they are probed by the rpmsg bus
  85 * (using the rx callback provided when they registered to the rpmsg bus).
  86 *
  87 * So things should just work for simple drivers: they already have an
  88 * endpoint, their rx callback is bound to their rpmsg address, and when
  89 * relevant inbound messages arrive (i.e. messages which their dst address
  90 * equals to the src address of their rpmsg channel), the driver's handler
  91 * is invoked to process it.
  92 *
  93 * That said, more complicated drivers might need to allocate
  94 * additional rpmsg addresses, and bind them to different rx callbacks.
  95 * To accomplish that, those drivers need to call this function.
  96 *
  97 * Drivers should provide their @rpdev channel (so the new endpoint would belong
  98 * to the same remote processor their channel belongs to), an rx callback
  99 * function, an optional private data (which is provided back when the
 100 * rx callback is invoked), and an address they want to bind with the
 101 * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
 102 * dynamically assign them an available rpmsg address (drivers should have
 103 * a very good reason why not to always use RPMSG_ADDR_ANY here).
 104 *
 105 * Returns a pointer to the endpoint on success, or NULL on error.
 106 */
 107struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
 108                                        rpmsg_rx_cb_t cb, void *priv,
 109                                        struct rpmsg_channel_info chinfo)
 110{
 111        if (WARN_ON(!rpdev))
 112                return NULL;
 113
 114        return rpdev->ops->create_ept(rpdev, cb, priv, chinfo);
 115}
 116EXPORT_SYMBOL(rpmsg_create_ept);
 117
 118/**
 119 * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
 120 * @ept: endpoing to destroy
 121 *
 122 * Should be used by drivers to destroy an rpmsg endpoint previously
 123 * created with rpmsg_create_ept(). As with other types of "free" NULL
 124 * is a valid parameter.
 125 */
 126void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
 127{
 128        if (ept && ept->ops)
 129                ept->ops->destroy_ept(ept);
 130}
 131EXPORT_SYMBOL(rpmsg_destroy_ept);
 132
 133/**
 134 * rpmsg_send() - send a message across to the remote processor
 135 * @ept: the rpmsg endpoint
 136 * @data: payload of message
 137 * @len: length of payload
 138 *
 139 * This function sends @data of length @len on the @ept endpoint.
 140 * The message will be sent to the remote processor which the @ept
 141 * endpoint belongs to, using @ept's address and its associated rpmsg
 142 * device destination addresses.
 143 * In case there are no TX buffers available, the function will block until
 144 * one becomes available, or a timeout of 15 seconds elapses. When the latter
 145 * happens, -ERESTARTSYS is returned.
 146 *
 147 * Can only be called from process context (for now).
 148 *
 149 * Returns 0 on success and an appropriate error value on failure.
 150 */
 151int rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
 152{
 153        if (WARN_ON(!ept))
 154                return -EINVAL;
 155        if (!ept->ops->send)
 156                return -ENXIO;
 157
 158        return ept->ops->send(ept, data, len);
 159}
 160EXPORT_SYMBOL(rpmsg_send);
 161
 162/**
 163 * rpmsg_sendto() - send a message across to the remote processor, specify dst
 164 * @ept: the rpmsg endpoint
 165 * @data: payload of message
 166 * @len: length of payload
 167 * @dst: destination address
 168 *
 169 * This function sends @data of length @len to the remote @dst address.
 170 * The message will be sent to the remote processor which the @ept
 171 * endpoint belongs to, using @ept's address as source.
 172 * In case there are no TX buffers available, the function will block until
 173 * one becomes available, or a timeout of 15 seconds elapses. When the latter
 174 * happens, -ERESTARTSYS is returned.
 175 *
 176 * Can only be called from process context (for now).
 177 *
 178 * Returns 0 on success and an appropriate error value on failure.
 179 */
 180int rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
 181{
 182        if (WARN_ON(!ept))
 183                return -EINVAL;
 184        if (!ept->ops->sendto)
 185                return -ENXIO;
 186
 187        return ept->ops->sendto(ept, data, len, dst);
 188}
 189EXPORT_SYMBOL(rpmsg_sendto);
 190
 191/**
 192 * rpmsg_send_offchannel() - send a message using explicit src/dst addresses
 193 * @ept: the rpmsg endpoint
 194 * @src: source address
 195 * @dst: destination address
 196 * @data: payload of message
 197 * @len: length of payload
 198 *
 199 * This function sends @data of length @len to the remote @dst address,
 200 * and uses @src as the source address.
 201 * The message will be sent to the remote processor which the @ept
 202 * endpoint belongs to.
 203 * In case there are no TX buffers available, the function will block until
 204 * one becomes available, or a timeout of 15 seconds elapses. When the latter
 205 * happens, -ERESTARTSYS is returned.
 206 *
 207 * Can only be called from process context (for now).
 208 *
 209 * Returns 0 on success and an appropriate error value on failure.
 210 */
 211int rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
 212                          void *data, int len)
 213{
 214        if (WARN_ON(!ept))
 215                return -EINVAL;
 216        if (!ept->ops->send_offchannel)
 217                return -ENXIO;
 218
 219        return ept->ops->send_offchannel(ept, src, dst, data, len);
 220}
 221EXPORT_SYMBOL(rpmsg_send_offchannel);
 222
 223/**
 224 * rpmsg_trysend() - send a message across to the remote processor
 225 * @ept: the rpmsg endpoint
 226 * @data: payload of message
 227 * @len: length of payload
 228 *
 229 * This function sends @data of length @len on the @ept endpoint.
 230 * The message will be sent to the remote processor which the @ept
 231 * endpoint belongs to, using @ept's address as source and its associated
 232 * rpdev's address as destination.
 233 * In case there are no TX buffers available, the function will immediately
 234 * return -ENOMEM without waiting until one becomes available.
 235 *
 236 * Can only be called from process context (for now).
 237 *
 238 * Returns 0 on success and an appropriate error value on failure.
 239 */
 240int rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)
 241{
 242        if (WARN_ON(!ept))
 243                return -EINVAL;
 244        if (!ept->ops->trysend)
 245                return -ENXIO;
 246
 247        return ept->ops->trysend(ept, data, len);
 248}
 249EXPORT_SYMBOL(rpmsg_trysend);
 250
 251/**
 252 * rpmsg_trysendto() - send a message across to the remote processor, specify dst
 253 * @ept: the rpmsg endpoint
 254 * @data: payload of message
 255 * @len: length of payload
 256 * @dst: destination address
 257 *
 258 * This function sends @data of length @len to the remote @dst address.
 259 * The message will be sent to the remote processor which the @ept
 260 * endpoint belongs to, using @ept's address as source.
 261 * In case there are no TX buffers available, the function will immediately
 262 * return -ENOMEM without waiting until one becomes available.
 263 *
 264 * Can only be called from process context (for now).
 265 *
 266 * Returns 0 on success and an appropriate error value on failure.
 267 */
 268int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
 269{
 270        if (WARN_ON(!ept))
 271                return -EINVAL;
 272        if (!ept->ops->trysendto)
 273                return -ENXIO;
 274
 275        return ept->ops->trysendto(ept, data, len, dst);
 276}
 277EXPORT_SYMBOL(rpmsg_trysendto);
 278
 279/**
 280 * rpmsg_poll() - poll the endpoint's send buffers
 281 * @ept:        the rpmsg endpoint
 282 * @filp:       file for poll_wait()
 283 * @wait:       poll_table for poll_wait()
 284 *
 285 * Returns mask representing the current state of the endpoint's send buffers
 286 */
 287__poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
 288                        poll_table *wait)
 289{
 290        if (WARN_ON(!ept))
 291                return 0;
 292        if (!ept->ops->poll)
 293                return 0;
 294
 295        return ept->ops->poll(ept, filp, wait);
 296}
 297EXPORT_SYMBOL(rpmsg_poll);
 298
 299/**
 300 * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses
 301 * @ept: the rpmsg endpoint
 302 * @src: source address
 303 * @dst: destination address
 304 * @data: payload of message
 305 * @len: length of payload
 306 *
 307 * This function sends @data of length @len to the remote @dst address,
 308 * and uses @src as the source address.
 309 * The message will be sent to the remote processor which the @ept
 310 * endpoint belongs to.
 311 * In case there are no TX buffers available, the function will immediately
 312 * return -ENOMEM without waiting until one becomes available.
 313 *
 314 * Can only be called from process context (for now).
 315 *
 316 * Returns 0 on success and an appropriate error value on failure.
 317 */
 318int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
 319                             void *data, int len)
 320{
 321        if (WARN_ON(!ept))
 322                return -EINVAL;
 323        if (!ept->ops->trysend_offchannel)
 324                return -ENXIO;
 325
 326        return ept->ops->trysend_offchannel(ept, src, dst, data, len);
 327}
 328EXPORT_SYMBOL(rpmsg_trysend_offchannel);
 329
 330/*
 331 * match a rpmsg channel with a channel info struct.
 332 * this is used to make sure we're not creating rpmsg devices for channels
 333 * that already exist.
 334 */
 335static int rpmsg_device_match(struct device *dev, void *data)
 336{
 337        struct rpmsg_channel_info *chinfo = data;
 338        struct rpmsg_device *rpdev = to_rpmsg_device(dev);
 339
 340        if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src)
 341                return 0;
 342
 343        if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst)
 344                return 0;
 345
 346        if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE))
 347                return 0;
 348
 349        /* found a match ! */
 350        return 1;
 351}
 352
 353struct device *rpmsg_find_device(struct device *parent,
 354                                 struct rpmsg_channel_info *chinfo)
 355{
 356        return device_find_child(parent, chinfo, rpmsg_device_match);
 357
 358}
 359EXPORT_SYMBOL(rpmsg_find_device);
 360
 361/* sysfs show configuration fields */
 362#define rpmsg_show_attr(field, path, format_string)                     \
 363static ssize_t                                                          \
 364field##_show(struct device *dev,                                        \
 365                        struct device_attribute *attr, char *buf)       \
 366{                                                                       \
 367        struct rpmsg_device *rpdev = to_rpmsg_device(dev);              \
 368                                                                        \
 369        return sprintf(buf, format_string, rpdev->path);                \
 370}                                                                       \
 371static DEVICE_ATTR_RO(field);
 372
 373#define rpmsg_string_attr(field, member)                                \
 374static ssize_t                                                          \
 375field##_store(struct device *dev, struct device_attribute *attr,        \
 376              const char *buf, size_t sz)                               \
 377{                                                                       \
 378        struct rpmsg_device *rpdev = to_rpmsg_device(dev);              \
 379        char *new, *old;                                                \
 380                                                                        \
 381        new = kstrndup(buf, sz, GFP_KERNEL);                            \
 382        if (!new)                                                       \
 383                return -ENOMEM;                                         \
 384        new[strcspn(new, "\n")] = '\0';                                 \
 385                                                                        \
 386        device_lock(dev);                                               \
 387        old = rpdev->member;                                            \
 388        if (strlen(new)) {                                              \
 389                rpdev->member = new;                                    \
 390        } else {                                                        \
 391                kfree(new);                                             \
 392                rpdev->member = NULL;                                   \
 393        }                                                               \
 394        device_unlock(dev);                                             \
 395                                                                        \
 396        kfree(old);                                                     \
 397                                                                        \
 398        return sz;                                                      \
 399}                                                                       \
 400static ssize_t                                                          \
 401field##_show(struct device *dev,                                        \
 402             struct device_attribute *attr, char *buf)                  \
 403{                                                                       \
 404        struct rpmsg_device *rpdev = to_rpmsg_device(dev);              \
 405                                                                        \
 406        return sprintf(buf, "%s\n", rpdev->member);                     \
 407}                                                                       \
 408static DEVICE_ATTR_RW(field)
 409
 410/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
 411rpmsg_show_attr(name, id.name, "%s\n");
 412rpmsg_show_attr(src, src, "0x%x\n");
 413rpmsg_show_attr(dst, dst, "0x%x\n");
 414rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
 415rpmsg_string_attr(driver_override, driver_override);
 416
 417static ssize_t modalias_show(struct device *dev,
 418                             struct device_attribute *attr, char *buf)
 419{
 420        struct rpmsg_device *rpdev = to_rpmsg_device(dev);
 421        ssize_t len;
 422
 423        len = of_device_modalias(dev, buf, PAGE_SIZE);
 424        if (len != -ENODEV)
 425                return len;
 426
 427        return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name);
 428}
 429static DEVICE_ATTR_RO(modalias);
 430
 431static struct attribute *rpmsg_dev_attrs[] = {
 432        &dev_attr_name.attr,
 433        &dev_attr_modalias.attr,
 434        &dev_attr_dst.attr,
 435        &dev_attr_src.attr,
 436        &dev_attr_announce.attr,
 437        &dev_attr_driver_override.attr,
 438        NULL,
 439};
 440ATTRIBUTE_GROUPS(rpmsg_dev);
 441
 442/* rpmsg devices and drivers are matched using the service name */
 443static inline int rpmsg_id_match(const struct rpmsg_device *rpdev,
 444                                  const struct rpmsg_device_id *id)
 445{
 446        return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;
 447}
 448
 449/* match rpmsg channel and rpmsg driver */
 450static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
 451{
 452        struct rpmsg_device *rpdev = to_rpmsg_device(dev);
 453        struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);
 454        const struct rpmsg_device_id *ids = rpdrv->id_table;
 455        unsigned int i;
 456
 457        if (rpdev->driver_override)
 458                return !strcmp(rpdev->driver_override, drv->name);
 459
 460        if (ids)
 461                for (i = 0; ids[i].name[0]; i++)
 462                        if (rpmsg_id_match(rpdev, &ids[i]))
 463                                return 1;
 464
 465        return of_driver_match_device(dev, drv);
 466}
 467
 468static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env)
 469{
 470        struct rpmsg_device *rpdev = to_rpmsg_device(dev);
 471        int ret;
 472
 473        ret = of_device_uevent_modalias(dev, env);
 474        if (ret != -ENODEV)
 475                return ret;
 476
 477        return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT,
 478                                        rpdev->id.name);
 479}
 480
 481/*
 482 * when an rpmsg driver is probed with a channel, we seamlessly create
 483 * it an endpoint, binding its rx callback to a unique local rpmsg
 484 * address.
 485 *
 486 * if we need to, we also announce about this channel to the remote
 487 * processor (needed in case the driver is exposing an rpmsg service).
 488 */
 489static int rpmsg_dev_probe(struct device *dev)
 490{
 491        struct rpmsg_device *rpdev = to_rpmsg_device(dev);
 492        struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
 493        struct rpmsg_channel_info chinfo = {};
 494        struct rpmsg_endpoint *ept = NULL;
 495        int err;
 496
 497        err = dev_pm_domain_attach(dev, true);
 498        if (err)
 499                goto out;
 500
 501        if (rpdrv->callback) {
 502                strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
 503                chinfo.src = rpdev->src;
 504                chinfo.dst = RPMSG_ADDR_ANY;
 505
 506                ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
 507                if (!ept) {
 508                        dev_err(dev, "failed to create endpoint\n");
 509                        err = -ENOMEM;
 510                        goto out;
 511                }
 512
 513                rpdev->ept = ept;
 514                rpdev->src = ept->addr;
 515        }
 516
 517        err = rpdrv->probe(rpdev);
 518        if (err) {
 519                dev_err(dev, "%s: failed: %d\n", __func__, err);
 520                if (ept)
 521                        rpmsg_destroy_ept(ept);
 522                goto out;
 523        }
 524
 525        if (ept && rpdev->ops->announce_create)
 526                err = rpdev->ops->announce_create(rpdev);
 527out:
 528        return err;
 529}
 530
 531static int rpmsg_dev_remove(struct device *dev)
 532{
 533        struct rpmsg_device *rpdev = to_rpmsg_device(dev);
 534        struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
 535        int err = 0;
 536
 537        if (rpdev->ops->announce_destroy)
 538                err = rpdev->ops->announce_destroy(rpdev);
 539
 540        if (rpdrv->remove)
 541                rpdrv->remove(rpdev);
 542
 543        dev_pm_domain_detach(dev, true);
 544
 545        if (rpdev->ept)
 546                rpmsg_destroy_ept(rpdev->ept);
 547
 548        return err;
 549}
 550
 551static struct bus_type rpmsg_bus = {
 552        .name           = "rpmsg",
 553        .match          = rpmsg_dev_match,
 554        .dev_groups     = rpmsg_dev_groups,
 555        .uevent         = rpmsg_uevent,
 556        .probe          = rpmsg_dev_probe,
 557        .remove         = rpmsg_dev_remove,
 558};
 559
 560int rpmsg_register_device(struct rpmsg_device *rpdev)
 561{
 562        struct device *dev = &rpdev->dev;
 563        int ret;
 564
 565        dev_set_name(&rpdev->dev, "%s.%s.%d.%d", dev_name(dev->parent),
 566                     rpdev->id.name, rpdev->src, rpdev->dst);
 567
 568        rpdev->dev.bus = &rpmsg_bus;
 569
 570        ret = device_register(&rpdev->dev);
 571        if (ret) {
 572                dev_err(dev, "device_register failed: %d\n", ret);
 573                put_device(&rpdev->dev);
 574        }
 575
 576        return ret;
 577}
 578EXPORT_SYMBOL(rpmsg_register_device);
 579
 580/*
 581 * find an existing channel using its name + address properties,
 582 * and destroy it
 583 */
 584int rpmsg_unregister_device(struct device *parent,
 585                            struct rpmsg_channel_info *chinfo)
 586{
 587        struct device *dev;
 588
 589        dev = rpmsg_find_device(parent, chinfo);
 590        if (!dev)
 591                return -EINVAL;
 592
 593        device_unregister(dev);
 594
 595        put_device(dev);
 596
 597        return 0;
 598}
 599EXPORT_SYMBOL(rpmsg_unregister_device);
 600
 601/**
 602 * __register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus
 603 * @rpdrv: pointer to a struct rpmsg_driver
 604 * @owner: owning module/driver
 605 *
 606 * Returns 0 on success, and an appropriate error value on failure.
 607 */
 608int __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
 609{
 610        rpdrv->drv.bus = &rpmsg_bus;
 611        rpdrv->drv.owner = owner;
 612        return driver_register(&rpdrv->drv);
 613}
 614EXPORT_SYMBOL(__register_rpmsg_driver);
 615
 616/**
 617 * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus
 618 * @rpdrv: pointer to a struct rpmsg_driver
 619 *
 620 * Returns 0 on success, and an appropriate error value on failure.
 621 */
 622void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv)
 623{
 624        driver_unregister(&rpdrv->drv);
 625}
 626EXPORT_SYMBOL(unregister_rpmsg_driver);
 627
 628
 629static int __init rpmsg_init(void)
 630{
 631        int ret;
 632
 633        ret = bus_register(&rpmsg_bus);
 634        if (ret)
 635                pr_err("failed to register rpmsg bus: %d\n", ret);
 636
 637        return ret;
 638}
 639postcore_initcall(rpmsg_init);
 640
 641static void __exit rpmsg_fini(void)
 642{
 643        bus_unregister(&rpmsg_bus);
 644}
 645module_exit(rpmsg_fini);
 646
 647MODULE_DESCRIPTION("remote processor messaging bus");
 648MODULE_LICENSE("GPL v2");
 649