blob: 1beabc2634ab045587b1ba2c1d0b9361840bf587 [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 "qmidev.h"
#include "qmimsg.h"
#include "qrb.h"
#include "util.h"
/* This will move to qmi.h once that exists and we add multiple services */
#define QMI_SERVICE_CTL 0x00
/* CTL is always client zero. */
#define QMI_CTL_CLIENT 0x00
enum {
QMICTL_MSG_GET_CID = 0x0022,
QMICTL_MSG_RELEASE_CID = 0x0023
};
#define QMI_TLV_ID 0x01
#define QMI_TLV_RESULT 0x02
static struct qmimsg *qmimsg_ctl_new(uint16_t transaction, uint16_t message)
{
struct qmimsg *msg;
if (qmimsg_new(0, QMI_SERVICE_CTL, QMI_CTL_CLIENT,
0, transaction, message,
&msg))
return NULL;
return msg;
}
struct qmi_cid {
uint8_t service;
uint8_t client;
};
struct get_cid_ctx {
qmidev *qmidev;
qmictl_get_cid_response_fn cb;
void *context;
uint8_t service;
};
static void get_cid_cb(struct qrb *qrb, struct qmimsg *msg)
{
struct get_cid_ctx *ctx = qrb_priv(qrb);
assert(ctx);
struct qmi_cid id;
struct { uint16_t status; uint16_t error; } result;
if (qmimsg_tlv_get(msg, QMI_TLV_RESULT, sizeof(result), &result) ||
result.status != 0) {
goto fail;
}
if (qmimsg_tlv_get(msg, QMI_TLV_ID, sizeof(id), &id) ||
id.service != ctx->service) {
goto fail;
}
ctx->cb(ctx->qmidev, ctx->context, 0, id.client);
goto out;
fail:
ctx->cb(ctx->qmidev, ctx->context, -1, 0);
out:
xfree(ctx);
qrb_set_priv(qrb, NULL);
}
int qmictl_get_cid(struct qmidev *qmidev, uint8_t service,
qmictl_get_cid_response_fn callback, void *context)
{
struct qmimsg *msg;
struct qrb *qrb;
struct get_cid_ctx *ctx = xmalloc(sizeof(*ctx));
uint16_t tid = 0x0001; /* TODO: ACtually allocate a tid. */
msg = qmimsg_ctl_new(tid, QMICTL_MSG_GET_CID);
qmimsg_tlv_add(msg, QMI_TLV_ID, sizeof(service), &service);
qmidev_send(qmidev, msg);
ctx->qmidev = qmidev;
ctx->cb = callback;
ctx->context = context;
ctx->service = service;
qrb = qrb_alloc(QMI_SERVICE_CTL, QMI_CTL_CLIENT, tid, 0, get_cid_cb, ctx);
qmidev_listen(qmidev, qrb);
return 0;
}
struct release_cid_ctx {
qmidev *qmidev;
qmidev_response_fn cb;
void *context;
struct qmi_cid id;
};
static void release_cid_cb(struct qrb *qrb, struct qmimsg *msg)
{
struct release_cid_ctx *ctx = qrb_priv(qrb);
assert(ctx);
struct qmi_cid id;
struct { uint16_t status; uint16_t error; } result;
if (qmimsg_tlv_get(msg, QMI_TLV_RESULT, sizeof(result), &result) ||
result.status != 0) {
goto fail;
}
if (qmimsg_tlv_get(msg, QMI_TLV_ID, sizeof(id), &id) ||
id.service != ctx->id.service ||
id.client != ctx->id.client) {
goto fail;
}
ctx->cb(ctx->qmidev, ctx->context, 0);
goto out;
fail:
ctx->cb(ctx->qmidev, ctx->context, -1);
out:
xfree(ctx);
qrb_set_priv(qrb, NULL);
}
int qmictl_release_cid(struct qmidev *qmidev, uint8_t service, uint8_t client,
qmidev_response_fn callback, void *context)
{
struct qmimsg *msg;
struct qrb *qrb;
struct release_cid_ctx *ctx = xmalloc(sizeof(*ctx));
struct qmi_cid id;
uint16_t tid = 0x0001; /* TODO: Allocate a transaction ID. */
msg = qmimsg_ctl_new(tid, QMICTL_MSG_RELEASE_CID);
id.service = service;
id.client = client;
qmimsg_tlv_add(msg, QMI_TLV_ID, sizeof(id), &id);
qmidev_send(qmidev, msg);
ctx->qmidev = qmidev;
ctx->cb = callback;
ctx->context = context;
ctx->id.service = service;
ctx->id.client = client;
qrb = qrb_alloc(QMI_SERVICE_CTL, QMI_CTL_CLIENT, tid, 0, release_cid_cb, ctx);
qmidev_listen(qmidev, qrb);
return 0;
}