greybus: connection: set request handlers at creation
[cascardo/linux.git] / drivers / staging / greybus / svc.c
index 3e760e6..feadb62 100644 (file)
@@ -7,6 +7,7 @@
  * Released under the GPLv2 only.
  */
 
+#include <linux/input.h>
 #include <linux/workqueue.h>
 
 #include "greybus.h"
@@ -15,6 +16,7 @@
 #define CPORT_FLAGS_CSD_N       BIT(1)
 #define CPORT_FLAGS_CSV_N       BIT(2)
 
+#define SVC_KEY_ARA_BUTTON     KEY_A
 
 struct gb_svc_deferred_request {
        struct work_struct work;
@@ -40,9 +42,38 @@ static ssize_t ap_intf_id_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(ap_intf_id);
 
+
+// FIXME
+// This is a hack, we need to do this "right" and clean the interface up
+// properly, not just forcibly yank the thing out of the system and hope for the
+// best.  But for now, people want their modules to come out without having to
+// throw the thing to the ground or get out a screwdriver.
+static ssize_t intf_eject_store(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t len)
+{
+       struct gb_svc *svc = to_gb_svc(dev);
+       unsigned short intf_id;
+       int ret;
+
+       ret = kstrtou16(buf, 10, &intf_id);
+       if (ret < 0)
+               return ret;
+
+       dev_warn(dev, "Forcibly trying to eject interface %d\n", intf_id);
+
+       ret = gb_svc_intf_eject(svc, intf_id);
+       if (ret < 0)
+               return ret;
+
+       return len;
+}
+static DEVICE_ATTR_WO(intf_eject);
+
 static struct attribute *svc_attrs[] = {
        &dev_attr_endo_id.attr,
        &dev_attr_ap_intf_id.attr,
+       &dev_attr_intf_eject.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(svc);
@@ -69,6 +100,23 @@ int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
 }
 EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
 
+int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id)
+{
+       struct gb_svc_intf_eject_request request;
+
+       request.intf_id = intf_id;
+
+       /*
+        * The pulse width for module release in svc is long so we need to
+        * increase the timeout so the operation will not return to soon.
+        */
+       return gb_operation_sync_timeout(svc->connection,
+                                        GB_SVC_TYPE_INTF_EJECT, &request,
+                                        sizeof(request), NULL, 0,
+                                        GB_SVC_EJECT_TIME);
+}
+EXPORT_SYMBOL_GPL(gb_svc_intf_eject);
+
 int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector,
                        u32 *value)
 {
@@ -285,22 +333,42 @@ static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id)
        }
 }
 
-int gb_svc_link_config(struct gb_svc *svc, u8 intf_id,
-                      unsigned int burst, unsigned int gear,
-                      unsigned int nlanes, unsigned int flags)
+int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series,
+                              u8 tx_mode, u8 tx_gear, u8 tx_nlanes,
+                              u8 rx_mode, u8 rx_gear, u8 rx_nlanes,
+                              u8 flags, u32 quirks)
 {
-       struct gb_svc_link_config_request request;
+       struct gb_svc_intf_set_pwrm_request request;
+       struct gb_svc_intf_set_pwrm_response response;
+       int ret;
 
        request.intf_id = intf_id;
-       request.burst = burst;
-       request.gear = gear;
-       request.nlanes = nlanes;
+       request.hs_series = hs_series;
+       request.tx_mode = tx_mode;
+       request.tx_gear = tx_gear;
+       request.tx_nlanes = tx_nlanes;
+       request.rx_mode = rx_mode;
+       request.rx_gear = rx_gear;
+       request.rx_nlanes = rx_nlanes;
        request.flags = flags;
+       request.quirks = cpu_to_le32(quirks);
 
-       return gb_operation_sync(svc->connection, GB_SVC_TYPE_LINK_CONFIG,
-                                &request, sizeof(request), NULL, 0);
+       ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM,
+                               &request, sizeof(request),
+                               &response, sizeof(response));
+       if (ret < 0)
+               return ret;
+
+       return le16_to_cpu(response.result_code);
 }
-EXPORT_SYMBOL_GPL(gb_svc_link_config);
+EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode);
+
+int gb_svc_ping(struct gb_svc *svc)
+{
+       return gb_operation_sync(svc->connection, GB_SVC_TYPE_PING,
+                                NULL, 0, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_svc_ping);
 
 static int gb_svc_version_request(struct gb_operation *op)
 {
@@ -324,15 +392,15 @@ static int gb_svc_version_request(struct gb_operation *op)
                return -ENOTSUPP;
        }
 
-       connection->module_major = request->major;
-       connection->module_minor = request->minor;
+       svc->protocol_major = request->major;
+       svc->protocol_minor = request->minor;
 
        if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL))
                return -ENOMEM;
 
        response = op->response->payload;
