/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include "debug.h"
#include "protocol.h"
#include "transport.h"
#define COMMAND_BUF_SIZE 64
ssize_t transport_handle_write(struct transport_handle *thandle, char *buffer, size_t len)
{
return thandle->transport->write(thandle, buffer, len);
}
void transport_handle_close(struct transport_handle *thandle)
{
thandle->transport->close(thandle);
}
int transport_handle_download(struct transport_handle *thandle, size_t len)
{
ssize_t ret;
size_t n = 0;
int fd;
// TODO: move out of /dev
char tempname[] = "/dev/fastboot_download_XXXXXX";
char *buffer;
fd = mkstemp(tempname);
if (fd < 0)
return -1;
unlink(tempname);
ftruncate(fd, len);
buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (buffer == MAP_FAILED) {
D(ERR, "mmap(%zu) failed: %d %s", len, errno, strerror(errno));
goto err;
}
while (n < len) {
ret = thandle->transport->read(thandle, buffer + n, len - n);
if (ret <= 0) {
D(WARN, "transport read failed, ret=%zd %s", ret, strerror(-ret));
break;
}
n += ret;
}
munmap(buffer, len);
if (n != len)
goto err;
return fd;
err:
close(fd);
transport_handle_close(thandle);
return -1;
}
static void *transport_data_thread(void *arg)
{
struct transport_handle *thandle = arg;
struct protocol_handle *phandle = create_protocol_handle(thandle);
while (!thandle->stopped) {
int ret;
char buffer[COMMAND_BUF_SIZE + 1];
D(VERBOSE, "transport_data_thread\n");
ret = thandle->transport->read(thandle, buffer, COMMAND_BUF_SIZE);
if (ret <= 0) {
D(DEBUG, "ret = %d\n", ret);
break;
}
if (ret > 0) {
buffer[ret] = 0;
//TODO: multiple threads
protocol_handle_command(phandle, buffer);
}
}
transport_handle_close(thandle);
free(thandle);
return NULL;
}
static void *transport_connect_thread(void *arg)
{
struct transport *transport = arg;
while (!transport->stopped) {
struct transport_handle *thandle;
pthread_t thread;
pthread_attr_t attr;
D(VERBOSE, "transport_connect_thread\n");
thandle = transport->connect(transport);
if (thandle == NULL) {
D(ERR, "transport connect failed\n");
sleep(1);
continue;
}
thandle->transport = transport;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &attr, transport_data_thread, thandle);
sleep(1);
}
return NULL;
}
void transport_register(struct transport *transport)
{
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &attr, transport_connect_thread, transport);
}