12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430 |
- /*
- * Remote Processor Procedure Call Driver
- *
- * Copyright (C) 2012-2017 Texas Instruments Incorporated - http://www.ti.com/
- *
- * Erik Rainey <erik.rainey@ti.com>
- * Suman Anna <s-anna@ti.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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.
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/idr.h>
- #include <linux/poll.h>
- #include <linux/mutex.h>
- #include <linux/sched.h>
- #include <linux/fdtable.h>
- #include <linux/remoteproc.h>
- #include <linux/rpmsg.h>
- #include <linux/rpmsg_rpc.h>
- #include <linux/rpmsg/virtio_rpmsg.h>
- #include "rpmsg_rpc_internal.h"
- #define RPPC_MAX_DEVICES (8)
- #define RPPC_MAX_REG_FDS (10)
- #define RPPC_SIG_NUM_PARAM(sig) ((sig).num_param - 1)
- /* TODO: remove these fields */
- #define RPPC_JOBID_DISCRETE (0)
- #define RPPC_POOLID_DEFAULT (0x8000)
- static struct class *rppc_class;
- static dev_t rppc_dev;
- /* store all remote rpc connection services (usually one per remoteproc) */
- static DEFINE_IDR(rppc_devices);
- static DEFINE_MUTEX(rppc_devices_lock);
- /*
- * Retrieve the rproc instance so that it can be used for performing
- * address translations
- */
- static struct rproc *rpdev_to_rproc(struct rpmsg_device *rpdev)
- {
- struct virtio_device *vdev;
- vdev = virtio_rpmsg_get_vdev(rpdev);
- if (!vdev)
- return NULL;
- return rproc_vdev_to_rproc_safe(vdev);
- }
- /*
- * A wrapper function to translate local physical addresses to the remote core
- * device addresses (virtual addresses that a code on remote processor can use
- * directly.
- *
- * XXX: Fix this to return negative values on errors to follow normal kernel
- * conventions, and since 0 can also be a valid remote processor address
- *
- * Returns a remote processor device address on success, 0 otherwise
- */
- dev_addr_t rppc_local_to_remote_da(struct rppc_instance *rpc, phys_addr_t pa)
- {
- int ret;
- struct rproc *rproc;
- u64 da = 0;
- dev_addr_t rda;
- struct device *dev = rpc->dev;
- if (mutex_lock_interruptible(&rpc->rppcdev->lock))
- return 0;
- rproc = rpdev_to_rproc(rpc->rppcdev->rpdev);
- if (!rproc) {
- dev_err(dev, "error getting rproc for rpdev 0x%x\n",
- (u32)rpc->rppcdev->rpdev);
- } else {
- ret = rproc_pa_to_da(rproc, pa, &da);
- if (ret) {
- dev_err(dev, "error from rproc_pa_to_da, rproc = %p, pa = %pa ret = %d\n",
- rproc, &pa, ret);
- }
- }
- rda = (dev_addr_t)da;
- mutex_unlock(&rpc->rppcdev->lock);
- return rda;
- }
- static void rppc_print_msg(struct rppc_instance *rpc, char *prefix,
- char buffer[512])
- {
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)buffer;
- struct rppc_instance_handle *hdl = NULL;
- struct rppc_query_function *info = NULL;
- struct rppc_packet *packet = NULL;
- struct rppc_param_data *param = NULL;
- struct device *dev = rpc->dev;
- u32 i = 0, paramsz = sizeof(*param);
- dev_dbg(dev, "%s HDR: msg_type = %d msg_len = %d\n",
- prefix, hdr->msg_type, hdr->msg_len);
- switch (hdr->msg_type) {
- case RPPC_MSGTYPE_CREATE_RESP:
- case RPPC_MSGTYPE_DELETE_RESP:
- hdl = RPPC_PAYLOAD(buffer, rppc_instance_handle);
- dev_dbg(dev, "%s endpoint = %d status = %d\n",
- prefix, hdl->endpoint_address, hdl->status);
- break;
- case RPPC_MSGTYPE_FUNCTION_INFO:
- info = RPPC_PAYLOAD(buffer, rppc_query_function);
- dev_dbg(dev, "%s (info not yet implemented)\n", prefix);
- break;
- case RPPC_MSGTYPE_FUNCTION_CALL:
- packet = RPPC_PAYLOAD(buffer, rppc_packet);
- dev_dbg(dev, "%s PACKET: desc = %04x msg_id = %04x flags = %08x func = 0x%08x result = %d size = %u\n",
- prefix, packet->desc, packet->msg_id,
- packet->flags, packet->fxn_id,
- packet->result, packet->data_size);
- param = (struct rppc_param_data *)packet->data;
- for (i = 0; i < (packet->data_size / paramsz); i++) {
- dev_dbg(dev, "%s param[%u] size = %zu data = %zu (0x%08x)",
- prefix, i, param[i].size, param[i].data,
- param[i].data);
- }
- break;
- default:
- break;
- }
- }
- /* free any outstanding function calls */
- static void rppc_delete_fxns(struct rppc_instance *rpc)
- {
- struct rppc_function_list *pos, *n;
- if (!list_empty(&rpc->fxn_list)) {
- mutex_lock(&rpc->lock);
- list_for_each_entry_safe(pos, n, &rpc->fxn_list, list) {
- list_del(&pos->list);
- kfree(pos->function);
- kfree(pos);
- }
- mutex_unlock(&rpc->lock);
- }
- }
- static
- struct rppc_function *rppc_find_fxn(struct rppc_instance *rpc, u16 msg_id)
- {
- struct rppc_function *function = NULL;
- struct rppc_function_list *pos, *n;
- struct device *dev = rpc->dev;
- mutex_lock(&rpc->lock);
- list_for_each_entry_safe(pos, n, &rpc->fxn_list, list) {
- dev_dbg(dev, "looking for msg %u, found msg %u\n",
- msg_id, pos->msg_id);
- if (pos->msg_id == msg_id) {
- function = pos->function;
- list_del(&pos->list);
- kfree(pos);
- break;
- }
- }
- mutex_unlock(&rpc->lock);
- return function;
- }
- static int rppc_add_fxn(struct rppc_instance *rpc,
- struct rppc_function *function, u16 msg_id)
- {
- struct rppc_function_list *fxn = NULL;
- struct device *dev = rpc->dev;
- fxn = kzalloc(sizeof(*fxn), GFP_KERNEL);
- if (!fxn)
- return -ENOMEM;
- fxn->function = function;
- fxn->msg_id = msg_id;
- mutex_lock(&rpc->lock);
- list_add(&fxn->list, &rpc->fxn_list);
- mutex_unlock(&rpc->lock);
- dev_dbg(dev, "added msg id %u to list", msg_id);
- return 0;
- }
- static
- void rppc_handle_create_resp(struct rppc_instance *rpc, char *data, int len)
- {
- struct device *dev = rpc->dev;
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)data;
- struct rppc_instance_handle *hdl;
- u32 exp_len = sizeof(*hdl) + sizeof(*hdr);
- if (len != exp_len) {
- dev_err(dev, "invalid response message length %d (expected %d bytes)",
- len, exp_len);
- rpc->state = RPPC_STATE_STALE;
- return;
- }
- hdl = RPPC_PAYLOAD(data, rppc_instance_handle);
- mutex_lock(&rpc->lock);
- if (rpc->state != RPPC_STATE_STALE && hdl->status == 0) {
- rpc->dst = hdl->endpoint_address;
- rpc->state = RPPC_STATE_CONNECTED;
- } else {
- rpc->state = RPPC_STATE_STALE;
- }
- rpc->in_transition = 0;
- dev_dbg(dev, "creation response: status %d addr 0x%x\n",
- hdl->status, hdl->endpoint_address);
- complete(&rpc->reply_arrived);
- mutex_unlock(&rpc->lock);
- }
- static
- void rppc_handle_delete_resp(struct rppc_instance *rpc, char *data, int len)
- {
- struct device *dev = rpc->dev;
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)data;
- struct rppc_instance_handle *hdl;
- u32 exp_len = sizeof(*hdl) + sizeof(*hdr);
- if (len != exp_len) {
- dev_err(dev, "invalid response message length %d (expected %d bytes)",
- len, exp_len);
- rpc->state = RPPC_STATE_STALE;
- return;
- }
- if (hdr->msg_len != sizeof(*hdl)) {
- dev_err(dev, "disconnect message was incorrect size!\n");
- rpc->state = RPPC_STATE_STALE;
- return;
- }
- hdl = RPPC_PAYLOAD(data, rppc_instance_handle);
- dev_dbg(dev, "deletion response: status %d addr 0x%x\n",
- hdl->status, hdl->endpoint_address);
- mutex_lock(&rpc->lock);
- rpc->dst = 0;
- rpc->state = RPPC_STATE_DISCONNECTED;
- rpc->in_transition = 0;
- complete(&rpc->reply_arrived);
- mutex_unlock(&rpc->lock);
- }
- /*
- * store the received message and wake up any blocking processes,
- * waiting for new data. The allocated buffer would be freed after
- * the user-space reads the packet.
- */
- static void rppc_handle_fxn_resp(struct rppc_instance *rpc, char *data, int len)
- {
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)data;
- struct sk_buff *skb;
- char *skbdata;
- /* TODO: need to check the response length? */
- skb = alloc_skb(hdr->msg_len, GFP_KERNEL);
- if (!skb)
- return;
- skbdata = skb_put(skb, hdr->msg_len);
- memcpy(skbdata, hdr->msg_data, hdr->msg_len);
- mutex_lock(&rpc->lock);
- skb_queue_tail(&rpc->queue, skb);
- mutex_unlock(&rpc->lock);
- wake_up_interruptible(&rpc->readq);
- }
- /*
- * callback function for processing the different responses
- * from the remote processor on a particular rpmsg channel
- * instance.
- */
- static int rppc_cb(struct rpmsg_device *rpdev,
- void *data, int len, void *priv, u32 src)
- {
- struct rppc_msg_header *hdr = data;
- struct rppc_instance *rpc = priv;
- struct device *dev = rpc->dev;
- char *buf = (char *)data;
- dev_dbg(dev, "<== incoming msg src %d len %d msg_type %d msg_len %d\n",
- src, len, hdr->msg_type, hdr->msg_len);
- rppc_print_msg(rpc, "RX:", buf);
- if (len <= sizeof(*hdr)) {
- dev_err(dev, "message truncated\n");
- rpc->state = RPPC_STATE_STALE;
- return -EINVAL;
- }
- switch (hdr->msg_type) {
- case RPPC_MSGTYPE_CREATE_RESP:
- rppc_handle_create_resp(rpc, data, len);
- break;
- case RPPC_MSGTYPE_DELETE_RESP:
- rppc_handle_delete_resp(rpc, data, len);
- break;
- case RPPC_MSGTYPE_FUNCTION_CALL:
- case RPPC_MSGTYPE_FUNCTION_RET:
- rppc_handle_fxn_resp(rpc, data, len);
- break;
- default:
- dev_warn(dev, "unexpected msg type: %d\n", hdr->msg_type);
- break;
- }
- return 0;
- }
- /*
- * send a connection request to the remote rpc connection service. Use
- * the new local address created during .open for this instance as the
- * source address to complete the connection.
- */
- static int rppc_connect(struct rppc_instance *rpc,
- struct rppc_create_instance *connect)
- {
- int ret = 0;
- u32 len = 0;
- char kbuf[512];
- struct rppc_device *rppcdev = rpc->rppcdev;
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)&kbuf[0];
- if (rpc->state == RPPC_STATE_CONNECTED) {
- dev_dbg(rpc->dev, "endpoint already connected\n");
- return -EISCONN;
- }
- hdr->msg_type = RPPC_MSGTYPE_CREATE_REQ;
- hdr->msg_len = sizeof(*connect);
- memcpy(hdr->msg_data, connect, hdr->msg_len);
- len = sizeof(struct rppc_msg_header) + hdr->msg_len;
- init_completion(&rpc->reply_arrived);
- rpc->in_transition = 1;
- ret = rpmsg_send_offchannel(rppcdev->rpdev->ept, rpc->ept->addr,
- rppcdev->rpdev->dst, (char *)kbuf, len);
- if (ret > 0) {
- dev_err(rpc->dev, "rpmsg_send failed: %d\n", ret);
- return ret;
- }
- ret = wait_for_completion_interruptible_timeout(&rpc->reply_arrived,
- msecs_to_jiffies(5000));
- if (rpc->state == RPPC_STATE_CONNECTED)
- return 0;
- if (rpc->state == RPPC_STATE_STALE)
- return -ENXIO;
- if (ret > 0) {
- dev_err(rpc->dev, "premature wakeup: %d\n", ret);
- return -EIO;
- }
- return -ETIMEDOUT;
- }
- static void rppc_disconnect(struct rppc_instance *rpc)
- {
- int ret;
- size_t len;
- char kbuf[512];
- struct rppc_device *rppcdev = rpc->rppcdev;
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)&kbuf[0];
- struct rppc_instance_handle *handle =
- RPPC_PAYLOAD(kbuf, rppc_instance_handle);
- if (rpc->state != RPPC_STATE_CONNECTED)
- return;
- hdr->msg_type = RPPC_MSGTYPE_DELETE_REQ;
- hdr->msg_len = sizeof(u32);
- handle->endpoint_address = rpc->dst;
- handle->status = 0;
- len = sizeof(struct rppc_msg_header) + hdr->msg_len;
- dev_dbg(rpc->dev, "disconnecting from RPC service at %d\n",
- rpc->dst);
- ret = rpmsg_send_offchannel(rppcdev->rpdev->ept, rpc->ept->addr,
- rppcdev->rpdev->dst, kbuf, len);
- if (ret)
- dev_err(rpc->dev, "rpmsg_send failed: %d\n", ret);
- /*
- * TODO: should we wait for a message to come back?
- * For now, no.
- */
- wait_for_completion_interruptible(&rpc->reply_arrived);
- }
- static int rppc_register_buffers(struct rppc_instance *rpc,
- unsigned long arg)
- {
- struct rppc_buf_fds data;
- int *fds = NULL;
- struct rppc_dma_buf **bufs = NULL;
- struct rppc_dma_buf *tmp;
- int i = 0, ret = 0;
- if (copy_from_user(&data, (char __user *)arg, sizeof(data)))
- return -EFAULT;
- /* impose a maximum number of buffers for now */
- if (data.num > RPPC_MAX_REG_FDS)
- return -EINVAL;
- fds = kcalloc(data.num, sizeof(*fds), GFP_KERNEL);
- if (!fds)
- return -ENOMEM;
- if (copy_from_user(fds, (char __user *)data.fds,
- sizeof(*fds) * data.num)) {
- ret = -EFAULT;
- goto free_fds;
- }
- for (i = 0; i < data.num; i++) {
- rcu_read_lock();
- if (!fcheck(fds[i])) {
- rcu_read_unlock();
- ret = -EBADF;
- goto free_fds;
- }
- rcu_read_unlock();
- tmp = rppc_find_dmabuf(rpc, fds[i]);
- if (!IS_ERR_OR_NULL(tmp)) {
- ret = -EEXIST;
- goto free_fds;
- }
- }
- bufs = kcalloc(data.num, sizeof(*bufs), GFP_KERNEL);
- if (!bufs) {
- ret = -ENOMEM;
- goto free_fds;
- }
- for (i = 0; i < data.num; i++) {
- bufs[i] = rppc_alloc_dmabuf(rpc, fds[i], false);
- if (IS_ERR(bufs[i])) {
- ret = PTR_ERR(bufs[i]);
- break;
- }
- }
- if (i == data.num)
- goto free_bufs;
- for (i -= 1; i >= 0; i--)
- rppc_free_dmabuf(bufs[i]->id, bufs[i], rpc);
- free_bufs:
- kfree(bufs);
- free_fds:
- kfree(fds);
- return ret;
- }
- static int rppc_unregister_buffers(struct rppc_instance *rpc,
- unsigned long arg)
- {
- struct rppc_buf_fds data;
- int *fds = NULL;
- struct rppc_dma_buf **bufs = NULL;
- int i = 0, ret = 0;
- if (copy_from_user(&data, (char __user *)arg, sizeof(data)))
- return -EFAULT;
- /* impose a maximum number of buffers for now */
- if (data.num > RPPC_MAX_REG_FDS)
- return -EINVAL;
- fds = kcalloc(data.num, sizeof(*fds), GFP_KERNEL);
- if (!fds)
- return -ENOMEM;
- if (copy_from_user(fds, (char __user *)data.fds,
- sizeof(*fds) * data.num)) {
- ret = -EFAULT;
- goto free_fds;
- }
- bufs = kcalloc(data.num, sizeof(*bufs), GFP_KERNEL);
- if (!bufs) {
- ret = -ENOMEM;
- goto free_fds;
- }
- for (i = 0; i < data.num; i++) {
- rcu_read_lock();
- if (!fcheck(fds[i])) {
- rcu_read_unlock();
- ret = -EBADF;
- goto free_bufs;
- }
- rcu_read_unlock();
- bufs[i] = rppc_find_dmabuf(rpc, fds[i]);
- if (IS_ERR_OR_NULL(bufs[i])) {
- ret = -EEXIST;
- goto free_bufs;
- }
- }
- for (i = 0; i < data.num; i++)
- rppc_free_dmabuf(bufs[i]->id, bufs[i], rpc);
- free_bufs:
- kfree(bufs);
- free_fds:
- kfree(fds);
- return ret;
- }
- /*
- * create a new rpc instance that a user-space client can use to invoke
- * remote functions. A new local address would be created and tied with
- * this instance for uniquely identifying the messages communicated by
- * this instance with the remote side.
- *
- * The function is blocking if there is no underlying connection manager
- * channel, unless the device is opened with non-blocking flags specifically.
- */
- static int rppc_open(struct inode *inode, struct file *filp)
- {
- struct rppc_device *rppcdev;
- struct rppc_instance *rpc;
- struct rpmsg_channel_info chinfo = {};
- rppcdev = container_of(inode->i_cdev, struct rppc_device, cdev);
- if (!rppcdev->rpdev)
- if ((filp->f_flags & O_NONBLOCK) ||
- wait_for_completion_interruptible(&rppcdev->comp))
- return -EBUSY;
- rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
- if (!rpc)
- return -ENOMEM;
- mutex_init(&rpc->lock);
- skb_queue_head_init(&rpc->queue);
- init_waitqueue_head(&rpc->readq);
- INIT_LIST_HEAD(&rpc->fxn_list);
- idr_init(&rpc->dma_idr);
- rpc->in_transition = 0;
- rpc->msg_id = 0;
- rpc->state = RPPC_STATE_DISCONNECTED;
- rpc->rppcdev = rppcdev;
- rpc->dev = get_device(rppcdev->dev);
- chinfo.src = RPMSG_ADDR_ANY;
- chinfo.dst = RPMSG_ADDR_ANY;
- rpc->ept = rpmsg_create_ept(rppcdev->rpdev, rppc_cb, rpc, chinfo);
- if (!rpc->ept) {
- dev_err(rpc->dev, "create ept failed\n");
- put_device(rpc->dev);
- kfree(rpc);
- return -ENOMEM;
- }
- filp->private_data = rpc;
- mutex_lock(&rppcdev->lock);
- list_add(&rpc->list, &rppcdev->instances);
- mutex_unlock(&rppcdev->lock);
- dev_dbg(rpc->dev, "local addr assigned: 0x%x\n", rpc->ept->addr);
- return 0;
- }
- /*
- * release and free all the resources associated with a particular rpc
- * instance. This includes the data structures maintaining the current
- * outstanding function invocations, and all the buffers registered for
- * use with this instance. Send a disconnect message and cleanup the
- * local end-point only if the instance is in a normal state, with the
- * remote connection manager functional.
- */
- static int rppc_release(struct inode *inode, struct file *filp)
- {
- struct rppc_instance *rpc = filp->private_data;
- struct rppc_device *rppcdev = rpc->rppcdev;
- struct sk_buff *skb = NULL;
- dev_dbg(rpc->dev, "releasing Instance %p, in state %d\n", rpc,
- rpc->state);
- if (rpc->state != RPPC_STATE_STALE) {
- if (rpc->ept) {
- rppc_disconnect(rpc);
- rpmsg_destroy_ept(rpc->ept);
- rpc->ept = NULL;
- }
- }
- rppc_delete_fxns(rpc);
- while (!skb_queue_empty(&rpc->queue)) {
- skb = skb_dequeue(&rpc->queue);
- kfree_skb(skb);
- }
- mutex_lock(&rpc->lock);
- idr_for_each(&rpc->dma_idr, rppc_free_dmabuf, rpc);
- idr_destroy(&rpc->dma_idr);
- mutex_unlock(&rpc->lock);
- mutex_lock(&rppcdev->lock);
- list_del(&rpc->list);
- mutex_unlock(&rppcdev->lock);
- dev_dbg(rpc->dev, "instance %p has been deleted!\n", rpc);
- if (list_empty(&rppcdev->instances))
- dev_dbg(rpc->dev, "all instances have been removed!\n");
- put_device(rpc->dev);
- kfree(rpc);
- return 0;
- }
- static long rppc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- struct rppc_instance *rpc = filp->private_data;
- struct rppc_create_instance connect;
- int ret = 0;
- dev_dbg(rpc->dev, "%s: cmd %d, arg 0x%lx\n", __func__, cmd, arg);
- if (_IOC_TYPE(cmd) != RPPC_IOC_MAGIC)
- return -ENOTTY;
- if (_IOC_NR(cmd) > RPPC_IOC_MAXNR)
- return -ENOTTY;
- switch (cmd) {
- case RPPC_IOC_CREATE:
- ret = copy_from_user(&connect, (char __user *)arg,
- sizeof(connect));
- if (ret) {
- dev_err(rpc->dev, "%s: %d: copy_from_user fail: %d\n",
- __func__, _IOC_NR(cmd), ret);
- ret = -EFAULT;
- } else {
- connect.name[sizeof(connect.name) - 1] = '\0';
- ret = rppc_connect(rpc, &connect);
- }
- break;
- case RPPC_IOC_BUFREGISTER:
- ret = rppc_register_buffers(rpc, arg);
- break;
- case RPPC_IOC_BUFUNREGISTER:
- ret = rppc_unregister_buffers(rpc, arg);
- break;
- default:
- dev_err(rpc->dev, "unhandled ioctl cmd: %d\n", cmd);
- break;
- }
- return ret;
- }
- static ssize_t rppc_read(struct file *filp, char __user *buf, size_t len,
- loff_t *offp)
- {
- struct rppc_instance *rpc = filp->private_data;
- struct rppc_packet *packet = NULL;
- struct rppc_param_data *parameters = NULL;
- struct rppc_function *function = NULL;
- struct rppc_function_return returned;
- struct sk_buff *skb = NULL;
- int ret = 0;
- int use = sizeof(returned);
- DEFINE_WAIT(wait);
- if (mutex_lock_interruptible(&rpc->lock))
- return -ERESTARTSYS;
- /* instance is invalid */
- if (rpc->state == RPPC_STATE_STALE) {
- mutex_unlock(&rpc->lock);
- return -ENXIO;
- }
- /* not yet connected to the remote side */
- if (rpc->state == RPPC_STATE_DISCONNECTED) {
- mutex_unlock(&rpc->lock);
- return -ENOTCONN;
- }
- if (len > use) {
- mutex_unlock(&rpc->lock);
- return -EOVERFLOW;
- }
- if (len < use) {
- mutex_unlock(&rpc->lock);
- return -EINVAL;
- }
- /* TODO: Use the much simpler wait_event_interruptible API */
- while (skb_queue_empty(&rpc->queue)) {
- mutex_unlock(&rpc->lock);
- /* non-blocking requested ? return now */
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- prepare_to_wait_exclusive(&rpc->readq, &wait,
- TASK_INTERRUPTIBLE);
- if (skb_queue_empty(&rpc->queue) &&
- rpc->state != RPPC_STATE_STALE)
- schedule();
- finish_wait(&rpc->readq, &wait);
- if (signal_pending(current))
- return -ERESTARTSYS;
- ret = mutex_lock_interruptible(&rpc->lock);
- if (ret < 0)
- return -ERESTARTSYS;
- if (rpc->state == RPPC_STATE_STALE) {
- mutex_unlock(&rpc->lock);
- return -ENXIO;
- }
- /* make sure state is sane while we waited */
- if (rpc->state != RPPC_STATE_CONNECTED) {
- mutex_unlock(&rpc->lock);
- ret = -EIO;
- goto out;
- }
- }
- skb = skb_dequeue(&rpc->queue);
- if (WARN_ON(!skb)) {
- mutex_unlock(&rpc->lock);
- ret = -EIO;
- goto out;
- }
- mutex_unlock(&rpc->lock);
- packet = (struct rppc_packet *)skb->data;
- parameters = (struct rppc_param_data *)packet->data;
- /*
- * pull the function memory from the list and untranslate
- * the remote device address pointers in the packet back
- * to MPU pointers.
- */
- function = rppc_find_fxn(rpc, packet->msg_id);
- if (function && function->num_translations > 0) {
- ret = rppc_xlate_buffers(rpc, function, RPPC_RPA_TO_UVA);
- if (ret < 0) {
- dev_err(rpc->dev, "failed to translate back pointers from remote core!\n");
- goto failure;
- }
- }
- returned.fxn_id = RPPC_FXN_MASK(packet->fxn_id);
- returned.status = packet->result;
- if (copy_to_user(buf, &returned, use)) {
- dev_err(rpc->dev, "%s: copy_to_user fail\n", __func__);
- ret = -EFAULT;
- } else {
- ret = use;
- }
- failure:
- kfree(function);
- kfree_skb(skb);
- out:
- return ret;
- }
- static ssize_t rppc_write(struct file *filp, const char __user *ubuf,
- size_t len, loff_t *offp)
- {
- struct rppc_instance *rpc = filp->private_data;
- struct rppc_device *rppcdev = rpc->rppcdev;
- struct device *dev = rpc->dev;
- struct rppc_msg_header *hdr = NULL;
- struct rppc_function *function = NULL;
- struct rppc_packet *packet = NULL;
- struct rppc_param_data *parameters = NULL;
- char kbuf[512];
- int use = 0, ret = 0, param = 0;
- u32 sig_idx = 0;
- u32 sig_prm = 0;
- static u32 rppc_atomic_size[RPPC_PARAM_ATOMIC_MAX] = {
- 0, /* RPPC_PARAM_VOID */
- 1, /* RPPC_PARAM_S08 */
- 1, /* RPPC_PARAM_U08 */
- 2, /* RPPC_PARAM_S16 */
- 2, /* RPPC_PARAM_U16 */
- 4, /* RPPC_PARAM_S32 */
- 4, /* RPPC_PARAM_U32 */
- 8, /* RPPC_PARAM_S64 */
- 8 /* RPPC_PARAM_U64 */
- };
- if (len < sizeof(*function)) {
- ret = -ENOTSUPP;
- goto failure;
- }
- if (len > (sizeof(*function) + RPPC_MAX_TRANSLATIONS *
- sizeof(struct rppc_param_translation))) {
- ret = -ENOTSUPP;
- goto failure;
- }
- if (rpc->state != RPPC_STATE_CONNECTED) {
- ret = -ENOTCONN;
- goto failure;
- }
- function = kzalloc(len, GFP_KERNEL);
- if (!function) {
- ret = -ENOMEM;
- goto failure;
- }
- if (copy_from_user(function, ubuf, len)) {
- ret = -EMSGSIZE;
- goto failure;
- }
- /* increment the message id and wrap if needed */
- rpc->msg_id = (rpc->msg_id + 1) & 0xFFFF;
- memset(kbuf, 0, sizeof(kbuf));
- sig_idx = function->fxn_id + 1;
- hdr = (struct rppc_msg_header *)kbuf;
- hdr->msg_type = RPPC_MSGTYPE_FUNCTION_CALL;
- hdr->msg_len = sizeof(*packet);
- packet = RPPC_PAYLOAD(kbuf, rppc_packet);
- packet->desc = RPPC_DESC_EXEC_SYNC;
- packet->msg_id = rpc->msg_id;
- packet->flags = (RPPC_JOBID_DISCRETE << 16) | RPPC_POOLID_DEFAULT;
- packet->fxn_id = RPPC_SET_FXN_IDX(function->fxn_id);
- packet->result = 0;
- packet->data_size = sizeof(*parameters) * function->num_params;
- /* check the signatures against what were published */
- if (RPPC_SIG_NUM_PARAM(rppcdev->signatures[sig_idx]) !=
- function->num_params) {
- dev_err(dev, "number of parameters mismatch! params = %u expected = %u\n",
- function->num_params,
- RPPC_SIG_NUM_PARAM(rppcdev->signatures[sig_idx]));
- ret = -EINVAL;
- goto failure;
- }
- /*
- * compute the parameter pointer changes last since this will cause the
- * cache operations
- */
- parameters = (struct rppc_param_data *)packet->data;
- for (param = 0; param < function->num_params; param++) {
- sig_prm = param + 1;
- /*
- * check to make sure the parameter description matches the
- * signature published from the other side.
- */
- if (function->params[param].type == RPPC_PARAM_TYPE_PTR &&
- !RPPC_IS_PTR(
- rppcdev->signatures[sig_idx].params[sig_prm].type)) {
- dev_err(dev, "parameter %u Pointer Type Mismatch sig type:%x func %u\n",
- param, rppcdev->signatures[sig_idx].
- params[sig_prm].type, sig_idx);
- ret = -EINVAL;
- goto failure;
- } else if (param > 0 && function->params[param].type ==
- RPPC_PARAM_TYPE_ATOMIC) {
- if (!RPPC_IS_ATOMIC(
- rppcdev->signatures[sig_idx].params[sig_prm].type)) {
- dev_err(dev, "parameter Atomic Type Mismatch\n");
- ret = -EINVAL;
- goto failure;
- } else {
- u32 t = rppcdev->signatures[sig_idx].
- params[sig_prm].type;
- if (rppc_atomic_size[t] !=
- function->params[param].size) {
- dev_err(dev, "size mismatch! u:%u sig:%u\n",
- function->params[param].size,
- rppc_atomic_size[t]);
- ret = -EINVAL;
- goto failure;
- }
- }
- }
- parameters[param].size = function->params[param].size;
- /* check the type and lookup if it's a pointer */
- if (function->params[param].type == RPPC_PARAM_TYPE_PTR) {
- /*
- * internally the buffer translations takes care of the
- * offsets.
- */
- int fd = function->params[param].fd;
- parameters[param].data = (size_t)rppc_buffer_lookup(
- rpc,
- (virt_addr_t)
- function->
- params[param].data,
- (virt_addr_t)
- function->
- params[param].base,
- fd);
- } else if (function->params[param].type ==
- RPPC_PARAM_TYPE_ATOMIC) {
- parameters[param].data = function->params[param].data;
- } else {
- ret = -ENOTSUPP;
- goto failure;
- }
- }
- /* compute the size of the rpmsg packet */
- use = sizeof(*hdr) + hdr->msg_len + packet->data_size;
- /* failed to provide the translation data */
- if (function->num_translations > 0 &&
- len < (sizeof(*function) + (function->num_translations *
- sizeof(struct rppc_param_translation)))) {
- ret = -EINVAL;
- goto failure;
- }
- /*
- * if there are pointers to translate for the user, do so now.
- * alter our copy of function and the user's parameters so that
- * the proper pointers can be sent to remote cores
- */
- if (function->num_translations > 0) {
- ret = rppc_xlate_buffers(rpc, function, RPPC_UVA_TO_RPA);
- if (ret < 0) {
- dev_err(dev, "failed to translate all pointers for remote core!\n");
- goto failure;
- }
- }
- ret = rppc_add_fxn(rpc, function, rpc->msg_id);
- if (ret < 0) {
- rppc_xlate_buffers(rpc, function, RPPC_RPA_TO_UVA);
- goto failure;
- }
- rppc_print_msg(rpc, "TX:", kbuf);
- ret = rpmsg_send_offchannel(rppcdev->rpdev->ept, rpc->ept->addr,
- rpc->dst, kbuf, use);
- if (ret) {
- dev_err(dev, "rpmsg_send failed: %d\n", ret);
- rppc_find_fxn(rpc, rpc->msg_id);
- rppc_xlate_buffers(rpc, function, RPPC_RPA_TO_UVA);
- goto failure;
- }
- dev_dbg(dev, "==> sent msg to remote endpoint %u\n", rpc->dst);
- failure:
- if (ret >= 0)
- ret = len;
- else
- kfree(function);
- return ret;
- }
- static unsigned int rppc_poll(struct file *filp, struct poll_table_struct *wait)
- {
- struct rppc_instance *rpc = filp->private_data;
- unsigned int mask = 0;
- if (mutex_lock_interruptible(&rpc->lock))
- return -ERESTARTSYS;
- poll_wait(filp, &rpc->readq, wait);
- if (rpc->state == RPPC_STATE_STALE) {
- mask = POLLERR;
- goto out;
- }
- /* if the queue is not empty set the poll bit correctly */
- if (!skb_queue_empty(&rpc->queue))
- mask |= (POLLIN | POLLRDNORM);
- /* TODO: writes are deemed to be successful always, fix this later */
- if (true)
- mask |= POLLOUT | POLLWRNORM;
- out:
- mutex_unlock(&rpc->lock);
- return mask;
- }
- static const struct file_operations rppc_fops = {
- .owner = THIS_MODULE,
- .open = rppc_open,
- .release = rppc_release,
- .unlocked_ioctl = rppc_ioctl,
- .read = rppc_read,
- .write = rppc_write,
- .poll = rppc_poll,
- };
- /*
- * send a function query message, the sysfs entry will be created
- * during the processing of the response message
- */
- static int rppc_query_function(struct rpmsg_device *rpdev)
- {
- int ret = 0;
- u32 len = 0;
- char kbuf[512];
- struct rppc_device *rppcdev = dev_get_drvdata(&rpdev->dev);
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)&kbuf[0];
- struct rppc_query_function *fxn_info =
- (struct rppc_query_function *)hdr->msg_data;
- if (rppcdev->cur_func >= rppcdev->num_funcs)
- return -EINVAL;
- hdr->msg_type = RPPC_MSGTYPE_FUNCTION_QUERY;
- hdr->msg_len = sizeof(*fxn_info);
- len = sizeof(*hdr) + hdr->msg_len;
- fxn_info->info_type = RPPC_INFOTYPE_FUNC_SIGNATURE;
- fxn_info->fxn_id = rppcdev->cur_func++;
- dev_dbg(&rpdev->dev, "sending function query type %u for function %u\n",
- fxn_info->info_type, fxn_info->fxn_id);
- ret = rpmsg_send(rpdev->ept, (char *)kbuf, len);
- if (ret) {
- dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
- return ret;
- }
- return 0;
- }
- static void
- rppc_handle_devinfo_resp(struct rpmsg_device *rpdev, char *data, int len)
- {
- struct rppc_device *rppcdev = dev_get_drvdata(&rpdev->dev);
- struct rppc_device_info *info;
- u32 exp_len = sizeof(*info) + sizeof(struct rppc_msg_header);
- if (len != exp_len) {
- dev_err(&rpdev->dev, "invalid message length %d (expected %d bytes)",
- len, exp_len);
- return;
- }
- info = RPPC_PAYLOAD(data, rppc_device_info);
- if (info->num_funcs > RPPC_MAX_NUM_FUNCS) {
- rppcdev->num_funcs = 0;
- dev_err(&rpdev->dev, "number of functions (%d) exceeds the limit supported(%d)\n",
- info->num_funcs, RPPC_MAX_NUM_FUNCS);
- return;
- }
- rppcdev->num_funcs = info->num_funcs;
- rppcdev->signatures = kcalloc(rppcdev->num_funcs,
- sizeof(struct rppc_func_signature),
- GFP_KERNEL);
- if (!rppcdev->signatures)
- return;
- dev_info(&rpdev->dev, "published functions = %u\n", info->num_funcs);
- /* send the function query for first function */
- if (rppc_query_function(rpdev) == -EINVAL)
- dev_err(&rpdev->dev, "failed to get a reasonable number of functions!\n");
- }
- static void
- rppc_handle_fxninfo_resp(struct rpmsg_device *rpdev, char *data, int len)
- {
- struct rppc_device *rppcdev = dev_get_drvdata(&rpdev->dev);
- struct rppc_query_function *fxn_info;
- struct rppc_func_signature *signature;
- u32 exp_len = sizeof(*fxn_info) + sizeof(struct rppc_msg_header);
- int i;
- if (len != exp_len) {
- dev_err(&rpdev->dev, "invalid message length %d (expected %d bytes)",
- len, exp_len);
- return;
- }
- fxn_info = RPPC_PAYLOAD(data, rppc_query_function);
- dev_dbg(&rpdev->dev, "response for function query of type %u\n",
- fxn_info->info_type);
- switch (fxn_info->info_type) {
- case RPPC_INFOTYPE_FUNC_SIGNATURE:
- if (fxn_info->fxn_id >= rppcdev->num_funcs) {
- dev_err(&rpdev->dev, "function(%d) is out of range!\n",
- fxn_info->fxn_id);
- break;
- }
- memcpy(&rppcdev->signatures[fxn_info->fxn_id],
- &fxn_info->info.signature, sizeof(*signature));
- /* TODO: delete these debug prints later */
- dev_dbg(&rpdev->dev, "received info for func(%d); name = %s #params = %u\n",
- fxn_info->fxn_id, fxn_info->info.signature.name,
- fxn_info->info.signature.num_param);
- signature = &rppcdev->signatures[fxn_info->fxn_id];
- for (i = 0; i < signature->num_param; i++) {
- dev_dbg(&rpdev->dev, "param[%u] type = %x dir = %u\n",
- i, signature->params[i].type,
- signature->params[i].direction);
- }
- /* query again until we've hit our limit */
- if (rppc_query_function(rpdev) == -EINVAL) {
- dev_dbg(&rpdev->dev, "reached end of function list!\n");
- rppc_create_sysfs(rppcdev);
- }
- break;
- default:
- dev_err(&rpdev->dev, "unrecognized fxn query response %u\n",
- fxn_info->info_type);
- break;
- }
- }
- static int rppc_driver_cb(struct rpmsg_device *rpdev, void *data, int len,
- void *priv, u32 src)
- {
- struct rppc_msg_header *hdr = data;
- char *buf = (char *)data;
- dev_dbg(&rpdev->dev, "<== incoming drv msg src %d len %d msg_type %d msg_len %d\n",
- src, len, hdr->msg_type, hdr->msg_len);
- if (len <= sizeof(*hdr)) {
- dev_err(&rpdev->dev, "message truncated\n");
- return -EINVAL;
- }
- switch (hdr->msg_type) {
- case RPPC_MSGTYPE_DEVINFO_RESP:
- rppc_handle_devinfo_resp(rpdev, buf, len);
- break;
- case RPPC_MSGTYPE_FUNCTION_INFO:
- rppc_handle_fxninfo_resp(rpdev, buf, len);
- break;
- default:
- dev_err(&rpdev->dev, "unrecognized message type %u\n",
- hdr->msg_type);
- break;
- }
- return 0;
- }
- static int find_rpccdev_by_name(int id, void *p, void *data)
- {
- struct rppc_device *rppcdev = p;
- return strcmp(rppcdev->desc, data) ? 0 : (int)p;
- }
- /*
- * send a device info query message, the device will be created
- * during the processing of the response message
- */
- static int rppc_device_create(struct rpmsg_device *rpdev)
- {
- int ret;
- u32 len;
- char kbuf[512];
- struct rppc_msg_header *hdr = (struct rppc_msg_header *)&kbuf[0];
- hdr->msg_type = RPPC_MSGTYPE_DEVINFO_REQ;
- hdr->msg_len = 0;
- len = sizeof(*hdr);
- ret = rpmsg_send(rpdev->ept, (char *)kbuf, len);
- if (ret) {
- dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
- return ret;
- }
- return 0;
- }
- static int rppc_probe(struct rpmsg_device *rpdev)
- {
- int ret, minor;
- int major = MAJOR(rppc_dev);
- struct rppc_device *rppcdev = NULL;
- dev_t dev;
- char namedesc[RPMSG_NAME_SIZE];
- dev_info(&rpdev->dev, "probing service %s with src %u dst %u\n",
- rpdev->desc, rpdev->src, rpdev->dst);
- mutex_lock(&rppc_devices_lock);
- snprintf(namedesc, sizeof(namedesc), "%s", rpdev->desc);
- rppcdev = (struct rppc_device *)idr_for_each(&rppc_devices,
- find_rpccdev_by_name, namedesc);
- if (rppcdev) {
- rppcdev->rpdev = rpdev;
- dev_set_drvdata(&rpdev->dev, rppcdev);
- goto serv_up;
- }
- rppcdev = kzalloc(sizeof(*rppcdev), GFP_KERNEL);
- if (!rppcdev) {
- ret = -ENOMEM;
- goto exit;
- }
- minor = idr_alloc(&rppc_devices, rppcdev, 0, 0, GFP_KERNEL);
- if (minor < 0) {
- ret = minor;
- dev_err(&rpdev->dev, "failed to get a minor number: %d\n", ret);
- goto free_rppcdev;
- }
- INIT_LIST_HEAD(&rppcdev->instances);
- mutex_init(&rppcdev->lock);
- init_completion(&rppcdev->comp);
- rppcdev->minor = minor;
- rppcdev->rpdev = rpdev;
- strncpy(rppcdev->desc, namedesc, RPMSG_NAME_SIZE);
- dev_set_drvdata(&rpdev->dev, rppcdev);
- cdev_init(&rppcdev->cdev, &rppc_fops);
- rppcdev->cdev.owner = THIS_MODULE;
- dev = MKDEV(major, minor);
- ret = cdev_add(&rppcdev->cdev, dev, 1);
- if (ret) {
- dev_err(&rpdev->dev, "cdev_add failed: %d\n", ret);
- goto free_id;
- }
- serv_up:
- rppcdev->dev = device_create(rppc_class, &rpdev->dev,
- MKDEV(major, rppcdev->minor), NULL,
- namedesc);
- if (IS_ERR(rppcdev->dev)) {
- int ret = PTR_ERR(rppcdev->dev);
- dev_err(&rpdev->dev, "device_create failed: %d\n", ret);
- goto free_cdev;
- }
- dev_set_drvdata(rppcdev->dev, rppcdev);
- ret = rppc_device_create(rpdev);
- if (ret) {
- dev_err(&rpdev->dev, "failed to query channel info: %d\n", ret);
- dev = MKDEV(MAJOR(rppc_dev), rppcdev->minor);
- goto free_dev;
- }
- complete_all(&rppcdev->comp);
- dev_dbg(&rpdev->dev, "new RPPC connection srv channel: %u -> %u!\n",
- rpdev->src, rpdev->dst);
- mutex_unlock(&rppc_devices_lock);
- return 0;
- free_dev:
- device_destroy(rppc_class, dev);
- free_cdev:
- cdev_del(&rppcdev->cdev);
- free_id:
- idr_remove(&rppc_devices, rppcdev->minor);
- free_rppcdev:
- kfree(rppcdev);
- exit:
- mutex_unlock(&rppc_devices_lock);
- return ret;
- }
- static void rppc_remove(struct rpmsg_device *rpdev)
- {
- struct rppc_device *rppcdev = dev_get_drvdata(&rpdev->dev);
- struct rppc_instance *rpc = NULL;
- int major = MAJOR(rppc_dev);
- dev_dbg(&rpdev->dev, "removing rpmsg-rpc device %u.%u\n",
- major, rppcdev->minor);
- mutex_lock(&rppc_devices_lock);
- rppc_remove_sysfs(rppcdev);
- rppcdev->cur_func = 0;
- kfree(rppcdev->signatures);
- /* if there are no instances in the list, just teardown */
- if (list_empty(&rppcdev->instances)) {
- dev_dbg(&rpdev->dev, "no instances, removing device!\n");
- device_destroy(rppc_class, MKDEV(major, rppcdev->minor));
- cdev_del(&rppcdev->cdev);
- idr_remove(&rppc_devices, rppcdev->minor);
- kfree(rppcdev);
- mutex_unlock(&rppc_devices_lock);
- return;
- }
- /*
- * if there are rpc instances that means that this is a recovery
- * operation. Don't clean the rppcdev, and retain it for reuse.
- * mark each instance as invalid, and complete any on-going transactions
- */
- init_completion(&rppcdev->comp);
- mutex_lock(&rppcdev->lock);
- list_for_each_entry(rpc, &rppcdev->instances, list) {
- dev_dbg(&rpdev->dev, "instance %p in state %d\n",
- rpc, rpc->state);
- if ((rpc->state == RPPC_STATE_CONNECTED) && rpc->in_transition)
- complete_all(&rpc->reply_arrived);
- rpc->state = RPPC_STATE_STALE;
- if (rpc->ept) {
- rpmsg_destroy_ept(rpc->ept);
- rpc->ept = NULL;
- }
- wake_up_interruptible(&rpc->readq);
- }
- device_destroy(rppc_class, MKDEV(major, rppcdev->minor));
- rppcdev->dev = NULL;
- rppcdev->rpdev = NULL;
- mutex_unlock(&rppcdev->lock);
- mutex_unlock(&rppc_devices_lock);
- dev_dbg(&rpdev->dev, "removed rpmsg rpmsg-rpc service %s\n",
- rpdev->desc);
- }
- static struct rpmsg_device_id rppc_id_table[] = {
- {.name = "rpmsg-rpc"},
- {},
- };
- static struct rpmsg_driver rppc_driver = {
- .drv.name = KBUILD_MODNAME,
- .id_table = rppc_id_table,
- .probe = rppc_probe,
- .remove = rppc_remove,
- .callback = rppc_driver_cb,
- };
- static int __init rppc_init(void)
- {
- int ret;
- ret = alloc_chrdev_region(&rppc_dev, 0, RPPC_MAX_DEVICES,
- KBUILD_MODNAME);
- if (ret) {
- pr_err("alloc_chrdev_region failed: %d\n", ret);
- goto out;
- }
- rppc_class = class_create(THIS_MODULE, KBUILD_MODNAME);
- if (IS_ERR(rppc_class)) {
- ret = PTR_ERR(rppc_class);
- pr_err("class_create failed: %d\n", ret);
- goto unreg_region;
- }
- ret = register_rpmsg_driver(&rppc_driver);
- if (ret) {
- pr_err("register_rpmsg_driver failed: %d\n", ret);
- goto destroy_class;
- }
- return 0;
- destroy_class:
- class_destroy(rppc_class);
- unreg_region:
- unregister_chrdev_region(rppc_dev, RPPC_MAX_DEVICES);
- out:
- return ret;
- }
- static void __exit rppc_exit(void)
- {
- unregister_rpmsg_driver(&rppc_driver);
- class_destroy(rppc_class);
- unregister_chrdev_region(rppc_dev, RPPC_MAX_DEVICES);
- }
- module_init(rppc_init);
- module_exit(rppc_exit);
- MODULE_DEVICE_TABLE(rpmsg, rppc_id_table);
- MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
- MODULE_AUTHOR("Erik Rainey <erik.rainey@ti.com>");
- MODULE_DESCRIPTION("Remote Processor Procedure Call Driver");
- MODULE_ALIAS("rpmsg:rpmsg-rpc");
- MODULE_LICENSE("GPL v2");
|