qmidms: get/set operating mode ("power") requests

Implement requests to get and set the device's operating mode.

BUG=chromium-os:28019
TEST=manual, works

Change-Id: I24a42ad8e8c270dfa64eeb560856cdeb0eb555b9
diff --git a/src/qmi.h b/src/qmi.h
index 9f27801..cfca765 100644
--- a/src/qmi.h
+++ b/src/qmi.h
@@ -15,8 +15,14 @@
 
 struct qmimsg;
 
-#define QMI_CID_NONE ((uint8_t)0x00)
-#define QMI_CID_BROADCAST ((uint8_t)0xFF)
+enum {
+  QMI_CID_NONE = 0x00,
+  QMI_CID_BROADCAST = 0xFF
+};
+
+enum {
+  QMI_TLV_VALUE = 0x01
+};
 
 enum {
   /** Control service */
diff --git a/src/qmictl.c b/src/qmictl.c
index 7799b0c..d442d3e 100644
--- a/src/qmictl.c
+++ b/src/qmictl.c
@@ -30,12 +30,20 @@
 #define QMI_CTL_CLIENT 0x00
 
 enum {
-  QMICTL_MSG_GET_CID = 0x0022,
-  QMICTL_MSG_RELEASE_CID = 0x0023
+  QMICTL_MSG_SET_INSTANCE_ID = 0x0020,
+  QMICTL_MSG_GET_VERSION_INFO,
+  QMICTL_MSG_GET_CLIENT_ID,
+  QMICTL_MSG_RELEASE_CLIENT_ID,
+  QMICTL_MSG_REVOKE_CLIENT_ID,
+  QMICTL_MSG_INVALID_CLIENT_ID,
+  QMICTL_MSG_SET_DATA_FORMAT,
+  QMICTL_MSG_SYNC,
+  QMICTL_MSG_EVENT,
+  QMICTL_MSG_SET_POWER_SAVE_CONFIG,
+  QMICTL_MSG_SET_POWER_SAVE_MODE,
+  QMICTL_MSG_GET_POWER_SAVE_MODE
 };
 
-#define QMI_TLV_ID ((uint8_t)0x01)
-
 struct qmi_cid {
   uint8_t service;
   uint8_t client;
@@ -46,7 +54,7 @@
   assert(message);
   assert(id_out);
 
-  return qmimsg_tlv_get(message, QMI_TLV_ID, sizeof(*id_out), id_out);
+  return qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(*id_out), id_out);
 }
 
 struct get_cid_context {
@@ -103,15 +111,17 @@
   context->service  = service;
 
   struct qmimsg *message;
-  result = qmidev_make_request(qmidev, QMI_SVC_CTL, QMICTL_MSG_GET_CID,
+  result = qmidev_make_request(qmidev,
+                               QMI_SVC_CTL,
+                               QMICTL_MSG_GET_CLIENT_ID,
                                &message);
   if (result)
     goto fail;
 
-  qmimsg_tlv_add(message, QMI_TLV_ID, sizeof(service), &service);
+  qmimsg_tlv_add(message, QMI_TLV_VALUE, sizeof(service), &service);
 
   result = qmidev_send_request(qmidev, message,
-                               QMICTL_MSG_GET_CID,
+                               QMICTL_MSG_GET_CLIENT_ID,
                                &got_cid, context);
   if (result)
     goto fail;
@@ -178,15 +188,17 @@
   context->id.client  = client;
 
   struct qmimsg *message;
-  result = qmidev_make_request(qmidev, QMI_SVC_CTL, QMICTL_MSG_RELEASE_CID,
+  result = qmidev_make_request(qmidev,
+                               QMI_SVC_CTL,
+                               QMICTL_MSG_RELEASE_CLIENT_ID,
                                &message);
   if (result)
     goto fail;
 
-  qmimsg_tlv_add(message, QMI_TLV_ID, sizeof(context->id), &context->id);
+  qmimsg_tlv_add(message, QMI_TLV_VALUE, sizeof(context->id), &context->id);
 
   result = qmidev_send_request(qmidev, message,
-                               QMICTL_MSG_RELEASE_CID,
+                               QMICTL_MSG_RELEASE_CLIENT_ID,
                                &released_cid, context);
   if (result)
     goto fail;
diff --git a/src/qmidms.c b/src/qmidms.c
index ccc3f1d..6ac9d40 100644
--- a/src/qmidms.c
+++ b/src/qmidms.c
@@ -25,7 +25,9 @@
 #include "qmimsg.h"
 
 enum {
-  QMIDMS_MSG_GET_IDS = 37
+  QMIDMS_MSG_GET_IDS = 0x0025,
+  QMIDMS_MSG_GET_OPERATING_MODE = 0x002D,
+  QMIDMS_MSG_SET_OPERATING_MODE = 0x002E
 };
 
 enum {
@@ -40,13 +42,12 @@
 };
 
 static void got_ids(struct qmidev *qmidev,
-                   void *data,
-                   int status,
-                   struct qmimsg *message)
+                    void *data,
+                    int status,
+                    struct qmimsg *message)
 {
   assert(qmidev);
   assert(data);
-  assert(message);
 
   struct get_ids_context *context = data;
   int result;
@@ -101,3 +102,128 @@
   g_slice_free(struct get_ids_context, context);
   return result;
 }
+
+struct get_power_context {
+  qmidev_power_state_response_fn caller_callback;
+  void *caller_context;
+};
+
+static void got_power(struct qmidev *qmidev,
+                      void *data,
+                      int status,
+                      struct qmimsg *message)
+{
+  uint8_t mode;
+
+  assert(qmidev);
+  assert(data);
+
+  struct get_power_context *context = data;
+  int result;
+
+  if (status) {
+    result = status;
+    goto out;
+  }
+
+  result = qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(mode), &mode);
+  if (result)
+    goto out;
+
+  result = 0;
+
+out:
+  context->caller_callback(qmidev, context->caller_context, result, mode);
+  g_slice_free(struct get_power_context, context);
+}
+
+int qmidev_get_power(struct qmidev *qmidev,
+                     qmidev_power_state_response_fn caller_callback,
+                     void *caller_context)
+{
+  int result;
+
+  struct get_power_context *context = g_slice_new(struct get_power_context);
+  context->caller_callback = caller_callback;
+  context->caller_context  = caller_context;
+
+  struct qmimsg *message;
+  result = qmidev_make_request(qmidev,
+                               QMI_SVC_DMS,
+                               QMIDMS_MSG_GET_OPERATING_MODE,
+                               &message);
+  if (result)
+    goto fail;
+
+  result = qmidev_send_request(qmidev, message,
+                               QMIDMS_MSG_GET_OPERATING_MODE,
+                               &got_power, context);
+  if (result)
+    goto fail;
+
+  return 0;
+
+fail:
+  g_slice_free(struct get_power_context, context);
+  return result;
+}
+
+struct set_power_context {
+  qmidev_response_fn caller_callback;
+  void *caller_context;
+};
+
+static void set_power(struct qmidev *qmidev,
+                         void *data,
+                         int status,
+                         struct qmimsg *message)
+{
+  assert(qmidev);
+  assert(data);
+
+  struct set_power_context *context = data;
+  int result;
+
+  if (status) {
+    result = status;
+    goto out;
+  }
+
+  message = message;
+  result = 0;
+
+out:
+  context->caller_callback(qmidev, context->caller_context, result);
+  g_slice_free(struct set_power_context, context);
+}
+
+int qmidev_set_power(struct qmidev *qmidev,
+                     qmidev_power_state state,
+                     qmidev_response_fn caller_callback,
+                     void *caller_context)
+{
+  uint8_t mode = state;
+  int result;
+
+  struct set_power_context *context = g_slice_new(struct set_power_context);
+  context->caller_callback = caller_callback;
+  context->caller_context  = caller_context;
+
+  struct qmimsg *message;
+  result = qmidev_make_request(qmidev,
+                               QMI_SVC_DMS,
+                               QMIDMS_MSG_SET_OPERATING_MODE,
+                               &message);
+  qmimsg_tlv_add(message, QMI_TLV_VALUE, sizeof(mode), &mode);
+  result = qmidev_send_request(qmidev, message,
+                               QMIDMS_MSG_SET_OPERATING_MODE,
+                               &set_power, context);
+  if (result)
+    goto fail;
+
+  return 0;
+
+fail:
+  g_slice_free(struct set_power_context, context);
+  return result;
+}