fastbootd: userspace implementation of the fastboot device-side protocol
Initial commit of fastbootd. A few commands work, but not
fully functional yet.
Change-Id: I589dee7b327b4460e94b4434aaf9bcf780faa839
diff --git a/fastbootd/transport.c b/fastbootd/transport.c
new file mode 100644
index 0000000..01a5a8a
--- /dev/null
+++ b/fastbootd/transport.c
@@ -0,0 +1,149 @@
+/*
+ * 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 == NULL) {
+ D(ERR, "mmap(%u) 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=%d %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;
+ 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);
+}