Linux-2.6.33.2/drivers/staging/dream/smd/smd_rpcrouter_device.c

Compare this file to the similar file:
Show the results in this format:

/* arch/arm/mach-msm/smd_rpcrouter_device.c
 *
 * Copyright (C) 2007 Google, Inc.
 * Copyright (c) 2007-2009 QUALCOMM Incorporated.
 * Author: San Mehat <san@android.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 *
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/msm_rpcrouter.h>

#include <asm/uaccess.h>
#include <asm/byteorder.h>

#include "smd_rpcrouter.h"

#define SAFETY_MEM_SIZE 65536

/* Next minor # available for a remote server */
static int next_minor = 1;

struct class *msm_rpcrouter_class;
dev_t msm_rpcrouter_devno;

static struct cdev rpcrouter_cdev;
static struct device *rpcrouter_device;

static int rpcrouter_open(struct inode *inode, struct file *filp)
{
	int rc;
	struct msm_rpc_endpoint *ept;

	rc = nonseekable_open(inode, filp);
	if (rc < 0)
		return rc;

	ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev);
	if (!ept)
		return -ENOMEM;

	filp->private_data = ept;
	return 0;
}

static int rpcrouter_release(struct inode *inode, struct file *filp)
{
	struct msm_rpc_endpoint *ept;
	ept = (struct msm_rpc_endpoint *) filp->private_data;

	return msm_rpcrouter_destroy_local_endpoint(ept);
}

static ssize_t rpcrouter_read(struct file *filp, char __user *buf,
			      size_t count, loff_t *ppos)
{
	struct msm_rpc_endpoint *ept;
	struct rr_fragment *frag, *next;
	int rc;

	ept = (struct msm_rpc_endpoint *) filp->private_data;

	rc = __msm_rpc_read(ept, &frag, count, -1);
	if (rc < 0)
		return rc;

	count = rc;

	while (frag != NULL) {
		if (copy_to_user(buf, frag->data, frag->length)) {
			printk(KERN_ERR
			       "rpcrouter: could not copy all read data to user!\n");
			rc = -EFAULT;
		}
		buf += frag->length;
		next = frag->next;
		kfree(frag);
		frag = next;
	}

	return rc;
}

static ssize_t rpcrouter_write(struct file *filp, const char __user *buf,
				size_t count, loff_t *ppos)
{
	struct msm_rpc_endpoint	*ept;
	int rc = 0;
	void *k_buffer;

	ept = (struct msm_rpc_endpoint *) filp->private_data;

	/* A check for safety, this seems non-standard */
	if (count > SAFETY_MEM_SIZE)
		return -EINVAL;

	k_buffer = kmalloc(count, GFP_KERNEL);
	if (!k_buffer)
		return -ENOMEM;

	if (copy_from_user(k_buffer, buf, count)) {
		rc = -EFAULT;
		goto write_out_free;
	}

	rc = msm_rpc_write(ept, k_buffer, count);
	if (rc < 0)
		goto write_out_free;

	rc = count;
write_out_free:
	kfree(k_buffer);
	return rc;
}

static unsigned int rpcrouter_poll(struct file *filp,
				   struct poll_table_struct *wait)
{
	struct msm_rpc_endpoint *ept;
	unsigned mask = 0;
	ept = (struct msm_rpc_endpoint *) filp->private_data;

	/* If there's data already in the read queue, return POLLIN.
	 * Else, wait for the requested amount of time, and check again.
	 */

	if (!list_empty(&ept->read_q))
		mask |= POLLIN;

	if (!mask) {
		poll_wait(filp, &ept->wait_q, wait);
		if (!list_empty(&ept->read_q))
			mask |= POLLIN;
	}

	return mask;
}

static long rpcrouter_ioctl(struct file *filp, unsigned int cmd,
			    unsigned long arg)
{
	struct msm_rpc_endpoint *ept;
	struct rpcrouter_ioctl_server_args server_args;
	int rc = 0;
	uint32_t n;

	ept = (struct msm_rpc_endpoint *) filp->private_data;
	switch (cmd) {

	case RPC_ROUTER_IOCTL_GET_VERSION:
		n = RPC_ROUTER_VERSION_V1;
		rc = put_user(n, (unsigned int *) arg);
		break;

	case RPC_ROUTER_IOCTL_GET_MTU:
		/* the pacmark word reduces the actual payload
		 * possible per message
		 */
		n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t);
		rc = put_user(n, (unsigned int *) arg);
		break;

	case RPC_ROUTER_IOCTL_REGISTER_SERVER:
		rc = copy_from_user(&server_args, (void *) arg,
				    sizeof(server_args));
		if (rc < 0)
			break;
		msm_rpc_register_server(ept,
					server_args.prog,
					server_args.vers);
		break;

	case RPC_ROUTER_IOCTL_UNREGISTER_SERVER:
		rc = copy_from_user(&server_args, (void *) arg,
				    sizeof(server_args));
		if (rc < 0)
			break;

		msm_rpc_unregister_server(ept,
					  server_args.prog,
					  server_args.vers);
		break;

	case RPC_ROUTER_IOCTL_GET_MINOR_VERSION:
		n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept));
		rc = put_user(n, (unsigned int *)arg);
		break;

	default:
		rc = -EINVAL;
		break;
	}

	return rc;
}

