[patch 13/32] greybus: HID driver

2016-09-16 Thread Greg KH

This driver implements the Greybus HID protocol.

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

--- /dev/null
+++ b/drivers/greybus/hid.c
@@ -0,0 +1,536 @@
+/*
+ * HID class driver for the Greybus.
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "greybus.h"
+
+/* Greybus HID device's structure */
+struct gb_hid {
+   struct gb_bundle *bundle;
+   struct gb_connection*connection;
+
+   struct hid_device   *hid;
+   struct gb_hid_desc_response hdesc;
+
+   unsigned long   flags;
+#define GB_HID_STARTED 0x01
+#define GB_HID_READ_PENDING0x04
+
+   unsigned intbufsize;
+   char*inbuf;
+};
+
+static DEFINE_MUTEX(gb_hid_open_mutex);
+
+/* Routines to get controller's information over greybus */
+
+/* Operations performed on greybus */
+static int gb_hid_get_desc(struct gb_hid *ghid)
+{
+   return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_DESC, NULL,
+0, >hdesc, sizeof(ghid->hdesc));
+}
+
+static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc)
+{
+   int ret;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC,
+NULL, 0, rdesc,
+le16_to_cpu(ghid->hdesc.wReportDescLength));
+
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_set_power(struct gb_hid *ghid, int type)
+{
+   int ret;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   ret = gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0);
+
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id,
+unsigned char *buf, int len)
+{
+   struct gb_hid_get_report_request request;
+   int ret;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   request.report_type = report_type;
+   request.report_id = report_id;
+
+   ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT,
+, sizeof(request), buf, len);
+
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id,
+unsigned char *buf, int len)
+{
+   struct gb_hid_set_report_request *request;
+   struct gb_operation *operation;
+   int ret, size = sizeof(*request) + len - 1;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   operation = gb_operation_create(ghid->connection,
+   GB_HID_TYPE_SET_REPORT, size, 0,
+   GFP_KERNEL);
+   if (!operation) {
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+   return -ENOMEM;
+   }
+
+   request = operation->request->payload;
+   request->report_type = report_type;
+   request->report_id = report_id;
+   memcpy(request->report, buf, len);
+
+   ret = gb_operation_request_send_sync(operation);
+   if (ret) {
+   dev_err(>connection->bundle->dev,
+   "failed to set report: %d\n", ret);
+   } else {
+   ret = len;
+   }
+
+   gb_operation_put(operation);
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_request_handler(struct gb_operation *op)
+{
+   struct gb_connection *connection = op->connection;
+   struct gb_hid *ghid = gb_connection_get_data(connection);
+   struct gb_hid_input_report_request *request = op->request->payload;
+
+   if (op->type != GB_HID_TYPE_IRQ_EVENT) {
+   dev_err(>bundle->dev,
+   "unsupported unsolicited request\n");
+   return -EINVAL;
+   }
+
+   if (test_bit(GB_HID_STARTED, >flags))
+   hid_input_report(ghid->hid, HID_INPUT_REPORT,
+request->report, op->request->payload_size, 1);
+
+   return 0;
+}
+
+static int gb_hid_report_len(struct hid_report *report)
+{
+   return ((report->size - 1) >> 3) + 1 +
+   report->device->report_enum[report->type].numbered;
+}
+
+static void gb_hid_find_max_report(struct hid_device *hid, unsigned int type,
+

[patch 13/32] greybus: HID driver

2016-09-16 Thread Greg KH

This driver implements the Greybus HID protocol.

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

--- /dev/null
+++ b/drivers/greybus/hid.c
@@ -0,0 +1,536 @@
+/*
+ * HID class driver for the Greybus.
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "greybus.h"
+
+/* Greybus HID device's structure */
+struct gb_hid {
+   struct gb_bundle *bundle;
+   struct gb_connection*connection;
+
+   struct hid_device   *hid;
+   struct gb_hid_desc_response hdesc;
+
+   unsigned long   flags;
+#define GB_HID_STARTED 0x01
+#define GB_HID_READ_PENDING0x04
+
+   unsigned intbufsize;
+   char*inbuf;
+};
+
+static DEFINE_MUTEX(gb_hid_open_mutex);
+
+/* Routines to get controller's information over greybus */
+
+/* Operations performed on greybus */
+static int gb_hid_get_desc(struct gb_hid *ghid)
+{
+   return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_DESC, NULL,
+0, >hdesc, sizeof(ghid->hdesc));
+}
+
+static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc)
+{
+   int ret;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC,
+NULL, 0, rdesc,
+le16_to_cpu(ghid->hdesc.wReportDescLength));
+
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_set_power(struct gb_hid *ghid, int type)
+{
+   int ret;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   ret = gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0);
+
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id,
+unsigned char *buf, int len)
+{
+   struct gb_hid_get_report_request request;
+   int ret;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   request.report_type = report_type;
+   request.report_id = report_id;
+
+   ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT,
+, sizeof(request), buf, len);
+
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id,
+unsigned char *buf, int len)
+{
+   struct gb_hid_set_report_request *request;
+   struct gb_operation *operation;
+   int ret, size = sizeof(*request) + len - 1;
+
+   ret = gb_pm_runtime_get_sync(ghid->bundle);
+   if (ret)
+   return ret;
+
+   operation = gb_operation_create(ghid->connection,
+   GB_HID_TYPE_SET_REPORT, size, 0,
+   GFP_KERNEL);
+   if (!operation) {
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+   return -ENOMEM;
+   }
+
+   request = operation->request->payload;
+   request->report_type = report_type;
+   request->report_id = report_id;
+   memcpy(request->report, buf, len);
+
+   ret = gb_operation_request_send_sync(operation);
+   if (ret) {
+   dev_err(>connection->bundle->dev,
+   "failed to set report: %d\n", ret);
+   } else {
+   ret = len;
+   }
+
+   gb_operation_put(operation);
+   gb_pm_runtime_put_autosuspend(ghid->bundle);
+
+   return ret;
+}
+
+static int gb_hid_request_handler(struct gb_operation *op)
+{
+   struct gb_connection *connection = op->connection;
+   struct gb_hid *ghid = gb_connection_get_data(connection);
+   struct gb_hid_input_report_request *request = op->request->payload;
+
+   if (op->type != GB_HID_TYPE_IRQ_EVENT) {
+   dev_err(>bundle->dev,
+   "unsupported unsolicited request\n");
+   return -EINVAL;
+   }
+
+   if (test_bit(GB_HID_STARTED, >flags))
+   hid_input_report(ghid->hid, HID_INPUT_REPORT,
+request->report, op->request->payload_size, 1);
+
+   return 0;
+}
+
+static int gb_hid_report_len(struct hid_report *report)
+{
+   return ((report->size - 1) >> 3) + 1 +
+   report->device->report_enum[report->type].numbered;
+}
+
+static void gb_hid_find_max_report(struct hid_device *hid, unsigned int type,
+  unsigned int *max)
+{
+