[patch 18/32] greybus: raw driver

2016-09-16 Thread Greg KH

This driver implements the Greybus raw protocol.

Signed-off-by: Greg Kroah-Hartman 
---
 drivers/greybus/raw.c |  381 ++
 1 file changed, 381 insertions(+)

--- /dev/null
+++ b/drivers/greybus/raw.c
@@ -0,0 +1,381 @@
+/*
+ * Greybus driver for the Raw protocol
+ *
+ * Copyright 2015 Google Inc.
+ * Copyright 2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "greybus.h"
+
+struct gb_raw {
+   struct gb_connection *connection;
+
+   struct list_head list;
+   int list_data;
+   struct mutex list_lock;
+   dev_t dev;
+   struct cdev cdev;
+   struct device *device;
+};
+
+struct raw_data {
+   struct list_head entry;
+   u32 len;
+   u8 data[0];
+};
+
+static struct class *raw_class;
+static int raw_major;
+static const struct file_operations raw_fops;
+static DEFINE_IDA(minors);
+
+/* Number of minor devices this driver supports */
+#define NUM_MINORS 256
+
+/* Maximum size of any one send data buffer we support */
+#define MAX_PACKET_SIZE(PAGE_SIZE * 2)
+
+/*
+ * Maximum size of the data in the receive buffer we allow before we start to
+ * drop messages on the floor
+ */
+#define MAX_DATA_SIZE  (MAX_PACKET_SIZE * 8)
+
+/*
+ * Add the raw data message to the list of received messages.
+ */
+static int receive_data(struct gb_raw *raw, u32 len, u8 *data)
+{
+   struct raw_data *raw_data;
+   struct device *dev = >connection->bundle->dev;
+   int retval = 0;
+
+   if (len > MAX_PACKET_SIZE) {
+   dev_err(dev, "Too big of a data packet, rejected\n");
+   return -EINVAL;
+   }
+
+   mutex_lock(>list_lock);
+   if ((raw->list_data + len) > MAX_DATA_SIZE) {
+   dev_err(dev, "Too much data in receive buffer, now dropping 
packets\n");
+   retval = -EINVAL;
+   goto exit;
+   }
+
+   raw_data = kmalloc(sizeof(*raw_data) + len, GFP_KERNEL);
+   if (!raw_data) {
+   retval = -ENOMEM;
+   goto exit;
+   }
+
+   raw->list_data += len;
+   raw_data->len = len;
+   memcpy(_data->data[0], data, len);
+
+   list_add_tail(_data->entry, >list);
+exit:
+   mutex_unlock(>list_lock);
+   return retval;
+}
+
+static int gb_raw_request_handler(struct gb_operation *op)
+{
+   struct gb_connection *connection = op->connection;
+   struct device *dev = >bundle->dev;
+   struct gb_raw *raw = greybus_get_drvdata(connection->bundle);
+   struct gb_raw_send_request *receive;
+   u32 len;
+
+   if (op->type != GB_RAW_TYPE_SEND) {
+   dev_err(dev, "unknown request type 0x%02x\n", op->type);
+   return -EINVAL;
+   }
+
+   /* Verify size of payload */
+   if (op->request->payload_size < sizeof(*receive)) {
+   dev_err(dev, "raw receive request too small (%zu < %zu)\n",
+   op->request->payload_size, sizeof(*receive));
+   return -EINVAL;
+   }
+   receive = op->request->payload;
+   len = le32_to_cpu(receive->len);
+   if (len != (int)(op->request->payload_size - sizeof(__le32))) {
+   dev_err(dev, "raw receive request wrong size %d vs %d\n", len,
+   (int)(op->request->payload_size - sizeof(__le32)));
+   return -EINVAL;
+   }
+   if (len == 0) {
+   dev_err(dev, "raw receive request of 0 bytes?\n");
+   return -EINVAL;
+   }
+
+   return receive_data(raw, len, receive->data);
+}
+
+static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data)
+{
+   struct gb_connection *connection = raw->connection;
+   struct gb_raw_send_request *request;
+   int retval;
+
+   request = kmalloc(len + sizeof(*request), GFP_KERNEL);
+   if (!request)
+   return -ENOMEM;
+
+   if (copy_from_user(>data[0], data, len)) {
+   kfree(request);
+   return -EFAULT;
+   }
+
+   request->len = cpu_to_le32(len);
+
+   retval = gb_operation_sync(connection, GB_RAW_TYPE_SEND,
+  request, len + sizeof(*request),
+  NULL, 0);
+
+   kfree(request);
+   return retval;
+}
+
+static int gb_raw_probe(struct gb_bundle *bundle,
+   const struct greybus_bundle_id *id)
+{
+   struct greybus_descriptor_cport *cport_desc;
+   struct gb_connection *connection;
+   struct gb_raw *raw;
+   int retval;
+   int minor;
+
+   if (bundle->num_cports != 1)
+   return -ENODEV;
+
+   cport_desc = >cport_desc[0];
+   if (cport_desc->protocol_id != GREYBUS_PROTOCOL_RAW)
+   return -ENODEV;
+
+   raw = kzalloc(sizeof(*raw), GFP_KERNEL);
+   if (!raw)
+   

[patch 18/32] greybus: raw driver

2016-09-16 Thread Greg KH

This driver implements the Greybus raw protocol.

Signed-off-by: Greg Kroah-Hartman 
---
 drivers/greybus/raw.c |  381 ++
 1 file changed, 381 insertions(+)

--- /dev/null
+++ b/drivers/greybus/raw.c
@@ -0,0 +1,381 @@
+/*
+ * Greybus driver for the Raw protocol
+ *
+ * Copyright 2015 Google Inc.
+ * Copyright 2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "greybus.h"
+
+struct gb_raw {
+   struct gb_connection *connection;
+
+   struct list_head list;
+   int list_data;
+   struct mutex list_lock;
+   dev_t dev;
+   struct cdev cdev;
+   struct device *device;
+};
+
+struct raw_data {
+   struct list_head entry;
+   u32 len;
+   u8 data[0];
+};
+
+static struct class *raw_class;
+static int raw_major;
+static const struct file_operations raw_fops;
+static DEFINE_IDA(minors);
+
+/* Number of minor devices this driver supports */
+#define NUM_MINORS 256
+
+/* Maximum size of any one send data buffer we support */
+#define MAX_PACKET_SIZE(PAGE_SIZE * 2)
+
+/*
+ * Maximum size of the data in the receive buffer we allow before we start to
+ * drop messages on the floor
+ */
+#define MAX_DATA_SIZE  (MAX_PACKET_SIZE * 8)
+
+/*
+ * Add the raw data message to the list of received messages.
+ */
+static int receive_data(struct gb_raw *raw, u32 len, u8 *data)
+{
+   struct raw_data *raw_data;
+   struct device *dev = >connection->bundle->dev;
+   int retval = 0;
+
+   if (len > MAX_PACKET_SIZE) {
+   dev_err(dev, "Too big of a data packet, rejected\n");
+   return -EINVAL;
+   }
+
+   mutex_lock(>list_lock);
+   if ((raw->list_data + len) > MAX_DATA_SIZE) {
+   dev_err(dev, "Too much data in receive buffer, now dropping 
packets\n");
+   retval = -EINVAL;
+   goto exit;
+   }
+
+   raw_data = kmalloc(sizeof(*raw_data) + len, GFP_KERNEL);
+   if (!raw_data) {
+   retval = -ENOMEM;
+   goto exit;
+   }
+
+   raw->list_data += len;
+   raw_data->len = len;
+   memcpy(_data->data[0], data, len);
+
+   list_add_tail(_data->entry, >list);
+exit:
+   mutex_unlock(>list_lock);
+   return retval;
+}
+
+static int gb_raw_request_handler(struct gb_operation *op)
+{
+   struct gb_connection *connection = op->connection;
+   struct device *dev = >bundle->dev;
+   struct gb_raw *raw = greybus_get_drvdata(connection->bundle);
+   struct gb_raw_send_request *receive;
+   u32 len;
+
+   if (op->type != GB_RAW_TYPE_SEND) {
+   dev_err(dev, "unknown request type 0x%02x\n", op->type);
+   return -EINVAL;
+   }
+
+   /* Verify size of payload */
+   if (op->request->payload_size < sizeof(*receive)) {
+   dev_err(dev, "raw receive request too small (%zu < %zu)\n",
+   op->request->payload_size, sizeof(*receive));
+   return -EINVAL;
+   }
+   receive = op->request->payload;
+   len = le32_to_cpu(receive->len);
+   if (len != (int)(op->request->payload_size - sizeof(__le32))) {
+   dev_err(dev, "raw receive request wrong size %d vs %d\n", len,
+   (int)(op->request->payload_size - sizeof(__le32)));
+   return -EINVAL;
+   }
+   if (len == 0) {
+   dev_err(dev, "raw receive request of 0 bytes?\n");
+   return -EINVAL;
+   }
+
+   return receive_data(raw, len, receive->data);
+}
+
+static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data)
+{
+   struct gb_connection *connection = raw->connection;
+   struct gb_raw_send_request *request;
+   int retval;
+
+   request = kmalloc(len + sizeof(*request), GFP_KERNEL);
+   if (!request)
+   return -ENOMEM;
+
+   if (copy_from_user(>data[0], data, len)) {
+   kfree(request);
+   return -EFAULT;
+   }
+
+   request->len = cpu_to_le32(len);
+
+   retval = gb_operation_sync(connection, GB_RAW_TYPE_SEND,
+  request, len + sizeof(*request),
+  NULL, 0);
+
+   kfree(request);
+   return retval;
+}
+
+static int gb_raw_probe(struct gb_bundle *bundle,
+   const struct greybus_bundle_id *id)
+{
+   struct greybus_descriptor_cport *cport_desc;
+   struct gb_connection *connection;
+   struct gb_raw *raw;
+   int retval;
+   int minor;
+
+   if (bundle->num_cports != 1)
+   return -ENODEV;
+
+   cport_desc = >cport_desc[0];
+   if (cport_desc->protocol_id != GREYBUS_PROTOCOL_RAW)
+   return -ENODEV;
+
+   raw = kzalloc(sizeof(*raw), GFP_KERNEL);
+   if (!raw)
+   return -ENOMEM;
+
+