-       response->major = connection->module_major;
-       response->minor = connection->module_minor;
+       response->major = svc->protocol_major;
+       response->minor = svc->protocol_minor;
 
        return 0;
 }
@@ -361,6 +429,13 @@ static int gb_svc_hello(struct gb_operation *op)
                return ret;
        }
 
+       ret = input_register_device(svc->input);
+       if (ret) {
+               dev_err(&svc->dev, "failed to register input: %d\n", ret);
+               device_del(&svc->dev);
+               return ret;
+       }
+
        return 0;
 }
 
@@ -425,6 +500,12 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation)
                return;
        }
 
+       intf->ddbl1_manufacturer_id = le32_to_cpu(request->data.ddbl1_mfr_id);
+       intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id);
+       intf->vendor_id = le32_to_cpu(request->data.ara_vend_id);
+       intf->product_id = le32_to_cpu(request->data.ara_prod_id);
+       intf->serial_number = le64_to_cpu(request->data.serial_number);
+
        ret = gb_svc_read_and_clear_module_boot_status(intf);
        if (ret) {
                dev_err(&svc->dev, "failed to clear boot status of interface %u: %d\n",
@@ -432,11 +513,6 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation)
                goto destroy_interface;
        }
 
-       intf->ddbl1_manufacturer_id = le32_to_cpu(request->data.ddbl1_mfr_id);
-       intf->ddbl1_product_id = le32_to_cpu(request->data.ddbl1_prod_id);
-       intf->vendor_id = le32_to_cpu(request->data.ara_vend_id);
-       intf->product_id = le32_to_cpu(request->data.ara_prod_id);
-
        /*
         * Create a device id for the interface:
         * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC
@@ -630,10 +706,58 @@ static int gb_svc_intf_reset_recv(struct gb_operation *op)
        return 0;
 }
 
-static int gb_svc_request_recv(u8 type, struct gb_operation *op)
+static int gb_svc_key_code_map(struct gb_svc *svc, u16 key_code, u16 *code)
+{
+       switch (key_code) {
+       case GB_KEYCODE_ARA:
+               *code = SVC_KEY_ARA_BUTTON;
+               break;
+       default:
+               dev_warn(&svc->dev, "unknown keycode received: %u\n", key_code);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int gb_svc_key_event_recv(struct gb_operation *op)
+{
+       struct gb_svc *svc = op->connection->private;
+       struct gb_message *request = op->request;
+       struct gb_svc_key_event_request *key;
+       u16 code;
+       u8 event;
+       int ret;
+
+       if (request->payload_size < sizeof(*key)) {
+               dev_warn(&svc->dev, "short key request received (%zu < %zu)\n",
+                        request->payload_size, sizeof(*key));
+               return -EINVAL;
+       }
+
+       key = request->payload;
+
+       ret = gb_svc_key_code_map(svc, le16_to_cpu(key->key_code), &code);
+       if (ret < 0)
+               return ret;
+
+       event = key->key_event;
+       if ((event != GB_SVC_KEY_PRESSED) && (event != GB_SVC_KEY_RELEASED)) {
+               dev_warn(&svc->dev, "unknown key event received: %u\n", event);
+               return -EINVAL;
+       }
+
+       input_report_key(svc->input, code, (event == GB_SVC_KEY_PRESSED));
+       input_sync(svc->input);
+
+       return 0;
+}
+
+static int gb_svc_request_handler(struct gb_operation *op)
 {
        struct gb_connection *connection = op->connection;
        struct gb_svc *svc = connection->private;
+       u8 type = op->type;
        int ret = 0;
 
        /*
@@ -684,12 +808,42 @@ static int gb_svc_request_recv(u8 type, struct gb_operation *op)
                return gb_svc_intf_hot_unplug_recv(op);
        case GB_SVC_TYPE_INTF_RESET:
                return gb_svc_intf_reset_recv(op);
+       case GB_SVC_TYPE_KEY_EVENT:
+               return gb_svc_key_event_recv(op);
        default:
                dev_warn(&svc->dev, "unsupported request 0x%02x\n", type);
                return -EINVAL;
        }
 }
 
+static struct input_dev *gb_svc_input_create(struct gb_svc *svc)
+{
+       struct input_dev *input_dev;
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               return ERR_PTR(-ENOMEM);
+
+       input_dev->name = dev_name(&svc->dev);
+       svc->input_phys = kasprintf(GFP_KERNEL, "greybus-%s/input0",
+                                   input_dev->name);
+       if (!svc->input_phys)
+               goto err_free_input;
+
+       input_dev->phys = svc->input_phys;
+       input_dev->dev.parent = &svc->dev;
+
+       input_set_drvdata(input_dev, svc);
+
+       input_set_capability(input_dev, EV_KEY, SVC_KEY_ARA_BUTTON);
+
+       return input_dev;
+
+err_free_input:
+       input_free_device(svc->input);
+       return ERR_PTR(-ENOMEM);
+}
+
 static void gb_svc_release(struct device *dev)
 {
        struct gb_svc *svc = to_gb_svc(dev);
@@ -698,6 +852,7 @@ static void gb_svc_release(struct device *dev)
                gb_connection_destroy(svc->connection);
        ida_destroy(&svc->device_id_map);
        destroy_workqueue(svc->wq);
+       kfree(svc->input_phys);
        kfree(svc);
 }
 
@@ -733,17 +888,30 @@ struct gb_svc *gb_svc_create(struct gb_host_device *hd)
        svc->state = GB_SVC_STATE_RESET;
        svc->hd = hd;
 
+       svc->input = gb_svc_input_create(svc);
+       if (IS_ERR(svc->input)) {
+               dev_err(&svc->dev, "failed to create input device: %ld\n",
+                       PTR_ERR(svc->input));
+               goto err_put_device;
+       }
+
        svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID,
-                                                       GREYBUS_PROTOCOL_SVC);
-       if (!svc->connection) {
-               dev_err(&svc->dev, "failed to create connection\n");
-               put_device(&svc->dev);
-               return NULL;
+                                               gb_svc_request_handler);
+       if (IS_ERR(svc->connection)) {
+               dev_err(&svc->dev, "failed to create connection: %ld\n",
+                               PTR_ERR(svc->connection));
+               goto err_free_input;
        }
 
        svc->connection->private = svc;
 
        return svc;
+
+err_free_input:
+       input_free_device(svc->input);
+err_put_device:
+       put_device(&svc->dev);
+       return NULL;
 }
 
 int gb_svc_add(struct gb_svc *svc)
@@ -755,7 +923,7 @@ int gb_svc_add(struct gb_svc *svc)
         * is added from the connection request handler when enough
         * information has been received.
         */
