blob: d442d3e7c645958f0492569df39dcda9c4fc3258 [file] [log] [blame]
/*
* 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.
*/
/*
* qmictl: QMI calls on the CTL (control) service.
*
* 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 "qmictl.h"
#include <assert.h>
#include <errno.h>
#include <glib.h>
#include "error.h"
#include "qmi.h"
#include "qmidev.h"
#include "qmimsg.h"
#include "util.h"
/* CTL is always client zero. */
#define QMI_CTL_CLIENT 0x00
enum {
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
};
struct qmi_cid {
uint8_t service;
uint8_t client;
};
static int ctl_get_id(struct qmimsg *message, struct qmi_cid *id_out)
{
assert(message);
assert(id_out);
return qmimsg_tlv_get(message, QMI_TLV_VALUE, sizeof(*id_out), id_out);
}
struct get_cid_context {
qmictl_get_cid_response_fn callback;
void *context;
uint8_t service;
};
static void got_cid(struct qmidev *qmidev,
void *data,
int status,
struct qmimsg *message)
{
assert(qmidev);
assert(data);
assert(message);
struct get_cid_context *context = data;
struct qmi_cid id;
int result;
id.client = 0;
if (status) {
result = status;
goto out;
}
result = ctl_get_id(message, &id);
if (result)
goto out;
if (id.service != context->service) {
result = QMI_ERR_MESSAGE_INVALID;
goto out;
}
result = 0;
out:
context->callback(qmidev, context->context, result, id.client);
g_slice_free(struct get_cid_context, context);
}
int qmictl_get_cid(struct qmidev *qmidev, uint8_t service,
qmictl_get_cid_response_fn caller_callback,
void *caller_context)
{
int result;
struct get_cid_context *context = g_slice_new(struct get_cid_context);
context->callback = caller_callback;
context->context = caller_context;
context->service = service;
struct qmimsg *message;
result = qmidev_make_request(qmidev,
QMI_SVC_CTL,
QMICTL_MSG_GET_CLIENT_ID,
&message);
if (result)
goto fail;
qmimsg_tlv_add(message, QMI_TLV_VALUE, sizeof(service), &service);
result = qmidev_send_request(qmidev, message,
QMICTL_MSG_GET_CLIENT_ID,
&got_cid, context);
if (result)
goto fail;
return 0;
fail:
g_slice_free(struct get_cid_context, context);
return result;
}
struct release_cid_context {
qmidev_response_fn callback;
void *context;
struct qmi_cid id;
};
static void released_cid(struct qmidev *qmidev,
void *data,
int status,
struct qmimsg *message)
{
assert(qmidev);
assert(data);
assert(message);
struct release_cid_context *context = data;
struct qmi_cid id;
int result;
if (status) {
result = status;
goto out;
}
result = ctl_get_id(message, &id);
if (result)
goto out;
if (id.service != context->id.service ||
id.client != context->id.client) {
result = QMI_ERR_MESSAGE_INVALID;
goto out;
}
result = 0;
out:
context->callback(qmidev, context->context, result);
g_slice_free(struct release_cid_context, context);
}
int qmictl_release_cid(struct qmidev *qmidev, uint8_t service, uint8_t client,
qmidev_response_fn caller_callback,
void *caller_context)
{
int result;
struct release_cid_context *context =
g_slice_new(struct release_cid_context);
context->callback = caller_callback;
context->context = caller_context;
context->id.service = service;
context->id.client = client;
struct qmimsg *message;
result = qmidev_make_request(qmidev,
QMI_SVC_CTL,
QMICTL_MSG_RELEASE_CLIENT_ID,
&message);
if (result)
goto fail;
qmimsg_tlv_add(message, QMI_TLV_VALUE, sizeof(context->id), &context->id);
result = qmidev_send_request(qmidev, message,
QMICTL_MSG_RELEASE_CLIENT_ID,
&released_cid, context);
if (result)
goto fail;
return 0;
fail:
g_slice_free(struct release_cid_context, context);
return result;
}