linux/drivers/char/msm_smd_pkt.c
<<
>>
Prefs
   1/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 and
   5 * only version 2 as published by the Free Software Foundation.
   6 *
   7 * This program is distributed in the hope that it will be useful,
   8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10 * GNU General Public License for more details.
  11 *
  12 * You should have received a copy of the GNU General Public License
  13 * along with this program; if not, write to the Free Software
  14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15 * 02110-1301, USA.
  16 *
  17 */
  18/*
  19 * SMD Packet Driver -- Provides userspace interface to SMD packet ports.
  20 */
  21
  22#include <linux/slab.h>
  23#include <linux/cdev.h>
  24#include <linux/module.h>
  25#include <linux/fs.h>
  26#include <linux/device.h>
  27#include <linux/sched.h>
  28#include <linux/mutex.h>
  29#include <linux/delay.h>
  30#include <linux/uaccess.h>
  31#include <linux/workqueue.h>
  32#include <linux/poll.h>
  33
  34#include <mach/msm_smd.h>
  35
  36#define NUM_SMD_PKT_PORTS 9
  37#define DEVICE_NAME "smdpkt"
  38#define MAX_BUF_SIZE 2048
  39
  40struct smd_pkt_dev {
  41        struct cdev cdev;
  42        struct device *devicep;
  43
  44        struct smd_channel *ch;
  45        int open_count;
  46        struct mutex ch_lock;
  47        struct mutex rx_lock;
  48        struct mutex tx_lock;
  49        wait_queue_head_t ch_read_wait_queue;
  50        wait_queue_head_t ch_opened_wait_queue;
  51
  52        int i;
  53
  54        unsigned char tx_buf[MAX_BUF_SIZE];
  55        unsigned char rx_buf[MAX_BUF_SIZE];
  56        int remote_open;
  57
  58} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
  59
  60struct class *smd_pkt_classp;
  61static dev_t smd_pkt_number;
  62
  63static int msm_smd_pkt_debug_enable;
  64module_param_named(debug_enable, msm_smd_pkt_debug_enable,
  65                   int, S_IRUGO | S_IWUSR | S_IWGRP);
  66
  67#ifdef DEBUG
  68#define D_DUMP_BUFFER(prestr, cnt, buf) do {                    \
  69                int i;                                          \
  70                if (msm_smd_pkt_debug_enable) {                 \
  71                        pr_debug("%s", prestr);                 \
  72                        for (i = 0; i < cnt; i++)               \
  73                                pr_debug("%.2x", buf[i]);       \
  74                        pr_debug("\n");                         \
  75                }                                               \
  76        } while (0)
  77#else
  78#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
  79#endif
  80
  81#ifdef DEBUG
  82#define DBG(x...) do {                  \
  83        if (msm_smd_pkt_debug_enable)   \
  84                pr_debug(x);            \
  85        } while (0)
  86#else
  87#define DBG(x...) do {} while (0)
  88#endif
  89
  90static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
  91{
  92        int sz;
  93
  94        if (!smd_pkt_devp || !smd_pkt_devp->ch)
  95                return;
  96
  97        sz = smd_cur_packet_size(smd_pkt_devp->ch);
  98        if (sz == 0) {
  99                DBG("no packet\n");
 100                return;
 101        }
 102        if (sz > smd_read_avail(smd_pkt_devp->ch)) {
 103                DBG("incomplete packet\n");
 104                return;
 105        }
 106
 107        DBG("waking up reader\n");
 108        wake_up_interruptible(&smd_pkt_devp->ch_read_wait_queue);
 109}
 110
 111static int smd_pkt_read(struct file *file, char __user *buf,
 112                        size_t count, loff_t *ppos)
 113{
 114        int r, bytes_read;
 115        struct smd_pkt_dev *smd_pkt_devp;
 116        struct smd_channel *chl;
 117
 118        DBG("read %d bytes\n", count);
 119        if (count > MAX_BUF_SIZE)
 120                return -EINVAL;
 121
 122        smd_pkt_devp = file->private_data;
 123        if (!smd_pkt_devp || !smd_pkt_devp->ch)
 124                return -EINVAL;
 125
 126        chl = smd_pkt_devp->ch;
 127wait_for_packet:
 128        r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
 129                                     (smd_cur_packet_size(chl) > 0 &&
 130                                      smd_read_avail(chl) >=
 131                                      smd_cur_packet_size(chl)));
 132
 133        if (r < 0) {
 134                if (r != -ERESTARTSYS)
 135                        pr_err("wait returned %d\n", r);
 136                return r;
 137        }
 138
 139        mutex_lock(&smd_pkt_devp->rx_lock);
 140
 141        bytes_read = smd_cur_packet_size(smd_pkt_devp->ch);
 142        if (bytes_read == 0 ||
 143            bytes_read < smd_read_avail(smd_pkt_devp->ch)) {
 144                mutex_unlock(&smd_pkt_devp->rx_lock);
 145                DBG("Nothing to read\n");
 146                goto wait_for_packet;
 147        }
 148
 149        if (bytes_read > count) {
 150                mutex_unlock(&smd_pkt_devp->rx_lock);
 151                pr_info("packet size %d > buffer size %d", bytes_read, count);
 152                return -EINVAL;
 153        }
 154
 155        r = smd_read(smd_pkt_devp->ch, smd_pkt_devp->rx_buf, bytes_read);
 156        if (r != bytes_read) {
 157                mutex_unlock(&smd_pkt_devp->rx_lock);
 158                pr_err("smd_read failed to read %d bytes: %d\n", bytes_read, r);
 159                return -EIO;
 160        }
 161
 162        D_DUMP_BUFFER("read: ", bytes_read, smd_pkt_devp->rx_buf);
 163        r = copy_to_user(buf, smd_pkt_devp->rx_buf, bytes_read);
 164        mutex_unlock(&smd_pkt_devp->rx_lock);
 165        if (r) {
 166                pr_err("copy_to_user failed %d\n", r);
 167                return -EFAULT;
 168        }
 169
 170        DBG("read complete %d bytes\n", bytes_read);
 171        check_and_wakeup_reader(smd_pkt_devp);
 172
 173        return bytes_read;
 174}
 175
 176static int smd_pkt_write(struct file *file, const char __user *buf,
 177                         size_t count, loff_t *ppos)
 178{
 179        int r;
 180        struct smd_pkt_dev *smd_pkt_devp;
 181
 182        if (count > MAX_BUF_SIZE)
 183                return -EINVAL;
 184
 185        DBG("writting %d bytes\n", count);
 186
 187        smd_pkt_devp = file->private_data;
 188        if (!smd_pkt_devp || !smd_pkt_devp->ch)
 189                return -EINVAL;
 190
 191        mutex_lock(&smd_pkt_devp->tx_lock);
 192        if (smd_write_avail(smd_pkt_devp->ch) < count) {
 193                mutex_unlock(&smd_pkt_devp->tx_lock);
 194                DBG("Not enough space to write\n");
 195                return -ENOMEM;
 196        }
 197
 198        D_DUMP_BUFFER("write: ", count, buf);
 199        r = copy_from_user(smd_pkt_devp->tx_buf, buf, count);
 200        if (r) {
 201                mutex_unlock(&smd_pkt_devp->tx_lock);
 202                pr_err("copy_from_user failed %d\n", r);
 203                return -EFAULT;
 204        }
 205
 206        r = smd_write(smd_pkt_devp->ch, smd_pkt_devp->tx_buf, count);
 207        if (r != count) {
 208                mutex_unlock(&smd_pkt_devp->tx_lock);
 209                pr_err("smd_write failed to write %d bytes: %d.\n", count, r);
 210                return -EIO;
 211        }
 212        mutex_unlock(&smd_pkt_devp->tx_lock);
 213
 214        DBG("wrote %d bytes\n", count);
 215        return count;
 216}
 217
 218static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
 219{
 220        struct smd_pkt_dev *smd_pkt_devp;
 221        unsigned int mask = 0;
 222
 223        smd_pkt_devp = file->private_data;
 224        if (!smd_pkt_devp)
 225                return POLLERR;
 226
 227        DBG("poll waiting\n");
 228        poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
 229        if (smd_read_avail(smd_pkt_devp->ch))
 230                mask |= POLLIN | POLLRDNORM;
 231
 232        DBG("poll return\n");
 233        return mask;
 234}
 235
 236static void smd_pkt_ch_notify(void *priv, unsigned event)
 237{
 238        struct smd_pkt_dev *smd_pkt_devp = priv;
 239
 240        if (smd_pkt_devp->ch == 0)
 241                return;
 242
 243        switch (event) {
 244        case SMD_EVENT_DATA:
 245                DBG("data\n");
 246                check_and_wakeup_reader(smd_pkt_devp);
 247                break;
 248
 249        case SMD_EVENT_OPEN:
 250                DBG("remote open\n");
 251                smd_pkt_devp->remote_open = 1;
 252                wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
 253                break;
 254
 255        case SMD_EVENT_CLOSE:
 256                smd_pkt_devp->remote_open = 0;
 257                pr_info("remote closed\n");
 258                break;
 259
 260        default:
 261                pr_err("unknown event %d\n", event);
 262                break;
 263        }
 264}
 265
 266static char *smd_pkt_dev_name[] = {
 267        "smdcntl0",
 268        "smdcntl1",
 269        "smdcntl2",
 270        "smdcntl3",
 271        "smdcntl4",
 272        "smdcntl5",
 273        "smdcntl6",
 274        "smdcntl7",
 275        "smd22",
 276};
 277
 278static char *smd_ch_name[] = {
 279        "DATA5_CNTL",
 280        "DATA6_CNTL",
 281        "DATA7_CNTL",
 282        "DATA8_CNTL",
 283        "DATA9_CNTL",
 284        "DATA12_CNTL",
 285        "DATA13_CNTL",
 286        "DATA14_CNTL",
 287        "DATA22",
 288};
 289
 290static int smd_pkt_open(struct inode *inode, struct file *file)
 291{
 292        int r = 0;
 293        struct smd_pkt_dev *smd_pkt_devp;
 294
 295        smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
 296        if (!smd_pkt_devp)
 297                return -EINVAL;
 298
 299        file->private_data = smd_pkt_devp;
 300
 301        mutex_lock(&smd_pkt_devp->ch_lock);
 302        if (smd_pkt_devp->open_count == 0) {
 303                r = smd_open(smd_ch_name[smd_pkt_devp->i],
 304                             &smd_pkt_devp->ch, smd_pkt_devp,
 305                             smd_pkt_ch_notify);
 306                if (r < 0) {
 307                        pr_err("smd_open failed for %s, %d\n",
 308                               smd_ch_name[smd_pkt_devp->i], r);
 309                        goto out;
 310                }
 311
 312                r = wait_event_interruptible_timeout(
 313                                smd_pkt_devp->ch_opened_wait_queue,
 314                                smd_pkt_devp->remote_open,
 315                                msecs_to_jiffies(2 * HZ));
 316                if (r == 0)
 317                        r = -ETIMEDOUT;
 318
 319                if (r < 0) {
 320                        pr_err("wait returned %d\n", r);
 321                        smd_close(smd_pkt_devp->ch);
 322                        smd_pkt_devp->ch = 0;
 323                } else {
 324                        smd_pkt_devp->open_count++;
 325                        r = 0;
 326                }
 327        }
 328out:
 329        mutex_unlock(&smd_pkt_devp->ch_lock);
 330        return r;
 331}
 332
 333static int smd_pkt_release(struct inode *inode, struct file *file)
 334{
 335        int r = 0;
 336        struct smd_pkt_dev *smd_pkt_devp = file->private_data;
 337
 338        if (!smd_pkt_devp)
 339                return -EINVAL;
 340
 341        mutex_lock(&smd_pkt_devp->ch_lock);
 342        if (--smd_pkt_devp->open_count == 0) {
 343                r = smd_close(smd_pkt_devp->ch);
 344                smd_pkt_devp->ch = 0;
 345        }
 346        mutex_unlock(&smd_pkt_devp->ch_lock);
 347
 348        return r;
 349}
 350
 351static const struct file_operations smd_pkt_fops = {
 352        .owner = THIS_MODULE,
 353        .open = smd_pkt_open,
 354        .release = smd_pkt_release,
 355        .read = smd_pkt_read,
 356        .write = smd_pkt_write,
 357        .poll = smd_pkt_poll,
 358};
 359
 360static int __init smd_pkt_init(void)
 361{
 362        int i;
 363        int r;
 364
 365        r = alloc_chrdev_region(&smd_pkt_number, 0,
 366                                NUM_SMD_PKT_PORTS, DEVICE_NAME);
 367        if (r) {
 368                pr_err("alloc_chrdev_region() failed %d\n", r);
 369                return r;
 370        }
 371
 372        smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
 373        if (IS_ERR(smd_pkt_classp)) {
 374                r = PTR_ERR(smd_pkt_classp);
 375                pr_err("class_create() failed %d\n", r);
 376                goto unreg_chardev;
 377        }
 378
 379        for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
 380                smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
 381                                          GFP_KERNEL);
 382                if (!smd_pkt_devp[i]) {
 383                        pr_err("kmalloc() failed\n");
 384                        goto clean_cdevs;
 385                }
 386
 387                smd_pkt_devp[i]->i = i;
 388
 389                init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
 390                smd_pkt_devp[i]->remote_open = 0;
 391                init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
 392
 393                mutex_init(&smd_pkt_devp[i]->ch_lock);
 394                mutex_init(&smd_pkt_devp[i]->rx_lock);
 395                mutex_init(&smd_pkt_devp[i]->tx_lock);
 396
 397                cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
 398                smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
 399
 400                r = cdev_add(&smd_pkt_devp[i]->cdev,
 401                             (smd_pkt_number + i), 1);
 402                if (r) {
 403                        pr_err("cdev_add() failed %d\n", r);
 404                        kfree(smd_pkt_devp[i]);
 405                        goto clean_cdevs;
 406                }
 407
 408                smd_pkt_devp[i]->devicep =
 409                        device_create(smd_pkt_classp, NULL,
 410                                      (smd_pkt_number + i), NULL,
 411                                      smd_pkt_dev_name[i]);
 412                if (IS_ERR(smd_pkt_devp[i]->devicep)) {
 413                        r = PTR_ERR(smd_pkt_devp[i]->devicep);
 414                        pr_err("device_create() failed %d\n", r);
 415                        cdev_del(&smd_pkt_devp[i]->cdev);
 416                        kfree(smd_pkt_devp[i]);
 417                        goto clean_cdevs;
 418                }
 419
 420        }
 421
 422        pr_info("SMD Packet Port Driver Initialized.\n");
 423        return 0;
 424
 425clean_cdevs:
 426        if (i > 0) {
 427                while (--i >= 0) {
 428                        mutex_destroy(&smd_pkt_devp[i]->ch_lock);
 429                        mutex_destroy(&smd_pkt_devp[i]->rx_lock);
 430                        mutex_destroy(&smd_pkt_devp[i]->tx_lock);
 431                        cdev_del(&smd_pkt_devp[i]->cdev);
 432                        kfree(smd_pkt_devp[i]);
 433                        device_destroy(smd_pkt_classp,
 434                                       MKDEV(MAJOR(smd_pkt_number), i));
 435                }
 436        }
 437
 438        class_destroy(smd_pkt_classp);
 439unreg_chardev:
 440        unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
 441        return r;
 442}
 443module_init(smd_pkt_init);
 444
 445static void __exit smd_pkt_cleanup(void)
 446{
 447        int i;
 448
 449        for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
 450                mutex_destroy(&smd_pkt_devp[i]->ch_lock);
 451                mutex_destroy(&smd_pkt_devp[i]->rx_lock);
 452                mutex_destroy(&smd_pkt_devp[i]->tx_lock);
 453                cdev_del(&smd_pkt_devp[i]->cdev);
 454                kfree(smd_pkt_devp[i]);
 455                device_destroy(smd_pkt_classp,
 456                               MKDEV(MAJOR(smd_pkt_number), i));
 457        }
 458
 459        class_destroy(smd_pkt_classp);
 460        unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
 461}
 462module_exit(smd_pkt_cleanup);
 463
 464MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
 465MODULE_LICENSE("GPL v2");
 466