-       ret = gb_connection_init(svc->connection);
+       ret = gb_connection_enable(svc->connection);
        if (ret)
                return ret;
 
@@ -764,13 +932,16 @@ int gb_svc_add(struct gb_svc *svc)
 
 void gb_svc_del(struct gb_svc *svc)
 {
+       gb_connection_disable(svc->connection);
+
        /*
-        * The SVC device may have been registered from the request handler.
+        * The SVC device and input device may have been registered
+        * from the request handler.
         */
-       if (device_is_registered(&svc->dev))
+       if (device_is_registered(&svc->dev)) {
+               input_unregister_device(svc->input);
                device_del(&svc->dev);
-
-       gb_connection_exit(svc->connection);
+       }
 
        flush_workqueue(svc->wq);
 }
@@ -779,33 +950,3 @@ void gb_svc_put(struct gb_svc *svc)
 {
        put_device(&svc->dev);
 }
-
-static int gb_svc_connection_init(struct gb_connection *connection)
-{
-       struct gb_svc *svc = connection->private;
-
-       dev_dbg(&svc->dev, "%s\n", __func__);
-
-       return 0;
-}
-
-static void gb_svc_connection_exit(struct gb_connection *connection)
-{
-       struct gb_svc *svc = connection->private;
-
-       dev_dbg(&svc->dev, "%s\n", __func__);
-}
-
-static struct gb_protocol svc_protocol = {
-       .name                   = "svc",
-       .id                     = GREYBUS_PROTOCOL_SVC,
-       .major                  = GB_SVC_VERSION_MAJOR,
-       .minor                  = GB_SVC_VERSION_MINOR,
-       .connection_init        = gb_svc_connection_init,
-       .connection_exit        = gb_svc_connection_exit,
-       .request_recv           = gb_svc_request_recv,
-       .flags                  = GB_PROTOCOL_SKIP_CONTROL_CONNECTED |
-                                 GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED |
-                                 GB_PROTOCOL_SKIP_VERSION,
-};
-gb_builtin_protocol_driver(svc_protocol);