linux/drivers/staging/greybus/raw.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Greybus driver for the Raw protocol
   4 *
   5 * Copyright 2015 Google Inc.
   6 * Copyright 2015 Linaro Ltd.
   7 */
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/slab.h>
  11#include <linux/sizes.h>
  12#include <linux/cdev.h>
  13#include <linux/fs.h>
  14#include <linux/idr.h>
  15#include <linux/uaccess.h>
  16
  17#include "greybus.h"
  18
  19struct gb_raw {
  20        struct gb_connection *connection;
  21
  22        struct list_head list;
  23        int list_data;
  24        struct mutex list_lock;
  25        dev_t dev;
  26        struct cdev cdev;
  27        struct device *device;
  28};
  29
  30struct raw_data {
  31        struct list_head entry;
  32        u32 len;
  33        u8 data[0];
  34};
  35
  36static struct class *raw_class;
  37static int raw_major;
  38static const struct file_operations raw_fops;
  39static DEFINE_IDA(minors);
  40
  41/* Number of minor devices this driver supports */
  42#define NUM_MINORS      256
  43
  44/* Maximum size of any one send data buffer we support */
  45#define MAX_PACKET_SIZE (PAGE_SIZE * 2)
  46
  47/*
  48 * Maximum size of the data in the receive buffer we allow before we start to
  49 * drop messages on the floor
  50 */
  51#define MAX_DATA_SIZE   (MAX_PACKET_SIZE * 8)
  52
  53/*
  54 * Add the raw data message to the list of received messages.
  55 */
  56static int receive_data(struct gb_raw *raw, u32 len, u8 *data)
  57{
  58        struct raw_data *raw_data;
  59        struct device *dev = &raw->connection->bundle->dev;
  60        int retval = 0;
  61
  62        if (len > MAX_PACKET_SIZE) {
  63                dev_err(dev, "Too big of a data packet, rejected\n");
  64                return -EINVAL;
  65        }
  66
  67        mutex_lock(&raw->list_lock);
  68        if ((raw->list_data + len) > MAX_DATA_SIZE) {
  69                dev_err(dev, "Too much data in receive buffer, now dropping packets\n");
  70                retval = -EINVAL;
  71                goto exit;
  72        }
  73
  74        raw_data = kmalloc(sizeof(*raw_data) + len, GFP_KERNEL);
  75        if (!raw_data) {
  76                retval = -ENOMEM;
  77                goto exit;
  78        }
  79
  80        raw->list_data += len;
  81        raw_data->len = len;
  82        memcpy(&raw_data->data[0], data, len);
  83
  84        list_add_tail(&raw_data->entry, &raw->list);
  85exit:
  86        mutex_unlock(&raw->list_lock);
  87        return retval;
  88}
  89
  90static int gb_raw_request_handler(struct gb_operation *op)
  91{
  92        struct gb_connection *connection = op->connection;
  93        struct device *dev = &connection->bundle->dev;
  94        struct gb_raw *raw = greybus_get_drvdata(connection->bundle);
  95        struct gb_raw_send_request *receive;
  96        u32 len;
  97
  98        if (op->type != GB_RAW_TYPE_SEND) {
  99                dev_err(dev, "unknown request type 0x%02x\n", op->type);
 100                return -EINVAL;
 101        }
 102
 103        /* Verify size of payload */
 104        if (op->request->payload_size < sizeof(*receive)) {
 105                dev_err(dev, "raw receive request too small (%zu < %zu)\n",
 106                        op->request->payload_size, sizeof(*receive));
 107                return -EINVAL;
 108        }
 109        receive = op->request->payload;
 110        len = le32_to_cpu(receive->len);
 111        if (len != (int)(op->request->payload_size - sizeof(__le32))) {
 112                dev_err(dev, "raw receive request wrong size %d vs %d\n", len,
 113                        (int)(op->request->payload_size - sizeof(__le32)));
 114                return -EINVAL;
 115        }
 116        if (len == 0) {
 117                dev_err(dev, "raw receive request of 0 bytes?\n");
 118                return -EINVAL;
 119        }
 120
 121        return receive_data(raw, len, receive->data);
 122}
 123
 124static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data)
 125{
 126        struct gb_connection *connection = raw->connection;
 127        struct gb_raw_send_request *request;
 128        int retval;
 129
 130        request = kmalloc(len + sizeof(*request), GFP_KERNEL);
 131        if (!request)
 132                return -ENOMEM;
 133
 134        if (copy_from_user(&request->data[0], data, len)) {
 135                kfree(request);
 136                return -EFAULT;
 137        }
 138
 139        request->len = cpu_to_le32(len);
 140
 141        retval = gb_operation_sync(connection, GB_RAW_TYPE_SEND,
 142                                   request, len + sizeof(*request),
 143                                   NULL, 0);
 144
 145        kfree(request);
 146        return retval;
 147}
 148
 149static int gb_raw_probe(struct gb_bundle *bundle,
 150                        const struct greybus_bundle_id *id)
 151{
 152        struct greybus_descriptor_cport *cport_desc;
 153        struct gb_connection *connection;
 154        struct gb_raw *raw;
 155        int retval;
 156        int minor;
 157
 158        if (bundle->num_cports != 1)
 159                return -ENODEV;
 160
 161        cport_desc = &bundle->cport_desc[0];
 162        if (cport_desc->protocol_id != GREYBUS_PROTOCOL_RAW)
 163                return -ENODEV;
 164
 165        raw = kzalloc(sizeof(*raw), GFP_KERNEL);
 166        if (!raw)
 167                return -ENOMEM;
 168
 169        connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
 170                                          gb_raw_request_handler);
 171        if (IS_ERR(connection)) {
 172                retval = PTR_ERR(connection);
 173                goto error_free;
 174        }
 175
 176        INIT_LIST_HEAD(&raw->list);
 177        mutex_init(&raw->list_lock);
 178
 179        raw->connection = connection;
 180        greybus_set_drvdata(bundle, raw);
 181
 182        minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
 183        if (minor < 0) {
 184                retval = minor;
 185                goto error_connection_destroy;
 186        }
 187
 188        raw->dev = MKDEV(raw_major, minor);
 189        cdev_init(&raw->cdev, &raw_fops);
 190
 191        retval = gb_connection_enable(connection);
 192        if (retval)
 193                goto error_remove_ida;
 194
 195        retval = cdev_add(&raw->cdev, raw->dev, 1);
 196        if (retval)
 197                goto error_connection_disable;
 198
 199        raw->device = device_create(raw_class, &connection->bundle->dev,
 200                                    raw->dev, raw, "gb!raw%d", minor);
 201        if (IS_ERR(raw->device)) {
 202                retval = PTR_ERR(raw->device);
 203                goto error_del_cdev;
 204        }
 205
 206        return 0;
 207
 208error_del_cdev:
 209        cdev_del(&raw->cdev);
 210
 211error_connection_disable:
 212        gb_connection_disable(connection);
 213
 214error_remove_ida:
 215        ida_simple_remove(&minors, minor);
 216
 217error_connection_destroy:
 218        gb_connection_destroy(connection);
 219
 220error_free:
 221        kfree(raw);
 222        return retval;
 223}
 224
 225static void gb_raw_disconnect(struct gb_bundle *bundle)
 226{
 227        struct gb_raw *raw = greybus_get_drvdata(bundle);
 228        struct gb_connection *connection = raw->connection;
 229        struct raw_data *raw_data;
 230        struct raw_data *temp;
 231
 232        // FIXME - handle removing a connection when the char device node is open.
 233        device_destroy(raw_class, raw->dev);
 234        cdev_del(&raw->cdev);
 235        gb_connection_disable(connection);
 236        ida_simple_remove(&minors, MINOR(raw->dev));
 237        gb_connection_destroy(connection);
 238
 239        mutex_lock(&raw->list_lock);
 240        list_for_each_entry_safe(raw_data, temp, &raw->list, entry) {
 241                list_del(&raw_data->entry);
 242                kfree(raw_data);
 243        }
 244        mutex_unlock(&raw->list_lock);
 245
 246        kfree(raw);
 247}
 248
 249/*
 250 * Character device node interfaces.
 251 *
 252 * Note, we are using read/write to only allow a single read/write per message.
 253 * This means for read(), you have to provide a big enough buffer for the full
 254 * message to be copied into.  If the buffer isn't big enough, the read() will
 255 * fail with -ENOSPC.
 256 */
 257
 258static int raw_open(struct inode *inode, struct file *file)
 259{
 260        struct cdev *cdev = inode->i_cdev;
 261        struct gb_raw *raw = container_of(cdev, struct gb_raw, cdev);
 262
 263        file->private_data = raw;
 264        return 0;
 265}
 266
 267static ssize_t raw_write(struct file *file, const char __user *buf,
 268                         size_t count, loff_t *ppos)
 269{
 270        struct gb_raw *raw = file->private_data;
 271        int retval;
 272
 273        if (!count)
 274                return 0;
 275
 276        if (count > MAX_PACKET_SIZE)
 277                return -E2BIG;
 278
 279        retval = gb_raw_send(raw, count, buf);
 280        if (retval)
 281                return retval;
 282
 283        return count;
 284}
 285
 286static ssize_t raw_read(struct file *file, char __user *buf, size_t count,
 287                        loff_t *ppos)
 288{
 289        struct gb_raw *raw = file->private_data;
 290        int retval = 0;
 291        struct raw_data *raw_data;
 292
 293        mutex_lock(&raw->list_lock);
 294        if (list_empty(&raw->list))
 295                goto exit;
 296
 297        raw_data = list_first_entry(&raw->list, struct raw_data, entry);
 298        if (raw_data->len > count) {
 299                retval = -ENOSPC;
 300                goto exit;
 301        }
 302
 303        if (copy_to_user(buf, &raw_data->data[0], raw_data->len)) {
 304                retval = -EFAULT;
 305                goto exit;
 306        }
 307
 308        list_del(&raw_data->entry);
 309        raw->list_data -= raw_data->len;
 310        retval = raw_data->len;
 311        kfree(raw_data);
 312
 313exit:
 314        mutex_unlock(&raw->list_lock);
 315        return retval;
 316}
 317
 318static const struct file_operations raw_fops = {
 319        .owner          = THIS_MODULE,
 320        .write          = raw_write,
 321        .read           = raw_read,
 322        .open           = raw_open,
 323        .llseek         = noop_llseek,
 324};
 325
 326static const struct greybus_bundle_id gb_raw_id_table[] = {
 327        { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) },
 328        { }
 329};
 330MODULE_DEVICE_TABLE(greybus, gb_raw_id_table);
 331
 332static struct greybus_driver gb_raw_driver = {
 333        .name           = "raw",
 334        .probe          = gb_raw_probe,
 335        .disconnect     = gb_raw_disconnect,
 336        .id_table       = gb_raw_id_table,
 337};
 338
 339static int raw_init(void)
 340{
 341        dev_t dev;
 342        int retval;
 343
 344        raw_class = class_create(THIS_MODULE, "gb_raw");
 345        if (IS_ERR(raw_class)) {
 346                retval = PTR_ERR(raw_class);
 347                goto error_class;
 348        }
 349
 350        retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_raw");
 351        if (retval < 0)
 352                goto error_chrdev;
 353
 354        raw_major = MAJOR(dev);
 355
 356        retval = greybus_register(&gb_raw_driver);
 357        if (retval)
 358                goto error_gb;
 359
 360        return 0;
 361
 362error_gb:
 363        unregister_chrdev_region(dev, NUM_MINORS);
 364error_chrdev:
 365        class_destroy(raw_class);
 366error_class:
 367        return retval;
 368}
 369module_init(raw_init);
 370
 371static void __exit raw_exit(void)
 372{
 373        greybus_deregister(&gb_raw_driver);
 374        unregister_chrdev_region(MKDEV(raw_major, 0), NUM_MINORS);
 375        class_destroy(raw_class);
 376        ida_destroy(&minors);
 377}
 378module_exit(raw_exit);
 379
 380MODULE_LICENSE("GPL v2");
 381