linux/drivers/staging/dream/smd/smd_rpcrouter_device.c
<<
>>
Prefs
   1/* arch/arm/mach-msm/smd_rpcrouter_device.c
   2 *
   3 * Copyright (C) 2007 Google, Inc.
   4 * Copyright (c) 2007-2009 QUALCOMM Incorporated.
   5 * Author: San Mehat <san@android.com>
   6 *
   7 * This software is licensed under the terms of the GNU General Public
   8 * License version 2, as published by the Free Software Foundation, and
   9 * may be copied, distributed, and modified under those terms.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 */
  17#include <linux/module.h>
  18#include <linux/kernel.h>
  19#include <linux/string.h>
  20#include <linux/errno.h>
  21#include <linux/cdev.h>
  22#include <linux/init.h>
  23#include <linux/device.h>
  24#include <linux/types.h>
  25#include <linux/delay.h>
  26#include <linux/fs.h>
  27#include <linux/err.h>
  28#include <linux/sched.h>
  29#include <linux/poll.h>
  30#include <linux/platform_device.h>
  31#include <linux/msm_rpcrouter.h>
  32
  33#include <asm/uaccess.h>
  34#include <asm/byteorder.h>
  35
  36#include "smd_rpcrouter.h"
  37
  38#define SAFETY_MEM_SIZE 65536
  39
  40/* Next minor # available for a remote server */
  41static int next_minor = 1;
  42
  43struct class *msm_rpcrouter_class;
  44dev_t msm_rpcrouter_devno;
  45
  46static struct cdev rpcrouter_cdev;
  47static struct device *rpcrouter_device;
  48
  49static int rpcrouter_open(struct inode *inode, struct file *filp)
  50{
  51        int rc;
  52        struct msm_rpc_endpoint *ept;
  53
  54        rc = nonseekable_open(inode, filp);
  55        if (rc < 0)
  56                return rc;
  57
  58        ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev);
  59        if (!ept)
  60                return -ENOMEM;
  61
  62        filp->private_data = ept;
  63        return 0;
  64}
  65
  66static int rpcrouter_release(struct inode *inode, struct file *filp)
  67{
  68        struct msm_rpc_endpoint *ept;
  69        ept = (struct msm_rpc_endpoint *) filp->private_data;
  70
  71        return msm_rpcrouter_destroy_local_endpoint(ept);
  72}
  73
  74static ssize_t rpcrouter_read(struct file *filp, char __user *buf,
  75                              size_t count, loff_t *ppos)
  76{
  77        struct msm_rpc_endpoint *ept;
  78        struct rr_fragment *frag, *next;
  79        int rc;
  80
  81        ept = (struct msm_rpc_endpoint *) filp->private_data;
  82
  83        rc = __msm_rpc_read(ept, &frag, count, -1);
  84        if (rc < 0)
  85                return rc;
  86
  87        count = rc;
  88
  89        while (frag != NULL) {
  90                if (copy_to_user(buf, frag->data, frag->length)) {
  91                        printk(KERN_ERR
  92                               "rpcrouter: could not copy all read data to user!\n");
  93                        rc = -EFAULT;
  94                }
  95                buf += frag->length;
  96                next = frag->next;
  97                kfree(frag);
  98                frag = next;
  99        }
 100
 101        return rc;
 102}
 103
 104static ssize_t rpcrouter_write(struct file *filp, const char __user *buf,
 105                                size_t count, loff_t *ppos)
 106{
 107        struct msm_rpc_endpoint *ept;
 108        int rc = 0;
 109        void *k_buffer;
 110
 111        ept = (struct msm_rpc_endpoint *) filp->private_data;
 112
 113        /* A check for safety, this seems non-standard */
 114        if (count > SAFETY_MEM_SIZE)
 115                return -EINVAL;
 116
 117        k_buffer = kmalloc(count, GFP_KERNEL);
 118        if (!k_buffer)
 119                return -ENOMEM;
 120
 121        if (copy_from_user(k_buffer, buf, count)) {
 122                rc = -EFAULT;
 123                goto write_out_free;
 124        }
 125
 126        rc = msm_rpc_write(ept, k_buffer, count);
 127        if (rc < 0)
 128                goto write_out_free;
 129
 130        rc = count;
 131write_out_free:
 132        kfree(k_buffer);
 133        return rc;
 134}
 135
 136static unsigned int rpcrouter_poll(struct file *filp,
 137                                   struct poll_table_struct *wait)
 138{
 139        struct msm_rpc_endpoint *ept;
 140        unsigned mask = 0;
 141        ept = (struct msm_rpc_endpoint *) filp->private_data;
 142
 143        /* If there's data already in the read queue, return POLLIN.
 144         * Else, wait for the requested amount of time, and check again.
 145         */
 146
 147        if (!list_empty(&ept->read_q))
 148                mask |= POLLIN;
 149
 150        if (!mask) {
 151                poll_wait(filp, &ept->wait_q, wait);
 152                if (!list_empty(&ept->read_q))
 153                        mask |= POLLIN;
 154        }
 155
 156        return mask;
 157}
 158
 159static long rpcrouter_ioctl(struct file *filp, unsigned int cmd,
 160                            unsigned long arg)
 161{
 162        struct msm_rpc_endpoint *ept;
 163        struct rpcrouter_ioctl_server_args server_args;
 164        int rc = 0;
 165        uint32_t n;
 166
 167        ept = (struct msm_rpc_endpoint *) filp->private_data;
 168        switch (cmd) {
 169
 170        case RPC_ROUTER_IOCTL_GET_VERSION:
 171                n = RPC_ROUTER_VERSION_V1;
 172                rc = put_user(n, (unsigned int *) arg);
 173                break;
 174
 175        case RPC_ROUTER_IOCTL_GET_MTU:
 176                /* the pacmark word reduces the actual payload
 177                 * possible per message
 178                 */
 179                n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t);
 180                rc = put_user(n, (unsigned int *) arg);
 181                break;
 182
 183        case RPC_ROUTER_IOCTL_REGISTER_SERVER:
 184                rc = copy_from_user(&server_args, (void *) arg,
 185                                    sizeof(server_args));
 186                if (rc < 0)
 187                        break;
 188                msm_rpc_register_server(ept,
 189                                        server_args.prog,
 190                                        server_args.vers);
 191                break;
 192
 193        case RPC_ROUTER_IOCTL_UNREGISTER_SERVER:
 194                rc = copy_from_user(&server_args, (void *) arg,
 195                                    sizeof(server_args));
 196                if (rc < 0)
 197                        break;
 198
 199                msm_rpc_unregister_server(ept,
 200                                          server_args.prog,
 201                                          server_args.vers);
 202                break;
 203
 204        case RPC_ROUTER_IOCTL_GET_MINOR_VERSION:
 205                n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept));
 206                rc = put_user(n, (unsigned int *)arg);
 207                break;
 208
 209        default:
 210                rc = -EINVAL;
 211                break;
 212        }
 213
 214        return rc;
 215}
 216
 217static struct file_operations rpcrouter_server_fops = {
 218        .owner   = THIS_MODULE,
 219        .open    = rpcrouter_open,
 220        .release = rpcrouter_release,
 221        .read    = rpcrouter_read,
 222        .write   = rpcrouter_write,
 223        .poll    = rpcrouter_poll,
 224        .unlocked_ioctl  = rpcrouter_ioctl,
 225};
 226
 227static struct file_operations rpcrouter_router_fops = {
 228        .owner   = THIS_MODULE,
 229        .open    = rpcrouter_open,
 230        .release = rpcrouter_release,
 231        .read    = rpcrouter_read,
 232        .write   = rpcrouter_write,
 233        .poll    = rpcrouter_poll,
 234        .unlocked_ioctl = rpcrouter_ioctl,
 235};
 236
 237int msm_rpcrouter_create_server_cdev(struct rr_server *server)
 238{
 239        int rc;
 240        uint32_t dev_vers;
 241
 242        if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) {
 243                printk(KERN_ERR
 244                       "rpcrouter: Minor numbers exhausted - Increase "
 245                       "RPCROUTER_MAX_REMOTE_SERVERS\n");
 246                return -ENOBUFS;
 247        }
 248
 249#if CONFIG_MSM_AMSS_VERSION >= 6350
 250        /* Servers with bit 31 set are remote msm servers with hashkey version.
 251         * Servers with bit 31 not set are remote msm servers with
 252         * backwards compatible version type in which case the minor number
 253         * (lower 16 bits) is set to zero.
 254         *
 255         */
 256        if ((server->vers & RPC_VERSION_MODE_MASK))
 257                dev_vers = server->vers;
 258        else
 259                dev_vers = server->vers & RPC_VERSION_MAJOR_MASK;
 260#else
 261        dev_vers = server->vers;
 262#endif
 263
 264        server->device_number =
 265                MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++);
 266
 267        server->device =
 268                device_create(msm_rpcrouter_class, rpcrouter_device,
 269                              server->device_number, NULL, "%.8x:%.8x",
 270                              server->prog, dev_vers);
 271        if (IS_ERR(server->device)) {
 272                printk(KERN_ERR
 273                       "rpcrouter: Unable to create device (%ld)\n",
 274                       PTR_ERR(server->device));
 275                return PTR_ERR(server->device);;
 276        }
 277
 278        cdev_init(&server->cdev, &rpcrouter_server_fops);
 279        server->cdev.owner = THIS_MODULE;
 280
 281        rc = cdev_add(&server->cdev, server->device_number, 1);
 282        if (rc < 0) {
 283                printk(KERN_ERR
 284                       "rpcrouter: Unable to add chrdev (%d)\n", rc);
 285                device_destroy(msm_rpcrouter_class, server->device_number);
 286                return rc;
 287        }
 288        return 0;
 289}
 290
 291/* for backward compatible version type (31st bit cleared)
 292 * clearing minor number (lower 16 bits) in device name
 293 * is neccessary for driver binding
 294 */
 295int msm_rpcrouter_create_server_pdev(struct rr_server *server)
 296{
 297        sprintf(server->pdev_name, "rs%.8x:%.8x",
 298                server->prog,
 299#if CONFIG_MSM_AMSS_VERSION >= 6350
 300                (server->vers & RPC_VERSION_MODE_MASK) ? server->vers :
 301                (server->vers & RPC_VERSION_MAJOR_MASK));
 302#else
 303                server->vers);
 304#endif
 305
 306        server->p_device.base.id = -1;
 307        server->p_device.base.name = server->pdev_name;
 308
 309        server->p_device.prog = server->prog;
 310        server->p_device.vers = server->vers;
 311
 312        platform_device_register(&server->p_device.base);
 313        return 0;
 314}
 315
 316int msm_rpcrouter_init_devices(void)
 317{
 318        int rc;
 319        int major;
 320
 321        /* Create the device nodes */
 322        msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc");
 323        if (IS_ERR(msm_rpcrouter_class)) {
 324                rc = -ENOMEM;
 325                printk(KERN_ERR
 326                       "rpcrouter: failed to create oncrpc class\n");
 327                goto fail;
 328        }
 329
 330        rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0,
 331                                 RPCROUTER_MAX_REMOTE_SERVERS + 1,
 332                                 "oncrpc");
 333        if (rc < 0) {
 334                printk(KERN_ERR
 335                       "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
 336                goto fail_destroy_class;
 337        }
 338
 339        major = MAJOR(msm_rpcrouter_devno);
 340        rpcrouter_device = device_create(msm_rpcrouter_class, NULL,
 341                                         msm_rpcrouter_devno, NULL, "%.8x:%d",
 342                                         0, 0);
 343        if (IS_ERR(rpcrouter_device)) {
 344                rc = -ENOMEM;
 345                goto fail_unregister_cdev_region;
 346        }
 347
 348        cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops);
 349        rpcrouter_cdev.owner = THIS_MODULE;
 350
 351        rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1);
 352        if (rc < 0)
 353                goto fail_destroy_device;
 354
 355        return 0;
 356
 357fail_destroy_device:
 358        device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
 359fail_unregister_cdev_region:
 360        unregister_chrdev_region(msm_rpcrouter_devno,
 361                                 RPCROUTER_MAX_REMOTE_SERVERS + 1);
 362fail_destroy_class:
 363        class_destroy(msm_rpcrouter_class);
 364fail:
 365        return rc;
 366}
 367
 368void msm_rpcrouter_exit_devices(void)
 369{
 370        cdev_del(&rpcrouter_cdev);
 371        device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno);
 372        unregister_chrdev_region(msm_rpcrouter_devno,
 373                                 RPCROUTER_MAX_REMOTE_SERVERS + 1);
 374        class_destroy(msm_rpcrouter_class);
 375}
 376
 377