static struct file_operations rpcrouter_server_fops = {
	.owner	 = THIS_MODULE,
	.open	 = rpcrouter_open,
	.release = rpcrouter_release,
	.read	 = rpcrouter_read,
	.write	 = rpcrouter_write,
	.poll    = rpcrouter_poll,
	.unlocked_ioctl	 = rpcrouter_ioctl,
};

static struct file_operations rpcrouter_router_fops = {
	.owner	 = THIS_MODULE,
	.open	 = rpcrouter_open,
	.release = rpcrouter_release,
	.read	 = rpcrouter_read,
	.write	 = rpcrouter_write,
	.poll    = rpcrouter_poll,
	.unlocked_ioctl = rpcrouter_ioctl,
};

int msm_rpcrouter_create_server_cdev(struct rr_server *server)
{
	int rc;
	uint32_t dev_vers;

	if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) {
		printk(KERN_ERR
		       "rpcrouter: Minor numbers exhausted - Increase "
		       "RPCROUTER_MAX_REMOTE_SERVERS\n");
		return -ENOBUFS;
	}

#if CONFIG_MSM_AMSS_VERSION >= 6350
	/* Servers with bit 31 set are remote msm servers with hashkey version.
	 * Servers with bit 31 not set are remote msm servers with
	 * backwards compatible version type in which case the minor number
	 * (lower 16 bits) is set to zero.
	 *
	 */
	if ((server->vers & RPC_VERSION_MODE_MASK))
		dev_vers = server->vers;
	else
		dev_vers = server->vers & RPC_VERSION_MAJOR_MASK;
#else
	dev_vers = server->vers;
#endif

	server->device_number =
		MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++);

	server->device =
		device_create(msm_rpcrouter_class, rpcrouter_device,
			      server->device_number, NULL, "%.8x:%.8x",
			      server->prog, dev_vers);
	if (IS_ERR(server->device)) {
		printk(KERN_ERR
		       "rpcrouter: Unable to create device (%ld)\n",
		       PTR_ERR(server->device));
		return PTR_ERR(server->device);;
	}

	cdev_init(&server->cdev, &rpcrouter_server_fops);
	server->cdev.owner = THIS_MODULE;

	rc = cdev_add(&server->cdev, server->device_number, 1);
	if (rc < 0) {
		printk(KERN_ERR
		       "rpcrouter: Unable to add chrdev (%d)\n", rc);
		device_destroy(msm_rpcrouter_class, server->device_number);
		return rc;
	}
	return 0;
}

/* for backward compatible version type (31st bit cleared)
 * clearing minor number (lower 16 bits) in device name
 * is neccessary for driver binding
 */
int msm_rpcrouter_create_server_pdev(struct rr_server *server)
{
	sprintf(server->pdev_name, "rs%.8x:%.8x",
		server->prog,
#if CONFIG_MSM_AMSS_VERSION >= 6350
		(server->vers & RPC_VERSION_MODE_MASK) ? server->vers :
		(server->vers & RPC_VERSION_MAJOR_MASK));
#else
		server->vers);
#endif

	server->p_device.base.id = -1;
	server->p_device.base.name = server->pdev_name;

	server->p_device.prog = server->prog;
	server->p_device.vers = server->vers;

	platform_device_register(&server->p_device.base);
	return 0;
}

int msm_rpcrouter_init_devices(void)
{
	int rc;
	int major;

	/* Create the device nodes */
	msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc");
	if (IS_ERR(msm_rpcrouter_class)) {
		rc = -ENOMEM;
		printk(KERN_ERR
		       "rpcrouter: failed to create oncrpc class\n");
		goto fail;
	}

	rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0,
				 RPCROUTER_MAX_REMOTE_SERVERS + 1,
				 "oncrpc");
	if (rc < 0) {
		printk(KERN_ERR
		       "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
		goto fail_destroy_class;
	}

	major = MAJOR(msm_rpcrouter_devno);
	rpcrouter_device = device_create(msm_rpcrouter_class, NULL,
					 msm_rpcrouter_devno, NULL, "%.8x:%d",
					 0, 0);
	if (IS_ERR(rpcrouter_device)) {
		rc = -ENOMEM;
		goto fail_unregister_cdev_region;
	}

	cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops);
	rpcrouter_cdev.owner = THIS_MODULE;

	rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1);
	if (rc < 0)
		goto fail_destroy_device;

	return 0;

fail_destroy_device:
	device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
fail_unregister_cdev_region:
	unregister_chrdev_region(msm_rpcrouter_devno,
				 RPCROUTER_MAX_REMOTE_SERVERS + 1);
fail_destroy_class:
	class_destroy(msm_rpcrouter_class);
fail:
	return rc;
}

void msm_rpcrouter_exit_devices(void)
{
	cdev_del(&rpcrouter_cdev);
	device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
	unregister_chrdev_region(msm_rpcrouter_devno,
				 RPCROUTER_MAX_REMOTE_SERVERS + 1);
	class_destroy(msm_rpcrouter_class);
}