/*
* Copyright (c) 2009-2013, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google, Inc. nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "protocol.h"
#include "transport.h"
#define STATE_OFFLINE 0
#define STATE_COMMAND 1
#define STATE_COMPLETE 2
#define STATE_ERROR 3
struct fastboot_cmd {
struct fastboot_cmd *next;
const char *prefix;
unsigned prefix_len;
void (*execute)(struct protocol_handle *phandle, const char *arg);
};
struct fastboot_var {
struct fastboot_var *next;
const char *name;
const char *value;
};
static struct fastboot_cmd *cmdlist;
void fastboot_register(const char *prefix,
void (*phandle)(struct protocol_handle *phandle, const char *arg))
{
struct fastboot_cmd *cmd;
cmd = malloc(sizeof(*cmd));
if (cmd) {
cmd->prefix = prefix;
cmd->prefix_len = strlen(prefix);
cmd->execute = phandle;
cmd->next = cmdlist;
cmdlist = cmd;
}
}
static struct fastboot_var *varlist;
void fastboot_publish(const char *name, const char *value)
{
struct fastboot_var *var;
var = malloc(sizeof(*var));
if (var) {
var->name = name;
var->value = value;
var->next = varlist;
varlist = var;
}
}
const char *fastboot_getvar(const char *name)
{
struct fastboot_var *var;
for (var = varlist; var; var = var->next) {
if (!strcmp(var->name, name)) {
return var->value;
}
}
return "";
}
int protocol_handle_download(struct protocol_handle *phandle, size_t len)
{
return transport_handle_download(phandle->transport_handle, len);
}
static ssize_t protocol_handle_write(struct protocol_handle *phandle,
char *buffer, size_t len)
{
return transport_handle_write(phandle->transport_handle, buffer, len);
}
static void fastboot_ack(struct protocol_handle *phandle, const char *code,
const char *reason)
{
char response[64];
if (phandle->state != STATE_COMMAND)
return;
if (reason == 0)
reason = "";
snprintf(response, 64, "%s%s", code, reason);
phandle->state = STATE_COMPLETE;
protocol_handle_write(phandle, response, strlen(response));
}
void fastboot_fail(struct protocol_handle *phandle, const char *reason)
{
fastboot_ack(phandle, "FAIL", reason);
}
void fastboot_okay(struct protocol_handle *phandle, const char *info)
{
fastboot_ack(phandle, "OKAY", info);
}
void fastboot_data(struct protocol_handle *phandle, size_t len)
{
char response[64];
ssize_t ret;
snprintf(response, 64, "DATA%08zx", len);
ret = protocol_handle_write(phandle, response, strlen(response));
if (ret < 0)
return;
}
void protocol_handle_command(struct protocol_handle *phandle, char *buffer)
{
D(INFO,"fastboot: %s\n", buffer);
struct fastboot_cmd *cmd;
for (cmd = cmdlist; cmd; cmd = cmd->next) {
if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
continue;
phandle->state = STATE_COMMAND;
cmd->execute(phandle, buffer + cmd->prefix_len);
if (phandle->state == STATE_COMMAND)
fastboot_fail(phandle, "unknown reason");
return;
}
fastboot_fail(phandle, "unknown command");
}
struct protocol_handle *create_protocol_handle(struct transport_handle *thandle)
{
struct protocol_handle *phandle;
phandle = calloc(sizeof(struct protocol_handle), 1);
phandle->transport_handle = thandle;
phandle->state = STATE_OFFLINE;
phandle->download_fd = -1;
pthread_mutex_init(&phandle->lock, NULL);
return phandle;
}
int protocol_get_download(struct protocol_handle *phandle)
{
int fd;
pthread_mutex_lock(&phandle->lock);
fd = phandle->download_fd;
phandle->download_fd = -1;
pthread_mutex_unlock(&phandle->lock);
return fd;
}