| /* |
| * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* |
| * qmidms: QMI calls on the DMS (Device Management Service) interface |
| * |
| * Sources used in writing this file (see README for links): |
| * [GobiNet]/QMI.c |
| * [GobiNet]/QMIDevice.c |
| * [cros-kerne]/drivers/net/usb/gobi/qmi.c |
| * [cros-kerne]/drivers/net/usb/gobi/qmidevice.c |
| */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <glib.h> |
| |
| #include <libqmi.h> |
| |
| #include "error.h" |
| #include "qmi.h" |
| #include "qmidev.h" |
| #include "qmimsg.h" |
| |
| struct qmidms_priv { |
| struct event operating_mode; |
| }; |
| |
| static void operating_mode_changed(struct qmidev *qmidev, |
| void *user_callback, void *user_context, |
| uint16_t length, void *value) |
| { |
| qmidev_power_callback_fn callback = user_callback; |
| qmidev_power_state state; |
| int status; |
| |
| if (length == 1) { |
| state = *(uint8_t *)value; |
| status = 0; |
| } else { |
| state = 0; |
| status = QMI_ERR_MESSAGE_INVALID; |
| } |
| |
| callback(qmidev, user_context, status, state); |
| } |
| |
| static void client_create(struct qmidev *qmidev, struct client *client) |
| { |
| struct qmidms_priv *priv = g_slice_new(struct qmidms_priv); |
| event_init(&priv->operating_mode, |
| QMIDMS_EVENT_OPERATING_MODE, |
| &operating_mode_changed); |
| client_set_priv(client, priv); |
| |
| /* unused */ |
| qmidev = qmidev; |
| } |
| |
| static void client_destroy(struct qmidev *qmidev, struct client *client) |
| { |
| struct qmidms_priv *priv = client_priv(client); |
| g_slice_free(struct qmidms_priv, priv); |
| |
| /* unused */ |
| qmidev = qmidev; |
| } |
| |
| struct service qmidms_service = { |
| .service_id = QMI_SVC_DMS, |
| .event_msg_id = QMIDMS_MSG_EVENT, |
| .create_fn = &client_create, |
| .destroy_fn = &client_destroy |
| };enum { |
| QMIDMS_TLV_GET_IDS_ESN = 0x10, |
| QMIDMS_TLV_GET_IDS_IMEI, |
| QMIDMS_TLV_GET_IDS_MEID |
| }; |
| |
| struct get_ids_context { |
| qmidev_get_ids_response_fn callback; |
| void *context; |
| }; |
| |
| static void get_ids_response(struct qmidev *qmidev, |
| void *data, |
| int result, |
| struct qmimsg *message) |
| { |
| struct get_ids_context *context = data; |
| char *esn = NULL, *imei = NULL, *meid = NULL; |
| |
| assert(qmidev); |
| assert(data); |
| |
| if (result) |
| goto out; |
| |
| qmi_tlv_strdup(message, QMIDMS_TLV_GET_IDS_ESN, NULL, &esn); |
| qmi_tlv_strdup(message, QMIDMS_TLV_GET_IDS_IMEI, NULL, &imei); |
| qmi_tlv_strdup(message, QMIDMS_TLV_GET_IDS_MEID, NULL, &meid); |
| |
| out: |
| context->callback(qmidev, context->context, result, |
| esn, imei, meid); |
| g_free(esn); |
| g_free(imei); |
| g_free(meid); |
| g_slice_free(struct get_ids_context, context); |
| } |
| |
| int qmidev_get_ids(struct qmidev *qmidev, |
| qmidev_get_ids_response_fn caller_callback, |
| void *caller_context) |
| { |
| struct get_ids_context *context; |
| int result; |
| |
| context = g_slice_new(struct get_ids_context); |
| context->callback = caller_callback; |
| context->context = caller_context; |
| |
| result = qmidev_request(qmidev, &qmidms_service, |
| QMIDMS_MSG_GET_IDS, |
| NULL, NULL, |
| &get_ids_response, context); |
| if (result) |
| 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 get_power_response(struct qmidev *qmidev, |
| void *data, |
| int result, |
| struct qmimsg *message) |
| { |
| struct get_power_context *context = data; |
| uint8_t mode; |
| |
| assert(qmidev); |
| assert(data); |
| |
| if (result) |
| goto out; |
| |
| result = qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(mode), &mode); |
| if (result) |
| goto out; |
| |
| 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) |
| { |
| struct get_power_context *context; |
| int result; |
| |
| context = g_slice_new(struct get_power_context); |
| context->caller_callback = caller_callback; |
| context->caller_context = caller_context; |
| |
| result = qmidev_request(qmidev, &qmidms_service, |
| QMIDMS_MSG_GET_OPERATING_MODE, |
| NULL, NULL, |
| &get_power_response, context); |
| if (result) |
| g_slice_free(struct get_power_context, context); |
| return result; |
| } |
| |
| static int set_power_request(void *data, |
| struct qmimsg *message) |
| { |
| uint8_t *mode = data; |
| return qmimsg_tlv_add(message, QMI_TLV_VALUE, sizeof(*mode), mode); |
| } |
| |
| int qmidev_set_power(struct qmidev *qmidev, |
| qmidev_power_state state, |
| qmidev_response_fn callback, |
| void *context) |
| { |
| uint8_t mode = state; |
| |
| return qmidev_void_request(qmidev, &qmidms_service, |
| QMIDMS_MSG_SET_OPERATING_MODE, |
| &set_power_request, &mode, |
| callback, context); |
| } |
| |
| |
| |
| int qmidev_set_power_callback(struct qmidev *qmidev, |
| qmidev_power_callback_fn callback, |
| void *context, |
| qmidev_response_fn set_callback, |
| void *set_context) |
| { |
| struct client *client = qmidev_default_client(qmidev, &qmidms_service); |
| struct qmidms_priv *priv = client_priv(client); |
| return qmidev_set_callback(qmidev, client, &priv->operating_mode, |
| callback, context, |
| set_callback, set_context); |
| } |