Initial Contribution
This commit is contained in:
commit
4f6e8d7a00
419 changed files with 98588 additions and 0 deletions
28
Android.mk
Normal file
28
Android.mk
Normal file
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# Copyright (C) 2008 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.
|
||||
#
|
||||
LOCAL_PATH := $(my-dir)
|
||||
|
||||
ifneq ($(TARGET_SIMULATOR),true)
|
||||
include $(call first-makefiles-under,$(LOCAL_PATH))
|
||||
else
|
||||
include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
|
||||
libcutils \
|
||||
liblog \
|
||||
libnetutils \
|
||||
libpixelflinger \
|
||||
libzipfile \
|
||||
))
|
||||
endif
|
20
README
Normal file
20
README
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
The system/ directory is intended for pieces of the world that are the
|
||||
core of the embedded linux platform at the heart of Android. These
|
||||
essential bits are required for basic booting, operation, and debugging.
|
||||
|
||||
They should not depend on libraries outside of system/... (some of them
|
||||
do currently -- they need to be updated or changed) and they should not
|
||||
be required for the simulator build.
|
||||
|
||||
The license for all these pieces should be clean (Apache2, BSD, or MIT).
|
||||
|
||||
Currently system/bluetooth/... and system/extra/... have some pieces
|
||||
with GPL/LGPL licensed code.
|
||||
|
||||
Assorted Issues:
|
||||
|
||||
- pppd depends on libutils for logging
|
||||
- pppd depends on libcrypt/libcrypto
|
||||
- init, linker, debuggerd, toolbox, usbd depend on libcutils
|
||||
- should probably rename bionic to libc
|
116
adb/Android.mk
Normal file
116
adb/Android.mk
Normal file
|
@ -0,0 +1,116 @@
|
|||
# Copyright 2005 The Android Open Source Project
|
||||
#
|
||||
# Android.mk for adb
|
||||
#
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# adb host tool
|
||||
# =========================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Default to a virtual (sockets) usb interface
|
||||
USB_SRCS :=
|
||||
EXTRA_SRCS :=
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
USB_SRCS := usb_linux.c
|
||||
EXTRA_SRCS := get_my_path_linux.c
|
||||
LOCAL_LDLIBS += -lrt -lncurses -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),darwin)
|
||||
USB_SRCS := usb_osx.c
|
||||
EXTRA_SRCS := get_my_path_darwin.c
|
||||
LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
USB_SRCS := usb_windows.c
|
||||
EXTRA_SRCS := get_my_path_windows.c
|
||||
EXTRA_STATIC_LIBS := AdbWinApi
|
||||
LOCAL_C_INCLUDES += /usr/include/w32api/ddk $(LOCAL_PATH)/../windows/usb/api
|
||||
ifneq ($(strip $(USE_CYGWIN)),)
|
||||
LOCAL_LDLIBS += -lpthread
|
||||
else
|
||||
LOCAL_LDLIBS += -lws2_32
|
||||
USE_SYSDEPS_WIN32 := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb.c \
|
||||
console.c \
|
||||
transport.c \
|
||||
transport_local.c \
|
||||
transport_usb.c \
|
||||
commandline.c \
|
||||
adb_client.c \
|
||||
sockets.c \
|
||||
services.c \
|
||||
file_sync_client.c \
|
||||
$(EXTRA_SRCS) \
|
||||
$(USB_SRCS) \
|
||||
shlist.c
|
||||
|
||||
|
||||
ifneq ($(USE_SYSDEPS_WIN32),)
|
||||
LOCAL_SRC_FILES += sysdeps_win32.c
|
||||
endif
|
||||
|
||||
LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter
|
||||
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY
|
||||
LOCAL_MODULE := adb
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS)
|
||||
ifeq ($(USE_SYSDEPS_WIN32),)
|
||||
LOCAL_STATIC_LIBRARIES += libcutils
|
||||
endif
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
kdbg.c
|
||||
LOCAL_MODULE := kdbg
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
endif
|
||||
|
||||
|
||||
# adbd device daemon
|
||||
# =========================================================
|
||||
ifeq ($(TARGET_ARCH),arm)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
adb.c \
|
||||
transport.c \
|
||||
transport_local.c \
|
||||
transport_usb.c \
|
||||
sockets.c \
|
||||
services.c \
|
||||
file_sync_service.c \
|
||||
jdwp_service.c \
|
||||
framebuffer_service.c \
|
||||
remount_service.c \
|
||||
usb_linux_client.c \
|
||||
log_service.c
|
||||
|
||||
|
||||
LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -DANDROID_GADGET=1 -Wall -Wno-unused-parameter
|
||||
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
|
||||
LOCAL_MODULE := adbd
|
||||
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
|
||||
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libcutils libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
endif
|
402
adb/adb.h
Normal file
402
adb/adb.h
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef __ADB_H
|
||||
#define __ADB_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#define MAX_PAYLOAD 4096
|
||||
|
||||
#define A_SYNC 0x434e5953
|
||||
#define A_CNXN 0x4e584e43
|
||||
#define A_OPEN 0x4e45504f
|
||||
#define A_OKAY 0x59414b4f
|
||||
#define A_CLSE 0x45534c43
|
||||
#define A_WRTE 0x45545257
|
||||
|
||||
#define A_VERSION 0x01000000 // ADB protocol version
|
||||
|
||||
#define ADB_VERSION_MAJOR 1 // Used for help/version information
|
||||
#define ADB_VERSION_MINOR 0 // Used for help/version information
|
||||
|
||||
#define ADB_SERVER_VERSION 20 // Increment this when we want to force users to start a new adb server
|
||||
|
||||
typedef struct amessage amessage;
|
||||
typedef struct apacket apacket;
|
||||
typedef struct asocket asocket;
|
||||
typedef struct alistener alistener;
|
||||
typedef struct aservice aservice;
|
||||
typedef struct atransport atransport;
|
||||
typedef struct adisconnect adisconnect;
|
||||
typedef struct usb_handle usb_handle;
|
||||
|
||||
struct amessage {
|
||||
unsigned command; /* command identifier constant */
|
||||
unsigned arg0; /* first argument */
|
||||
unsigned arg1; /* second argument */
|
||||
unsigned data_length; /* length of payload (0 is allowed) */
|
||||
unsigned data_check; /* checksum of data payload */
|
||||
unsigned magic; /* command ^ 0xffffffff */
|
||||
};
|
||||
|
||||
struct apacket
|
||||
{
|
||||
apacket *next;
|
||||
|
||||
unsigned len;
|
||||
unsigned char *ptr;
|
||||
|
||||
amessage msg;
|
||||
unsigned char data[MAX_PAYLOAD];
|
||||
};
|
||||
|
||||
/* An asocket represents one half of a connection between a local and
|
||||
** remote entity. A local asocket is bound to a file descriptor. A
|
||||
** remote asocket is bound to the protocol engine.
|
||||
*/
|
||||
struct asocket {
|
||||
/* chain pointers for the local/remote list of
|
||||
** asockets that this asocket lives in
|
||||
*/
|
||||
asocket *next;
|
||||
asocket *prev;
|
||||
|
||||
/* the unique identifier for this asocket
|
||||
*/
|
||||
unsigned id;
|
||||
|
||||
/* the asocket we are connected to
|
||||
*/
|
||||
|
||||
asocket *peer;
|
||||
|
||||
/* For local asockets, the fde is used to bind
|
||||
** us to our fd event system. For remote asockets
|
||||
** these fields are not used.
|
||||
*/
|
||||
fdevent fde;
|
||||
int fd;
|
||||
|
||||
/* queue of apackets waiting to be written
|
||||
*/
|
||||
apacket *pkt_first;
|
||||
apacket *pkt_last;
|
||||
|
||||
/* enqueue is called by our peer when it has data
|
||||
** for us. It should return 0 if we can accept more
|
||||
** data or 1 if not. If we return 1, we must call
|
||||
** peer->ready() when we once again are ready to
|
||||
** receive data.
|
||||
*/
|
||||
int (*enqueue)(asocket *s, apacket *pkt);
|
||||
|
||||
/* ready is called by the peer when it is ready for
|
||||
** us to send data via enqueue again
|
||||
*/
|
||||
void (*ready)(asocket *s);
|
||||
|
||||
/* close is called by the peer when it has gone away.
|
||||
** we are not allowed to make any further calls on the
|
||||
** peer once our close method is called.
|
||||
*/
|
||||
void (*close)(asocket *s);
|
||||
|
||||
/* socket-type-specific extradata */
|
||||
void *extra;
|
||||
|
||||
/* A socket is bound to atransport */
|
||||
atransport *transport;
|
||||
};
|
||||
|
||||
|
||||
/* the adisconnect structure is used to record a callback that
|
||||
** will be called whenever a transport is disconnected (e.g. by the user)
|
||||
** this should be used to cleanup objects that depend on the
|
||||
** transport (e.g. remote sockets, listeners, etc...)
|
||||
*/
|
||||
struct adisconnect
|
||||
{
|
||||
void (*func)(void* opaque, atransport* t);
|
||||
void* opaque;
|
||||
adisconnect* next;
|
||||
adisconnect* prev;
|
||||
};
|
||||
|
||||
|
||||
/* a transport object models the connection to a remote device or emulator
|
||||
** there is one transport per connected device/emulator. a "local transport"
|
||||
** connects through TCP (for the emulator), while a "usb transport" through
|
||||
** USB (for real devices)
|
||||
**
|
||||
** note that kTransportHost doesn't really correspond to a real transport
|
||||
** object, it's a special value used to indicate that a client wants to
|
||||
** connect to a service implemented within the ADB server itself.
|
||||
*/
|
||||
typedef enum transport_type {
|
||||
kTransportUsb,
|
||||
kTransportLocal,
|
||||
kTransportAny,
|
||||
kTransportHost,
|
||||
} transport_type;
|
||||
|
||||
struct atransport
|
||||
{
|
||||
atransport *next;
|
||||
atransport *prev;
|
||||
|
||||
int (*read_from_remote)(apacket *p, atransport *t);
|
||||
int (*write_to_remote)(apacket *p, atransport *t);
|
||||
void (*close)(atransport *t);
|
||||
void (*kick)(atransport *t);
|
||||
|
||||
int fd;
|
||||
int transport_socket;
|
||||
fdevent transport_fde;
|
||||
int ref_count;
|
||||
unsigned sync_token;
|
||||
int connection_state;
|
||||
transport_type type;
|
||||
|
||||
/* usb handle or socket fd as needed */
|
||||
usb_handle *usb;
|
||||
int sfd;
|
||||
|
||||
/* used to identify transports for clients */
|
||||
char *serial;
|
||||
char *product;
|
||||
|
||||
/* a list of adisconnect callbacks called when the transport is kicked */
|
||||
int kicked;
|
||||
adisconnect disconnects;
|
||||
};
|
||||
|
||||
|
||||
/* A listener is an entity which binds to a local port
|
||||
** and, upon receiving a connection on that port, creates
|
||||
** an asocket to connect the new local connection to a
|
||||
** specific remote service.
|
||||
**
|
||||
** TODO: some listeners read from the new connection to
|
||||
** determine what exact service to connect to on the far
|
||||
** side.
|
||||
*/
|
||||
struct alistener
|
||||
{
|
||||
alistener *next;
|
||||
alistener *prev;
|
||||
|
||||
fdevent fde;
|
||||
int fd;
|
||||
|
||||
const char *local_name;
|
||||
const char *connect_to;
|
||||
atransport *transport;
|
||||
adisconnect disconnect;
|
||||
};
|
||||
|
||||
|
||||
void print_packet(const char *label, apacket *p);
|
||||
|
||||
asocket *find_local_socket(unsigned id);
|
||||
void install_local_socket(asocket *s);
|
||||
void remove_socket(asocket *s);
|
||||
void close_all_sockets(atransport *t);
|
||||
|
||||
#define LOCAL_CLIENT_PREFIX "emulator-"
|
||||
|
||||
asocket *create_local_socket(int fd);
|
||||
asocket *create_local_service_socket(const char *destination);
|
||||
|
||||
asocket *create_remote_socket(unsigned id, atransport *t);
|
||||
void connect_to_remote(asocket *s, const char *destination);
|
||||
void connect_to_smartsocket(asocket *s);
|
||||
|
||||
void fatal(const char *fmt, ...);
|
||||
void fatal_errno(const char *fmt, ...);
|
||||
|
||||
void handle_packet(apacket *p, atransport *t);
|
||||
void send_packet(apacket *p, atransport *t);
|
||||
|
||||
void get_my_path(char s[PATH_MAX]);
|
||||
int launch_server();
|
||||
int adb_main(int is_daemon);
|
||||
|
||||
|
||||
/* transports are ref-counted
|
||||
** get_device_transport does an acquire on your behalf before returning
|
||||
*/
|
||||
void init_transport_registration(void);
|
||||
int list_transports(char *buf, size_t bufsize);
|
||||
void update_transports(void);
|
||||
|
||||
asocket* create_device_tracker(void);
|
||||
|
||||
/* Obtain a transport from the available transports.
|
||||
** If state is != CS_ANY, only transports in that state are considered.
|
||||
** If serial is non-NULL then only the device with that serial will be chosen.
|
||||
** If no suitable transport is found, error is set.
|
||||
*/
|
||||
atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out);
|
||||
void add_transport_disconnect( atransport* t, adisconnect* dis );
|
||||
void remove_transport_disconnect( atransport* t, adisconnect* dis );
|
||||
void run_transport_disconnects( atransport* t );
|
||||
void kick_transport( atransport* t );
|
||||
|
||||
/* initialize a transport object's func pointers and state */
|
||||
int init_socket_transport(atransport *t, int s, int port);
|
||||
void init_usb_transport(atransport *t, usb_handle *usb);
|
||||
|
||||
/* for MacOS X cleanup */
|
||||
void close_usb_devices();
|
||||
|
||||
/* cause new transports to be init'd and added to the list */
|
||||
void register_socket_transport(int s, const char *serial, int port);
|
||||
void register_usb_transport(usb_handle *h, const char *serial);
|
||||
|
||||
int service_to_fd(const char *name);
|
||||
#if ADB_HOST
|
||||
asocket *host_service_to_socket(const char* name, const char *serial);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
int init_jdwp(void);
|
||||
asocket* create_jdwp_service_socket();
|
||||
asocket* create_jdwp_tracker_service_socket();
|
||||
int create_jdwp_connection_fd(int jdwp_pid);
|
||||
#endif
|
||||
|
||||
#if !ADB_HOST
|
||||
void framebuffer_service(int fd, void *cookie);
|
||||
void log_service(int fd, void *cookie);
|
||||
void remount_service(int fd, void *cookie);
|
||||
char * get_log_file_path(const char * log_name);
|
||||
#endif
|
||||
|
||||
/* packet allocator */
|
||||
apacket *get_apacket(void);
|
||||
void put_apacket(apacket *p);
|
||||
|
||||
int check_header(apacket *p);
|
||||
int check_data(apacket *p);
|
||||
|
||||
/* convenience wrappers around read/write that will retry on
|
||||
** EINTR and/or short read/write. Returns 0 on success, -1
|
||||
** on error or EOF.
|
||||
*/
|
||||
int readx(int fd, void *ptr, size_t len);
|
||||
int writex(int fd, const void *ptr, size_t len);
|
||||
|
||||
/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */
|
||||
|
||||
#define ADB_TRACE 1
|
||||
|
||||
/* IMPORTANT: if you change the following list, don't
|
||||
* forget to update the corresponding 'tags' table in
|
||||
* the adb_trace_init() function implemented in adb.c
|
||||
*/
|
||||
typedef enum {
|
||||
TRACE_ADB = 0,
|
||||
TRACE_SOCKETS,
|
||||
TRACE_PACKETS,
|
||||
TRACE_TRANSPORT,
|
||||
TRACE_RWX,
|
||||
TRACE_USB,
|
||||
TRACE_SYNC,
|
||||
TRACE_SYSDEPS,
|
||||
TRACE_JDWP,
|
||||
} AdbTrace;
|
||||
|
||||
#if ADB_TRACE
|
||||
|
||||
int adb_trace_mask;
|
||||
|
||||
void adb_trace_init(void);
|
||||
|
||||
# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
|
||||
|
||||
/* you must define TRACE_TAG before using this macro */
|
||||
#define D(...) \
|
||||
do { \
|
||||
if (ADB_TRACING) \
|
||||
fprintf(stderr, __VA_ARGS__ ); \
|
||||
} while (0)
|
||||
#else
|
||||
# define D(...) ((void)0)
|
||||
# define ADB_TRACING 0
|
||||
#endif
|
||||
|
||||
|
||||
/* set this to log to /data/adb/adb_<time>.txt on the device.
|
||||
* has no effect if the /data/adb/ directory does not exist.
|
||||
*/
|
||||
#define ADB_DEVICE_LOG 0
|
||||
|
||||
#if !TRACE_PACKETS
|
||||
#define print_packet(tag,p) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define ADB_PORT 5037
|
||||
#define ADB_LOCAL_TRANSPORT_PORT 5555
|
||||
|
||||
// Google's USB Vendor ID
|
||||
#define VENDOR_ID_GOOGLE 0x18d1
|
||||
// HTC's USB Vendor ID
|
||||
#define VENDOR_ID_HTC 0x0bb4
|
||||
|
||||
// products for VENDOR_ID_GOOGLE
|
||||
#define PRODUCT_ID_SOONER 0xd00d // Sooner bootloader
|
||||
#define PRODUCT_ID_SOONER_COMP 0xdeed // Sooner composite device
|
||||
|
||||
// products for VENDOR_ID_HTC
|
||||
#define PRODUCT_ID_DREAM 0x0c01 // Dream bootloader
|
||||
#define PRODUCT_ID_DREAM_COMP 0x0c02 // Dream composite device
|
||||
|
||||
void local_init();
|
||||
int local_connect(int port);
|
||||
|
||||
/* usb host/client interface */
|
||||
void usb_init();
|
||||
void usb_cleanup();
|
||||
int usb_write(usb_handle *h, const void *data, int len);
|
||||
int usb_read(usb_handle *h, void *data, int len);
|
||||
int usb_close(usb_handle *h);
|
||||
void usb_kick(usb_handle *h);
|
||||
|
||||
/* used for USB device detection */
|
||||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
|
||||
|
||||
unsigned host_to_le32(unsigned n);
|
||||
int adb_commandline(int argc, char **argv);
|
||||
|
||||
int connection_state(atransport *t);
|
||||
|
||||
#define CS_ANY -1
|
||||
#define CS_OFFLINE 0
|
||||
#define CS_BOOTLOADER 1
|
||||
#define CS_DEVICE 2
|
||||
#define CS_HOST 3
|
||||
#define CS_RECOVERY 4
|
||||
#define CS_ERROR 5
|
||||
|
||||
extern int HOST;
|
||||
|
||||
#define CHUNK_SIZE (64*1024)
|
||||
|
||||
int sendfailmsg(int fd, const char *reason);
|
||||
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
|
||||
|
||||
#endif
|
318
adb/adb_client.c
Normal file
318
adb/adb_client.c
Normal file
|
@ -0,0 +1,318 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <zipfile/zipfile.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_ADB
|
||||
#include "adb_client.h"
|
||||
|
||||
static transport_type __adb_transport = kTransportAny;
|
||||
static const char* __adb_serial = NULL;
|
||||
|
||||
void adb_set_transport(transport_type type, const char* serial)
|
||||
{
|
||||
__adb_transport = type;
|
||||
__adb_serial = serial;
|
||||
}
|
||||
|
||||
int adb_get_emulator_console_port(void)
|
||||
{
|
||||
const char* serial = __adb_serial;
|
||||
int port;
|
||||
|
||||
if (serial == NULL) {
|
||||
/* if no specific device was specified, we need to look at */
|
||||
/* the list of connected devices, and extract an emulator */
|
||||
/* name from it. two emulators is an error */
|
||||
char* tmp = adb_query("host:devices");
|
||||
char* p = tmp;
|
||||
if(!tmp) {
|
||||
printf("no emulator connected\n");
|
||||
return -1;
|
||||
}
|
||||
while (*p) {
|
||||
char* q = strchr(p, '\n');
|
||||
if (q != NULL)
|
||||
*q++ = 0;
|
||||
else
|
||||
q = p + strlen(p);
|
||||
|
||||
if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) {
|
||||
if (serial != NULL) { /* more than one emulator listed */
|
||||
free(tmp);
|
||||
return -2;
|
||||
}
|
||||
serial = p;
|
||||
}
|
||||
|
||||
p = q;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
if (serial == NULL)
|
||||
return -1; /* no emulator found */
|
||||
}
|
||||
else {
|
||||
if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0)
|
||||
return -1; /* not an emulator */
|
||||
}
|
||||
|
||||
serial += sizeof(LOCAL_CLIENT_PREFIX)-1;
|
||||
port = strtol(serial, NULL, 10);
|
||||
return port;
|
||||
}
|
||||
|
||||
static char __adb_error[256] = { 0 };
|
||||
|
||||
const char *adb_error(void)
|
||||
{
|
||||
return __adb_error;
|
||||
}
|
||||
|
||||
static int switch_socket_transport(int fd)
|
||||
{
|
||||
char service[64];
|
||||
char tmp[5];
|
||||
int len;
|
||||
|
||||
if (__adb_serial)
|
||||
snprintf(service, sizeof service, "host:transport:%s", __adb_serial);
|
||||
else {
|
||||
char* transport_type = "???";
|
||||
|
||||
switch (__adb_transport) {
|
||||
case kTransportUsb:
|
||||
transport_type = "transport-usb";
|
||||
break;
|
||||
case kTransportLocal:
|
||||
transport_type = "transport-local";
|
||||
break;
|
||||
case kTransportAny:
|
||||
transport_type = "transport-any";
|
||||
break;
|
||||
case kTransportHost:
|
||||
// no switch necessary
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(service, sizeof service, "host:%s", transport_type);
|
||||
}
|
||||
len = strlen(service);
|
||||
snprintf(tmp, sizeof tmp, "%04x", len);
|
||||
|
||||
if(writex(fd, tmp, 4) || writex(fd, service, len)) {
|
||||
strcpy(__adb_error, "write failure during connection");
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
D("Switch transport in progress\n");
|
||||
|
||||
if(adb_status(fd)) {
|
||||
adb_close(fd);
|
||||
D("Switch transport failed\n");
|
||||
return -1;
|
||||
}
|
||||
D("Switch transport success\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adb_status(int fd)
|
||||
{
|
||||
unsigned char buf[5];
|
||||
unsigned len;
|
||||
|
||||
if(readx(fd, buf, 4)) {
|
||||
strcpy(__adb_error, "protocol fault (no status)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!memcmp(buf, "OKAY", 4)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(buf, "FAIL", 4)) {
|
||||
sprintf(__adb_error,
|
||||
"protocol fault (status %02x %02x %02x %02x?!)",
|
||||
buf[0], buf[1], buf[2], buf[3]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(readx(fd, buf, 4)) {
|
||||
strcpy(__adb_error, "protocol fault (status len)");
|
||||
return -1;
|
||||
}
|
||||
buf[4] = 0;
|
||||
len = strtoul((char*)buf, 0, 16);
|
||||
if(len > 255) len = 255;
|
||||
if(readx(fd, __adb_error, len)) {
|
||||
strcpy(__adb_error, "protocol fault (status read)");
|
||||
return -1;
|
||||
}
|
||||
__adb_error[len] = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _adb_connect(const char *service)
|
||||
{
|
||||
char tmp[5];
|
||||
int len;
|
||||
int fd;
|
||||
|
||||
D("_adb_connect: %s\n", service);
|
||||
len = strlen(service);
|
||||
if((len < 1) || (len > 1024)) {
|
||||
strcpy(__adb_error, "service name too long");
|
||||
return -1;
|
||||
}
|
||||
snprintf(tmp, sizeof tmp, "%04x", len);
|
||||
|
||||
fd = socket_loopback_client(ADB_PORT, SOCK_STREAM);
|
||||
if(fd < 0) {
|
||||
strcpy(__adb_error, "cannot connect to daemon");
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(writex(fd, tmp, 4) || writex(fd, service, len)) {
|
||||
strcpy(__adb_error, "write failure during connection");
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(adb_status(fd)) {
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int adb_connect(const char *service)
|
||||
{
|
||||
// first query the adb server's version
|
||||
int fd = _adb_connect("host:version");
|
||||
|
||||
if(fd == -2) {
|
||||
fprintf(stdout,"* daemon not running. starting it now *\n");
|
||||
start_server:
|
||||
if(launch_server(0)) {
|
||||
fprintf(stderr,"* failed to start daemon *\n");
|
||||
return -1;
|
||||
} else {
|
||||
fprintf(stdout,"* daemon started successfully *\n");
|
||||
}
|
||||
/* give the server some time to start properly and detect devices */
|
||||
adb_sleep_ms(2000);
|
||||
// fall through to _adb_connect
|
||||
} else {
|
||||
// if server was running, check its version to make sure it is not out of date
|
||||
char buf[100];
|
||||
int n;
|
||||
int version = ADB_SERVER_VERSION - 1;
|
||||
|
||||
// if we have a file descriptor, then parse version result
|
||||
if(fd >= 0) {
|
||||
if(readx(fd, buf, 4)) goto error;
|
||||
|
||||
buf[4] = 0;
|
||||
n = strtoul(buf, 0, 16);
|
||||
if(n > (int)sizeof(buf)) goto error;
|
||||
if(readx(fd, buf, n)) goto error;
|
||||
adb_close(fd);
|
||||
|
||||
if (sscanf(buf, "%04x", &version) != 1) goto error;
|
||||
} else {
|
||||
// if fd is -1, then check for "unknown host service",
|
||||
// which would indicate a version of adb that does not support the version command
|
||||
if (strcmp(__adb_error, "unknown host service") != 0)
|
||||
return fd;
|
||||
}
|
||||
|
||||
if(version != ADB_SERVER_VERSION) {
|
||||
printf("adb server is out of date. killing...\n");
|
||||
fd = _adb_connect("host:kill");
|
||||
adb_close(fd);
|
||||
|
||||
/* XXX can we better detect its death? */
|
||||
adb_sleep_ms(2000);
|
||||
goto start_server;
|
||||
}
|
||||
}
|
||||
|
||||
// if the command is start-server, we are done.
|
||||
if (!strcmp(service, "host:start-server"))
|
||||
return 0;
|
||||
|
||||
fd = _adb_connect(service);
|
||||
if(fd == -2) {
|
||||
fprintf(stderr,"** daemon still not running");
|
||||
}
|
||||
|
||||
return fd;
|
||||
error:
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int adb_command(const char *service)
|
||||
{
|
||||
int fd = adb_connect(service);
|
||||
if(fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(adb_status(fd)) {
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *adb_query(const char *service)
|
||||
{
|
||||
char buf[5];
|
||||
unsigned n;
|
||||
char *tmp;
|
||||
|
||||
D("adb_query: %s\n", service);
|
||||
int fd = adb_connect(service);
|
||||
if(fd < 0) {
|
||||
fprintf(stderr,"error: %s\n", __adb_error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(readx(fd, buf, 4)) goto oops;
|
||||
|
||||
buf[4] = 0;
|
||||
n = strtoul(buf, 0, 16);
|
||||
if(n > 1024) goto oops;
|
||||
|
||||
tmp = malloc(n + 1);
|
||||
if(tmp == 0) goto oops;
|
||||
|
||||
if(readx(fd, tmp, n) == 0) {
|
||||
tmp[n] = 0;
|
||||
adb_close(fd);
|
||||
return tmp;
|
||||
}
|
||||
free(tmp);
|
||||
|
||||
oops:
|
||||
adb_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
49
adb/adb_client.h
Normal file
49
adb/adb_client.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef _ADB_CLIENT_H_
|
||||
#define _ADB_CLIENT_H_
|
||||
|
||||
#include "adb.h"
|
||||
|
||||
/* connect to adb, connect to the named service, and return
|
||||
** a valid fd for interacting with that service upon success
|
||||
** or a negative number on failure
|
||||
*/
|
||||
int adb_connect(const char *service);
|
||||
int _adb_connect(const char *service);
|
||||
|
||||
/* connect to adb, connect to the named service, return 0 if
|
||||
** the connection succeeded AND the service returned OKAY
|
||||
*/
|
||||
int adb_command(const char *service);
|
||||
|
||||
/* connect to adb, connect to the named service, return
|
||||
** a malloc'd string of its response upon success or NULL
|
||||
** on failure.
|
||||
*/
|
||||
char *adb_query(const char *service);
|
||||
|
||||
/* Set the preferred transport to connect to.
|
||||
*/
|
||||
void adb_set_transport(transport_type type, const char* serial);
|
||||
|
||||
/* Return the console port of the currently connected emulator (if any)
|
||||
* of -1 if there is no emulator, and -2 if there is more than one.
|
||||
* assumes adb_set_transport() was alled previously...
|
||||
*/
|
||||
int adb_get_emulator_console_port(void);
|
||||
|
||||
/* send commands to the current emulator instance. will fail if there
|
||||
* is zero, or more than one emulator connected (or if you use -s <serial>
|
||||
* with a <serial> that does not designate an emulator)
|
||||
*/
|
||||
int adb_send_emulator_command(int argc, char** argv);
|
||||
|
||||
/* return verbose error string from last operation */
|
||||
const char *adb_error(void);
|
||||
|
||||
/* read a standard adb status response (OKAY|FAIL) and
|
||||
** return 0 in the event of OKAY, -1 in the event of FAIL
|
||||
** or protocol error
|
||||
*/
|
||||
int adb_status(int fd);
|
||||
|
||||
#endif
|
1371
adb/commandline.c
Normal file
1371
adb/commandline.c
Normal file
File diff suppressed because it is too large
Load diff
45
adb/console.c
Normal file
45
adb/console.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include "sysdeps.h"
|
||||
#include "adb.h"
|
||||
#include "adb_client.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static int connect_to_console(void)
|
||||
{
|
||||
int fd, port;
|
||||
|
||||
port = adb_get_emulator_console_port();
|
||||
if (port < 0) {
|
||||
if (port == -2)
|
||||
fprintf(stderr, "error: more than one emulator detected. use -s option\n");
|
||||
else
|
||||
fprintf(stderr, "error: no emulator detected\n");
|
||||
return -1;
|
||||
}
|
||||
fd = socket_loopback_client( port, SOCK_STREAM );
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "error: could not connect to TCP port %d\n", port);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
int adb_send_emulator_command(int argc, char** argv)
|
||||
{
|
||||
int fd, nn;
|
||||
|
||||
fd = connect_to_console();
|
||||
if (fd < 0)
|
||||
return 1;
|
||||
|
||||
#define QUIT "quit\n"
|
||||
|
||||
for (nn = 1; nn < argc; nn++) {
|
||||
adb_write( fd, argv[nn], strlen(argv[nn]) );
|
||||
adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
|
||||
}
|
||||
adb_write( fd, QUIT, sizeof(QUIT)-1 );
|
||||
adb_close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
1022
adb/file_sync_client.c
Normal file
1022
adb/file_sync_client.c
Normal file
File diff suppressed because it is too large
Load diff
412
adb/file_sync_service.c
Normal file
412
adb/file_sync_service.c
Normal file
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_SYNC
|
||||
#include "adb.h"
|
||||
#include "file_sync_service.h"
|
||||
|
||||
static int mkdirs(char *name)
|
||||
{
|
||||
int ret;
|
||||
char *x = name + 1;
|
||||
|
||||
if(name[0] != '/') return -1;
|
||||
|
||||
for(;;) {
|
||||
x = adb_dirstart(x);
|
||||
if(x == 0) return 0;
|
||||
*x = 0;
|
||||
ret = adb_mkdir(name, 0775);
|
||||
if((ret < 0) && (errno != EEXIST)) {
|
||||
D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
|
||||
*x = '/';
|
||||
return ret;
|
||||
}
|
||||
*x++ = '/';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_stat(int s, const char *path)
|
||||
{
|
||||
syncmsg msg;
|
||||
struct stat st;
|
||||
|
||||
msg.stat.id = ID_STAT;
|
||||
|
||||
if(lstat(path, &st)) {
|
||||
msg.stat.mode = 0;
|
||||
msg.stat.size = 0;
|
||||
msg.stat.time = 0;
|
||||
} else {
|
||||
msg.stat.mode = htoll(st.st_mode);
|
||||
msg.stat.size = htoll(st.st_size);
|
||||
msg.stat.time = htoll(st.st_mtime);
|
||||
}
|
||||
|
||||
return writex(s, &msg.stat, sizeof(msg.stat));
|
||||
}
|
||||
|
||||
static int do_list(int s, const char *path)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
struct stat st;
|
||||
syncmsg msg;
|
||||
int len;
|
||||
|
||||
char tmp[1024 + 256 + 1];
|
||||
char *fname;
|
||||
|
||||
len = strlen(path);
|
||||
memcpy(tmp, path, len);
|
||||
tmp[len] = '/';
|
||||
fname = tmp + len + 1;
|
||||
|
||||
msg.dent.id = ID_DENT;
|
||||
|
||||
d = opendir(path);
|
||||
if(d == 0) goto done;
|
||||
|
||||
while((de = readdir(d))) {
|
||||
int len = strlen(de->d_name);
|
||||
|
||||
/* not supposed to be possible, but
|
||||
if it does happen, let's not buffer overrun */
|
||||
if(len > 256) continue;
|
||||
|
||||
strcpy(fname, de->d_name);
|
||||
if(lstat(tmp, &st) == 0) {
|
||||
msg.dent.mode = htoll(st.st_mode);
|
||||
msg.dent.size = htoll(st.st_size);
|
||||
msg.dent.time = htoll(st.st_mtime);
|
||||
msg.dent.namelen = htoll(len);
|
||||
|
||||
if(writex(s, &msg.dent, sizeof(msg.dent)) ||
|
||||
writex(s, de->d_name, len)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
done:
|
||||
msg.dent.id = ID_DONE;
|
||||
msg.dent.mode = 0;
|
||||
msg.dent.size = 0;
|
||||
msg.dent.time = 0;
|
||||
msg.dent.namelen = 0;
|
||||
return writex(s, &msg.dent, sizeof(msg.dent));
|
||||
}
|
||||
|
||||
static int fail_message(int s, const char *reason)
|
||||
{
|
||||
syncmsg msg;
|
||||
int len = strlen(reason);
|
||||
|
||||
D("sync: failure: %s\n", reason);
|
||||
|
||||
msg.data.id = ID_FAIL;
|
||||
msg.data.size = htoll(len);
|
||||
if(writex(s, &msg.data, sizeof(msg.data)) ||
|
||||
writex(s, reason, len)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int fail_errno(int s)
|
||||
{
|
||||
return fail_message(s, strerror(errno));
|
||||
}
|
||||
|
||||
static int handle_send_file(int s, char *path, mode_t mode, char *buffer)
|
||||
{
|
||||
syncmsg msg;
|
||||
unsigned int timestamp = 0;
|
||||
int fd;
|
||||
|
||||
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
||||
if(fd < 0 && errno == ENOENT) {
|
||||
mkdirs(path);
|
||||
fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
||||
}
|
||||
if(fd < 0 && errno == EEXIST) {
|
||||
fd = adb_open_mode(path, O_WRONLY, mode);
|
||||
}
|
||||
if(fd < 0) {
|
||||
if(fail_errno(s))
|
||||
return -1;
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
unsigned int len;
|
||||
|
||||
if(readx(s, &msg.data, sizeof(msg.data)))
|
||||
goto fail;
|
||||
|
||||
if(msg.data.id != ID_DATA) {
|
||||
if(msg.data.id == ID_DONE) {
|
||||
timestamp = ltohl(msg.data.size);
|
||||
break;
|
||||
}
|
||||
fail_message(s, "invalid data message");
|
||||
goto fail;
|
||||
}
|
||||
len = ltohl(msg.data.size);
|
||||
if(len > SYNC_DATA_MAX) {
|
||||
fail_message(s, "oversize data message");
|
||||
goto fail;
|
||||
}
|
||||
if(readx(s, buffer, len))
|
||||
goto fail;
|
||||
|
||||
if(fd < 0)
|
||||
continue;
|
||||
if(writex(fd, buffer, len)) {
|
||||
adb_close(fd);
|
||||
adb_unlink(path);
|
||||
fd = -1;
|
||||
if(fail_errno(s)) return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(fd >= 0) {
|
||||
struct utimbuf u;
|
||||
adb_close(fd);
|
||||
u.actime = timestamp;
|
||||
u.modtime = timestamp;
|
||||
utime(path, &u);
|
||||
|
||||
msg.status.id = ID_OKAY;
|
||||
msg.status.msglen = 0;
|
||||
if(writex(s, &msg.status, sizeof(msg.status)))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if(fd >= 0)
|
||||
adb_close(fd);
|
||||
adb_unlink(path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYMLINKS
|
||||
static int handle_send_link(int s, char *path, char *buffer)
|
||||
{
|
||||
syncmsg msg;
|
||||
unsigned int len;
|
||||
int ret;
|
||||
|
||||
if(readx(s, &msg.data, sizeof(msg.data)))
|
||||
return -1;
|
||||
|
||||
if(msg.data.id != ID_DATA) {
|
||||
fail_message(s, "invalid data message: expected ID_DATA");
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = ltohl(msg.data.size);
|
||||
if(len > SYNC_DATA_MAX) {
|
||||
fail_message(s, "oversize data message");
|
||||
return -1;
|
||||
}
|
||||
if(readx(s, buffer, len))
|
||||
return -1;
|
||||
|
||||
ret = symlink(buffer, path);
|
||||
if(ret && errno == ENOENT) {
|
||||
mkdirs(path);
|
||||
ret = symlink(buffer, path);
|
||||
}
|
||||
if(ret) {
|
||||
fail_errno(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(readx(s, &msg.data, sizeof(msg.data)))
|
||||
return -1;
|
||||
|
||||
if(msg.data.id == ID_DONE) {
|
||||
msg.status.id = ID_OKAY;
|
||||
msg.status.msglen = 0;
|
||||
if(writex(s, &msg.status, sizeof(msg.status)))
|
||||
return -1;
|
||||
} else {
|
||||
fail_message(s, "invalid data message: expected ID_DONE");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_SYMLINKS */
|
||||
|
||||
static int do_send(int s, char *path, char *buffer)
|
||||
{
|
||||
char *tmp;
|
||||
mode_t mode;
|
||||
int is_link, ret;
|
||||
|
||||
tmp = strrchr(path,',');
|
||||
if(tmp) {
|
||||
*tmp = 0;
|
||||
errno = 0;
|
||||
mode = strtoul(tmp + 1, NULL, 0);
|
||||
#ifndef HAVE_SYMLINKS
|
||||
is_link = 0;
|
||||
#else
|
||||
is_link = S_ISLNK(mode);
|
||||
#endif
|
||||
mode &= 0777;
|
||||
}
|
||||
if(!tmp || errno) {
|
||||
mode = 0644;
|
||||
is_link = 0;
|
||||
}
|
||||
|
||||
adb_unlink(path);
|
||||
|
||||
|
||||
#ifdef HAVE_SYMLINKS
|
||||
if(is_link)
|
||||
ret = handle_send_link(s, path, buffer);
|
||||
else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
/* copy user permission bits to "group" and "other" permissions */
|
||||
mode |= ((mode >> 3) & 0070);
|
||||
mode |= ((mode >> 3) & 0007);
|
||||
|
||||
ret = handle_send_file(s, path, mode, buffer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_recv(int s, const char *path, char *buffer)
|
||||
{
|
||||
syncmsg msg;
|
||||
int fd, r;
|
||||
|
||||
fd = adb_open(path, O_RDONLY);
|
||||
if(fd < 0) {
|
||||
if(fail_errno(s)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg.data.id = ID_DATA;
|
||||
for(;;) {
|
||||
r = adb_read(fd, buffer, SYNC_DATA_MAX);
|
||||
if(r <= 0) {
|
||||
if(r == 0) break;
|
||||
if(errno == EINTR) continue;
|
||||
r = fail_errno(s);
|
||||
adb_close(fd);
|
||||
return r;
|
||||
}
|
||||
msg.data.size = htoll(r);
|
||||
if(writex(s, &msg.data, sizeof(msg.data)) ||
|
||||
writex(s, buffer, r)) {
|
||||
adb_close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
|
||||
msg.data.id = ID_DONE;
|
||||
msg.data.size = 0;
|
||||
if(writex(s, &msg.data, sizeof(msg.data))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void file_sync_service(int fd, void *cookie)
|
||||
{
|
||||
syncmsg msg;
|
||||
char name[1025];
|
||||
unsigned namelen;
|
||||
|
||||
char *buffer = malloc(SYNC_DATA_MAX);
|
||||
if(buffer == 0) goto fail;
|
||||
|
||||
for(;;) {
|
||||
D("sync: waiting for command\n");
|
||||
|
||||
if(readx(fd, &msg.req, sizeof(msg.req))) {
|
||||
fail_message(fd, "command read failure");
|
||||
break;
|
||||
}
|
||||
namelen = ltohl(msg.req.namelen);
|
||||
if(namelen > 1024) {
|
||||
fail_message(fd, "invalid namelen");
|
||||
break;
|
||||
}
|
||||
if(readx(fd, name, namelen)) {
|
||||
fail_message(fd, "filename read failure");
|
||||
break;
|
||||
}
|
||||
name[namelen] = 0;
|
||||
|
||||
msg.req.namelen = 0;
|
||||
D("sync: '%s' '%s'\n", (char*) &msg.req, name);
|
||||
|
||||
switch(msg.req.id) {
|
||||
case ID_STAT:
|
||||
if(do_stat(fd, name)) goto fail;
|
||||
break;
|
||||
case ID_LIST:
|
||||
if(do_list(fd, name)) goto fail;
|
||||
break;
|
||||
case ID_SEND:
|
||||
if(do_send(fd, name, buffer)) goto fail;
|
||||
break;
|
||||
case ID_RECV:
|
||||
if(do_recv(fd, name, buffer)) goto fail;
|
||||
break;
|
||||
case ID_QUIT:
|
||||
goto fail;
|
||||
default:
|
||||
fail_message(fd, "unknown command");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
if(buffer != 0) free(buffer);
|
||||
D("sync: done\n");
|
||||
adb_close(fd);
|
||||
}
|
87
adb/file_sync_service.h
Normal file
87
adb/file_sync_service.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef _FILE_SYNC_SERVICE_H_
|
||||
#define _FILE_SYNC_SERVICE_H_
|
||||
|
||||
#ifdef __ppc__
|
||||
static inline unsigned __swap_uint32(unsigned x)
|
||||
{
|
||||
return (((x) & 0xFF000000) >> 24)
|
||||
| (((x) & 0x00FF0000) >> 8)
|
||||
| (((x) & 0x0000FF00) << 8)
|
||||
| (((x) & 0x000000FF) << 24);
|
||||
}
|
||||
#define htoll(x) __swap_uint32(x)
|
||||
#define ltohl(x) __swap_uint32(x)
|
||||
#define MKID(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))
|
||||
#else
|
||||
#define htoll(x) (x)
|
||||
#define ltohl(x) (x)
|
||||
#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
|
||||
#endif
|
||||
|
||||
#define ID_STAT MKID('S','T','A','T')
|
||||
#define ID_LIST MKID('L','I','S','T')
|
||||
#define ID_ULNK MKID('U','L','N','K')
|
||||
#define ID_SEND MKID('S','E','N','D')
|
||||
#define ID_RECV MKID('R','E','C','V')
|
||||
#define ID_DENT MKID('D','E','N','T')
|
||||
#define ID_DONE MKID('D','O','N','E')
|
||||
#define ID_DATA MKID('D','A','T','A')
|
||||
#define ID_OKAY MKID('O','K','A','Y')
|
||||
#define ID_FAIL MKID('F','A','I','L')
|
||||
#define ID_QUIT MKID('Q','U','I','T')
|
||||
|
||||
typedef union {
|
||||
unsigned id;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned namelen;
|
||||
} req;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned mode;
|
||||
unsigned size;
|
||||
unsigned time;
|
||||
} stat;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned mode;
|
||||
unsigned size;
|
||||
unsigned time;
|
||||
unsigned namelen;
|
||||
} dent;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned size;
|
||||
} data;
|
||||
struct {
|
||||
unsigned id;
|
||||
unsigned msglen;
|
||||
} status;
|
||||
} syncmsg;
|
||||
|
||||
|
||||
void file_sync_service(int fd, void *cookie);
|
||||
int do_sync_ls(const char *path);
|
||||
int do_sync_push(const char *lpath, const char *rpath, int verifyApk);
|
||||
int do_sync_sync(const char *lpath, const char *rpath);
|
||||
int do_sync_pull(const char *rpath, const char *lpath);
|
||||
|
||||
#define SYNC_DATA_MAX (64*1024)
|
||||
|
||||
#endif
|
69
adb/framebuffer_service.c
Normal file
69
adb/framebuffer_service.c
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <cutils/fdevent.h>
|
||||
#include "adb.h"
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/* TODO:
|
||||
** - grab the current buffer, not the first buffer
|
||||
** - sync with vsync to avoid tearing
|
||||
*/
|
||||
|
||||
void framebuffer_service(int fd, void *cookie)
|
||||
{
|
||||
struct fb_var_screeninfo vinfo;
|
||||
int fb;
|
||||
void *ptr = MAP_FAILED;
|
||||
char x;
|
||||
|
||||
unsigned fbinfo[4];
|
||||
|
||||
fb = open("/dev/graphics/fb0", O_RDONLY);
|
||||
if(fb < 0) goto done;
|
||||
|
||||
if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;
|
||||
fcntl(fb, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
fbinfo[0] = 16;
|
||||
fbinfo[1] = vinfo.xres * vinfo.yres * 2;
|
||||
fbinfo[2] = vinfo.xres;
|
||||
fbinfo[3] = vinfo.yres;
|
||||
|
||||
ptr = mmap(0, fbinfo[1], PROT_READ, MAP_SHARED, fb, 0);
|
||||
if(ptr == MAP_FAILED) goto done;
|
||||
|
||||
if(writex(fd, fbinfo, sizeof(unsigned) * 4)) goto done;
|
||||
|
||||
for(;;) {
|
||||
if(readx(fd, &x, 1)) goto done;
|
||||
if(writex(fd, ptr, fbinfo[1])) goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if(ptr != MAP_FAILED) munmap(ptr, fbinfo[1]);
|
||||
if(fb >= 0) close(fb);
|
||||
close(fd);
|
||||
}
|
||||
|
31
adb/get_my_path_darwin.c
Normal file
31
adb/get_my_path_darwin.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <utils/executablepath.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void get_my_path(char s[PATH_MAX])
|
||||
{
|
||||
ProcessSerialNumber psn;
|
||||
GetCurrentProcess(&psn);
|
||||
CFDictionaryRef dict;
|
||||
dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
|
||||
CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
|
||||
CFSTR("CFBundleExecutable"));
|
||||
CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
|
||||
}
|
||||
|
33
adb/get_my_path_linux.c
Normal file
33
adb/get_my_path_linux.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void get_my_path(char exe[PATH_MAX])
|
||||
{
|
||||
char proc[64];
|
||||
snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
|
||||
int err = readlink(proc, exe, PATH_MAX - 1);
|
||||
if(err > 0) {
|
||||
exe[err] = 0;
|
||||
} else {
|
||||
exe[0] = 0;
|
||||
}
|
||||
}
|
||||
|
31
adb/get_my_path_windows.c
Normal file
31
adb/get_my_path_windows.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <limits.h>
|
||||
#include <assert.h>
|
||||
#include <windows.h>
|
||||
|
||||
void get_my_path(char exe[PATH_MAX])
|
||||
{
|
||||
char* r;
|
||||
|
||||
GetModuleFileName( NULL, exe, PATH_MAX-1 );
|
||||
exe[PATH_MAX-1] = 0;
|
||||
r = strrchr( exe, '\\' );
|
||||
if (r)
|
||||
*r = 0;
|
||||
}
|
||||
|
13
adb/history.h
Executable file
13
adb/history.h
Executable file
|
@ -0,0 +1,13 @@
|
|||
#ifndef _HISTORY_H_
|
||||
#define _HISTORY_H_
|
||||
|
||||
#define SH_ARROW_ANY "\x1b\x5b"
|
||||
#define SH_ARROW_UP '\x41'
|
||||
#define SH_ARROW_DOWN '\x42'
|
||||
#define SH_ARROW_RIGHT '\x43'
|
||||
#define SH_ARROW_LEFT '\x44'
|
||||
#define SH_DEL_CHAR '\x7F'
|
||||
#define SH_BLANK_CHAR '\x20'
|
||||
|
||||
#endif
|
||||
|
709
adb/jdwp_service.c
Normal file
709
adb/jdwp_service.c
Normal file
|
@ -0,0 +1,709 @@
|
|||
/* implement the "debug-ports" and "track-debug-ports" device services */
|
||||
#include "sysdeps.h"
|
||||
#define TRACE_TAG TRACE_JDWP
|
||||
#include "adb.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* here's how these things work.
|
||||
|
||||
when adbd starts, it creates a unix server socket
|
||||
named @vm-debug-control (@ is a shortcut for "first byte is zero"
|
||||
to use the private namespace instead of the file system)
|
||||
|
||||
when a new JDWP daemon thread starts in a new VM process, it creates
|
||||
a connection to @vm-debug-control to announce its availability.
|
||||
|
||||
|
||||
JDWP thread @vm-debug-control
|
||||
| |
|
||||
|-------------------------------> |
|
||||
| hello I'm in process <pid> |
|
||||
| |
|
||||
| |
|
||||
|
||||
the connection is kept alive. it will be closed automatically if
|
||||
the JDWP process terminates (this allows adbd to detect dead
|
||||
processes).
|
||||
|
||||
adbd thus maintains a list of "active" JDWP processes. it can send
|
||||
its content to clients through the "device:debug-ports" service,
|
||||
or even updates through the "device:track-debug-ports" service.
|
||||
|
||||
when a debugger wants to connect, it simply runs the command
|
||||
equivalent to "adb forward tcp:<hostport> jdwp:<pid>"
|
||||
|
||||
"jdwp:<pid>" is a new forward destination format used to target
|
||||
a given JDWP process on the device. when sutch a request arrives,
|
||||
adbd does the following:
|
||||
|
||||
- first, it calls socketpair() to create a pair of equivalent
|
||||
sockets.
|
||||
|
||||
- it attaches the first socket in the pair to a local socket
|
||||
which is itself attached to the transport's remote socket:
|
||||
|
||||
|
||||
- it sends the file descriptor of the second socket directly
|
||||
to the JDWP process with the help of sendmsg()
|
||||
|
||||
|
||||
JDWP thread @vm-debug-control
|
||||
| |
|
||||
| <----------------------|
|
||||
| OK, try this file descriptor |
|
||||
| |
|
||||
| |
|
||||
|
||||
then, the JDWP thread uses this new socket descriptor as its
|
||||
pass-through connection to the debugger (and receives the
|
||||
JDWP-Handshake message, answers to it, etc...)
|
||||
|
||||
this gives the following graphics:
|
||||
____________________________________
|
||||
| |
|
||||
| ADB Server (host) |
|
||||
| |
|
||||
Debugger <---> LocalSocket <----> RemoteSocket |
|
||||
| ^^ |
|
||||
|___________________________||_______|
|
||||
||
|
||||
Transport ||
|
||||
(TCP for emulator - USB for device) ||
|
||||
||
|
||||
___________________________||_______
|
||||
| || |
|
||||
| ADBD (device) || |
|
||||
| VV |
|
||||
JDWP <======> LocalSocket <----> RemoteSocket |
|
||||
| |
|
||||
|____________________________________|
|
||||
|
||||
due to the way adb works, this doesn't need a special socket
|
||||
type or fancy handling of socket termination if either the debugger
|
||||
or the JDWP process closes the connection.
|
||||
|
||||
THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN
|
||||
TO HAVE A BETTER IDEA, LET ME KNOW - Digit
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
/** JDWP PID List Support Code
|
||||
** for each JDWP process, we record its pid and its connected socket
|
||||
**/
|
||||
|
||||
#define MAX_OUT_FDS 4
|
||||
|
||||
#if !ADB_HOST
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
typedef struct JdwpProcess JdwpProcess;
|
||||
struct JdwpProcess {
|
||||
JdwpProcess* next;
|
||||
JdwpProcess* prev;
|
||||
int pid;
|
||||
int socket;
|
||||
fdevent* fde;
|
||||
|
||||
char in_buff[4]; /* input character to read PID */
|
||||
int in_len; /* number from JDWP process */
|
||||
|
||||
int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */
|
||||
int out_count; /* to send to the JDWP process */
|
||||
};
|
||||
|
||||
static JdwpProcess _jdwp_list;
|
||||
|
||||
static int
|
||||
jdwp_process_list( char* buffer, int bufferlen )
|
||||
{
|
||||
char* end = buffer + bufferlen;
|
||||
char* p = buffer;
|
||||
JdwpProcess* proc = _jdwp_list.next;
|
||||
|
||||
for ( ; proc != &_jdwp_list; proc = proc->next ) {
|
||||
int len;
|
||||
|
||||
/* skip transient connections */
|
||||
if (proc->pid < 0)
|
||||
continue;
|
||||
|
||||
len = snprintf(p, end-p, "%d\n", proc->pid);
|
||||
if (p + len >= end)
|
||||
break;
|
||||
p += len;
|
||||
}
|
||||
p[0] = 0;
|
||||
return (p - buffer);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
jdwp_process_list_msg( char* buffer, int bufferlen )
|
||||
{
|
||||
char head[5];
|
||||
int len = jdwp_process_list( buffer+4, bufferlen-4 );
|
||||
snprintf(head, sizeof head, "%04x", len);
|
||||
memcpy(buffer, head, 4);
|
||||
return len + 4;
|
||||
}
|
||||
|
||||
|
||||
static void jdwp_process_list_updated(void);
|
||||
|
||||
static void
|
||||
jdwp_process_free( JdwpProcess* proc )
|
||||
{
|
||||
if (proc) {
|
||||
int n;
|
||||
|
||||
proc->prev->next = proc->next;
|
||||
proc->next->prev = proc->prev;
|
||||
|
||||
if (proc->socket >= 0) {
|
||||
shutdown(proc->socket, SHUT_RDWR);
|
||||
adb_close(proc->socket);
|
||||
proc->socket = -1;
|
||||
}
|
||||
|
||||
if (proc->fde != NULL) {
|
||||
fdevent_destroy(proc->fde);
|
||||
proc->fde = NULL;
|
||||
}
|
||||
proc->pid = -1;
|
||||
|
||||
for (n = 0; n < proc->out_count; n++) {
|
||||
adb_close(proc->out_fds[n]);
|
||||
}
|
||||
proc->out_count = 0;
|
||||
|
||||
free(proc);
|
||||
|
||||
jdwp_process_list_updated();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void jdwp_process_event(int, unsigned, void*); /* forward */
|
||||
|
||||
|
||||
static JdwpProcess*
|
||||
jdwp_process_alloc( int socket )
|
||||
{
|
||||
JdwpProcess* proc = calloc(1,sizeof(*proc));
|
||||
|
||||
if (proc == NULL) {
|
||||
D("not enough memory to create new JDWP process\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proc->socket = socket;
|
||||
proc->pid = -1;
|
||||
proc->next = proc;
|
||||
proc->prev = proc;
|
||||
|
||||
proc->fde = fdevent_create( socket, jdwp_process_event, proc );
|
||||
if (proc->fde == NULL) {
|
||||
D("could not create fdevent for new JDWP process\n" );
|
||||
free(proc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
proc->fde->state |= FDE_DONT_CLOSE;
|
||||
proc->in_len = 0;
|
||||
proc->out_count = 0;
|
||||
|
||||
/* append to list */
|
||||
proc->next = &_jdwp_list;
|
||||
proc->prev = proc->next->prev;
|
||||
|
||||
proc->prev->next = proc;
|
||||
proc->next->prev = proc;
|
||||
|
||||
/* start by waiting for the PID */
|
||||
fdevent_add(proc->fde, FDE_READ);
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
jdwp_process_event( int socket, unsigned events, void* _proc )
|
||||
{
|
||||
JdwpProcess* proc = _proc;
|
||||
|
||||
if (events & FDE_READ) {
|
||||
if (proc->pid < 0) {
|
||||
/* read the PID as a 4-hexchar string */
|
||||
char* p = proc->in_buff + proc->in_len;
|
||||
int size = 4 - proc->in_len;
|
||||
char temp[5];
|
||||
while (size > 0) {
|
||||
int len = recv( socket, p, size, 0 );
|
||||
if (len < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == EAGAIN)
|
||||
return;
|
||||
/* this can fail here if the JDWP process crashes very fast */
|
||||
D("weird unknown JDWP process failure: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
goto CloseProcess;
|
||||
}
|
||||
if (len == 0) { /* end of stream ? */
|
||||
D("weird end-of-stream from unknown JDWP process\n");
|
||||
goto CloseProcess;
|
||||
}
|
||||
p += len;
|
||||
proc->in_len += len;
|
||||
size -= len;
|
||||
}
|
||||
/* we have read 4 characters, now decode the pid */
|
||||
memcpy(temp, proc->in_buff, 4);
|
||||
temp[4] = 0;
|
||||
|
||||
if (sscanf( temp, "%04x", &proc->pid ) != 1) {
|
||||
D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
/* all is well, keep reading to detect connection closure */
|
||||
D("Adding pid %d to jdwp process list\n", proc->pid);
|
||||
jdwp_process_list_updated();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the pid was read, if we get there it's probably because the connection
|
||||
* was closed (e.g. the JDWP process exited or crashed) */
|
||||
char buf[32];
|
||||
|
||||
for (;;) {
|
||||
int len = recv(socket, buf, sizeof(buf), 0);
|
||||
|
||||
if (len <= 0) {
|
||||
if (len < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (len < 0 && errno == EAGAIN)
|
||||
return;
|
||||
else {
|
||||
D("terminating JDWP %d connection: %s\n", proc->pid,
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
|
||||
proc->pid, len );
|
||||
}
|
||||
}
|
||||
|
||||
CloseProcess:
|
||||
if (proc->pid >= 0)
|
||||
D( "remove pid %d to jdwp process list\n", proc->pid );
|
||||
jdwp_process_free(proc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (events & FDE_WRITE) {
|
||||
D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
|
||||
proc->pid, proc->out_count, proc->out_fds[0]);
|
||||
if (proc->out_count > 0) {
|
||||
int fd = proc->out_fds[0];
|
||||
int n, ret;
|
||||
struct cmsghdr* cmsg;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char dummy = '!';
|
||||
char buffer[sizeof(struct cmsghdr) + sizeof(int)];
|
||||
|
||||
iov.iov_base = &dummy;
|
||||
iov.iov_len = 1;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_control = buffer;
|
||||
msg.msg_controllen = sizeof(buffer);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = msg.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
((int*)CMSG_DATA(cmsg))[0] = fd;
|
||||
|
||||
for (;;) {
|
||||
ret = sendmsg(proc->socket, &msg, 0);
|
||||
if (ret >= 0)
|
||||
break;
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
D("sending new file descriptor to JDWP %d failed: %s\n",
|
||||
proc->pid, strerror(errno));
|
||||
goto CloseProcess;
|
||||
}
|
||||
|
||||
D("sent file descriptor %d to JDWP process %d\n",
|
||||
fd, proc->pid);
|
||||
|
||||
for (n = 1; n < proc->out_count; n++)
|
||||
proc->out_fds[n-1] = proc->out_fds[n];
|
||||
|
||||
if (--proc->out_count == 0)
|
||||
fdevent_del( proc->fde, FDE_WRITE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
create_jdwp_connection_fd(int pid)
|
||||
{
|
||||
JdwpProcess* proc = _jdwp_list.next;
|
||||
|
||||
D("looking for pid %d in JDWP process list\n", pid);
|
||||
for ( ; proc != &_jdwp_list; proc = proc->next ) {
|
||||
if (proc->pid == pid) {
|
||||
goto FoundIt;
|
||||
}
|
||||
}
|
||||
D("search failed !!\n");
|
||||
return -1;
|
||||
|
||||
FoundIt:
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if (proc->out_count >= MAX_OUT_FDS) {
|
||||
D("%s: too many pending JDWP connection for pid %d\n",
|
||||
__FUNCTION__, pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (adb_socketpair(fds) < 0) {
|
||||
D("%s: socket pair creation failed: %s\n",
|
||||
__FUNCTION__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
proc->out_fds[ proc->out_count ] = fds[1];
|
||||
if (++proc->out_count == 1)
|
||||
fdevent_add( proc->fde, FDE_WRITE );
|
||||
|
||||
return fds[0];
|
||||
}
|
||||
}
|
||||
|
||||
/** VM DEBUG CONTROL SOCKET
|
||||
**
|
||||
** we do implement a custom asocket to receive the data
|
||||
**/
|
||||
|
||||
/* name of the debug control Unix socket */
|
||||
#define JDWP_CONTROL_NAME "\0jdwp-control"
|
||||
#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1)
|
||||
|
||||
typedef struct {
|
||||
int listen_socket;
|
||||
fdevent* fde;
|
||||
|
||||
} JdwpControl;
|
||||
|
||||
|
||||
static void
|
||||
jdwp_control_event(int s, unsigned events, void* user);
|
||||
|
||||
|
||||
static int
|
||||
jdwp_control_init( JdwpControl* control,
|
||||
const char* sockname,
|
||||
int socknamelen )
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addrlen;
|
||||
int s;
|
||||
int maxpath = sizeof(addr.sun_path);
|
||||
int pathlen = socknamelen;
|
||||
|
||||
if (pathlen >= maxpath) {
|
||||
D( "vm debug control socket name too long (%d extra chars)\n",
|
||||
pathlen+1-maxpath );
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
memcpy(addr.sun_path, sockname, socknamelen);
|
||||
|
||||
s = socket( AF_UNIX, SOCK_STREAM, 0 );
|
||||
if (s < 0) {
|
||||
D( "could not create vm debug control socket. %d: %s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
addrlen = (pathlen + sizeof(addr.sun_family));
|
||||
|
||||
if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
|
||||
D( "could not bind vm debug control socket: %d: %s\n",
|
||||
errno, strerror(errno) );
|
||||
adb_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( listen(s, 4) < 0 ) {
|
||||
D("listen failed in jdwp control socket: %d: %s\n",
|
||||
errno, strerror(errno));
|
||||
adb_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
control->listen_socket = s;
|
||||
|
||||
control->fde = fdevent_create(s, jdwp_control_event, control);
|
||||
if (control->fde == NULL) {
|
||||
D( "could not create fdevent for jdwp control socket\n" );
|
||||
adb_close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* only wait for incoming connections */
|
||||
fdevent_add(control->fde, FDE_READ);
|
||||
|
||||
D("jdwp control socket started (%d)\n", control->listen_socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
jdwp_control_event( int s, unsigned events, void* _control )
|
||||
{
|
||||
JdwpControl* control = (JdwpControl*) _control;
|
||||
|
||||
if (events & FDE_READ) {
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int s = -1;
|
||||
JdwpProcess* proc;
|
||||
|
||||
do {
|
||||
s = adb_socket_accept( control->listen_socket, &addr, &addrlen );
|
||||
if (s < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno == ECONNABORTED) {
|
||||
/* oops, the JDWP process died really quick */
|
||||
D("oops, the JDWP process died really quick\n");
|
||||
return;
|
||||
}
|
||||
/* the socket is probably closed ? */
|
||||
D( "weird accept() failed on jdwp control socket: %s\n",
|
||||
strerror(errno) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (s < 0);
|
||||
|
||||
proc = jdwp_process_alloc( s );
|
||||
if (proc == NULL)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static JdwpControl _jdwp_control;
|
||||
|
||||
/** "jdwp" local service implementation
|
||||
** this simply returns the list of known JDWP process pids
|
||||
**/
|
||||
|
||||
typedef struct {
|
||||
asocket socket;
|
||||
int pass;
|
||||
} JdwpSocket;
|
||||
|
||||
static void
|
||||
jdwp_socket_close( asocket* s )
|
||||
{
|
||||
asocket* peer = s->peer;
|
||||
|
||||
remove_socket(s);
|
||||
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
static int
|
||||
jdwp_socket_enqueue( asocket* s, apacket* p )
|
||||
{
|
||||
/* you can't write to this asocket */
|
||||
put_apacket(p);
|
||||
s->peer->close(s->peer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
jdwp_socket_ready( asocket* s )
|
||||
{
|
||||
JdwpSocket* jdwp = (JdwpSocket*)s;
|
||||
asocket* peer = jdwp->socket.peer;
|
||||
|
||||
/* on the first call, send the list of pids,
|
||||
* on the second one, close the connection
|
||||
*/
|
||||
if (jdwp->pass == 0) {
|
||||
apacket* p = get_apacket();
|
||||
p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
|
||||
peer->enqueue(peer, p);
|
||||
jdwp->pass = 1;
|
||||
}
|
||||
else {
|
||||
peer->close(peer);
|
||||
}
|
||||
}
|
||||
|
||||
asocket*
|
||||
create_jdwp_service_socket( void )
|
||||
{
|
||||
JdwpSocket* s = calloc(sizeof(*s),1);
|
||||
|
||||
if (s == NULL)
|
||||
return NULL;
|
||||
|
||||
install_local_socket(&s->socket);
|
||||
|
||||
s->socket.ready = jdwp_socket_ready;
|
||||
s->socket.enqueue = jdwp_socket_enqueue;
|
||||
s->socket.close = jdwp_socket_close;
|
||||
s->pass = 0;
|
||||
|
||||
return &s->socket;
|
||||
}
|
||||
|
||||
/** "track-jdwp" local service implementation
|
||||
** this periodically sends the list of known JDWP process pids
|
||||
** to the client...
|
||||
**/
|
||||
|
||||
typedef struct JdwpTracker JdwpTracker;
|
||||
|
||||
struct JdwpTracker {
|
||||
asocket socket;
|
||||
JdwpTracker* next;
|
||||
JdwpTracker* prev;
|
||||
int need_update;
|
||||
};
|
||||
|
||||
static JdwpTracker _jdwp_trackers_list;
|
||||
|
||||
|
||||
static void
|
||||
jdwp_process_list_updated(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
int len;
|
||||
JdwpTracker* t = _jdwp_trackers_list.next;
|
||||
|
||||
len = jdwp_process_list_msg(buffer, sizeof(buffer));
|
||||
|
||||
for ( ; t != &_jdwp_trackers_list; t = t->next ) {
|
||||
apacket* p = get_apacket();
|
||||
asocket* peer = t->socket.peer;
|
||||
memcpy(p->data, buffer, len);
|
||||
p->len = len;
|
||||
peer->enqueue( peer, p );
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
jdwp_tracker_close( asocket* s )
|
||||
{
|
||||
JdwpTracker* tracker = (JdwpTracker*) s;
|
||||
asocket* peer = s->peer;
|
||||
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
|
||||
remove_socket(s);
|
||||
|
||||
tracker->prev->next = tracker->next;
|
||||
tracker->next->prev = tracker->prev;
|
||||
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void
|
||||
jdwp_tracker_ready( asocket* s )
|
||||
{
|
||||
JdwpTracker* t = (JdwpTracker*) s;
|
||||
|
||||
if (t->need_update) {
|
||||
apacket* p = get_apacket();
|
||||
t->need_update = 0;
|
||||
p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
|
||||
s->peer->enqueue(s->peer, p);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
jdwp_tracker_enqueue( asocket* s, apacket* p )
|
||||
{
|
||||
/* you can't write to this socket */
|
||||
put_apacket(p);
|
||||
s->peer->close(s->peer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
asocket*
|
||||
create_jdwp_tracker_service_socket( void )
|
||||
{
|
||||
JdwpTracker* t = calloc(sizeof(*t),1);
|
||||
|
||||
if (t == NULL)
|
||||
return NULL;
|
||||
|
||||
t->next = &_jdwp_trackers_list;
|
||||
t->prev = t->next->prev;
|
||||
|
||||
t->next->prev = t;
|
||||
t->prev->next = t;
|
||||
|
||||
install_local_socket(&t->socket);
|
||||
|
||||
t->socket.ready = jdwp_tracker_ready;
|
||||
t->socket.enqueue = jdwp_tracker_enqueue;
|
||||
t->socket.close = jdwp_tracker_close;
|
||||
t->need_update = 1;
|
||||
|
||||
return &t->socket;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
init_jdwp(void)
|
||||
{
|
||||
_jdwp_list.next = &_jdwp_list;
|
||||
_jdwp_list.prev = &_jdwp_list;
|
||||
|
||||
_jdwp_trackers_list.next = &_jdwp_trackers_list;
|
||||
_jdwp_trackers_list.prev = &_jdwp_trackers_list;
|
||||
|
||||
return jdwp_control_init( &_jdwp_control,
|
||||
JDWP_CONTROL_NAME,
|
||||
JDWP_CONTROL_NAME_LEN );
|
||||
}
|
||||
|
||||
#endif /* !ADB_HOST */
|
||||
|
474
adb/kdbg.c
Normal file
474
adb/kdbg.c
Normal file
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
|
||||
#include <linux/usb/ch9.h>
|
||||
#else
|
||||
#include <linux/usb_ch9.h>
|
||||
#endif
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include <cutils/fdevent.h>
|
||||
#include "adb.h"
|
||||
|
||||
|
||||
#define TRACE_USB 0
|
||||
|
||||
#if TRACE_USB
|
||||
#define DBG1(x...) fprintf(stderr, x)
|
||||
#define DBG(x...) fprintf(stderr, x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#define DBG1(x...)
|
||||
#endif
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
struct usb_handle *next;
|
||||
char fname[32];
|
||||
int desc;
|
||||
unsigned char ep_in;
|
||||
unsigned char ep_out;
|
||||
unsigned int interface;
|
||||
};
|
||||
|
||||
static struct usb_handle *g_first_usb_device;
|
||||
static struct usb_handle *g_last_usb_device;
|
||||
|
||||
static void new_device(char *dev_name, unsigned char ep_in, unsigned char ep_out, unsigned int interface)
|
||||
{
|
||||
struct usb_handle* usb;
|
||||
|
||||
DBG("New device being added %s \n", dev_name);
|
||||
|
||||
usb = (struct usb_handle *)calloc(1, sizeof(struct usb_handle));
|
||||
strcpy(usb->fname, dev_name);
|
||||
usb->ep_in = ep_in;
|
||||
usb->ep_out = ep_out;
|
||||
usb->interface = interface;
|
||||
usb->next = NULL;
|
||||
if(g_last_usb_device)
|
||||
g_last_usb_device->next = usb;
|
||||
else
|
||||
g_first_usb_device = usb;
|
||||
g_last_usb_device = usb;
|
||||
}
|
||||
|
||||
|
||||
static inline int badname(const char *name)
|
||||
{
|
||||
if(!isdigit(name[0])) return 1;
|
||||
if(!isdigit(name[1])) return 1;
|
||||
if(!isdigit(name[2])) return 1;
|
||||
if(name[3] != 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_usb_devices(const char *base, unsigned vendor, unsigned product1, unsigned product2,
|
||||
unsigned ifclass, unsigned ifsubclass,
|
||||
unsigned ifprotocol, unsigned numendpoints)
|
||||
{
|
||||
char busname[32], devname[32];
|
||||
unsigned char local_ep_in, local_ep_out;
|
||||
DIR *busdir , *devdir ;
|
||||
struct dirent *de;
|
||||
int fd ;
|
||||
int ret_val = -1;
|
||||
int found_device = 0;
|
||||
|
||||
busdir = opendir(base);
|
||||
if(busdir == 0) return 0;
|
||||
|
||||
while((de = readdir(busdir)) != 0) {
|
||||
if(badname(de->d_name)) continue;
|
||||
|
||||
snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
|
||||
devdir = opendir(busname);
|
||||
if(devdir == 0) continue;
|
||||
|
||||
DBG("[ scanning %s ]\n", busname);
|
||||
while((de = readdir(devdir))) {
|
||||
if(badname(de->d_name)) continue;
|
||||
snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
|
||||
|
||||
DBG("[ scanning %s ]\n", devname);
|
||||
fd = open(devname, O_RDWR);
|
||||
if(fd < 0) {
|
||||
continue;
|
||||
} else {
|
||||
unsigned char devdesc[256];
|
||||
unsigned char* bufptr = devdesc;
|
||||
struct usb_device_descriptor* device;
|
||||
struct usb_config_descriptor* config;
|
||||
struct usb_interface_descriptor* interface;
|
||||
struct usb_endpoint_descriptor *ep1, *ep2;
|
||||
unsigned vid, pid;
|
||||
int i, interfaces;
|
||||
|
||||
size_t desclength = read(fd, devdesc, sizeof(devdesc));
|
||||
|
||||
// should have device and configuration descriptors, and atleast two endpoints
|
||||
if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
|
||||
DBG("desclength %d is too small\n", desclength);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
device = (struct usb_device_descriptor*)bufptr;
|
||||
bufptr += USB_DT_DEVICE_SIZE;
|
||||
if(device->bLength == USB_DT_DEVICE_SIZE && device->bDescriptorType == USB_DT_DEVICE) {
|
||||
vid = __le16_to_cpu(device->idVendor);
|
||||
pid = __le16_to_cpu(device->idProduct);
|
||||
pid = devdesc[10] | (devdesc[11] << 8);
|
||||
DBG("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
|
||||
if((vendor == vid) && (product1 == pid || product2 == pid)){
|
||||
|
||||
// should have config descriptor next
|
||||
config = (struct usb_config_descriptor *)bufptr;
|
||||
bufptr += USB_DT_CONFIG_SIZE;
|
||||
if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
|
||||
DBG("usb_config_descriptor not found\n");
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// loop through all the interfaces and look for the ADB interface
|
||||
interfaces = config->bNumInterfaces;
|
||||
for (i = 0; i < interfaces; i++) {
|
||||
if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength)
|
||||
break;
|
||||
|
||||
interface = (struct usb_interface_descriptor *)bufptr;
|
||||
bufptr += USB_DT_INTERFACE_SIZE;
|
||||
if (interface->bLength != USB_DT_INTERFACE_SIZE ||
|
||||
interface->bDescriptorType != USB_DT_INTERFACE) {
|
||||
DBG("usb_interface_descriptor not found\n");
|
||||
break;
|
||||
}
|
||||
|
||||
DBG("bInterfaceClass: %d, bInterfaceSubClass: %d,\
|
||||
bInterfaceProtocol: %d, bNumEndpoints: %d\n",
|
||||
interface->bInterfaceClass, interface->bInterfaceSubClass,
|
||||
interface->bInterfaceProtocol, interface->bNumEndpoints);
|
||||
// Sooner bootloader has zero for bInterfaceClass, while adb has USB_CLASS_CDC_DATA
|
||||
if (interface->bInterfaceClass == ifclass &&
|
||||
interface->bInterfaceSubClass == ifsubclass &&
|
||||
interface->bInterfaceProtocol == ifprotocol &&
|
||||
interface->bNumEndpoints == numendpoints) {
|
||||
|
||||
DBG("looking for bulk endpoints\n");
|
||||
// looks like ADB...
|
||||
ep1 = (struct usb_endpoint_descriptor *)bufptr;
|
||||
bufptr += USB_DT_ENDPOINT_SIZE;
|
||||
ep2 = (struct usb_endpoint_descriptor *)bufptr;
|
||||
bufptr += USB_DT_ENDPOINT_SIZE;
|
||||
|
||||
if (bufptr > devdesc + desclength ||
|
||||
ep1->bLength != USB_DT_ENDPOINT_SIZE ||
|
||||
ep1->bDescriptorType != USB_DT_ENDPOINT ||
|
||||
ep2->bLength != USB_DT_ENDPOINT_SIZE ||
|
||||
ep2->bDescriptorType != USB_DT_ENDPOINT) {
|
||||
DBG("endpoints not found\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// both endpoints should be bulk
|
||||
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
|
||||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
|
||||
DBG("bulk endpoints not found\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// we have a match. now we just need to figure out which is in and which is out.
|
||||
if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
|
||||
local_ep_in = ep1->bEndpointAddress;
|
||||
local_ep_out = ep2->bEndpointAddress;
|
||||
} else {
|
||||
local_ep_in = ep2->bEndpointAddress;
|
||||
local_ep_out = ep1->bEndpointAddress;
|
||||
}
|
||||
|
||||
new_device(devname, local_ep_in, local_ep_out, i);
|
||||
found_device = 1;
|
||||
close(fd);
|
||||
} else {
|
||||
// skip to next interface
|
||||
bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE);
|
||||
}
|
||||
} // end of for
|
||||
} //end of productid if
|
||||
}
|
||||
close(fd);
|
||||
} // end of if
|
||||
} // end of devdir while
|
||||
closedir(devdir);
|
||||
} //end of busdir while
|
||||
closedir(busdir);
|
||||
|
||||
return found_device;
|
||||
}
|
||||
|
||||
|
||||
static void find_devices(unsigned vendor, unsigned product1, unsigned product2)
|
||||
{
|
||||
// don't scan /proc/bus/usb if we find something in /dev/bus/usb, to avoid duplication of devices.
|
||||
if (!find_usb_devices("/dev/bus/usb", vendor, product1, product2, USB_CLASS_VENDOR_SPEC, 1, 0, 2)) {
|
||||
find_usb_devices("/proc/bus/usb", vendor, product1, product2, USB_CLASS_VENDOR_SPEC, 1, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
void usb_open_device(struct usb_handle *h)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
h->desc = open(h->fname, O_RDWR);
|
||||
//DBG("[ usb open %s fd = %d]\n", h->fname, h->desc);
|
||||
n = ioctl(h->desc, USBDEVFS_CLAIMINTERFACE, &h->interface);
|
||||
if(n != 0) goto fail;
|
||||
// t->usb_is_open = 1;
|
||||
return;
|
||||
|
||||
|
||||
fail:
|
||||
DBG("[ usb open %s error=%d, err_str = %s]\n",
|
||||
h->fname, errno, strerror(errno));
|
||||
if(h->desc >= 0) {
|
||||
close(h->desc);
|
||||
h->desc = -1;
|
||||
}
|
||||
// t->usb_is_open = 0;
|
||||
}
|
||||
|
||||
int usb_write(struct usb_handle *h, const void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
struct usbdevfs_bulktransfer bulk;
|
||||
int n;
|
||||
|
||||
while(len >= 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
bulk.ep = h->ep_out;
|
||||
bulk.len = xfer;
|
||||
bulk.data = data;
|
||||
bulk.timeout = 500 + xfer * 8;
|
||||
|
||||
bulk.timeout *= 10;
|
||||
|
||||
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
|
||||
if(n != xfer) {
|
||||
DBG("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if(len == 0)
|
||||
break;
|
||||
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
if(len == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_read(struct usb_handle *h, void *_data, int len)
|
||||
{
|
||||
unsigned char *data_start = (unsigned char*) _data;
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
struct usbdevfs_bulktransfer bulk;
|
||||
int n;
|
||||
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
bulk.ep = h->ep_in;
|
||||
bulk.len = xfer;
|
||||
bulk.data = data;
|
||||
|
||||
// adjust timeout based on the data we're transferring,
|
||||
// otherwise the timeout interrupts us partway through
|
||||
// and we get out of sync...
|
||||
bulk.timeout = 500 + xfer * 8;
|
||||
|
||||
bulk.timeout = 500 + xfer / 128;
|
||||
|
||||
// bulk.timeout *= 10;
|
||||
DBG1("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
|
||||
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
|
||||
DBG1("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
|
||||
if(n < 0) {
|
||||
if((errno == ETIMEDOUT) && (h->desc != -1)) {
|
||||
DBG("[ timeout ]\n");
|
||||
if(n > 0){
|
||||
data += n;
|
||||
len -= n;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
DBG1("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= n;
|
||||
data += n;
|
||||
if(n != xfer)
|
||||
break;
|
||||
}
|
||||
|
||||
return data - data_start;
|
||||
}
|
||||
|
||||
void usb_kick(struct usb_handle *h)
|
||||
{
|
||||
close(h->desc);
|
||||
h->desc = -1;
|
||||
}
|
||||
|
||||
int usb_close(struct usb_handle *h)
|
||||
{
|
||||
close(h->desc);
|
||||
h->desc = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void list_devices()
|
||||
{
|
||||
int i = 0;
|
||||
struct usb_handle *h = g_first_usb_device;
|
||||
while(h) {
|
||||
printf("%d: %s\n", i, h->fname);
|
||||
i++;
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char buffer[4096/*-64*/];
|
||||
int len;
|
||||
int c;
|
||||
char *arg;
|
||||
int device_index = 0;
|
||||
struct usb_handle *h;
|
||||
int i;
|
||||
|
||||
find_devices(VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER, PRODUCT_ID_SOONER_COMP);
|
||||
while(1) {
|
||||
c = getopt(argc, argv, "d:l");
|
||||
if (c == EOF)
|
||||
break;
|
||||
switch(c) {
|
||||
case 'd':
|
||||
device_index = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
case 'l':
|
||||
list_devices();
|
||||
return 0;
|
||||
case '?':
|
||||
fprintf(stderr, "%s: invalid option -%c\n",
|
||||
argv[0], optopt);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind - 1;
|
||||
argv += optind - 1;
|
||||
|
||||
h = g_first_usb_device;
|
||||
i = device_index;
|
||||
while(i-- > 0 && h) {
|
||||
h = h->next;
|
||||
}
|
||||
if(h == NULL) {
|
||||
fprintf(stderr, "no device %d\n", device_index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
usb_open_device(h);
|
||||
if(g_first_usb_device->desc < 0) {
|
||||
fprintf(stderr, "could not open device (%s), %s\n", h->fname, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
len = 0;
|
||||
if(argc == 1) {
|
||||
char *line = NULL;
|
||||
size_t line_size = 0;
|
||||
while((len = getline(&line, &line_size, stdin)) >= 0) {
|
||||
//if(len > 0 && line[len - 1] == '\n')
|
||||
// len--;
|
||||
usb_write(h, line, len);
|
||||
while(1) {
|
||||
len = usb_read(h, buffer, sizeof(buffer));
|
||||
if(len < 0)
|
||||
break;
|
||||
write(STDOUT_FILENO, buffer, len);
|
||||
if(len < (int)sizeof(buffer))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
while(argc > 1) {
|
||||
argc--;
|
||||
argv++;
|
||||
arg = *argv;
|
||||
while(arg) {
|
||||
if(*arg)
|
||||
buffer[len++] = *arg++;
|
||||
else {
|
||||
arg = NULL;
|
||||
if(argc > 1)
|
||||
buffer[len++] = ' ';
|
||||
else
|
||||
break;
|
||||
}
|
||||
if(len == sizeof(buffer)) {
|
||||
usb_write(h, buffer, len);
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
usb_write(h, buffer, len);
|
||||
while(1) {
|
||||
len = usb_read(h, buffer, sizeof(buffer));
|
||||
if(len < 0)
|
||||
break;
|
||||
write(STDOUT_FILENO, buffer, len);
|
||||
if(len < (int)sizeof(buffer))
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
92
adb/log_service.c
Normal file
92
adb/log_service.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <utils/logger.h>
|
||||
#include "sysdeps.h"
|
||||
#include "adb.h"
|
||||
|
||||
#define LOG_FILE_DIR "/dev/log/"
|
||||
|
||||
void write_log_entry(int fd, struct logger_entry *buf);
|
||||
|
||||
void log_service(int fd, void *cookie)
|
||||
{
|
||||
/* get the name of the log filepath to read */
|
||||
char * log_filepath = cookie;
|
||||
|
||||
/* open the log file. */
|
||||
int logfd = unix_open(log_filepath, O_RDONLY);
|
||||
if (logfd < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// temp buffer to read the entries
|
||||
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
|
||||
struct logger_entry *entry = (struct logger_entry *) buf;
|
||||
|
||||
while (1) {
|
||||
int ret;
|
||||
|
||||
ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
// perror("logcat read");
|
||||
goto done;
|
||||
}
|
||||
else if (!ret) {
|
||||
// fprintf(stderr, "read: Unexpected EOF!\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* NOTE: driver guarantees we read exactly one full entry */
|
||||
|
||||
entry->msg[entry->len] = '\0';
|
||||
|
||||
write_log_entry(fd, entry);
|
||||
}
|
||||
|
||||
done:
|
||||
unix_close(fd);
|
||||
free(log_filepath);
|
||||
}
|
||||
|
||||
/* returns the full path to the log file in a newly allocated string */
|
||||
char * get_log_file_path(const char * log_name) {
|
||||
char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1);
|
||||
|
||||
strcpy(log_device, LOG_FILE_DIR);
|
||||
strcat(log_device, log_name);
|
||||
|
||||
return log_device;
|
||||
}
|
||||
|
||||
|
||||
/* prints one log entry into the file descriptor fd */
|
||||
void write_log_entry(int fd, struct logger_entry *buf)
|
||||
{
|
||||
size_t size = sizeof(struct logger_entry) + buf->len;
|
||||
|
||||
writex(fd, buf, size);
|
||||
}
|
14
adb/mutex_list.h
Normal file
14
adb/mutex_list.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* the list of mutexes used by addb */
|
||||
#ifndef ADB_MUTEX
|
||||
#error ADB_MUTEX not defined when including this file
|
||||
#endif
|
||||
|
||||
ADB_MUTEX(dns_lock)
|
||||
ADB_MUTEX(socket_list_lock)
|
||||
ADB_MUTEX(transport_lock)
|
||||
#if ADB_HOST
|
||||
ADB_MUTEX(local_transports_lock)
|
||||
#endif
|
||||
ADB_MUTEX(usb_lock)
|
||||
|
||||
#undef ADB_MUTEX
|
252
adb/protocol.txt
Normal file
252
adb/protocol.txt
Normal file
|
@ -0,0 +1,252 @@
|
|||
|
||||
--- a replacement for aproto -------------------------------------------
|
||||
|
||||
When it comes down to it, aproto's primary purpose is to forward
|
||||
various streams between the host computer and client device (in either
|
||||
direction).
|
||||
|
||||
This replacement further simplifies the concept, reducing the protocol
|
||||
to an extremely straightforward model optimized to accomplish the
|
||||
forwarding of these streams and removing additional state or
|
||||
complexity.
|
||||
|
||||
The host side becomes a simple comms bridge with no "UI", which will
|
||||
be used by either commandline or interactive tools to communicate with
|
||||
a device or emulator that is connected to the bridge.
|
||||
|
||||
The protocol is designed to be straightforward and well-defined enough
|
||||
that if it needs to be reimplemented in another environment (Java
|
||||
perhaps), there should not problems ensuring perfect interoperability.
|
||||
|
||||
The protocol discards the layering aproto has and should allow the
|
||||
implementation to be much more robust.
|
||||
|
||||
|
||||
--- protocol overview and basics ---------------------------------------
|
||||
|
||||
The transport layer deals in "messages", which consist of a 24 byte
|
||||
header followed (optionally) by a payload. The header consists of 6
|
||||
32 bit words which are sent across the wire in little endian format.
|
||||
|
||||
struct message {
|
||||
unsigned command; /* command identifier constant */
|
||||
unsigned arg0; /* first argument */
|
||||
unsigned arg1; /* second argument */
|
||||
unsigned data_length; /* length of payload (0 is allowed) */
|
||||
unsigned data_crc32; /* crc32 of data payload */
|
||||
unsigned magic; /* command ^ 0xffffffff */
|
||||
};
|
||||
|
||||
Receipt of an invalid message header, corrupt message payload, or an
|
||||
unrecognized command MUST result in the closing of the remote
|
||||
connection. The protocol depends on shared state and any break in the
|
||||
message stream will result in state getting out of sync.
|
||||
|
||||
The following sections describe the six defined message types in
|
||||
detail. Their format is COMMAND(arg0, arg1, payload) where the payload
|
||||
is represented by a quoted string or an empty string if none should be
|
||||
sent.
|
||||
|
||||
The identifiers "local-id" and "remote-id" are always relative to the
|
||||
*sender* of the message, so for a receiver, the meanings are effectively
|
||||
reversed.
|
||||
|
||||
|
||||
|
||||
--- CONNECT(version, maxdata, "system-identity-string") ----------------
|
||||
|
||||
The CONNECT message establishes the presence of a remote system.
|
||||
The version is used to ensure protocol compatibility and maxdata
|
||||
declares the maximum message body size that the remote system
|
||||
is willing to accept.
|
||||
|
||||
Currently, version=0x01000000 and maxdata=4096
|
||||
|
||||
Both sides send a CONNECT message when the connection between them is
|
||||
established. Until a CONNECT message is received no other messages may
|
||||
be sent. Any messages received before a CONNECT message MUST be ignored.
|
||||
|
||||
If a CONNECT message is received with an unknown version or insufficiently
|
||||
large maxdata value, the connection with the other side must be closed.
|
||||
|
||||
The system identity string should be "<systemtype>:<serialno>:<banner>"
|
||||
where systemtype is "bootloader", "device", or "host", serialno is some
|
||||
kind of unique ID (or empty), and banner is a human-readable version
|
||||
or identifier string (informational only).
|
||||
|
||||
|
||||
--- OPEN(local-id, 0, "destination") -----------------------------------
|
||||
|
||||
The OPEN message informs the recipient that the sender has a stream
|
||||
identified by local-id that it wishes to connect to the named
|
||||
destination in the message payload. The local-id may not be zero.
|
||||
|
||||
The OPEN message MUST result in either a READY message indicating that
|
||||
the connection has been established (and identifying the other end) or
|
||||
a CLOSE message, indicating failure. An OPEN message also implies
|
||||
a READY message sent at the same time.
|
||||
|
||||
Common destination naming conventions include:
|
||||
|
||||
* "tcp:<host>:<port>" - host may be omitted to indicate localhost
|
||||
* "udp:<host>:<port>" - host may be omitted to indicate localhost
|
||||
* "local-dgram:<identifier>"
|
||||
* "local-stream:<identifier>"
|
||||
* "shell" - local shell service
|
||||
* "upload" - service for pushing files across (like aproto's /sync)
|
||||
* "fs-bridge" - FUSE protocol filesystem bridge
|
||||
|
||||
|
||||
--- READY(local-id, remote-id, "") -------------------------------------
|
||||
|
||||
The READY message informs the recipient that the sender's stream
|
||||
identified by local-id is ready for write messages and that it is
|
||||
connected to the recipient's stream identified by remote-id.
|
||||
|
||||
Neither the local-id nor the remote-id may be zero.
|
||||
|
||||
A READY message containing a remote-id which does not map to an open
|
||||
stream on the recipient's side is ignored. The stream may have been
|
||||
closed while this message was in-flight.
|
||||
|
||||
The local-id is ignored on all but the first READY message (where it
|
||||
is used to establish the connection). Nonetheless, the local-id MUST
|
||||
not change on later READY messages sent to the same stream.
|
||||
|
||||
|
||||
|
||||
--- WRITE(0, remote-id, "data") ----------------------------------------
|
||||
|
||||
The WRITE message sends data to the recipient's stream identified by
|
||||
remote-id. The payload MUST be <= maxdata in length.
|
||||
|
||||
A WRITE message containing a remote-id which does not map to an open
|
||||
stream on the recipient's side is ignored. The stream may have been
|
||||
closed while this message was in-flight.
|
||||
|
||||
A WRITE message may not be sent until a READY message is received.
|
||||
Once a WRITE message is sent, an additional WRITE message may not be
|
||||
sent until another READY message has been received. Recipients of
|
||||
a WRITE message that is in violation of this requirement will CLOSE
|
||||
the connection.
|
||||
|
||||
|
||||
--- CLOSE(local-id, remote-id, "") -------------------------------------
|
||||
|
||||
The CLOSE message informs recipient that the connection between the
|
||||
sender's stream (local-id) and the recipient's stream (remote-id) is
|
||||
broken. The remote-id MUST not be zero, but the local-id MAY be zero
|
||||
if this CLOSE indicates a failed OPEN.
|
||||
|
||||
A CLOSE message containing a remote-id which does not map to an open
|
||||
stream on the recipient's side is ignored. The stream may have
|
||||
already been closed by the recipient while this message was in-flight.
|
||||
|
||||
The recipient should not respond to a CLOSE message in any way. The
|
||||
recipient should cancel pending WRITEs or CLOSEs, but this is not a
|
||||
requirement, since they will be ignored.
|
||||
|
||||
|
||||
--- SYNC(online, sequence, "") -----------------------------------------
|
||||
|
||||
The SYNC message is used by the io pump to make sure that stale
|
||||
outbound messages are discarded when the connection to the remote side
|
||||
is broken. It is only used internally to the bridge and never valid
|
||||
to send across the wire.
|
||||
|
||||
* when the connection to the remote side goes offline, the io pump
|
||||
sends a SYNC(0, 0) and starts discarding all messages
|
||||
* when the connection to the remote side is established, the io pump
|
||||
sends a SYNC(1, token) and continues to discard messages
|
||||
* when the io pump receives a matching SYNC(1, token), it once again
|
||||
starts accepting messages to forward to the remote side
|
||||
|
||||
|
||||
--- message command constants ------------------------------------------
|
||||
|
||||
#define A_SYNC 0x434e5953
|
||||
#define A_CNXN 0x4e584e43
|
||||
#define A_OPEN 0x4e45504f
|
||||
#define A_OKAY 0x59414b4f
|
||||
#define A_CLSE 0x45534c43
|
||||
#define A_WRTE 0x45545257
|
||||
|
||||
|
||||
|
||||
--- implementation details ---------------------------------------------
|
||||
|
||||
The core of the bridge program will use three threads. One thread
|
||||
will be a select/epoll loop to handle io between various inbound and
|
||||
outbound connections and the connection to the remote side.
|
||||
|
||||
The remote side connection will be implemented as two threads (one for
|
||||
reading, one for writing) and a datagram socketpair to provide the
|
||||
channel between the main select/epoll thread and the remote connection
|
||||
threadpair. The reason for this is that for usb connections, the
|
||||
kernel interface on linux and osx does not allow you to do meaningful
|
||||
nonblocking IO.
|
||||
|
||||
The endian swapping for the message headers will happen (as needed) in
|
||||
the remote connection threadpair and that the rest of the program will
|
||||
always treat message header values as native-endian.
|
||||
|
||||
The bridge program will be able to have a number of mini-servers
|
||||
compiled in. They will be published under known names (examples
|
||||
"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a
|
||||
service, the bridge program will create a stream socketpair and spawn
|
||||
a thread or subprocess to handle the io.
|
||||
|
||||
|
||||
--- simplified / embedded implementation -------------------------------
|
||||
|
||||
For limited environments, like the bootloader, it is allowable to
|
||||
support a smaller, fixed number of channels using pre-assigned channel
|
||||
ID numbers such that only one stream may be connected to a bootloader
|
||||
endpoint at any given time. The protocol remains unchanged, but the
|
||||
"embedded" version of it is less dynamic.
|
||||
|
||||
The bootloader will support two streams. A "bootloader:debug" stream,
|
||||
which may be opened to get debug messages from the bootloader and a
|
||||
"bootloader:control", stream which will support the set of basic
|
||||
bootloader commands.
|
||||
|
||||
Example command stream dialogues:
|
||||
"flash_kernel,2515049,........\n" "okay\n"
|
||||
"flash_ramdisk,5038,........\n" "fail,flash write error\n"
|
||||
"bogus_command......" <CLOSE>
|
||||
|
||||
|
||||
--- future expansion ---------------------------------------------------
|
||||
|
||||
I plan on providing either a message or a special control stream so that
|
||||
the client device could ask the host computer to setup inbound socket
|
||||
translations on the fly on behalf of the client device.
|
||||
|
||||
|
||||
The initial design does handshaking to provide flow control, with a
|
||||
message flow that looks like:
|
||||
|
||||
>OPEN <READY >WRITE <READY >WRITE <READY >WRITE <CLOSE
|
||||
|
||||
The far side may choose to issue the READY message as soon as it receives
|
||||
a WRITE or it may defer the READY until the write to the local stream
|
||||
succeeds. A future version may want to do some level of windowing where
|
||||
multiple WRITEs may be sent without requiring individual READY acks.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
--- smartsockets -------------------------------------------------------
|
||||
|
||||
Port 5037 is used for smart sockets which allow a client on the host
|
||||
side to request access to a service in the host adb daemon or in the
|
||||
remote (device) daemon. The service is requested by ascii name,
|
||||
preceeded by a 4 digit hex length. Upon successful connection an
|
||||
"OKAY" response is sent, otherwise a "FAIL" message is returned. Once
|
||||
connected the client is talking to that (remote or local) service.
|
||||
|
||||
client: <hex4> <service-name>
|
||||
server: "OKAY"
|
||||
|
||||
client: <hex4> <service-name>
|
||||
server: "FAIL" <hex4> <reason>
|
||||
|
103
adb/remount_service.c
Normal file
103
adb/remount_service.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_ADB
|
||||
#include "adb.h"
|
||||
|
||||
|
||||
static int system_ro = 1;
|
||||
|
||||
/* Returns the mount number of the requested partition from /proc/mtd */
|
||||
static int find_mount(const char *findme)
|
||||
{
|
||||
int fd;
|
||||
int res;
|
||||
int size;
|
||||
char *token = NULL;
|
||||
const char delims[] = "\n";
|
||||
char buf[1024];
|
||||
|
||||
fd = unix_open("/proc/mtd", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
size = adb_read(fd, buf, sizeof(buf) - 1);
|
||||
adb_close(fd);
|
||||
|
||||
token = strtok(buf, delims);
|
||||
|
||||
while (token) {
|
||||
char mtdname[16];
|
||||
int mtdnum, mtdsize, mtderasesize;
|
||||
|
||||
res = sscanf(token, "mtd%d: %x %x %15s",
|
||||
&mtdnum, &mtdsize, &mtderasesize, mtdname);
|
||||
|
||||
if (res == 4 && !strcmp(mtdname, findme))
|
||||
return mtdnum;
|
||||
|
||||
token = strtok(NULL, delims);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init mounts /system as read only, remount to enable writes. */
|
||||
static int remount_system()
|
||||
{
|
||||
int num;
|
||||
char source[64];
|
||||
if (system_ro == 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((num = find_mount("\"system\"")) < 0)
|
||||
return -1;
|
||||
|
||||
snprintf(source, sizeof source, "/dev/block/mtdblock%d", num);
|
||||
system_ro = mount(source, "/system", "yaffs2", MS_REMOUNT, NULL);
|
||||
return system_ro;
|
||||
}
|
||||
|
||||
static void write_string(int fd, const char* str)
|
||||
{
|
||||
writex(fd, str, strlen(str));
|
||||
}
|
||||
|
||||
void remount_service(int fd, void *cookie)
|
||||
{
|
||||
int ret = remount_system();
|
||||
|
||||
if (!ret)
|
||||
write_string(fd, "remount succeeded\n");
|
||||
else {
|
||||
char buffer[200];
|
||||
snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno));
|
||||
write_string(fd, buffer);
|
||||
}
|
||||
|
||||
adb_close(fd);
|
||||
}
|
||||
|
370
adb/services.c
Normal file
370
adb/services.c
Normal file
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_ADB
|
||||
#include "adb.h"
|
||||
#include "file_sync_service.h"
|
||||
|
||||
#if ADB_HOST
|
||||
# ifndef HAVE_WINSOCK
|
||||
# include <netinet/in.h>
|
||||
# include <netdb.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct stinfo stinfo;
|
||||
|
||||
struct stinfo {
|
||||
void (*func)(int fd, void *cookie);
|
||||
int fd;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
|
||||
void *service_bootstrap_func(void *x)
|
||||
{
|
||||
stinfo *sti = x;
|
||||
sti->func(sti->fd, sti->cookie);
|
||||
free(sti);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
ADB_MUTEX_DEFINE( dns_lock );
|
||||
|
||||
static void dns_service(int fd, void *cookie)
|
||||
{
|
||||
char *hostname = cookie;
|
||||
struct hostent *hp;
|
||||
unsigned zero = 0;
|
||||
|
||||
adb_mutex_lock(&dns_lock);
|
||||
hp = gethostbyname(hostname);
|
||||
if(hp == 0) {
|
||||
writex(fd, &zero, 4);
|
||||
} else {
|
||||
writex(fd, hp->h_addr, 4);
|
||||
}
|
||||
adb_mutex_unlock(&dns_lock);
|
||||
adb_close(fd);
|
||||
}
|
||||
#else
|
||||
extern int recovery_mode;
|
||||
|
||||
static void recover_service(int s, void *cookie)
|
||||
{
|
||||
unsigned char buf[4096];
|
||||
unsigned count = (unsigned) cookie;
|
||||
int fd;
|
||||
|
||||
fd = adb_creat("/tmp/update", 0644);
|
||||
if(fd < 0) {
|
||||
adb_close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
while(count > 0) {
|
||||
unsigned xfer = (count > 4096) ? 4096 : count;
|
||||
if(readx(s, buf, xfer)) break;
|
||||
if(writex(fd, buf, xfer)) break;
|
||||
count -= xfer;
|
||||
}
|
||||
|
||||
if(count == 0) {
|
||||
writex(s, "OKAY", 4);
|
||||
} else {
|
||||
writex(s, "FAIL", 4);
|
||||
}
|
||||
adb_close(fd);
|
||||
adb_close(s);
|
||||
|
||||
fd = adb_creat("/tmp/update.begin", 0644);
|
||||
adb_close(fd);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static void echo_service(int fd, void *cookie)
|
||||
{
|
||||
char buf[4096];
|
||||
int r;
|
||||
char *p;
|
||||
int c;
|
||||
|
||||
for(;;) {
|
||||
r = read(fd, buf, 4096);
|
||||
if(r == 0) goto done;
|
||||
if(r < 0) {
|
||||
if(errno == EINTR) continue;
|
||||
else goto done;
|
||||
}
|
||||
|
||||
c = r;
|
||||
p = buf;
|
||||
while(c > 0) {
|
||||
r = write(fd, p, c);
|
||||
if(r > 0) {
|
||||
c -= r;
|
||||
p += r;
|
||||
continue;
|
||||
}
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
close(fd);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int create_service_thread(void (*func)(int, void *), void *cookie)
|
||||
{
|
||||
stinfo *sti;
|
||||
adb_thread_t t;
|
||||
int s[2];
|
||||
|
||||
if(adb_socketpair(s)) {
|
||||
printf("cannot create service socket pair\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sti = malloc(sizeof(stinfo));
|
||||
if(sti == 0) fatal("cannot allocate stinfo");
|
||||
sti->func = func;
|
||||
sti->cookie = cookie;
|
||||
sti->fd = s[1];
|
||||
|
||||
if(adb_thread_create( &t, service_bootstrap_func, sti)){
|
||||
free(sti);
|
||||
adb_close(s[0]);
|
||||
adb_close(s[1]);
|
||||
printf("cannot create service thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
D("service thread started, %d:%d\n",s[0], s[1]);
|
||||
return s[0];
|
||||
}
|
||||
|
||||
static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
|
||||
{
|
||||
#ifdef HAVE_WIN32_PROC
|
||||
fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
|
||||
return -1;
|
||||
#else /* !HAVE_WIN32_PROC */
|
||||
char *devname;
|
||||
int ptm;
|
||||
pid_t pid;
|
||||
|
||||
ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
|
||||
if(ptm < 0){
|
||||
printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
fcntl(ptm, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
if(grantpt(ptm) || unlockpt(ptm) ||
|
||||
((devname = (char*) ptsname(ptm)) == 0)){
|
||||
printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if(pid < 0) {
|
||||
printf("- fork failed: %s -\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(pid == 0){
|
||||
int pts;
|
||||
|
||||
setsid();
|
||||
|
||||
pts = unix_open(devname, O_RDWR);
|
||||
if(pts < 0) exit(-1);
|
||||
|
||||
dup2(pts, 0);
|
||||
dup2(pts, 1);
|
||||
dup2(pts, 2);
|
||||
|
||||
adb_close(ptm);
|
||||
|
||||
execl(cmd, cmd, arg0, arg1, NULL);
|
||||
fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
|
||||
cmd, strerror(errno), errno);
|
||||
exit(-1);
|
||||
} else {
|
||||
return ptm;
|
||||
}
|
||||
#endif /* !HAVE_WIN32_PROC */
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
#define SHELL_COMMAND "/bin/sh"
|
||||
#else
|
||||
#define SHELL_COMMAND "/system/bin/sh"
|
||||
#endif
|
||||
|
||||
int service_to_fd(const char *name)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if(!strncmp(name, "tcp:", 4)) {
|
||||
int port = atoi(name + 4);
|
||||
name = strchr(name + 4, ':');
|
||||
if(name == 0) {
|
||||
ret = socket_loopback_client(port, SOCK_STREAM);
|
||||
if (ret >= 0)
|
||||
disable_tcp_nagle(ret);
|
||||
} else {
|
||||
#if ADB_HOST
|
||||
adb_mutex_lock(&dns_lock);
|
||||
ret = socket_network_client(name + 1, port, SOCK_STREAM);
|
||||
adb_mutex_unlock(&dns_lock);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#ifndef HAVE_WINSOCK /* winsock doesn't implement unix domain sockets */
|
||||
} else if(!strncmp(name, "local:", 6)) {
|
||||
ret = socket_local_client(name + 6,
|
||||
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localreserved:", 14)) {
|
||||
ret = socket_local_client(name + 14,
|
||||
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localabstract:", 14)) {
|
||||
ret = socket_local_client(name + 14,
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
|
||||
} else if(!strncmp(name, "localfilesystem:", 16)) {
|
||||
ret = socket_local_client(name + 16,
|
||||
ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
|
||||
#endif
|
||||
#if ADB_HOST
|
||||
} else if(!strncmp("dns:", name, 4)){
|
||||
char *n = strdup(name + 4);
|
||||
if(n == 0) return -1;
|
||||
ret = create_service_thread(dns_service, n);
|
||||
#else /* !ADB_HOST */
|
||||
} else if(!strncmp("dev:", name, 4)) {
|
||||
ret = unix_open(name + 4, O_RDWR);
|
||||
} else if(!strncmp(name, "framebuffer:", 12)) {
|
||||
ret = create_service_thread(framebuffer_service, 0);
|
||||
} else if(recovery_mode && !strncmp(name, "recover:", 8)) {
|
||||
ret = create_service_thread(recover_service, (void*) atoi(name + 8));
|
||||
} else if (!strncmp(name, "jdwp:", 5)) {
|
||||
ret = create_jdwp_connection_fd(atoi(name+5));
|
||||
} else if (!strncmp(name, "log:", 4)) {
|
||||
ret = create_service_thread(log_service, get_log_file_path(name + 4));
|
||||
#endif
|
||||
} else if(!HOST && !strncmp(name, "shell:", 6)) {
|
||||
if(name[6]) {
|
||||
ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
|
||||
} else {
|
||||
ret = create_subprocess(SHELL_COMMAND, "-", 0);
|
||||
}
|
||||
#if !ADB_HOST
|
||||
} else if(!strncmp(name, "sync:", 5)) {
|
||||
ret = create_service_thread(file_sync_service, NULL);
|
||||
} else if(!strncmp(name, "remount:", 8)) {
|
||||
ret = create_service_thread(remount_service, NULL);
|
||||
#endif
|
||||
#if 0
|
||||
} else if(!strncmp(name, "echo:", 5)){
|
||||
ret = create_service_thread(echo_service, 0);
|
||||
#endif
|
||||
}
|
||||
if (ret >= 0) {
|
||||
close_on_exec(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
struct state_info {
|
||||
transport_type transport;
|
||||
char* serial;
|
||||
int state;
|
||||
};
|
||||
|
||||
static void wait_for_state(int fd, void* cookie)
|
||||
{
|
||||
struct state_info* sinfo = cookie;
|
||||
char* err = "unknown error";
|
||||
|
||||
D("wait_for_state %d\n", sinfo->state);
|
||||
|
||||
atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
|
||||
if(t != 0) {
|
||||
writex(fd, "OKAY", 4);
|
||||
} else {
|
||||
sendfailmsg(fd, err);
|
||||
}
|
||||
|
||||
if (sinfo->serial)
|
||||
free(sinfo->serial);
|
||||
free(sinfo);
|
||||
adb_close(fd);
|
||||
D("wait_for_state is done\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
asocket* host_service_to_socket(const char* name, const char *serial)
|
||||
{
|
||||
if (!strcmp(name,"track-devices")) {
|
||||
return create_device_tracker();
|
||||
} else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
|
||||
struct state_info* sinfo = malloc(sizeof(struct state_info));
|
||||
|
||||
if (serial)
|
||||
sinfo->serial = strdup(serial);
|
||||
else
|
||||
sinfo->serial = NULL;
|
||||
|
||||
name += strlen("wait-for-");
|
||||
|
||||
if (!strncmp(name, "bootloader", strlen("bootloader"))) {
|
||||
sinfo->transport = kTransportUsb;
|
||||
sinfo->state = CS_BOOTLOADER;
|
||||
} else if (!strncmp(name, "local", strlen("local"))) {
|
||||
sinfo->transport = kTransportLocal;
|
||||
sinfo->state = CS_DEVICE;
|
||||
} else if (!strncmp(name, "usb", strlen("usb"))) {
|
||||
sinfo->transport = kTransportUsb;
|
||||
sinfo->state = CS_DEVICE;
|
||||
} else if (!strncmp(name, "any", strlen("any"))) {
|
||||
sinfo->transport = kTransportAny;
|
||||
sinfo->state = CS_DEVICE;
|
||||
} else {
|
||||
free(sinfo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd = create_service_thread(wait_for_state, sinfo);
|
||||
return create_local_socket(fd);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif /* ADB_HOST */
|
185
adb/shlist.c
Executable file
185
adb/shlist.c
Executable file
|
@ -0,0 +1,185 @@
|
|||
/*-------------------------------------------------------------------*/
|
||||
/* List Functionality */
|
||||
/*-------------------------------------------------------------------*/
|
||||
/* #define SH_LIST_DEBUG */
|
||||
/*-------------------------------------------------------------------*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "shlist.h"
|
||||
/*-------------------------------------------------------------------*/
|
||||
void shListInitList( SHLIST *listPtr )
|
||||
{
|
||||
listPtr->data = (void *)0L;
|
||||
listPtr->next = listPtr;
|
||||
listPtr->prev = listPtr;
|
||||
}
|
||||
|
||||
SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func )
|
||||
{
|
||||
SHLIST *item;
|
||||
|
||||
for(item=head->next;( item != head );item=item->next)
|
||||
if( func ) {
|
||||
if( func( val, item->data ) ) {
|
||||
return( item );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( item->data == val ) {
|
||||
return( item );
|
||||
}
|
||||
}
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
SHLIST *shListGetLastItem( SHLIST *head )
|
||||
{
|
||||
if( head->prev != head )
|
||||
return( head->prev );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
SHLIST *shListGetFirstItem( SHLIST *head )
|
||||
{
|
||||
if( head->next != head )
|
||||
return( head->next );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
SHLIST *shListGetNItem( SHLIST *head, unsigned long num )
|
||||
{
|
||||
SHLIST *item;
|
||||
unsigned long i;
|
||||
|
||||
for(i=0,item=head->next;( (i < num) && (item != head) );i++,item=item->next);
|
||||
if( item != head )
|
||||
return( item );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item )
|
||||
{
|
||||
if( item == NULL )
|
||||
return( NULL );
|
||||
if( item->next != head )
|
||||
return( item->next );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item )
|
||||
{
|
||||
if( item == NULL )
|
||||
return( NULL );
|
||||
if( item->prev != head )
|
||||
return( item->prev );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
void shListDelItem( SHLIST *head, SHLIST *item, shListFree func )
|
||||
{
|
||||
if( item == NULL )
|
||||
return;
|
||||
#ifdef SH_LIST_DEBUG
|
||||
fprintf(stderr, "Del %lx\n", (unsigned long)(item->data));
|
||||
#endif
|
||||
(item->prev)->next = item->next;
|
||||
(item->next)->prev = item->prev;
|
||||
if( func && item->data ) {
|
||||
func( (void *)(item->data) );
|
||||
}
|
||||
free( item );
|
||||
head->data = (void *)((unsigned long)(head->data) - 1);
|
||||
}
|
||||
|
||||
void shListInsFirstItem( SHLIST *head, void *val )
|
||||
{ /* Insert to the beginning of the list */
|
||||
SHLIST *item;
|
||||
|
||||
item = (SHLIST *)malloc( sizeof(SHLIST) );
|
||||
if( item == NULL )
|
||||
return;
|
||||
item->data = val;
|
||||
item->next = head->next;
|
||||
item->prev = head;
|
||||
(head->next)->prev = item;
|
||||
head->next = item;
|
||||
#ifdef SH_LIST_DEBUG
|
||||
fprintf(stderr, "Ins First %lx\n", (unsigned long)(item->data));
|
||||
#endif
|
||||
head->data = (void *)((unsigned long)(head->data) + 1);
|
||||
}
|
||||
|
||||
void shListInsLastItem( SHLIST *head, void *val )
|
||||
{ /* Insert to the end of the list */
|
||||
SHLIST *item;
|
||||
|
||||
item = (SHLIST *)malloc( sizeof(SHLIST) );
|
||||
if( item == NULL )
|
||||
return;
|
||||
item->data = val;
|
||||
item->next = head;
|
||||
item->prev = head->prev;
|
||||
(head->prev)->next = item;
|
||||
head->prev = item;
|
||||
#ifdef SH_LIST_DEBUG
|
||||
fprintf(stderr, "Ins Last %lx\n", (unsigned long)(item->data));
|
||||
#endif
|
||||
head->data = (void *)((unsigned long)(head->data) + 1);
|
||||
}
|
||||
|
||||
void shListInsBeforeItem( SHLIST *head, void *val, void *etal,
|
||||
shListCmp func )
|
||||
{
|
||||
SHLIST *item, *iptr;
|
||||
|
||||
if( func == NULL )
|
||||
shListInsFirstItem( head, val );
|
||||
else {
|
||||
item = (SHLIST *)malloc( sizeof(SHLIST) );
|
||||
if( item == NULL )
|
||||
return;
|
||||
item->data = val;
|
||||
for(iptr=head->next;( iptr != head );iptr=iptr->next)
|
||||
if( func( val, iptr->data, etal ) )
|
||||
break;
|
||||
item->next = iptr;
|
||||
item->prev = iptr->prev;
|
||||
(iptr->prev)->next = item;
|
||||
iptr->prev = item;
|
||||
#ifdef SH_LIST_DEBUG
|
||||
fprintf(stderr, "Ins Before %lx\n", (unsigned long)(item->data));
|
||||
#endif
|
||||
head->data = (void *)((unsigned long)(head->data) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void shListDelAllItems( SHLIST *head, shListFree func )
|
||||
{
|
||||
SHLIST *item;
|
||||
|
||||
for(item=head->next;( item != head );) {
|
||||
shListDelItem( head, item, func );
|
||||
item = head->next;
|
||||
}
|
||||
head->data = (void *)0L;
|
||||
}
|
||||
|
||||
void shListPrintAllItems( SHLIST *head, shListPrint func )
|
||||
{
|
||||
#ifdef SH_LIST_DEBUG
|
||||
SHLIST *item;
|
||||
|
||||
for(item=head->next;( item != head );item=item->next)
|
||||
if( func ) {
|
||||
func(item->data);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Item: %lx\n",(unsigned long)(item->data));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long shListGetCount( SHLIST *head )
|
||||
{
|
||||
return( (unsigned long)(head->data) );
|
||||
}
|
34
adb/shlist.h
Executable file
34
adb/shlist.h
Executable file
|
@ -0,0 +1,34 @@
|
|||
/*-------------------------------------------------------------------*/
|
||||
/* List Functionality */
|
||||
/*-------------------------------------------------------------------*/
|
||||
#ifndef _SHLIST_H_
|
||||
#define _SHLIST_H_
|
||||
|
||||
typedef struct SHLIST_STRUC {
|
||||
void *data;
|
||||
struct SHLIST_STRUC *next;
|
||||
struct SHLIST_STRUC *prev;
|
||||
} SHLIST;
|
||||
|
||||
typedef int (*shListCmp)( void *valo, void *valn, void *etalon );
|
||||
typedef int (*shListPrint)( void *val );
|
||||
typedef void (*shListFree)( void *val );
|
||||
typedef int (*shListEqual)( void *val, void *idata );
|
||||
|
||||
void shListInitList( SHLIST *listPtr );
|
||||
SHLIST *shListFindItem( SHLIST *head, void *val, shListEqual func );
|
||||
SHLIST *shListGetFirstItem( SHLIST *head );
|
||||
SHLIST *shListGetNItem( SHLIST *head, unsigned long num );
|
||||
SHLIST *shListGetLastItem( SHLIST *head );
|
||||
SHLIST *shListGetNextItem( SHLIST *head, SHLIST *item );
|
||||
SHLIST *shListGetPrevItem( SHLIST *head, SHLIST *item );
|
||||
void shListDelItem( SHLIST *head, SHLIST *item, shListFree func );
|
||||
void shListInsFirstItem( SHLIST *head, void *val );
|
||||
void shListInsBeforeItem( SHLIST *head, void *val, void *etalon,
|
||||
shListCmp func );
|
||||
void shListInsLastItem( SHLIST *head, void *val );
|
||||
void shListDelAllItems( SHLIST *head, shListFree func );
|
||||
void shListPrintAllItems( SHLIST *head, shListPrint func );
|
||||
unsigned long shListGetCount( SHLIST *head );
|
||||
|
||||
#endif
|
733
adb/sockets.c
Normal file
733
adb/sockets.c
Normal file
|
@ -0,0 +1,733 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_SOCKETS
|
||||
#include "adb.h"
|
||||
|
||||
ADB_MUTEX_DEFINE( socket_list_lock );
|
||||
|
||||
static void local_socket_close_locked(asocket *s);
|
||||
|
||||
int sendfailmsg(int fd, const char *reason)
|
||||
{
|
||||
char buf[9];
|
||||
int len;
|
||||
len = strlen(reason);
|
||||
if(len > 0xffff) len = 0xffff;
|
||||
snprintf(buf, sizeof buf, "FAIL%04x", len);
|
||||
if(writex(fd, buf, 8)) return -1;
|
||||
return writex(fd, reason, len);
|
||||
}
|
||||
|
||||
//extern int online;
|
||||
|
||||
static unsigned local_socket_next_id = 1;
|
||||
|
||||
static asocket local_socket_list = {
|
||||
.next = &local_socket_list,
|
||||
.prev = &local_socket_list,
|
||||
};
|
||||
|
||||
asocket *find_local_socket(unsigned id)
|
||||
{
|
||||
asocket *s;
|
||||
asocket *result = NULL;
|
||||
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
for(s = local_socket_list.next; s != &local_socket_list && !result; s = s->next) {
|
||||
if(s->id == id) result = s;
|
||||
}
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void install_local_socket(asocket *s)
|
||||
{
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
|
||||
s->id = local_socket_next_id++;
|
||||
|
||||
s->next = &local_socket_list;
|
||||
s->prev = local_socket_list.prev;
|
||||
s->prev->next = s;
|
||||
s->next->prev = s;
|
||||
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
}
|
||||
|
||||
void remove_socket(asocket *s)
|
||||
{
|
||||
// socket_list_lock should already be held
|
||||
if (s->prev && s->next)
|
||||
{
|
||||
s->prev->next = s->next;
|
||||
s->next->prev = s->prev;
|
||||
s->next = 0;
|
||||
s->prev = 0;
|
||||
s->id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void close_all_sockets(atransport *t)
|
||||
{
|
||||
asocket *s;
|
||||
|
||||
/* this is a little gross, but since s->close() *will* modify
|
||||
** the list out from under you, your options are limited.
|
||||
*/
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
restart:
|
||||
for(s = local_socket_list.next; s != &local_socket_list; s = s->next){
|
||||
if(s->transport == t || (s->peer && s->peer->transport == t)) {
|
||||
local_socket_close_locked(s);
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
}
|
||||
|
||||
static int local_socket_enqueue(asocket *s, apacket *p)
|
||||
{
|
||||
D("LS(%d): enqueue %d\n", s->id, p->len);
|
||||
|
||||
p->ptr = p->data;
|
||||
|
||||
/* if there is already data queue'd, we will receive
|
||||
** events when it's time to write. just add this to
|
||||
** the tail
|
||||
*/
|
||||
if(s->pkt_first) {
|
||||
goto enqueue;
|
||||
}
|
||||
|
||||
/* write as much as we can, until we
|
||||
** would block or there is an error/eof
|
||||
*/
|
||||
while(p->len > 0) {
|
||||
int r = adb_write(s->fd, p->ptr, p->len);
|
||||
if(r > 0) {
|
||||
p->len -= r;
|
||||
p->ptr += r;
|
||||
continue;
|
||||
}
|
||||
if((r == 0) || (errno != EAGAIN)) {
|
||||
D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
|
||||
s->close(s);
|
||||
return 1; /* not ready (error) */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(p->len == 0) {
|
||||
put_apacket(p);
|
||||
return 0; /* ready for more data */
|
||||
}
|
||||
|
||||
enqueue:
|
||||
p->next = 0;
|
||||
if(s->pkt_first) {
|
||||
s->pkt_last->next = p;
|
||||
} else {
|
||||
s->pkt_first = p;
|
||||
}
|
||||
s->pkt_last = p;
|
||||
|
||||
/* make sure we are notified when we can drain the queue */
|
||||
fdevent_add(&s->fde, FDE_WRITE);
|
||||
|
||||
return 1; /* not ready (backlog) */
|
||||
}
|
||||
|
||||
static void local_socket_ready(asocket *s)
|
||||
{
|
||||
/* far side is ready for data, pay attention to
|
||||
readable events */
|
||||
fdevent_add(&s->fde, FDE_READ);
|
||||
// D("LS(%d): ready()\n", s->id);
|
||||
}
|
||||
|
||||
static void local_socket_close(asocket *s)
|
||||
{
|
||||
adb_mutex_lock(&socket_list_lock);
|
||||
local_socket_close_locked(s);
|
||||
adb_mutex_unlock(&socket_list_lock);
|
||||
}
|
||||
|
||||
static void local_socket_close_locked(asocket *s)
|
||||
{
|
||||
apacket *p, *n;
|
||||
|
||||
if(s->peer) {
|
||||
s->peer->peer = 0;
|
||||
// tweak to avoid deadlock
|
||||
if (s->peer->close == local_socket_close)
|
||||
local_socket_close_locked(s->peer);
|
||||
else
|
||||
s->peer->close(s->peer);
|
||||
}
|
||||
|
||||
/* IMPORTANT: the remove closes the fd
|
||||
** that belongs to this socket
|
||||
*/
|
||||
fdevent_remove(&s->fde);
|
||||
|
||||
/* dispose of any unwritten data */
|
||||
for(p = s->pkt_first; p; p = n) {
|
||||
D("LS(%d): discarding %d bytes\n", s->id, p->len);
|
||||
n = p->next;
|
||||
put_apacket(p);
|
||||
}
|
||||
|
||||
D("LS(%d): closed\n", s->id);
|
||||
remove_socket(s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void local_socket_event_func(int fd, unsigned ev, void *_s)
|
||||
{
|
||||
asocket *s = _s;
|
||||
|
||||
if(ev & FDE_READ){
|
||||
apacket *p = get_apacket();
|
||||
unsigned char *x = p->data;
|
||||
size_t avail = MAX_PAYLOAD;
|
||||
int r;
|
||||
int is_eof = 0;
|
||||
|
||||
while(avail > 0) {
|
||||
r = adb_read(fd, x, avail);
|
||||
if(r > 0) {
|
||||
avail -= r;
|
||||
x += r;
|
||||
continue;
|
||||
}
|
||||
if(r < 0) {
|
||||
if(errno == EAGAIN) break;
|
||||
if(errno == EINTR) continue;
|
||||
}
|
||||
|
||||
/* r = 0 or unhandled error */
|
||||
is_eof = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
|
||||
put_apacket(p);
|
||||
} else {
|
||||
p->len = MAX_PAYLOAD - avail;
|
||||
|
||||
r = s->peer->enqueue(s->peer, p);
|
||||
|
||||
if(r < 0) {
|
||||
/* error return means they closed us as a side-effect
|
||||
** and we must retutn immediately
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if(r > 0) {
|
||||
/* if the remote cannot accept further events,
|
||||
** we disable notification of READs. They'll
|
||||
** be enabled again when we get a call to ready()
|
||||
*/
|
||||
fdevent_del(&s->fde, FDE_READ);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_eof) {
|
||||
s->close(s);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(ev & FDE_WRITE){
|
||||
apacket *p;
|
||||
|
||||
while((p = s->pkt_first) != 0) {
|
||||
while(p->len > 0) {
|
||||
int r = adb_write(fd, p->ptr, p->len);
|
||||
if(r > 0) {
|
||||
p->ptr += r;
|
||||
p->len -= r;
|
||||
continue;
|
||||
}
|
||||
if(r < 0) {
|
||||
if(errno == EAGAIN) return;
|
||||
if(errno == EINTR) continue;
|
||||
}
|
||||
s->close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if(p->len == 0) {
|
||||
s->pkt_first = p->next;
|
||||
if(s->pkt_first == 0) s->pkt_last = 0;
|
||||
put_apacket(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* no more packets queued, so we can ignore
|
||||
** writable events again and tell our peer
|
||||
** to resume writing
|
||||
*/
|
||||
fdevent_del(&s->fde, FDE_WRITE);
|
||||
s->peer->ready(s->peer);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ev & FDE_ERROR){
|
||||
/* this should be caught be the next read or write
|
||||
** catching it here means we may skip the last few
|
||||
** bytes of readable data.
|
||||
*/
|
||||
// s->close(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
asocket *create_local_socket(int fd)
|
||||
{
|
||||
asocket *s = calloc(1, sizeof(asocket));
|
||||
if(s == 0) fatal("cannot allocate socket");
|
||||
install_local_socket(s);
|
||||
s->fd = fd;
|
||||
s->enqueue = local_socket_enqueue;
|
||||
s->ready = local_socket_ready;
|
||||
s->close = local_socket_close;
|
||||
|
||||
fdevent_install(&s->fde, fd, local_socket_event_func, s);
|
||||
/* fdevent_add(&s->fde, FDE_ERROR); */
|
||||
//fprintf(stderr, "Created local socket in create_local_socket \n");
|
||||
D("LS(%d): created (fd=%d)\n", s->id, s->fd);
|
||||
return s;
|
||||
}
|
||||
|
||||
asocket *create_local_service_socket(const char *name)
|
||||
{
|
||||
asocket *s;
|
||||
int fd;
|
||||
|
||||
#if !ADB_HOST
|
||||
if (!strcmp(name,"jdwp")) {
|
||||
return create_jdwp_service_socket();
|
||||
}
|
||||
if (!strcmp(name,"track-jdwp")) {
|
||||
return create_jdwp_tracker_service_socket();
|
||||
}
|
||||
#endif
|
||||
fd = service_to_fd(name);
|
||||
if(fd < 0) return 0;
|
||||
|
||||
s = create_local_socket(fd);
|
||||
D("LS(%d): bound to '%s'\n", s->id, name);
|
||||
return s;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
static asocket *create_host_service_socket(const char *name, const char* serial)
|
||||
{
|
||||
asocket *s;
|
||||
|
||||
s = host_service_to_socket(name, serial);
|
||||
|
||||
if (s != NULL) {
|
||||
D("LS(%d) bound to '%s'\n", s->id, name);
|
||||
return s;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
/* a Remote socket is used to send/receive data to/from a given transport object
|
||||
** it needs to be closed when the transport is forcibly destroyed by the user
|
||||
*/
|
||||
typedef struct aremotesocket {
|
||||
asocket socket;
|
||||
adisconnect disconnect;
|
||||
} aremotesocket;
|
||||
|
||||
static int remote_socket_enqueue(asocket *s, apacket *p)
|
||||
{
|
||||
D("Calling remote_socket_enqueue\n");
|
||||
p->msg.command = A_WRTE;
|
||||
p->msg.arg0 = s->peer->id;
|
||||
p->msg.arg1 = s->id;
|
||||
p->msg.data_length = p->len;
|
||||
send_packet(p, s->transport);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void remote_socket_ready(asocket *s)
|
||||
{
|
||||
D("Calling remote_socket_ready\n");
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_OKAY;
|
||||
p->msg.arg0 = s->peer->id;
|
||||
p->msg.arg1 = s->id;
|
||||
send_packet(p, s->transport);
|
||||
}
|
||||
|
||||
static void remote_socket_close(asocket *s)
|
||||
{
|
||||
D("Calling remote_socket_close\n");
|
||||
apacket *p = get_apacket();
|
||||
p->msg.command = A_CLSE;
|
||||
if(s->peer) {
|
||||
p->msg.arg0 = s->peer->id;
|
||||
s->peer->peer = 0;
|
||||
s->peer->close(s->peer);
|
||||
}
|
||||
p->msg.arg1 = s->id;
|
||||
send_packet(p, s->transport);
|
||||
D("RS(%d): closed\n", s->id);
|
||||
remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void remote_socket_disconnect(void* _s, atransport* t)
|
||||
{
|
||||
asocket* s = _s;
|
||||
asocket* peer = s->peer;
|
||||
|
||||
D("remote_socket_disconnect RS(%d)\n", s->id);
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
|
||||
free(s);
|
||||
}
|
||||
|
||||
asocket *create_remote_socket(unsigned id, atransport *t)
|
||||
{
|
||||
asocket *s = calloc(1, sizeof(aremotesocket));
|
||||
adisconnect* dis = &((aremotesocket*)s)->disconnect;
|
||||
|
||||
if(s == 0) fatal("cannot allocate socket");
|
||||
s->id = id;
|
||||
s->enqueue = remote_socket_enqueue;
|
||||
s->ready = remote_socket_ready;
|
||||
s->close = remote_socket_close;
|
||||
s->transport = t;
|
||||
|
||||
dis->func = remote_socket_disconnect;
|
||||
dis->opaque = s;
|
||||
add_transport_disconnect( t, dis );
|
||||
D("RS(%d): created\n", s->id);
|
||||
return s;
|
||||
}
|
||||
|
||||
void connect_to_remote(asocket *s, const char *destination)
|
||||
{
|
||||
D("Connect_to_remote call \n");
|
||||
apacket *p = get_apacket();
|
||||
int len = strlen(destination) + 1;
|
||||
|
||||
if(len > (MAX_PAYLOAD-1)) {
|
||||
fatal("destination oversized");
|
||||
}
|
||||
|
||||
D("LS(%d): connect('%s')\n", s->id, destination);
|
||||
p->msg.command = A_OPEN;
|
||||
p->msg.arg0 = s->id;
|
||||
p->msg.data_length = len;
|
||||
strcpy((char*) p->data, destination);
|
||||
send_packet(p, s->transport);
|
||||
}
|
||||
|
||||
|
||||
/* this is used by magic sockets to rig local sockets to
|
||||
send the go-ahead message when they connect */
|
||||
static void local_socket_ready_notify(asocket *s)
|
||||
{
|
||||
s->ready = local_socket_ready;
|
||||
s->close = local_socket_close;
|
||||
adb_write(s->fd, "OKAY", 4);
|
||||
s->ready(s);
|
||||
}
|
||||
|
||||
/* this is used by magic sockets to rig local sockets to
|
||||
send the failure message if they are closed before
|
||||
connected (to avoid closing them without a status message) */
|
||||
static void local_socket_close_notify(asocket *s)
|
||||
{
|
||||
s->ready = local_socket_ready;
|
||||
s->close = local_socket_close;
|
||||
sendfailmsg(s->fd, "closed");
|
||||
s->close(s);
|
||||
}
|
||||
|
||||
unsigned unhex(unsigned char *s, int len)
|
||||
{
|
||||
unsigned n = 0, c;
|
||||
|
||||
while(len-- > 0) {
|
||||
switch((c = *s++)) {
|
||||
case '0': case '1': case '2':
|
||||
case '3': case '4': case '5':
|
||||
case '6': case '7': case '8':
|
||||
case '9':
|
||||
c -= '0';
|
||||
break;
|
||||
case 'a': case 'b': case 'c':
|
||||
case 'd': case 'e': case 'f':
|
||||
c = c - 'a' + 10;
|
||||
break;
|
||||
case 'A': case 'B': case 'C':
|
||||
case 'D': case 'E': case 'F':
|
||||
c = c - 'A' + 10;
|
||||
break;
|
||||
default:
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
n = (n << 4) | c;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int smart_socket_enqueue(asocket *s, apacket *p)
|
||||
{
|
||||
unsigned len;
|
||||
#if ADB_HOST
|
||||
char *service = NULL;
|
||||
char* serial = NULL;
|
||||
transport_type ttype = kTransportAny;
|
||||
#endif
|
||||
|
||||
D("SS(%d): enqueue %d\n", s->id, p->len);
|
||||
|
||||
if(s->pkt_first == 0) {
|
||||
s->pkt_first = p;
|
||||
s->pkt_last = p;
|
||||
} else {
|
||||
if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
|
||||
D("SS(%d): overflow\n", s->id);
|
||||
put_apacket(p);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(s->pkt_first->data + s->pkt_first->len,
|
||||
p->data, p->len);
|
||||
s->pkt_first->len += p->len;
|
||||
put_apacket(p);
|
||||
|
||||
p = s->pkt_first;
|
||||
}
|
||||
|
||||
/* don't bother if we can't decode the length */
|
||||
if(p->len < 4) return 0;
|
||||
|
||||
len = unhex(p->data, 4);
|
||||
if((len < 1) || (len > 1024)) {
|
||||
D("SS(%d): bad size (%d)\n", s->id, len);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
D("SS(%d): len is %d\n", s->id, len );
|
||||
/* can't do anything until we have the full header */
|
||||
if((len + 4) > p->len) {
|
||||
D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->data[len + 4] = 0;
|
||||
|
||||
D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
|
||||
|
||||
#if ADB_HOST
|
||||
service = (char *)p->data + 4;
|
||||
if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
|
||||
char* serial_end;
|
||||
service += strlen("host-serial:");
|
||||
|
||||
// serial number should follow "host:"
|
||||
serial_end = strchr(service, ':');
|
||||
if (serial_end) {
|
||||
*serial_end = 0; // terminate string
|
||||
serial = service;
|
||||
service = serial_end + 1;
|
||||
}
|
||||
} else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
|
||||
ttype = kTransportUsb;
|
||||
service += strlen("host-usb:");
|
||||
} else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
|
||||
ttype = kTransportLocal;
|
||||
service += strlen("host-local:");
|
||||
} else if (!strncmp(service, "host:", strlen("host:"))) {
|
||||
ttype = kTransportAny;
|
||||
service += strlen("host:");
|
||||
} else {
|
||||
service = NULL;
|
||||
}
|
||||
|
||||
if (service) {
|
||||
asocket *s2;
|
||||
|
||||
/* some requests are handled immediately -- in that
|
||||
** case the handle_host_request() routine has sent
|
||||
** the OKAY or FAIL message and all we have to do
|
||||
** is clean up.
|
||||
*/
|
||||
if(handle_host_request(service, ttype, serial, s->peer->fd, s) == 0) {
|
||||
/* XXX fail message? */
|
||||
D( "SS(%d): handled host service '%s'\n", s->id, service );
|
||||
goto fail;
|
||||
}
|
||||
if (!strncmp(service, "transport", strlen("transport"))) {
|
||||
D( "SS(%d): okay transport\n", s->id );
|
||||
p->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to find a local service with this name.
|
||||
** if no such service exists, we'll fail out
|
||||
** and tear down here.
|
||||
*/
|
||||
s2 = create_host_service_socket(service, serial);
|
||||
if(s2 == 0) {
|
||||
D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
|
||||
sendfailmsg(s->peer->fd, "unknown host service");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* we've connected to a local host service,
|
||||
** so we make our peer back into a regular
|
||||
** local socket and bind it to the new local
|
||||
** service socket, acknowledge the successful
|
||||
** connection, and close this smart socket now
|
||||
** that its work is done.
|
||||
*/
|
||||
adb_write(s->peer->fd, "OKAY", 4);
|
||||
|
||||
s->peer->ready = local_socket_ready;
|
||||
s->peer->close = local_socket_close;
|
||||
s->peer->peer = s2;
|
||||
s2->peer = s->peer;
|
||||
s->peer = 0;
|
||||
D( "SS(%d): okay\n", s->id );
|
||||
s->close(s);
|
||||
|
||||
/* initial state is "ready" */
|
||||
s2->ready(s2);
|
||||
return 0;
|
||||
}
|
||||
#else /* !ADB_HOST */
|
||||
if (s->transport == NULL) {
|
||||
char* error_string = "unknown failure";
|
||||
s->transport = acquire_one_transport (CS_ANY,
|
||||
kTransportAny, NULL, &error_string);
|
||||
|
||||
if (s->transport == NULL) {
|
||||
sendfailmsg(s->peer->fd, error_string);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
|
||||
/* if there's no remote we fail the connection
|
||||
** right here and terminate it
|
||||
*/
|
||||
sendfailmsg(s->peer->fd, "device offline (x)");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
/* instrument our peer to pass the success or fail
|
||||
** message back once it connects or closes, then
|
||||
** detach from it, request the connection, and
|
||||
** tear down
|
||||
*/
|
||||
s->peer->ready = local_socket_ready_notify;
|
||||
s->peer->close = local_socket_close_notify;
|
||||
s->peer->peer = 0;
|
||||
/* give him our transport and upref it */
|
||||
s->peer->transport = s->transport;
|
||||
|
||||
connect_to_remote(s->peer, (char*) (p->data + 4));
|
||||
s->peer = 0;
|
||||
s->close(s);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
/* we're going to close our peer as a side-effect, so
|
||||
** return -1 to signal that state to the local socket
|
||||
** who is enqueueing against us
|
||||
*/
|
||||
s->close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void smart_socket_ready(asocket *s)
|
||||
{
|
||||
D("SS(%d): ready\n", s->id);
|
||||
}
|
||||
|
||||
static void smart_socket_close(asocket *s)
|
||||
{
|
||||
D("SS(%d): closed\n", s->id);
|
||||
if(s->pkt_first){
|
||||
put_apacket(s->pkt_first);
|
||||
}
|
||||
if(s->peer) {
|
||||
s->peer->peer = 0;
|
||||
s->peer->close(s->peer);
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))
|
||||
{
|
||||
D("Creating smart socket \n");
|
||||
asocket *s = calloc(1, sizeof(asocket));
|
||||
if(s == 0) fatal("cannot allocate socket");
|
||||
s->id = 0;
|
||||
s->enqueue = smart_socket_enqueue;
|
||||
s->ready = smart_socket_ready;
|
||||
s->close = smart_socket_close;
|
||||
s->extra = action_cb;
|
||||
|
||||
D("SS(%d): created %p\n", s->id, action_cb);
|
||||
return s;
|
||||
}
|
||||
|
||||
void smart_socket_action(asocket *s, const char *act)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void connect_to_smartsocket(asocket *s)
|
||||
{
|
||||
D("Connecting to smart socket \n");
|
||||
asocket *ss = create_smart_socket(smart_socket_action);
|
||||
s->peer = ss;
|
||||
ss->peer = s;
|
||||
s->ready(s);
|
||||
}
|
BIN
adb/sockets.dia
Normal file
BIN
adb/sockets.dia
Normal file
Binary file not shown.
473
adb/sysdeps.h
Normal file
473
adb/sysdeps.h
Normal file
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/* this file contains system-dependent definitions used by ADB
|
||||
* they're related to threads, sockets and file descriptors
|
||||
*/
|
||||
#ifndef _ADB_SYSDEPS_H
|
||||
#define _ADB_SYSDEPS_H
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
# undef _WIN32
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <process.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define OS_PATH_SEPARATOR '\\'
|
||||
#define OS_PATH_SEPARATOR_STR "\\"
|
||||
|
||||
typedef CRITICAL_SECTION adb_mutex_t;
|
||||
|
||||
#define ADB_MUTEX_DEFINE(x) adb_mutex_t x
|
||||
|
||||
/* declare all mutexes */
|
||||
#define ADB_MUTEX(x) extern adb_mutex_t x;
|
||||
#include "mutex_list.h"
|
||||
|
||||
extern void adb_sysdeps_init(void);
|
||||
|
||||
static __inline__ void adb_mutex_lock( adb_mutex_t* lock )
|
||||
{
|
||||
EnterCriticalSection( lock );
|
||||
}
|
||||
|
||||
static __inline__ void adb_mutex_unlock( adb_mutex_t* lock )
|
||||
{
|
||||
LeaveCriticalSection( lock );
|
||||
}
|
||||
|
||||
typedef struct { unsigned tid; } adb_thread_t;
|
||||
|
||||
typedef void* (*adb_thread_func_t)(void* arg);
|
||||
|
||||
typedef void (*win_thread_func_t)(void* arg);
|
||||
|
||||
static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg)
|
||||
{
|
||||
thread->tid = _beginthread( (win_thread_func_t)func, 0, arg );
|
||||
if (thread->tid == (unsigned)-1L) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __inline__ void close_on_exec(int fd)
|
||||
{
|
||||
/* nothing really */
|
||||
}
|
||||
|
||||
extern void disable_tcp_nagle(int fd);
|
||||
|
||||
#define lstat stat /* no symlinks on Win32 */
|
||||
|
||||
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
|
||||
|
||||
static __inline__ int adb_unlink(const char* path)
|
||||
{
|
||||
int rc = unlink(path);
|
||||
|
||||
if (rc == -1 && errno == EACCES) {
|
||||
/* unlink returns EACCES when the file is read-only, so we first */
|
||||
/* try to make it writable, then unlink again... */
|
||||
rc = chmod(path, _S_IREAD|_S_IWRITE );
|
||||
if (rc == 0)
|
||||
rc = unlink(path);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#undef unlink
|
||||
#define unlink ___xxx_unlink
|
||||
|
||||
static __inline__ int adb_mkdir(const char* path, int mode)
|
||||
{
|
||||
return _mkdir(path);
|
||||
}
|
||||
#undef mkdir
|
||||
#define mkdir ___xxx_mkdir
|
||||
|
||||
extern int adb_open(const char* path, int options);
|
||||
extern int adb_creat(const char* path, int mode);
|
||||
extern int adb_read(int fd, void* buf, int len);
|
||||
extern int adb_write(int fd, const void* buf, int len);
|
||||
extern int adb_lseek(int fd, int pos, int where);
|
||||
extern int adb_close(int fd);
|
||||
|
||||
static __inline__ int unix_close(int fd)
|
||||
{
|
||||
return close(fd);
|
||||
}
|
||||
#undef close
|
||||
#define close ____xxx_close
|
||||
|
||||
static __inline__ int unix_read(int fd, void* buf, size_t len)
|
||||
{
|
||||
return read(fd, buf, len);
|
||||
}
|
||||
#undef read
|
||||
#define read ___xxx_read
|
||||
|
||||
static __inline__ int unix_write(int fd, const void* buf, size_t len)
|
||||
{
|
||||
return write(fd, buf, len);
|
||||
}
|
||||
#undef write
|
||||
#define write ___xxx_write
|
||||
|
||||
static __inline__ int adb_open_mode(const char* path, int options, int mode)
|
||||
{
|
||||
return adb_open(path, options);
|
||||
}
|
||||
|
||||
static __inline__ int unix_open(const char* path, int options,...)
|
||||
{
|
||||
if ((options & O_CREAT) == 0)
|
||||
{
|
||||
return open(path, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
int mode;
|
||||
va_list args;
|
||||
va_start( args, options );
|
||||
mode = va_arg( args, int );
|
||||
va_end( args );
|
||||
return open(path, options, mode);
|
||||
}
|
||||
}
|
||||
#define open ___xxx_unix_open
|
||||
|
||||
|
||||
/* normally provided by <cutils/misc.h> */
|
||||
extern void* load_file(const char* pathname, unsigned* psize);
|
||||
|
||||
/* normally provided by <cutils/sockets.h> */
|
||||
extern int socket_loopback_client(int port, int type);
|
||||
extern int socket_network_client(const char *host, int port, int type);
|
||||
extern int socket_loopback_server(int port, int type);
|
||||
extern int socket_inaddr_any_server(int port, int type);
|
||||
|
||||
/* normally provided by <cutils/fdevent.h> */
|
||||
|
||||
#define FDE_READ 0x0001
|
||||
#define FDE_WRITE 0x0002
|
||||
#define FDE_ERROR 0x0004
|
||||
#define FDE_DONT_CLOSE 0x0080
|
||||
|
||||
typedef struct fdevent fdevent;
|
||||
|
||||
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
|
||||
|
||||
fdevent *fdevent_create(int fd, fd_func func, void *arg);
|
||||
void fdevent_destroy(fdevent *fde);
|
||||
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
|
||||
void fdevent_remove(fdevent *item);
|
||||
void fdevent_set(fdevent *fde, unsigned events);
|
||||
void fdevent_add(fdevent *fde, unsigned events);
|
||||
void fdevent_del(fdevent *fde, unsigned events);
|
||||
void fdevent_loop();
|
||||
|
||||
struct fdevent {
|
||||
fdevent *next;
|
||||
fdevent *prev;
|
||||
|
||||
int fd;
|
||||
unsigned short state;
|
||||
unsigned short events;
|
||||
|
||||
fd_func func;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static __inline__ void adb_sleep_ms( int mseconds )
|
||||
{
|
||||
Sleep( mseconds );
|
||||
}
|
||||
|
||||
extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
|
||||
|
||||
#undef accept
|
||||
#define accept ___xxx_accept
|
||||
|
||||
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
|
||||
{
|
||||
int opt = bufsize;
|
||||
return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
|
||||
}
|
||||
|
||||
extern int adb_socketpair( int sv[2] );
|
||||
|
||||
static __inline__ char* adb_dirstart( const char* path )
|
||||
{
|
||||
char* p = strchr(path, '/');
|
||||
char* p2 = strchr(path, '\\');
|
||||
|
||||
if ( !p )
|
||||
p = p2;
|
||||
else if ( p2 && p2 > p )
|
||||
p = p2;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static __inline__ char* adb_dirstop( const char* path )
|
||||
{
|
||||
char* p = strrchr(path, '/');
|
||||
char* p2 = strrchr(path, '\\');
|
||||
|
||||
if ( !p )
|
||||
p = p2;
|
||||
else if ( p2 && p2 > p )
|
||||
p = p2;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static __inline__ int adb_is_absolute_host_path( const char* path )
|
||||
{
|
||||
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
|
||||
}
|
||||
|
||||
#else /* !_WIN32 a.k.a. Unix */
|
||||
|
||||
#include <cutils/fdevent.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <cutils/misc.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <string.h>
|
||||
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
#define OS_PATH_SEPARATOR_STR "/"
|
||||
|
||||
typedef pthread_mutex_t adb_mutex_t;
|
||||
#define ADB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
|
||||
#define adb_mutex_init pthread_mutex_init
|
||||
#define adb_mutex_lock pthread_mutex_lock
|
||||
#define adb_mutex_unlock pthread_mutex_unlock
|
||||
#define adb_mutex_destroy pthread_mutex_destroy
|
||||
|
||||
#define ADB_MUTEX_DEFINE(m) static adb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
|
||||
|
||||
#define adb_cond_t pthread_cond_t
|
||||
#define adb_cond_init pthread_cond_init
|
||||
#define adb_cond_wait pthread_cond_wait
|
||||
#define adb_cond_broadcast pthread_cond_broadcast
|
||||
#define adb_cond_signal pthread_cond_signal
|
||||
#define adb_cond_destroy pthread_cond_destroy
|
||||
|
||||
static __inline__ void close_on_exec(int fd)
|
||||
{
|
||||
fcntl( fd, F_SETFD, FD_CLOEXEC );
|
||||
}
|
||||
|
||||
static __inline__ int unix_open(const char* path, int options,...)
|
||||
{
|
||||
if ((options & O_CREAT) == 0)
|
||||
{
|
||||
return open(path, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
int mode;
|
||||
va_list args;
|
||||
va_start( args, options );
|
||||
mode = va_arg( args, int );
|
||||
va_end( args );
|
||||
return open(path, options, mode);
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ int adb_open_mode( const char* pathname, int options, int mode )
|
||||
{
|
||||
return open( pathname, options, mode );
|
||||
}
|
||||
|
||||
|
||||
static __inline__ int adb_open( const char* pathname, int options )
|
||||
{
|
||||
int fd = open( pathname, options );
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
close_on_exec( fd );
|
||||
return fd;
|
||||
}
|
||||
#undef open
|
||||
#define open ___xxx_open
|
||||
|
||||
static __inline__ int adb_close(int fd)
|
||||
{
|
||||
return close(fd);
|
||||
}
|
||||
#undef close
|
||||
#define close ____xxx_close
|
||||
|
||||
|
||||
static __inline__ int adb_read(int fd, void* buf, size_t len)
|
||||
{
|
||||
return read(fd, buf, len);
|
||||
}
|
||||
|
||||
#undef read
|
||||
#define read ___xxx_read
|
||||
|
||||
static __inline__ int adb_write(int fd, const void* buf, size_t len)
|
||||
{
|
||||
return write(fd, buf, len);
|
||||
}
|
||||
#undef write
|
||||
#define write ___xxx_write
|
||||
|
||||
static __inline__ int adb_lseek(int fd, int pos, int where)
|
||||
{
|
||||
return lseek(fd, pos, where);
|
||||
}
|
||||
#undef lseek
|
||||
#define lseek ___xxx_lseek
|
||||
|
||||
static __inline__ int adb_unlink(const char* path)
|
||||
{
|
||||
return unlink(path);
|
||||
}
|
||||
#undef unlink
|
||||
#define unlink ___xxx_unlink
|
||||
|
||||
static __inline__ int adb_creat(const char* path, int mode)
|
||||
{
|
||||
int fd = creat(path, mode);
|
||||
|
||||
if ( fd < 0 )
|
||||
return -1;
|
||||
|
||||
close_on_exec(fd);
|
||||
return fd;
|
||||
}
|
||||
#undef creat
|
||||
#define creat ___xxx_creat
|
||||
|
||||
static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
|
||||
{
|
||||
return accept( serverfd, addr, addrlen );
|
||||
}
|
||||
|
||||
#undef accept
|
||||
#define accept ___xxx_accept
|
||||
|
||||
#define unix_read adb_read
|
||||
#define unix_write adb_write
|
||||
#define unix_close adb_close
|
||||
|
||||
typedef pthread_t adb_thread_t;
|
||||
|
||||
typedef void* (*adb_thread_func_t)( void* arg );
|
||||
|
||||
static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg )
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
|
||||
pthread_attr_init (&attr);
|
||||
pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
return pthread_create( pthread, &attr, start, arg );
|
||||
}
|
||||
|
||||
static __inline__ int adb_socket_setbufsize( int fd, int bufsize )
|
||||
{
|
||||
int opt = bufsize;
|
||||
return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
|
||||
}
|
||||
|
||||
static __inline__ void disable_tcp_nagle(int fd)
|
||||
{
|
||||
int on = 1;
|
||||
setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) );
|
||||
}
|
||||
|
||||
|
||||
static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
|
||||
{
|
||||
return socketpair( d, type, protocol, sv );
|
||||
}
|
||||
|
||||
static __inline__ int adb_socketpair( int sv[2] )
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
close_on_exec( sv[0] );
|
||||
close_on_exec( sv[1] );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef socketpair
|
||||
#define socketpair ___xxx_socketpair
|
||||
|
||||
static __inline__ void adb_sleep_ms( int mseconds )
|
||||
{
|
||||
usleep( mseconds*1000 );
|
||||
}
|
||||
|
||||
static __inline__ int adb_mkdir(const char* path, int mode)
|
||||
{
|
||||
return mkdir(path, mode);
|
||||
}
|
||||
#undef mkdir
|
||||
#define mkdir ___xxx_mkdir
|
||||
|
||||
static __inline__ void adb_sysdeps_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
static __inline__ char* adb_dirstart(const char* path)
|
||||
{
|
||||
return strchr(path, '/');
|
||||
}
|
||||
|
||||
static __inline__ char* adb_dirstop(const char* path)
|
||||
{
|
||||
return strrchr(path, '/');
|
||||
}
|
||||
|
||||
static __inline__ int adb_is_absolute_host_path( const char* path )
|
||||
{
|
||||
return path[0] == '/';
|
||||
}
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#endif /* _ADB_SYSDEPS_H */
|
1953
adb/sysdeps_win32.c
Normal file
1953
adb/sysdeps_win32.c
Normal file
File diff suppressed because it is too large
Load diff
97
adb/test_track_devices.c
Normal file
97
adb/test_track_devices.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* a simple test program, connects to ADB server, and opens a track-devices session */
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <memory.h>
|
||||
|
||||
static void
|
||||
panic( const char* msg )
|
||||
{
|
||||
fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
unix_write( int fd, const char* buf, int len )
|
||||
{
|
||||
int result = 0;
|
||||
while (len > 0) {
|
||||
int len2 = write(fd, buf, len);
|
||||
if (len2 < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
result += len2;
|
||||
len -= len2;
|
||||
buf += len2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
unix_read( int fd, char* buf, int len )
|
||||
{
|
||||
int result = 0;
|
||||
while (len > 0) {
|
||||
int len2 = read(fd, buf, len);
|
||||
if (len2 < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
result += len2;
|
||||
len -= len2;
|
||||
buf += len2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int main( void )
|
||||
{
|
||||
int ret, s;
|
||||
struct sockaddr_in server;
|
||||
char buffer[1024];
|
||||
const char* request = "host:track-devices";
|
||||
int len;
|
||||
|
||||
memset( &server, 0, sizeof(server) );
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(5037);
|
||||
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
s = socket( PF_INET, SOCK_STREAM, 0 );
|
||||
ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
|
||||
if (ret < 0) panic( "could not connect to server" );
|
||||
|
||||
/* send the request */
|
||||
len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
|
||||
if (unix_write(s, buffer, len) < 0)
|
||||
panic( "could not send request" );
|
||||
|
||||
/* read the OKAY answer */
|
||||
if (unix_read(s, buffer, 4) != 4)
|
||||
panic( "could not read request" );
|
||||
|
||||
printf( "server answer: %.*s\n", 4, buffer );
|
||||
|
||||
/* now loop */
|
||||
for (;;) {
|
||||
char head[5] = "0000";
|
||||
|
||||
if (unix_read(s, head, 4) < 0)
|
||||
panic("could not read length");
|
||||
|
||||
if ( sscanf( head, "%04x", &len ) != 1 )
|
||||
panic("could not decode length");
|
||||
|
||||
if (unix_read(s, buffer, len) != len)
|
||||
panic("could not read data");
|
||||
|
||||
printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
|
||||
}
|
||||
close(s);
|
||||
}
|
97
adb/test_track_jdwp.c
Normal file
97
adb/test_track_jdwp.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* a simple test program, connects to ADB server, and opens a track-devices session */
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <memory.h>
|
||||
|
||||
static void
|
||||
panic( const char* msg )
|
||||
{
|
||||
fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
unix_write( int fd, const char* buf, int len )
|
||||
{
|
||||
int result = 0;
|
||||
while (len > 0) {
|
||||
int len2 = write(fd, buf, len);
|
||||
if (len2 < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
result += len2;
|
||||
len -= len2;
|
||||
buf += len2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
unix_read( int fd, char* buf, int len )
|
||||
{
|
||||
int result = 0;
|
||||
while (len > 0) {
|
||||
int len2 = read(fd, buf, len);
|
||||
if (len2 < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
result += len2;
|
||||
len -= len2;
|
||||
buf += len2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int main( void )
|
||||
{
|
||||
int ret, s;
|
||||
struct sockaddr_in server;
|
||||
char buffer[1024];
|
||||
const char* request = "track-jdwp";
|
||||
int len;
|
||||
|
||||
memset( &server, 0, sizeof(server) );
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(5037);
|
||||
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
s = socket( PF_INET, SOCK_STREAM, 0 );
|
||||
ret = connect( s, (struct sockaddr*) &server, sizeof(server) );
|
||||
if (ret < 0) panic( "could not connect to server" );
|
||||
|
||||
/* send the request */
|
||||
len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request );
|
||||
if (unix_write(s, buffer, len) < 0)
|
||||
panic( "could not send request" );
|
||||
|
||||
/* read the OKAY answer */
|
||||
if (unix_read(s, buffer, 4) != 4)
|
||||
panic( "could not read request" );
|
||||
|
||||
printf( "server answer: %.*s\n", 4, buffer );
|
||||
|
||||
/* now loop */
|
||||
for (;;) {
|
||||
char head[5] = "0000";
|
||||
|
||||
if (unix_read(s, head, 4) < 0)
|
||||
panic("could not read length");
|
||||
|
||||
if ( sscanf( head, "%04x", &len ) != 1 )
|
||||
panic("could not decode length");
|
||||
|
||||
if (unix_read(s, buffer, len) != len)
|
||||
panic("could not read data");
|
||||
|
||||
printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
|
||||
}
|
||||
close(s);
|
||||
}
|
958
adb/transport.c
Normal file
958
adb/transport.c
Normal file
|
@ -0,0 +1,958 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_TRANSPORT
|
||||
#include "adb.h"
|
||||
|
||||
static void transport_unref(atransport *t);
|
||||
|
||||
static atransport transport_list = {
|
||||
.next = &transport_list,
|
||||
.prev = &transport_list,
|
||||
};
|
||||
|
||||
ADB_MUTEX_DEFINE( transport_lock );
|
||||
|
||||
#if ADB_TRACE
|
||||
static void dump_hex( const unsigned char* ptr, size_t len )
|
||||
{
|
||||
int nn, len2 = len;
|
||||
|
||||
if (len2 > 16) len2 = 16;
|
||||
|
||||
for (nn = 0; nn < len2; nn++)
|
||||
D("%02x", ptr[nn]);
|
||||
D(" ");
|
||||
|
||||
for (nn = 0; nn < len2; nn++) {
|
||||
int c = ptr[nn];
|
||||
if (c < 32 || c > 127)
|
||||
c = '.';
|
||||
D("%c", c);
|
||||
}
|
||||
D("\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
kick_transport(atransport* t)
|
||||
{
|
||||
if (t && !t->kicked)
|
||||
{
|
||||
int kicked;
|
||||
|
||||
adb_mutex_lock(&transport_lock);
|
||||
kicked = t->kicked;
|
||||
if (!kicked)
|
||||
t->kicked = 1;
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
|
||||
if (!kicked)
|
||||
t->kick(t);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run_transport_disconnects(atransport* t)
|
||||
{
|
||||
adisconnect* dis = t->disconnects.next;
|
||||
|
||||
D("run_transport_disconnects: %p (%s)\n", t, t->serial ? t->serial : "unknown" );
|
||||
while (dis != &t->disconnects) {
|
||||
adisconnect* next = dis->next;
|
||||
dis->func( dis->opaque, t );
|
||||
dis = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
read_packet(int fd, apacket** ppacket)
|
||||
{
|
||||
char *p = (char*)ppacket; /* really read a packet address */
|
||||
int r;
|
||||
int len = sizeof(*ppacket);
|
||||
while(len > 0) {
|
||||
r = adb_read(fd, p, len);
|
||||
if(r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else {
|
||||
D("read_packet: %d error %d %d\n", fd, r, errno);
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if ADB_TRACE
|
||||
if (ADB_TRACING)
|
||||
{
|
||||
unsigned command = (*ppacket)->msg.command;
|
||||
int len = (*ppacket)->msg.data_length;
|
||||
char cmd[5];
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
int b = (command >> (n*8)) & 255;
|
||||
if (b >= 32 && b < 127)
|
||||
cmd[n] = (char)b;
|
||||
else
|
||||
cmd[n] = '.';
|
||||
}
|
||||
cmd[4] = 0;
|
||||
|
||||
D("read_packet: %d ok: [%08x %s] %08x %08x (%d) ",
|
||||
fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
|
||||
dump_hex((*ppacket)->data, len);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
write_packet(int fd, apacket** ppacket)
|
||||
{
|
||||
char *p = (char*) ppacket; /* we really write the packet address */
|
||||
int r, len = sizeof(ppacket);
|
||||
|
||||
#if ADB_TRACE
|
||||
if (ADB_TRACING)
|
||||
{
|
||||
unsigned command = (*ppacket)->msg.command;
|
||||
int len = (*ppacket)->msg.data_length;
|
||||
char cmd[5];
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 4; n++) {
|
||||
int b = (command >> (n*8)) & 255;
|
||||
if (b >= 32 && b < 127)
|
||||
cmd[n] = (char)b;
|
||||
else
|
||||
cmd[n] = '.';
|
||||
}
|
||||
cmd[4] = 0;
|
||||
|
||||
D("write_packet: %d [%08x %s] %08x %08x (%d) ",
|
||||
fd, command, cmd, (*ppacket)->msg.arg0, (*ppacket)->msg.arg1, len);
|
||||
dump_hex((*ppacket)->data, len);
|
||||
}
|
||||
#endif
|
||||
len = sizeof(ppacket);
|
||||
while(len > 0) {
|
||||
r = adb_write(fd, p, len);
|
||||
if(r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else {
|
||||
D("write_packet: %d error %d %d\n", fd, r, errno);
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void transport_socket_events(int fd, unsigned events, void *_t)
|
||||
{
|
||||
if(events & FDE_READ){
|
||||
apacket *p = 0;
|
||||
if(read_packet(fd, &p)){
|
||||
D("failed to read packet from transport socket on fd %d\n", fd);
|
||||
} else {
|
||||
handle_packet(p, (atransport *) _t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void send_packet(apacket *p, atransport *t)
|
||||
{
|
||||
unsigned char *x;
|
||||
unsigned sum;
|
||||
unsigned count;
|
||||
|
||||
p->msg.magic = p->msg.command ^ 0xffffffff;
|
||||
|
||||
count = p->msg.data_length;
|
||||
x = (unsigned char *) p->data;
|
||||
sum = 0;
|
||||
while(count-- > 0){
|
||||
sum += *x++;
|
||||
}
|
||||
p->msg.data_check = sum;
|
||||
|
||||
print_packet("send", p);
|
||||
|
||||
if (t == NULL) {
|
||||
fatal_errno("Transport is null");
|
||||
D("Transport is null \n");
|
||||
}
|
||||
|
||||
if(write_packet(t->transport_socket, &p)){
|
||||
fatal_errno("cannot enqueue packet on transport socket");
|
||||
}
|
||||
}
|
||||
|
||||
/* The transport is opened by transport_register_func before
|
||||
** the input and output threads are started.
|
||||
**
|
||||
** The output thread issues a SYNC(1, token) message to let
|
||||
** the input thread know to start things up. In the event
|
||||
** of transport IO failure, the output thread will post a
|
||||
** SYNC(0,0) message to ensure shutdown.
|
||||
**
|
||||
** The transport will not actually be closed until both
|
||||
** threads exit, but the input thread will kick the transport
|
||||
** on its way out to disconnect the underlying device.
|
||||
*/
|
||||
|
||||
static void *output_thread(void *_t)
|
||||
{
|
||||
atransport *t = _t;
|
||||
apacket *p;
|
||||
|
||||
D("from_remote: starting thread for transport %p, on fd %d\n", t, t->fd );
|
||||
|
||||
D("from_remote: transport %p SYNC online (%d)\n", t, t->sync_token + 1);
|
||||
p = get_apacket();
|
||||
p->msg.command = A_SYNC;
|
||||
p->msg.arg0 = 1;
|
||||
p->msg.arg1 = ++(t->sync_token);
|
||||
p->msg.magic = A_SYNC ^ 0xffffffff;
|
||||
if(write_packet(t->fd, &p)) {
|
||||
put_apacket(p);
|
||||
D("from_remote: failed to write SYNC apacket to transport %p", t);
|
||||
goto oops;
|
||||
}
|
||||
|
||||
D("from_remote: data pump for transport %p\n", t);
|
||||
for(;;) {
|
||||
p = get_apacket();
|
||||
|
||||
if(t->read_from_remote(p, t) == 0){
|
||||
D("from_remote: received remote packet, sending to transport %p\n",
|
||||
t);
|
||||
if(write_packet(t->fd, &p)){
|
||||
put_apacket(p);
|
||||
D("from_remote: failed to write apacket to transport %p", t);
|
||||
goto oops;
|
||||
}
|
||||
} else {
|
||||
D("from_remote: remote read failed for transport %p\n", p);
|
||||
put_apacket(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
D("from_remote: SYNC offline for transport %p\n", t);
|
||||
p = get_apacket();
|
||||
p->msg.command = A_SYNC;
|
||||
p->msg.arg0 = 0;
|
||||
p->msg.arg1 = 0;
|
||||
p->msg.magic = A_SYNC ^ 0xffffffff;
|
||||
if(write_packet(t->fd, &p)) {
|
||||
put_apacket(p);
|
||||
D("from_remote: failed to write SYNC apacket to transport %p", t);
|
||||
}
|
||||
|
||||
oops:
|
||||
D("from_remote: thread is exiting for transport %p\n", t);
|
||||
kick_transport(t);
|
||||
transport_unref(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *input_thread(void *_t)
|
||||
{
|
||||
atransport *t = _t;
|
||||
apacket *p;
|
||||
int active = 0;
|
||||
|
||||
D("to_remote: starting input_thread for %p, reading from fd %d\n",
|
||||
t, t->fd);
|
||||
|
||||
for(;;){
|
||||
if(read_packet(t->fd, &p)) {
|
||||
D("to_remote: failed to read apacket from transport %p on fd %d\n",
|
||||
t, t->fd );
|
||||
break;
|
||||
}
|
||||
if(p->msg.command == A_SYNC){
|
||||
if(p->msg.arg0 == 0) {
|
||||
D("to_remote: transport %p SYNC offline\n", t);
|
||||
put_apacket(p);
|
||||
break;
|
||||
} else {
|
||||
if(p->msg.arg1 == t->sync_token) {
|
||||
D("to_remote: transport %p SYNC online\n", t);
|
||||
active = 1;
|
||||
} else {
|
||||
D("to_remote: trandport %p ignoring SYNC %d != %d\n",
|
||||
t, p->msg.arg1, t->sync_token);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(active) {
|
||||
D("to_remote: transport %p got packet, sending to remote\n", t);
|
||||
t->write_to_remote(p, t);
|
||||
} else {
|
||||
D("to_remote: transport %p ignoring packet while offline\n", t);
|
||||
}
|
||||
}
|
||||
|
||||
put_apacket(p);
|
||||
}
|
||||
|
||||
// this is necessary to avoid a race condition that occured when a transport closes
|
||||
// while a client socket is still active.
|
||||
close_all_sockets(t);
|
||||
|
||||
D("to_remote: thread is exiting for transport %p, fd %d\n", t, t->fd);
|
||||
kick_transport(t);
|
||||
transport_unref(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int transport_registration_send = -1;
|
||||
static int transport_registration_recv = -1;
|
||||
static fdevent transport_registration_fde;
|
||||
|
||||
|
||||
#if ADB_HOST
|
||||
static int list_transports_msg(char* buffer, size_t bufferlen)
|
||||
{
|
||||
char head[5];
|
||||
int len;
|
||||
|
||||
len = list_transports(buffer+4, bufferlen-4);
|
||||
snprintf(head, sizeof(head), "%04x", len);
|
||||
memcpy(buffer, head, 4);
|
||||
len += 4;
|
||||
return len;
|
||||
}
|
||||
|
||||
/* this adds support required by the 'track-devices' service.
|
||||
* this is used to send the content of "list_transport" to any
|
||||
* number of client connections that want it through a single
|
||||
* live TCP connection
|
||||
*/
|
||||
typedef struct device_tracker device_tracker;
|
||||
struct device_tracker {
|
||||
asocket socket;
|
||||
int update_needed;
|
||||
device_tracker* next;
|
||||
};
|
||||
|
||||
/* linked list of all device trackers */
|
||||
static device_tracker* device_tracker_list;
|
||||
|
||||
static void
|
||||
device_tracker_remove( device_tracker* tracker )
|
||||
{
|
||||
device_tracker** pnode = &device_tracker_list;
|
||||
device_tracker* node = *pnode;
|
||||
|
||||
adb_mutex_lock( &transport_lock );
|
||||
while (node) {
|
||||
if (node == tracker) {
|
||||
*pnode = node->next;
|
||||
break;
|
||||
}
|
||||
pnode = &node->next;
|
||||
node = *pnode;
|
||||
}
|
||||
adb_mutex_unlock( &transport_lock );
|
||||
}
|
||||
|
||||
static void
|
||||
device_tracker_close( asocket* socket )
|
||||
{
|
||||
device_tracker* tracker = (device_tracker*) socket;
|
||||
asocket* peer = socket->peer;
|
||||
|
||||
D( "device tracker %p removed\n", tracker);
|
||||
if (peer) {
|
||||
peer->peer = NULL;
|
||||
peer->close(peer);
|
||||
}
|
||||
device_tracker_remove(tracker);
|
||||
free(tracker);
|
||||
}
|
||||
|
||||
static int
|
||||
device_tracker_enqueue( asocket* socket, apacket* p )
|
||||
{
|
||||
/* you can't read from a device tracker, close immediately */
|
||||
put_apacket(p);
|
||||
device_tracker_close(socket);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
device_tracker_send( device_tracker* tracker,
|
||||
const char* buffer,
|
||||
int len )
|
||||
{
|
||||
apacket* p = get_apacket();
|
||||
asocket* peer = tracker->socket.peer;
|
||||
|
||||
memcpy(p->data, buffer, len);
|
||||
p->len = len;
|
||||
return peer->enqueue( peer, p );
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
device_tracker_ready( asocket* socket )
|
||||
{
|
||||
device_tracker* tracker = (device_tracker*) socket;
|
||||
|
||||
/* we want to send the device list when the tracker connects
|
||||
* for the first time, even if no update occured */
|
||||
if (tracker->update_needed > 0) {
|
||||
char buffer[1024];
|
||||
int len;
|
||||
|
||||
tracker->update_needed = 0;
|
||||
|
||||
len = list_transports_msg(buffer, sizeof(buffer));
|
||||
device_tracker_send(tracker, buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
asocket*
|
||||
create_device_tracker(void)
|
||||
{
|
||||
device_tracker* tracker = calloc(1,sizeof(*tracker));
|
||||
|
||||
if(tracker == 0) fatal("cannot allocate device tracker");
|
||||
|
||||
D( "device tracker %p created\n", tracker);
|
||||
|
||||
tracker->socket.enqueue = device_tracker_enqueue;
|
||||
tracker->socket.ready = device_tracker_ready;
|
||||
tracker->socket.close = device_tracker_close;
|
||||
tracker->update_needed = 1;
|
||||
|
||||
tracker->next = device_tracker_list;
|
||||
device_tracker_list = tracker;
|
||||
|
||||
return &tracker->socket;
|
||||
}
|
||||
|
||||
|
||||
/* call this function each time the transport list has changed */
|
||||
void update_transports(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
int len;
|
||||
device_tracker* tracker;
|
||||
|
||||
len = list_transports_msg(buffer, sizeof(buffer));
|
||||
|
||||
tracker = device_tracker_list;
|
||||
while (tracker != NULL) {
|
||||
device_tracker* next = tracker->next;
|
||||
/* note: this may destroy the tracker if the connection is closed */
|
||||
device_tracker_send(tracker, buffer, len);
|
||||
tracker = next;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void update_transports(void)
|
||||
{
|
||||
// nothing to do on the device side
|
||||
}
|
||||
#endif // ADB_HOST
|
||||
|
||||
typedef struct tmsg tmsg;
|
||||
struct tmsg
|
||||
{
|
||||
atransport *transport;
|
||||
int action;
|
||||
};
|
||||
|
||||
static int
|
||||
transport_read_action(int fd, struct tmsg* m)
|
||||
{
|
||||
char *p = (char*)m;
|
||||
int len = sizeof(*m);
|
||||
int r;
|
||||
|
||||
while(len > 0) {
|
||||
r = adb_read(fd, p, len);
|
||||
if(r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else {
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
D("transport_read_action: on fd %d, error %d: %s\n",
|
||||
fd, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
transport_write_action(int fd, struct tmsg* m)
|
||||
{
|
||||
char *p = (char*)m;
|
||||
int len = sizeof(*m);
|
||||
int r;
|
||||
|
||||
while(len > 0) {
|
||||
r = adb_write(fd, p, len);
|
||||
if(r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else {
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
D("transport_write_action: on fd %d, error %d: %s\n",
|
||||
fd, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void transport_registration_func(int _fd, unsigned ev, void *data)
|
||||
{
|
||||
tmsg m;
|
||||
adb_thread_t output_thread_ptr;
|
||||
adb_thread_t input_thread_ptr;
|
||||
int s[2];
|
||||
atransport *t;
|
||||
|
||||
if(!(ev & FDE_READ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(transport_read_action(_fd, &m)) {
|
||||
fatal_errno("cannot read transport registration socket");
|
||||
}
|
||||
|
||||
t = m.transport;
|
||||
|
||||
if(m.action == 0){
|
||||
D("transport: %p removing and free'ing %d\n", t, t->transport_socket);
|
||||
|
||||
/* IMPORTANT: the remove closes one half of the
|
||||
** socket pair. The close closes the other half.
|
||||
*/
|
||||
fdevent_remove(&(t->transport_fde));
|
||||
adb_close(t->fd);
|
||||
|
||||
adb_mutex_lock(&transport_lock);
|
||||
t->next->prev = t->prev;
|
||||
t->prev->next = t->next;
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
|
||||
run_transport_disconnects(t);
|
||||
|
||||
if (t->product)
|
||||
free(t->product);
|
||||
if (t->serial)
|
||||
free(t->serial);
|
||||
|
||||
memset(t,0xee,sizeof(atransport));
|
||||
free(t);
|
||||
|
||||
update_transports();
|
||||
return;
|
||||
}
|
||||
|
||||
/* initial references are the two threads */
|
||||
t->ref_count = 2;
|
||||
|
||||
if(adb_socketpair(s)) {
|
||||
fatal_errno("cannot open transport socketpair");
|
||||
}
|
||||
|
||||
D("transport: %p (%d,%d) starting\n", t, s[0], s[1]);
|
||||
|
||||
t->transport_socket = s[0];
|
||||
t->fd = s[1];
|
||||
|
||||
/* put us on the master device list */
|
||||
adb_mutex_lock(&transport_lock);
|
||||
t->next = &transport_list;
|
||||
t->prev = transport_list.prev;
|
||||
t->next->prev = t;
|
||||
t->prev->next = t;
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
|
||||
D("transport: %p install %d\n", t, t->transport_socket );
|
||||
fdevent_install(&(t->transport_fde),
|
||||
t->transport_socket,
|
||||
transport_socket_events,
|
||||
t);
|
||||
|
||||
fdevent_set(&(t->transport_fde), FDE_READ);
|
||||
|
||||
if(adb_thread_create(&input_thread_ptr, input_thread, t)){
|
||||
fatal_errno("cannot create input thread");
|
||||
}
|
||||
|
||||
if(adb_thread_create(&output_thread_ptr, output_thread, t)){
|
||||
fatal_errno("cannot create output thread");
|
||||
}
|
||||
|
||||
t->disconnects.next = t->disconnects.prev = &t->disconnects;
|
||||
|
||||
update_transports();
|
||||
}
|
||||
|
||||
void init_transport_registration(void)
|
||||
{
|
||||
int s[2];
|
||||
|
||||
if(adb_socketpair(s)){
|
||||
fatal_errno("cannot open transport registration socketpair");
|
||||
}
|
||||
|
||||
transport_registration_send = s[0];
|
||||
transport_registration_recv = s[1];
|
||||
|
||||
fdevent_install(&transport_registration_fde,
|
||||
transport_registration_recv,
|
||||
transport_registration_func,
|
||||
0);
|
||||
|
||||
fdevent_set(&transport_registration_fde, FDE_READ);
|
||||
}
|
||||
|
||||
/* the fdevent select pump is single threaded */
|
||||
static void register_transport(atransport *transport)
|
||||
{
|
||||
tmsg m;
|
||||
m.transport = transport;
|
||||
m.action = 1;
|
||||
D("transport: %p registered\n", transport);
|
||||
if(transport_write_action(transport_registration_send, &m)) {
|
||||
fatal_errno("cannot write transport registration socket\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_transport(atransport *transport)
|
||||
{
|
||||
tmsg m;
|
||||
m.transport = transport;
|
||||
m.action = 0;
|
||||
D("transport: %p removed\n", transport);
|
||||
if(transport_write_action(transport_registration_send, &m)) {
|
||||
fatal_errno("cannot write transport registration socket\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void transport_unref(atransport *t)
|
||||
{
|
||||
if (t) {
|
||||
adb_mutex_lock(&transport_lock);
|
||||
t->ref_count--;
|
||||
D("transport: %p R- (ref=%d)\n", t, t->ref_count);
|
||||
if (t->ref_count == 0) {
|
||||
D("transport: %p kicking and closing\n", t);
|
||||
if (!t->kicked) {
|
||||
t->kicked = 1;
|
||||
t->kick(t);
|
||||
}
|
||||
t->close(t);
|
||||
remove_transport(t);
|
||||
}
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void add_transport_disconnect(atransport* t, adisconnect* dis)
|
||||
{
|
||||
adb_mutex_lock(&transport_lock);
|
||||
dis->next = &t->disconnects;
|
||||
dis->prev = dis->next->prev;
|
||||
dis->prev->next = dis;
|
||||
dis->next->prev = dis;
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
}
|
||||
|
||||
void remove_transport_disconnect(atransport* t, adisconnect* dis)
|
||||
{
|
||||
dis->prev->next = dis->next;
|
||||
dis->next->prev = dis->prev;
|
||||
dis->next = dis->prev = dis;
|
||||
}
|
||||
|
||||
|
||||
atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char** error_out)
|
||||
{
|
||||
atransport *t;
|
||||
atransport *result = NULL;
|
||||
int ambiguous = 0;
|
||||
|
||||
retry:
|
||||
if (error_out)
|
||||
*error_out = "device not found";
|
||||
|
||||
adb_mutex_lock(&transport_lock);
|
||||
for (t = transport_list.next; t != &transport_list; t = t->next) {
|
||||
/* check for matching serial number */
|
||||
if (serial) {
|
||||
if (t->serial && !strcmp(serial, t->serial)) {
|
||||
result = t;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (ttype == kTransportUsb && t->type == kTransportUsb) {
|
||||
if (result) {
|
||||
if (error_out)
|
||||
*error_out = "more than one device";
|
||||
ambiguous = 1;
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
result = t;
|
||||
} else if (ttype == kTransportLocal && t->type == kTransportLocal) {
|
||||
if (result) {
|
||||
if (error_out)
|
||||
*error_out = "more than one emulator";
|
||||
ambiguous = 1;
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
result = t;
|
||||
} else if (ttype == kTransportAny) {
|
||||
if (result) {
|
||||
if (error_out)
|
||||
*error_out = "more than one device and emulator";
|
||||
ambiguous = 1;
|
||||
result = NULL;
|
||||
break;
|
||||
}
|
||||
result = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
|
||||
if (result) {
|
||||
/* offline devices are ignored -- they are either being born or dying */
|
||||
if (result && result->connection_state == CS_OFFLINE) {
|
||||
if (error_out)
|
||||
*error_out = "device offline";
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
/* check for required connection state */
|
||||
if (result && state != CS_ANY && result->connection_state != state) {
|
||||
if (error_out)
|
||||
*error_out = "invalid device state";
|
||||
result = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
/* found one that we can take */
|
||||
if (error_out)
|
||||
*error_out = NULL;
|
||||
} else if (state != CS_ANY && (serial || !ambiguous)) {
|
||||
adb_sleep_ms(1000);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if ADB_HOST
|
||||
static const char *statename(atransport *t)
|
||||
{
|
||||
switch(t->connection_state){
|
||||
case CS_OFFLINE: return "offline";
|
||||
case CS_BOOTLOADER: return "bootloader";
|
||||
case CS_DEVICE: return "device";
|
||||
case CS_HOST: return "host";
|
||||
case CS_RECOVERY: return "recovery";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
int list_transports(char *buf, size_t bufsize)
|
||||
{
|
||||
char* p = buf;
|
||||
char* end = buf + bufsize;
|
||||
int len;
|
||||
atransport *t;
|
||||
|
||||
/* XXX OVERRUN PROBLEMS XXX */
|
||||
adb_mutex_lock(&transport_lock);
|
||||
for(t = transport_list.next; t != &transport_list; t = t->next) {
|
||||
len = snprintf(p, end - p, "%s\t%s\n",
|
||||
t->serial ? t->serial : "",
|
||||
statename(t));
|
||||
|
||||
if (p + len >= end) {
|
||||
/* discard last line if buffer is too short */
|
||||
break;
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
p[0] = 0;
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
|
||||
/* hack for osx */
|
||||
void close_usb_devices()
|
||||
{
|
||||
atransport *t;
|
||||
|
||||
adb_mutex_lock(&transport_lock);
|
||||
for(t = transport_list.next; t != &transport_list; t = t->next) {
|
||||
if ( !t->kicked ) {
|
||||
t->kicked = 1;
|
||||
t->kick(t);
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&transport_lock);
|
||||
}
|
||||
#endif // ADB_HOST
|
||||
|
||||
void register_socket_transport(int s, const char *serial, int port)
|
||||
{
|
||||
atransport *t = calloc(1, sizeof(atransport));
|
||||
D("transport: %p init'ing for socket %d, on port %d\n", t, s, port);
|
||||
if ( init_socket_transport(t, s, port) < 0 ) {
|
||||
adb_close(s);
|
||||
free(t);
|
||||
return;
|
||||
}
|
||||
if(serial) {
|
||||
t->serial = strdup(serial);
|
||||
}
|
||||
register_transport(t);
|
||||
}
|
||||
|
||||
void register_usb_transport(usb_handle *usb, const char *serial)
|
||||
{
|
||||
atransport *t = calloc(1, sizeof(atransport));
|
||||
D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
|
||||
serial ? serial : "");
|
||||
init_usb_transport(t, usb);
|
||||
if(serial) {
|
||||
t->serial = strdup(serial);
|
||||
}
|
||||
register_transport(t);
|
||||
}
|
||||
|
||||
|
||||
#undef TRACE_TAG
|
||||
#define TRACE_TAG TRACE_RWX
|
||||
|
||||
int readx(int fd, void *ptr, size_t len)
|
||||
{
|
||||
char *p = ptr;
|
||||
int r;
|
||||
#if ADB_TRACE
|
||||
int len0 = len;
|
||||
#endif
|
||||
D("readx: %d %p %d\n", fd, ptr, (int)len);
|
||||
while(len > 0) {
|
||||
r = adb_read(fd, p, len);
|
||||
if(r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else {
|
||||
D("readx: %d %d %s\n", fd, r, strerror(errno));
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#if ADB_TRACE
|
||||
D("readx: %d ok: ", fd);
|
||||
dump_hex( ptr, len0 );
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int writex(int fd, const void *ptr, size_t len)
|
||||
{
|
||||
char *p = (char*) ptr;
|
||||
int r;
|
||||
|
||||
#if ADB_TRACE
|
||||
D("writex: %d %p %d: ", fd, ptr, (int)len);
|
||||
dump_hex( ptr, len );
|
||||
#endif
|
||||
while(len > 0) {
|
||||
r = adb_write(fd, p, len);
|
||||
if(r > 0) {
|
||||
len -= r;
|
||||
p += r;
|
||||
} else {
|
||||
D("writex: %d %d %s\n", fd, r, strerror(errno));
|
||||
if((r < 0) && (errno == EINTR)) continue;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
D("writex: %d ok\n", fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_header(apacket *p)
|
||||
{
|
||||
if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
|
||||
D("check_header(): invalid magic\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(p->msg.data_length > MAX_PAYLOAD) {
|
||||
D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_data(apacket *p)
|
||||
{
|
||||
unsigned count, sum;
|
||||
unsigned char *x;
|
||||
|
||||
count = p->msg.data_length;
|
||||
x = p->data;
|
||||
sum = 0;
|
||||
while(count-- > 0) {
|
||||
sum += *x++;
|
||||
}
|
||||
|
||||
if(sum != p->msg.data_check) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
262
adb/transport_local.c
Normal file
262
adb/transport_local.c
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
#define TRACE_TAG TRACE_TRANSPORT
|
||||
#include "adb.h"
|
||||
|
||||
#ifdef __ppc__
|
||||
#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
|
||||
static inline void fix_endians(apacket *p)
|
||||
{
|
||||
p->msg.command = H4(p->msg.command);
|
||||
p->msg.arg0 = H4(p->msg.arg0);
|
||||
p->msg.arg1 = H4(p->msg.arg1);
|
||||
p->msg.data_length = H4(p->msg.data_length);
|
||||
p->msg.data_check = H4(p->msg.data_check);
|
||||
p->msg.magic = H4(p->msg.magic);
|
||||
}
|
||||
#else
|
||||
#define fix_endians(p) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if ADB_HOST
|
||||
/* we keep a list of opened transports, transport 0 is bound to 5555,
|
||||
* transport 1 to 5557, .. transport n to 5555 + n*2. the list is used
|
||||
* to detect when we're trying to connect twice to a given local transport
|
||||
*/
|
||||
#define ADB_LOCAL_TRANSPORT_MAX 16
|
||||
|
||||
ADB_MUTEX_DEFINE( local_transports_lock );
|
||||
|
||||
static atransport* local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
|
||||
#endif /* ADB_HOST */
|
||||
|
||||
static int remote_read(apacket *p, atransport *t)
|
||||
{
|
||||
if(readx(t->sfd, &p->msg, sizeof(amessage))){
|
||||
D("remote local: read terminated (message)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fix_endians(p);
|
||||
|
||||
#if 0 && defined __ppc__
|
||||
D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
|
||||
p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
|
||||
#endif
|
||||
if(check_header(p)) {
|
||||
D("bad header: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(readx(t->sfd, p->data, p->msg.data_length)){
|
||||
D("remote local: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(check_data(p)) {
|
||||
D("bad data: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_write(apacket *p, atransport *t)
|
||||
{
|
||||
int length = p->msg.data_length;
|
||||
|
||||
fix_endians(p);
|
||||
|
||||
#if 0 && defined __ppc__
|
||||
D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n",
|
||||
p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic);
|
||||
#endif
|
||||
if(writex(t->sfd, &p->msg, sizeof(amessage) + length)) {
|
||||
D("remote local: write terminated\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int local_connect(int port)
|
||||
{
|
||||
char buf[64];
|
||||
int fd = -1;
|
||||
|
||||
fd = socket_loopback_client(port, SOCK_STREAM);
|
||||
#if ADB_HOST
|
||||
if(fd < 0) {
|
||||
const char *host = getenv("ADBHOST");
|
||||
if(host) {
|
||||
fd = socket_network_client(host, port, SOCK_STREAM);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (fd >= 0) {
|
||||
D("client: connected on remote on fd %d\n", fd);
|
||||
close_on_exec(fd);
|
||||
disable_tcp_nagle(fd);
|
||||
snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, port - 1);
|
||||
register_socket_transport(fd, buf, port);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void *client_socket_thread(void *x)
|
||||
{
|
||||
#if ADB_HOST
|
||||
int port = ADB_LOCAL_TRANSPORT_PORT;
|
||||
int count = ADB_LOCAL_TRANSPORT_MAX;
|
||||
|
||||
D("transport: client_socket_thread() starting\n");
|
||||
|
||||
/* try to connect to any number of running emulator instances */
|
||||
/* this is only done when ADB starts up. later, each new emulator */
|
||||
/* will send a message to ADB to indicate that is is starting up */
|
||||
for ( ; count > 0; count--, port += 2 ) {
|
||||
(void) local_connect(port);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *server_socket_thread(void *x)
|
||||
{
|
||||
int serverfd, fd;
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
|
||||
D("transport: server_socket_thread() starting\n");
|
||||
serverfd = -1;
|
||||
for(;;) {
|
||||
if(serverfd == -1) {
|
||||
serverfd = socket_inaddr_any_server(ADB_LOCAL_TRANSPORT_PORT, SOCK_STREAM);
|
||||
if(serverfd < 0) {
|
||||
D("server: cannot bind socket yet\n");
|
||||
adb_sleep_ms(1000);
|
||||
continue;
|
||||
}
|
||||
close_on_exec(serverfd);
|
||||
}
|
||||
|
||||
alen = sizeof(addr);
|
||||
D("server: trying to get new connection from %d\n", ADB_LOCAL_TRANSPORT_PORT);
|
||||
fd = adb_socket_accept(serverfd, &addr, &alen);
|
||||
if(fd >= 0) {
|
||||
D("server: new connection on fd %d\n", fd);
|
||||
close_on_exec(fd);
|
||||
disable_tcp_nagle(fd);
|
||||
register_socket_transport(fd,"host",ADB_LOCAL_TRANSPORT_PORT);
|
||||
}
|
||||
}
|
||||
D("transport: server_socket_thread() exiting\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void local_init(void)
|
||||
{
|
||||
adb_thread_t thr;
|
||||
void* (*func)(void *);
|
||||
|
||||
if(HOST) {
|
||||
func = client_socket_thread;
|
||||
} else {
|
||||
func = server_socket_thread;
|
||||
}
|
||||
|
||||
D("transport: local %s init\n", HOST ? "client" : "server");
|
||||
|
||||
if(adb_thread_create(&thr, func, 0)) {
|
||||
fatal_errno("cannot create local socket %s thread",
|
||||
HOST ? "client" : "server");
|
||||
}
|
||||
}
|
||||
|
||||
static void remote_kick(atransport *t)
|
||||
{
|
||||
int fd = t->sfd;
|
||||
t->sfd = -1;
|
||||
adb_close(fd);
|
||||
|
||||
#if ADB_HOST
|
||||
if(HOST) {
|
||||
int nn;
|
||||
adb_mutex_lock( &local_transports_lock );
|
||||
for (nn = 0; nn < ADB_LOCAL_TRANSPORT_MAX; nn++) {
|
||||
if (local_transports[nn] == t) {
|
||||
local_transports[nn] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock( &local_transports_lock );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void remote_close(atransport *t)
|
||||
{
|
||||
adb_close(t->fd);
|
||||
}
|
||||
|
||||
int init_socket_transport(atransport *t, int s, int port)
|
||||
{
|
||||
int fail = 0;
|
||||
|
||||
t->kick = remote_kick;
|
||||
t->close = remote_close;
|
||||
t->read_from_remote = remote_read;
|
||||
t->write_to_remote = remote_write;
|
||||
t->sfd = s;
|
||||
t->sync_token = 1;
|
||||
t->connection_state = CS_OFFLINE;
|
||||
t->type = kTransportLocal;
|
||||
|
||||
#if ADB_HOST
|
||||
if (HOST) {
|
||||
adb_mutex_lock( &local_transports_lock );
|
||||
{
|
||||
int index = (port - ADB_LOCAL_TRANSPORT_PORT)/2;
|
||||
|
||||
if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) {
|
||||
D("bad local transport port number: %d\n", port);
|
||||
fail = -1;
|
||||
}
|
||||
else if (local_transports[index] != NULL) {
|
||||
D("local transport for port %d already registered (%p)?\n",
|
||||
port, local_transports[index]);
|
||||
fail = -1;
|
||||
}
|
||||
else
|
||||
local_transports[index] = t;
|
||||
}
|
||||
adb_mutex_unlock( &local_transports_lock );
|
||||
}
|
||||
#endif
|
||||
return fail;
|
||||
}
|
147
adb/transport_usb.c
Normal file
147
adb/transport_usb.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sysdeps.h>
|
||||
|
||||
#define TRACE_TAG TRACE_TRANSPORT
|
||||
#include "adb.h"
|
||||
|
||||
/* XXX better define? */
|
||||
#ifdef __ppc__
|
||||
#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)
|
||||
static inline void fix_endians(apacket *p)
|
||||
{
|
||||
p->msg.command = H4(p->msg.command);
|
||||
p->msg.arg0 = H4(p->msg.arg0);
|
||||
p->msg.arg1 = H4(p->msg.arg1);
|
||||
p->msg.data_length = H4(p->msg.data_length);
|
||||
p->msg.data_check = H4(p->msg.data_check);
|
||||
p->msg.magic = H4(p->msg.magic);
|
||||
}
|
||||
unsigned host_to_le32(unsigned n)
|
||||
{
|
||||
return H4(n);
|
||||
}
|
||||
#else
|
||||
#define fix_endians(p) do {} while (0)
|
||||
unsigned host_to_le32(unsigned n)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int remote_read(apacket *p, atransport *t)
|
||||
{
|
||||
if(usb_read(t->usb, &p->msg, sizeof(amessage))){
|
||||
D("remote usb: read terminated (message)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fix_endians(p);
|
||||
|
||||
if(check_header(p)) {
|
||||
D("remote usb: check_header failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(p->msg.data_length) {
|
||||
if(usb_read(t->usb, p->data, p->msg.data_length)){
|
||||
D("remote usb: terminated (data)\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(check_data(p)) {
|
||||
D("remote usb: check_data failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_write(apacket *p, atransport *t)
|
||||
{
|
||||
unsigned size = p->msg.data_length;
|
||||
|
||||
fix_endians(p);
|
||||
|
||||
if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
|
||||
D("remote usb: 1 - write terminated\n");
|
||||
return -1;
|
||||
}
|
||||
if(p->msg.data_length == 0) return 0;
|
||||
if(usb_write(t->usb, &p->data, size)) {
|
||||
D("remote usb: 2 - write terminated\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remote_close(atransport *t)
|
||||
{
|
||||
usb_close(t->usb);
|
||||
t->usb = 0;
|
||||
}
|
||||
|
||||
static void remote_kick(atransport *t)
|
||||
{
|
||||
usb_kick(t->usb);
|
||||
}
|
||||
|
||||
void init_usb_transport(atransport *t, usb_handle *h)
|
||||
{
|
||||
D("transport: usb\n");
|
||||
t->close = remote_close;
|
||||
t->kick = remote_kick;
|
||||
t->read_from_remote = remote_read;
|
||||
t->write_to_remote = remote_write;
|
||||
t->sync_token = 1;
|
||||
t->connection_state = CS_OFFLINE;
|
||||
t->type = kTransportUsb;
|
||||
t->usb = h;
|
||||
|
||||
#if ADB_HOST
|
||||
HOST = 1;
|
||||
#else
|
||||
HOST = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
|
||||
{
|
||||
if (vid == VENDOR_ID_GOOGLE) {
|
||||
/* might support adb */
|
||||
} else if (vid == VENDOR_ID_HTC) {
|
||||
/* might support adb */
|
||||
} else {
|
||||
/* not supported */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* class:vendor (0xff) subclass:android (0x42) proto:adb (0x01) */
|
||||
if(usb_class == 0xff) {
|
||||
if((usb_subclass == 0x42) && (usb_protocol == 0x01)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
653
adb/usb_linux.c
Normal file
653
adb/usb_linux.c
Normal file
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
|
||||
#include <linux/usb/ch9.h>
|
||||
#else
|
||||
#include <linux/usb_ch9.h>
|
||||
#endif
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_USB
|
||||
#include "adb.h"
|
||||
|
||||
|
||||
/* usb scan debugging is waaaay too verbose */
|
||||
#define DBGX(x...)
|
||||
|
||||
static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER;
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
usb_handle *prev;
|
||||
usb_handle *next;
|
||||
|
||||
char fname[64];
|
||||
int desc;
|
||||
unsigned char ep_in;
|
||||
unsigned char ep_out;
|
||||
|
||||
unsigned zero_mask;
|
||||
|
||||
struct usbdevfs_urb urb_in;
|
||||
struct usbdevfs_urb urb_out;
|
||||
|
||||
int urb_in_busy;
|
||||
int urb_out_busy;
|
||||
int dead;
|
||||
|
||||
adb_cond_t notify;
|
||||
adb_mutex_t lock;
|
||||
|
||||
// for garbage collecting disconnected devices
|
||||
int mark;
|
||||
|
||||
// ID of thread currently in REAPURB
|
||||
pthread_t reaper_thread;
|
||||
};
|
||||
|
||||
static usb_handle handle_list = {
|
||||
.prev = &handle_list,
|
||||
.next = &handle_list,
|
||||
};
|
||||
|
||||
static int known_device(const char *dev_name)
|
||||
{
|
||||
usb_handle *usb;
|
||||
|
||||
adb_mutex_lock(&usb_lock);
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
|
||||
if(!strcmp(usb->fname, dev_name)) {
|
||||
// set mark flag to indicate this device is still alive
|
||||
usb->mark = 1;
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kick_disconnected_devices()
|
||||
{
|
||||
usb_handle *usb;
|
||||
|
||||
adb_mutex_lock(&usb_lock);
|
||||
// kick any devices in the device list that were not found in the device scan
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
|
||||
if (usb->mark == 0) {
|
||||
usb_kick(usb);
|
||||
} else {
|
||||
usb->mark = 0;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
}
|
||||
|
||||
static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
|
||||
int ifc, const char *serial, unsigned zero_mask);
|
||||
|
||||
static inline int badname(const char *name)
|
||||
{
|
||||
while(*name) {
|
||||
if(!isdigit(*name++)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_usb_device(const char *base,
|
||||
void (*register_device_callback) (const char *, unsigned char, unsigned char, int, const char *, unsigned))
|
||||
{
|
||||
char busname[32], devname[32];
|
||||
unsigned char local_ep_in, local_ep_out;
|
||||
DIR *busdir , *devdir ;
|
||||
struct dirent *de;
|
||||
int fd ;
|
||||
int found_device = 0;
|
||||
char serial[256];
|
||||
|
||||
busdir = opendir(base);
|
||||
if(busdir == 0) return 0;
|
||||
|
||||
while((de = readdir(busdir)) != 0) {
|
||||
if(badname(de->d_name)) continue;
|
||||
|
||||
snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
|
||||
devdir = opendir(busname);
|
||||
if(devdir == 0) continue;
|
||||
|
||||
// DBGX("[ scanning %s ]\n", busname);
|
||||
while((de = readdir(devdir))) {
|
||||
unsigned char devdesc[256];
|
||||
unsigned char* bufptr = devdesc;
|
||||
struct usb_device_descriptor* device;
|
||||
struct usb_config_descriptor* config;
|
||||
struct usb_interface_descriptor* interface;
|
||||
struct usb_endpoint_descriptor *ep1, *ep2;
|
||||
unsigned zero_mask = 0;
|
||||
unsigned vid, pid;
|
||||
int i, interfaces;
|
||||
size_t desclength;
|
||||
|
||||
if(badname(de->d_name)) continue;
|
||||
snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name);
|
||||
|
||||
if(known_device(devname)) {
|
||||
DBGX("skipping %s\n", devname);
|
||||
continue;
|
||||
}
|
||||
|
||||
// DBGX("[ scanning %s ]\n", devname);
|
||||
if((fd = unix_open(devname, O_RDWR)) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
desclength = adb_read(fd, devdesc, sizeof(devdesc));
|
||||
|
||||
// should have device and configuration descriptors, and atleast two endpoints
|
||||
if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
|
||||
D("desclength %d is too small\n", desclength);
|
||||
adb_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
device = (struct usb_device_descriptor*)bufptr;
|
||||
bufptr += USB_DT_DEVICE_SIZE;
|
||||
|
||||
if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
|
||||
adb_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
vid = __le16_to_cpu(device->idVendor);
|
||||
pid = __le16_to_cpu(device->idProduct);
|
||||
pid = devdesc[10] | (devdesc[11] << 8);
|
||||
DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
|
||||
|
||||
// should have config descriptor next
|
||||
config = (struct usb_config_descriptor *)bufptr;
|
||||
bufptr += USB_DT_CONFIG_SIZE;
|
||||
if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
|
||||
D("usb_config_descriptor not found\n");
|
||||
adb_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
// loop through all the interfaces and look for the ADB interface
|
||||
interfaces = config->bNumInterfaces;
|
||||
for (i = 0; i < interfaces; i++) {
|
||||
if (bufptr + USB_DT_ENDPOINT_SIZE > devdesc + desclength)
|
||||
break;
|
||||
|
||||
interface = (struct usb_interface_descriptor *)bufptr;
|
||||
bufptr += USB_DT_INTERFACE_SIZE;
|
||||
if (interface->bLength != USB_DT_INTERFACE_SIZE ||
|
||||
interface->bDescriptorType != USB_DT_INTERFACE) {
|
||||
D("usb_interface_descriptor not found\n");
|
||||
break;
|
||||
}
|
||||
|
||||
DBGX("bInterfaceClass: %d, bInterfaceSubClass: %d,"
|
||||
"bInterfaceProtocol: %d, bNumEndpoints: %d\n",
|
||||
interface->bInterfaceClass, interface->bInterfaceSubClass,
|
||||
interface->bInterfaceProtocol, interface->bNumEndpoints);
|
||||
|
||||
if (interface->bNumEndpoints == 2 &&
|
||||
is_adb_interface(vid, pid, interface->bInterfaceClass,
|
||||
interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
|
||||
|
||||
DBGX("looking for bulk endpoints\n");
|
||||
// looks like ADB...
|
||||
ep1 = (struct usb_endpoint_descriptor *)bufptr;
|
||||
bufptr += USB_DT_ENDPOINT_SIZE;
|
||||
ep2 = (struct usb_endpoint_descriptor *)bufptr;
|
||||
bufptr += USB_DT_ENDPOINT_SIZE;
|
||||
|
||||
if (bufptr > devdesc + desclength ||
|
||||
ep1->bLength != USB_DT_ENDPOINT_SIZE ||
|
||||
ep1->bDescriptorType != USB_DT_ENDPOINT ||
|
||||
ep2->bLength != USB_DT_ENDPOINT_SIZE ||
|
||||
ep2->bDescriptorType != USB_DT_ENDPOINT) {
|
||||
D("endpoints not found\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// both endpoints should be bulk
|
||||
if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
|
||||
ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
|
||||
D("bulk endpoints not found\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* aproto 01 needs 0 termination */
|
||||
if(interface->bInterfaceProtocol == 0x01) {
|
||||
zero_mask = ep1->wMaxPacketSize - 1;
|
||||
}
|
||||
|
||||
// we have a match. now we just need to figure out which is in and which is out.
|
||||
if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
|
||||
local_ep_in = ep1->bEndpointAddress;
|
||||
local_ep_out = ep2->bEndpointAddress;
|
||||
} else {
|
||||
local_ep_in = ep2->bEndpointAddress;
|
||||
local_ep_out = ep1->bEndpointAddress;
|
||||
}
|
||||
|
||||
// read the device's serial number
|
||||
serial[0] = 0;
|
||||
memset(serial, 0, sizeof(serial));
|
||||
if (device->iSerialNumber) {
|
||||
struct usbdevfs_ctrltransfer ctrl;
|
||||
__u16 buffer[128];
|
||||
int result;
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
|
||||
ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
|
||||
ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
|
||||
ctrl.wValue = (USB_DT_STRING << 8) | device->iSerialNumber;
|
||||
ctrl.wIndex = 0;
|
||||
ctrl.wLength = sizeof(buffer);
|
||||
ctrl.data = buffer;
|
||||
|
||||
result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
|
||||
if (result > 0) {
|
||||
int i;
|
||||
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
|
||||
result /= 2;
|
||||
for (i = 1; i < result; i++)
|
||||
serial[i - 1] = buffer[i];
|
||||
serial[i - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
register_device_callback(devname, local_ep_in, local_ep_out, i, serial, zero_mask);
|
||||
|
||||
found_device = 1;
|
||||
break;
|
||||
} else {
|
||||
// skip to next interface
|
||||
bufptr += (interface->bNumEndpoints * USB_DT_ENDPOINT_SIZE);
|
||||
}
|
||||
} // end of for
|
||||
|
||||
adb_close(fd);
|
||||
} // end of devdir while
|
||||
closedir(devdir);
|
||||
} //end of busdir while
|
||||
closedir(busdir);
|
||||
|
||||
return found_device;
|
||||
}
|
||||
|
||||
void usb_cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
static int usb_bulk_write(usb_handle *h, const void *data, int len)
|
||||
{
|
||||
struct usbdevfs_urb *urb = &h->urb_out;
|
||||
int res;
|
||||
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->endpoint = h->ep_out;
|
||||
urb->status = -1;
|
||||
urb->buffer = (void*) data;
|
||||
urb->buffer_length = len;
|
||||
|
||||
D("++ write ++\n");
|
||||
|
||||
adb_mutex_lock(&h->lock);
|
||||
if(h->dead) {
|
||||
res = -1;
|
||||
goto fail;
|
||||
}
|
||||
do {
|
||||
res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
|
||||
} while((res < 0) && (errno == EINTR));
|
||||
|
||||
if(res < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = -1;
|
||||
h->urb_out_busy = 1;
|
||||
for(;;) {
|
||||
adb_cond_wait(&h->notify, &h->lock);
|
||||
if(h->dead) {
|
||||
break;
|
||||
}
|
||||
if(h->urb_out_busy == 0) {
|
||||
if(urb->status == 0) {
|
||||
res = urb->actual_length;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fail:
|
||||
adb_mutex_unlock(&h->lock);
|
||||
D("-- write --\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
static int usb_bulk_read(usb_handle *h, void *data, int len)
|
||||
{
|
||||
struct usbdevfs_urb *urb = &h->urb_in;
|
||||
struct usbdevfs_urb *out = NULL;
|
||||
int res;
|
||||
|
||||
memset(urb, 0, sizeof(*urb));
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
urb->endpoint = h->ep_in;
|
||||
urb->status = -1;
|
||||
urb->buffer = data;
|
||||
urb->buffer_length = len;
|
||||
|
||||
|
||||
adb_mutex_lock(&h->lock);
|
||||
if(h->dead) {
|
||||
res = -1;
|
||||
goto fail;
|
||||
}
|
||||
do {
|
||||
res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
|
||||
} while((res < 0) && (errno == EINTR));
|
||||
|
||||
if(res < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
h->urb_in_busy = 1;
|
||||
for(;;) {
|
||||
D("[ reap urb - wait ]\n");
|
||||
h->reaper_thread = pthread_self();
|
||||
adb_mutex_unlock(&h->lock);
|
||||
res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
|
||||
adb_mutex_lock(&h->lock);
|
||||
h->reaper_thread = 0;
|
||||
if(h->dead) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if(res < 0) {
|
||||
if(errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
D("[ reap urb - error ]\n");
|
||||
break;
|
||||
}
|
||||
D("[ urb @%p status = %d, actual = %d ]\n",
|
||||
out, out->status, out->actual_length);
|
||||
|
||||
if(out == &h->urb_in) {
|
||||
D("[ reap urb - IN complete ]\n");
|
||||
h->urb_in_busy = 0;
|
||||
if(urb->status == 0) {
|
||||
res = urb->actual_length;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(out == &h->urb_out) {
|
||||
D("[ reap urb - OUT compelete ]\n");
|
||||
h->urb_out_busy = 0;
|
||||
adb_cond_broadcast(&h->notify);
|
||||
}
|
||||
}
|
||||
fail:
|
||||
adb_mutex_unlock(&h->lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int usb_write(usb_handle *h, const void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
int n;
|
||||
int need_zero = 0;
|
||||
|
||||
if(h->zero_mask) {
|
||||
/* if we need 0-markers and our transfer
|
||||
** is an even multiple of the packet size,
|
||||
** we make note of it
|
||||
*/
|
||||
if(!(len & h->zero_mask)) {
|
||||
need_zero = 1;
|
||||
}
|
||||
}
|
||||
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
n = usb_bulk_write(h, data, xfer);
|
||||
if(n != xfer) {
|
||||
D("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
|
||||
if(need_zero){
|
||||
n = usb_bulk_write(h, _data, 0);
|
||||
return n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
int n;
|
||||
|
||||
D("++ usb_read ++\n");
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
|
||||
n = usb_bulk_read(h, data, xfer);
|
||||
D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
|
||||
if(n != xfer) {
|
||||
if((errno == ETIMEDOUT) && (h->desc != -1)) {
|
||||
D("[ timeout ]\n");
|
||||
if(n > 0){
|
||||
data += n;
|
||||
len -= n;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
D("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
|
||||
D("-- usb_read --\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *h)
|
||||
{
|
||||
D("[ kicking %p (fd = %d) ]\n", h, h->desc);
|
||||
adb_mutex_lock(&h->lock);
|
||||
if(h->dead == 0) {
|
||||
h->dead = 1;
|
||||
|
||||
/* HACK ALERT!
|
||||
** Sometimes we get stuck in ioctl(USBDEVFS_REAPURB).
|
||||
** This is a workaround for that problem.
|
||||
*/
|
||||
if (h->reaper_thread) {
|
||||
pthread_kill(h->reaper_thread, SIGALRM);
|
||||
}
|
||||
|
||||
/* cancel any pending transactions
|
||||
** these will quietly fail if the txns are not active,
|
||||
** but this ensures that a reader blocked on REAPURB
|
||||
** will get unblocked
|
||||
*/
|
||||
ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_in);
|
||||
ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out);
|
||||
h->urb_in.status = -ENODEV;
|
||||
h->urb_out.status = -ENODEV;
|
||||
h->urb_in_busy = 0;
|
||||
h->urb_out_busy = 0;
|
||||
adb_cond_broadcast(&h->notify);
|
||||
}
|
||||
adb_mutex_unlock(&h->lock);
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *h)
|
||||
{
|
||||
D("[ usb close ... ]\n");
|
||||
adb_mutex_lock(&usb_lock);
|
||||
h->next->prev = h->prev;
|
||||
h->prev->next = h->next;
|
||||
h->prev = 0;
|
||||
h->next = 0;
|
||||
|
||||
adb_close(h->desc);
|
||||
D("[ usb closed %p (fd = %d) ]\n", h, h->desc);
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
free(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_device(const char *dev_name,
|
||||
unsigned char ep_in, unsigned char ep_out,
|
||||
int interface,
|
||||
const char *serial, unsigned zero_mask)
|
||||
{
|
||||
usb_handle* usb = 0;
|
||||
int n = 0;
|
||||
|
||||
/* Since Linux will not reassign the device ID (and dev_name)
|
||||
** as long as the device is open, we can add to the list here
|
||||
** once we open it and remove from the list when we're finally
|
||||
** closed and everything will work out fine.
|
||||
**
|
||||
** If we have a usb_handle on the list 'o handles with a matching
|
||||
** name, we have no further work to do.
|
||||
*/
|
||||
adb_mutex_lock(&usb_lock);
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
|
||||
if(!strcmp(usb->fname, dev_name)) {
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
D("[ usb located new device %s (%d/%d/%d) ]\n",
|
||||
dev_name, ep_in, ep_out, interface);
|
||||
usb = calloc(1, sizeof(usb_handle));
|
||||
strcpy(usb->fname, dev_name);
|
||||
usb->ep_in = ep_in;
|
||||
usb->ep_out = ep_out;
|
||||
usb->zero_mask = zero_mask;
|
||||
|
||||
adb_cond_init(&usb->notify, 0);
|
||||
adb_mutex_init(&usb->lock, 0);
|
||||
/* initialize mark to 1 so we don't get garbage collected after the device scan */
|
||||
usb->mark = 1;
|
||||
usb->reaper_thread = 0;
|
||||
|
||||
usb->desc = unix_open(usb->fname, O_RDWR);
|
||||
if(usb->desc < 0) goto fail;
|
||||
D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
|
||||
n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
|
||||
if(n != 0) goto fail;
|
||||
|
||||
/* add to the end of the active handles */
|
||||
adb_mutex_lock(&usb_lock);
|
||||
usb->next = &handle_list;
|
||||
usb->prev = handle_list.prev;
|
||||
usb->prev->next = usb;
|
||||
usb->next->prev = usb;
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
register_usb_transport(usb, serial);
|
||||
return;
|
||||
|
||||
fail:
|
||||
D("[ usb open %s error=%d, err_str = %s]\n",
|
||||
usb->fname, errno, strerror(errno));
|
||||
if(usb->desc >= 0) {
|
||||
adb_close(usb->desc);
|
||||
}
|
||||
free(usb);
|
||||
}
|
||||
|
||||
void* device_poll_thread(void* unused)
|
||||
{
|
||||
D("Created device thread\n");
|
||||
for(;;) {
|
||||
/* XXX use inotify */
|
||||
find_usb_device("/dev/bus/usb", register_device);
|
||||
kick_disconnected_devices();
|
||||
sleep(1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sigalrm_handler(int signo)
|
||||
{
|
||||
// don't need to do anything here
|
||||
}
|
||||
|
||||
void usb_init()
|
||||
{
|
||||
adb_thread_t tid;
|
||||
struct sigaction actions;
|
||||
|
||||
memset(&actions, 0, sizeof(actions));
|
||||
sigemptyset(&actions.sa_mask);
|
||||
actions.sa_flags = 0;
|
||||
actions.sa_handler = sigalrm_handler;
|
||||
sigaction(SIGALRM,& actions, NULL);
|
||||
|
||||
if(adb_thread_create(&tid, device_poll_thread, NULL)){
|
||||
fatal_errno("cannot create input thread");
|
||||
}
|
||||
}
|
||||
|
156
adb/usb_linux_client.c
Normal file
156
adb/usb_linux_client.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_USB
|
||||
#include "adb.h"
|
||||
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
int fd;
|
||||
adb_cond_t notify;
|
||||
adb_mutex_t lock;
|
||||
};
|
||||
|
||||
void usb_cleanup()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
static void *usb_open_thread(void *x)
|
||||
{
|
||||
struct usb_handle *usb = (struct usb_handle *)x;
|
||||
int fd;
|
||||
|
||||
while (1) {
|
||||
// wait until the USB device needs opening
|
||||
adb_mutex_lock(&usb->lock);
|
||||
while (usb->fd != -1)
|
||||
adb_cond_wait(&usb->notify, &usb->lock);
|
||||
adb_mutex_unlock(&usb->lock);
|
||||
|
||||
D("[ usb_thread - opening device ]\n");
|
||||
do {
|
||||
/* XXX use inotify? */
|
||||
fd = unix_open("/dev/android_adb", O_RDWR);
|
||||
if (fd < 0) {
|
||||
// to support older kernels
|
||||
fd = unix_open("/dev/android", O_RDWR);
|
||||
}
|
||||
if (fd < 0) {
|
||||
adb_sleep_ms(1000);
|
||||
}
|
||||
} while (fd < 0);
|
||||
D("[ opening device succeeded ]\n");
|
||||
|
||||
close_on_exec(fd);
|
||||
usb->fd = fd;
|
||||
|
||||
D("[ usb_thread - registering device ]\n");
|
||||
register_usb_transport(usb, 0);
|
||||
}
|
||||
|
||||
// never gets here
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *h, const void *data, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
D("[ write %d ]\n", len);
|
||||
n = adb_write(h->fd, data, len);
|
||||
if(n != len) {
|
||||
D("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
D("[ done ]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *data, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
D("[ read %d ]\n", len);
|
||||
n = adb_read(h->fd, data, len);
|
||||
if(n != len) {
|
||||
D("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_init()
|
||||
{
|
||||
usb_handle *h;
|
||||
adb_thread_t tid;
|
||||
int fd;
|
||||
|
||||
h = calloc(1, sizeof(usb_handle));
|
||||
h->fd = -1;
|
||||
adb_cond_init(&h->notify, 0);
|
||||
adb_mutex_init(&h->lock, 0);
|
||||
|
||||
// Open the file /dev/android_adb_enable to trigger
|
||||
// the enabling of the adb USB function in the kernel.
|
||||
// We never touch this file again - just leave it open
|
||||
// indefinitely so the kernel will know when we are running
|
||||
// and when we are not.
|
||||
fd = unix_open("/dev/android_adb_enable", O_RDWR);
|
||||
if (fd < 0) {
|
||||
D("failed to open /dev/android_adb_enable\n");
|
||||
} else {
|
||||
close_on_exec(fd);
|
||||
}
|
||||
|
||||
D("[ usb_init - starting thread ]\n");
|
||||
if(adb_thread_create(&tid, usb_open_thread, h)){
|
||||
fatal_errno("cannot create usb thread");
|
||||
}
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *h)
|
||||
{
|
||||
D("usb_kick\n");
|
||||
adb_mutex_lock(&h->lock);
|
||||
adb_close(h->fd);
|
||||
h->fd = -1;
|
||||
|
||||
// notify usb_open_thread that we are disconnected
|
||||
adb_cond_signal(&h->notify);
|
||||
adb_mutex_unlock(&h->lock);
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *h)
|
||||
{
|
||||
// nothing to do here
|
||||
return 0;
|
||||
}
|
536
adb/usb_osx.c
Normal file
536
adb/usb_osx.c
Normal file
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define TRACE_TAG TRACE_USB
|
||||
#include "adb.h"
|
||||
|
||||
#define DBG D
|
||||
|
||||
typedef struct {
|
||||
int vid;
|
||||
int pid;
|
||||
} VendorProduct;
|
||||
|
||||
#define kSupportedDeviceCount 4
|
||||
VendorProduct kSupportedDevices[kSupportedDeviceCount] = {
|
||||
{ VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER },
|
||||
{ VENDOR_ID_GOOGLE, PRODUCT_ID_SOONER_COMP },
|
||||
{ VENDOR_ID_HTC, PRODUCT_ID_DREAM },
|
||||
{ VENDOR_ID_HTC, PRODUCT_ID_DREAM_COMP },
|
||||
};
|
||||
|
||||
static IONotificationPortRef notificationPort = 0;
|
||||
static io_iterator_t notificationIterators[kSupportedDeviceCount];
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
UInt8 bulkIn;
|
||||
UInt8 bulkOut;
|
||||
IOUSBInterfaceInterface **interface;
|
||||
io_object_t usbNotification;
|
||||
unsigned int zero_mask;
|
||||
};
|
||||
|
||||
static CFRunLoopRef currentRunLoop = 0;
|
||||
static pthread_mutex_t start_lock;
|
||||
static pthread_cond_t start_cond;
|
||||
|
||||
|
||||
static void AndroidDeviceAdded(void *refCon, io_iterator_t iterator);
|
||||
static void AndroidDeviceNotify(void *refCon, io_iterator_t iterator, natural_t messageType, void *messageArgument);
|
||||
static usb_handle* FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product);
|
||||
|
||||
static int
|
||||
InitUSB()
|
||||
{
|
||||
CFMutableDictionaryRef matchingDict;
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
SInt32 vendor, product;
|
||||
int i;
|
||||
|
||||
//* To set up asynchronous notifications, create a notification port and
|
||||
//* add its run loop event source to the program's run loop
|
||||
notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
memset(notificationIterators, 0, sizeof(notificationIterators));
|
||||
|
||||
//* loop through all supported vendor/product pairs
|
||||
for (i = 0; i < kSupportedDeviceCount; i++) {
|
||||
//* Create our matching dictionary to find the Android device
|
||||
//* IOServiceAddMatchingNotification consumes the reference, so we do not need to release this
|
||||
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
|
||||
if (!matchingDict) {
|
||||
DBG("ERR: Couldn't create USB matching dictionary.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//* Set up two matching dictionaries, one for each product ID we support.
|
||||
//* This will cause the kernel to notify us only if the vendor and product IDs match.
|
||||
vendor = kSupportedDevices[i].vid;
|
||||
product = kSupportedDevices[i].pid;
|
||||
CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor));
|
||||
CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product));
|
||||
|
||||
//* Now set up two notifications: one to be called when a raw device
|
||||
//* is first matched by the I/O Kit and another to be called when the
|
||||
//* device is terminated.
|
||||
//* we need to do this with each matching dictionary.
|
||||
IOServiceAddMatchingNotification(
|
||||
notificationPort,
|
||||
kIOFirstMatchNotification,
|
||||
matchingDict,
|
||||
AndroidDeviceAdded,
|
||||
NULL,
|
||||
¬ificationIterators[i]);
|
||||
|
||||
//* Iterate over set of matching devices to access already-present devices
|
||||
//* and to arm the notification
|
||||
AndroidDeviceAdded(NULL, notificationIterators[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidDeviceAdded(void *refCon, io_iterator_t iterator)
|
||||
{
|
||||
kern_return_t kr;
|
||||
io_service_t usbDevice;
|
||||
IOCFPlugInInterface **plugInInterface = NULL;
|
||||
IOUSBDeviceInterface182 **dev = NULL;
|
||||
HRESULT result;
|
||||
SInt32 score;
|
||||
UInt16 vendor;
|
||||
UInt16 product;
|
||||
UInt8 serialIndex;
|
||||
char serial[256];
|
||||
|
||||
while ((usbDevice = IOIteratorNext(iterator))) {
|
||||
//* Create an intermediate plugin
|
||||
kr = IOCreatePlugInInterfaceForService(usbDevice,
|
||||
kIOUSBDeviceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface, &score);
|
||||
|
||||
if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
|
||||
DBG("ERR: Unable to create a plug-in (%08x)\n", kr);
|
||||
goto continue1;
|
||||
}
|
||||
|
||||
//* Now create the device interface
|
||||
result = (*plugInInterface)->QueryInterface(plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
|
||||
|
||||
if (result || !dev) {
|
||||
DBG("ERR: Couldn't create a device interface (%08x)\n", (int) result);
|
||||
goto continue2;
|
||||
}
|
||||
|
||||
//* Check the device to see if it's ours
|
||||
kr = (*dev)->GetDeviceVendor(dev, &vendor);
|
||||
kr = (*dev)->GetDeviceProduct(dev, &product);
|
||||
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
|
||||
|
||||
if (serialIndex > 0) {
|
||||
IOUSBDevRequest req;
|
||||
UInt16 buffer[256];
|
||||
|
||||
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
|
||||
req.bRequest = kUSBRqGetDescriptor;
|
||||
req.wValue = (kUSBStringDesc << 8) | serialIndex;
|
||||
req.wIndex = 0;
|
||||
req.pData = buffer;
|
||||
req.wLength = sizeof(buffer);
|
||||
kr = (*dev)->DeviceRequest(dev, &req);
|
||||
|
||||
if (kr == kIOReturnSuccess && req.wLenDone > 0) {
|
||||
int i, count;
|
||||
|
||||
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
|
||||
count = (req.wLenDone - 1) / 2;
|
||||
for (i = 0; i < count; i++)
|
||||
serial[i] = buffer[i + 1];
|
||||
serial[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
usb_handle* handle = NULL;
|
||||
|
||||
//* Open the device
|
||||
kr = (*dev)->USBDeviceOpen(dev);
|
||||
|
||||
if (kr != kIOReturnSuccess) {
|
||||
DBG("ERR: Could not open device: %08x\n", kr);
|
||||
goto continue3;
|
||||
} else {
|
||||
//* Find an interface for the device
|
||||
handle = FindDeviceInterface((IOUSBDeviceInterface**)dev, vendor, product);
|
||||
}
|
||||
|
||||
if (handle == NULL) {
|
||||
DBG("ERR: Could not find device interface: %08x\n", kr);
|
||||
(*dev)->USBDeviceClose(dev);
|
||||
goto continue3;
|
||||
}
|
||||
|
||||
DBG("AndroidDeviceAdded calling register_usb_transport\n");
|
||||
register_usb_transport(handle, (serial[0] ? serial : NULL));
|
||||
|
||||
// Register for an interest notification of this device being removed. Pass the reference to our
|
||||
// private data as the refCon for the notification.
|
||||
kr = IOServiceAddInterestNotification(notificationPort,
|
||||
usbDevice,
|
||||
kIOGeneralInterest,
|
||||
AndroidDeviceNotify,
|
||||
handle,
|
||||
&handle->usbNotification);
|
||||
if (kIOReturnSuccess != kr) {
|
||||
DBG("ERR: Unable to create interest notification (%08x)\n", kr);
|
||||
}
|
||||
|
||||
continue3:
|
||||
(void)(*dev)->Release(dev);
|
||||
continue2:
|
||||
IODestroyPlugInInterface(plugInInterface);
|
||||
continue1:
|
||||
IOObjectRelease(usbDevice);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidDeviceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
|
||||
{
|
||||
usb_handle *handle = (usb_handle *)refCon;
|
||||
|
||||
if (messageType == kIOMessageServiceIsTerminated) {
|
||||
DBG("AndroidDeviceNotify\n");
|
||||
IOObjectRelease(handle->usbNotification);
|
||||
usb_kick(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static usb_handle*
|
||||
FindDeviceInterface(IOUSBDeviceInterface **dev, UInt16 vendor, UInt16 product)
|
||||
{
|
||||
usb_handle* handle = NULL;
|
||||
IOReturn kr;
|
||||
IOUSBFindInterfaceRequest request;
|
||||
io_iterator_t iterator;
|
||||
io_service_t usbInterface;
|
||||
IOCFPlugInInterface **plugInInterface;
|
||||
IOUSBInterfaceInterface **interface = NULL;
|
||||
HRESULT result;
|
||||
SInt32 score;
|
||||
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
|
||||
UInt8 endpoint, configuration;
|
||||
|
||||
//* Placing the constant KIOUSBFindInterfaceDontCare into the following
|
||||
//* fields of the IOUSBFindInterfaceRequest structure will allow us to
|
||||
//* find all of the interfaces
|
||||
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
|
||||
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
|
||||
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
|
||||
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
|
||||
|
||||
//* SetConfiguration will kill an existing UMS connection, so let's not do this if not necessary.
|
||||
configuration = 0;
|
||||
(*dev)->GetConfiguration(dev, &configuration);
|
||||
if (configuration != 1)
|
||||
(*dev)->SetConfiguration(dev, 1);
|
||||
|
||||
//* Get an iterator for the interfaces on the device
|
||||
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
|
||||
|
||||
if (kr != kIOReturnSuccess) {
|
||||
DBG("ERR: Couldn't create a device interface iterator: (%08x)\n", kr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((usbInterface = IOIteratorNext(iterator))) {
|
||||
//* Create an intermediate plugin
|
||||
kr = IOCreatePlugInInterfaceForService(
|
||||
usbInterface,
|
||||
kIOUSBInterfaceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface,
|
||||
&score);
|
||||
|
||||
//* No longer need the usbInterface object now that we have the plugin
|
||||
(void) IOObjectRelease(usbInterface);
|
||||
|
||||
if ((kr != kIOReturnSuccess) || (!plugInInterface)) {
|
||||
DBG("ERR: Unable to create plugin (%08x)\n", kr);
|
||||
break;
|
||||
}
|
||||
|
||||
//* Now create the interface interface for the interface
|
||||
result = (*plugInInterface)->QueryInterface(
|
||||
plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
|
||||
(LPVOID) &interface);
|
||||
|
||||
//* No longer need the intermediate plugin
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
|
||||
if (result || !interface) {
|
||||
DBG("ERR: Couldn't create interface interface: (%08x)\n",
|
||||
(unsigned int) result);
|
||||
break;
|
||||
}
|
||||
|
||||
//* Now open the interface. This will cause the pipes associated with
|
||||
//* the endpoints in the interface descriptor to be instantiated
|
||||
kr = (*interface)->USBInterfaceOpen(interface);
|
||||
|
||||
if (kr != kIOReturnSuccess)
|
||||
{
|
||||
DBG("ERR: Could not open interface: (%08x)\n", kr);
|
||||
(void) (*interface)->Release(interface);
|
||||
//* continue so we can try the next interface
|
||||
continue;
|
||||
}
|
||||
|
||||
//* Get the number of endpoints associated with this interface
|
||||
kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
|
||||
|
||||
if (kr != kIOReturnSuccess) {
|
||||
DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
|
||||
goto next_interface;
|
||||
}
|
||||
|
||||
//* Get interface class, subclass and protocol
|
||||
if ((*interface)->GetInterfaceClass(interface, &interfaceClass) != kIOReturnSuccess ||
|
||||
(*interface)->GetInterfaceSubClass(interface, &interfaceSubClass) != kIOReturnSuccess ||
|
||||
(*interface)->GetInterfaceProtocol(interface, &interfaceProtocol) != kIOReturnSuccess)
|
||||
{
|
||||
DBG("ERR: Unable to get interface class, subclass and protocol\n");
|
||||
goto next_interface;
|
||||
}
|
||||
|
||||
//* check to make sure interface class, subclass and protocol match ADB
|
||||
//* avoid opening mass storage endpoints
|
||||
if (is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
|
||||
handle = calloc(1, sizeof(usb_handle));
|
||||
|
||||
//* Iterate over the endpoints for this interface and find the first
|
||||
//* bulk in/out pipes available. These will be our read/write pipes.
|
||||
for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
|
||||
UInt8 transferType;
|
||||
UInt16 maxPacketSize;
|
||||
UInt8 interval;
|
||||
UInt8 number;
|
||||
UInt8 direction;
|
||||
|
||||
kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
|
||||
&number, &transferType, &maxPacketSize, &interval);
|
||||
|
||||
if (kIOReturnSuccess == kr) {
|
||||
if (kUSBBulk != transferType)
|
||||
continue;
|
||||
|
||||
if (kUSBIn == direction)
|
||||
handle->bulkIn = endpoint;
|
||||
|
||||
if (kUSBOut == direction)
|
||||
handle->bulkOut = endpoint;
|
||||
|
||||
if (interfaceProtocol == 0x01) {
|
||||
handle->zero_mask = maxPacketSize - 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
|
||||
}
|
||||
}
|
||||
|
||||
handle->interface = interface;
|
||||
break;
|
||||
}
|
||||
|
||||
next_interface:
|
||||
(*interface)->USBInterfaceClose(interface);
|
||||
(*interface)->Release(interface);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
void* RunLoopThread(void* unused)
|
||||
{
|
||||
int i;
|
||||
|
||||
InitUSB();
|
||||
|
||||
currentRunLoop = CFRunLoopGetCurrent();
|
||||
|
||||
// Signal the parent that we are running
|
||||
adb_mutex_lock(&start_lock);
|
||||
adb_cond_signal(&start_cond);
|
||||
adb_mutex_unlock(&start_lock);
|
||||
|
||||
CFRunLoopRun();
|
||||
currentRunLoop = 0;
|
||||
|
||||
for (i = 0; i < kSupportedDeviceCount; i++) {
|
||||
IOObjectRelease(notificationIterators[i]);
|
||||
}
|
||||
IONotificationPortDestroy(notificationPort);
|
||||
|
||||
DBG("RunLoopThread done\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int initialized = 0;
|
||||
void usb_init()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
adb_thread_t tid;
|
||||
|
||||
adb_mutex_init(&start_lock, NULL);
|
||||
adb_cond_init(&start_cond, NULL);
|
||||
|
||||
if(adb_thread_create(&tid, RunLoopThread, NULL))
|
||||
fatal_errno("cannot create input thread");
|
||||
|
||||
// Wait for initialization to finish
|
||||
adb_mutex_lock(&start_lock);
|
||||
adb_cond_wait(&start_cond, &start_lock);
|
||||
adb_mutex_unlock(&start_lock);
|
||||
|
||||
adb_mutex_destroy(&start_lock);
|
||||
adb_cond_destroy(&start_cond);
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cleanup()
|
||||
{
|
||||
DBG("usb_cleanup\n");
|
||||
close_usb_devices();
|
||||
if (currentRunLoop)
|
||||
CFRunLoopStop(currentRunLoop);
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *handle, const void *buf, int len)
|
||||
{
|
||||
IOReturn result;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (!handle)
|
||||
return -1;
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
DBG("ERR: usb_write interface was null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == handle->bulkOut) {
|
||||
DBG("ERR: bulkOut endpoint not assigned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result =
|
||||
(*handle->interface)->WritePipe(
|
||||
handle->interface, handle->bulkOut, (void *)buf, len);
|
||||
|
||||
if ((result == 0) && (handle->zero_mask)) {
|
||||
/* we need 0-markers and our transfer */
|
||||
if(!(len & handle->zero_mask)) {
|
||||
result =
|
||||
(*handle->interface)->WritePipe(
|
||||
handle->interface, handle->bulkOut, (void *)buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == result)
|
||||
return 0;
|
||||
|
||||
DBG("ERR: usb_write failed with status %d\n", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *handle, void *buf, int len)
|
||||
{
|
||||
IOReturn result;
|
||||
UInt32 numBytes = len;
|
||||
|
||||
if (!len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
DBG("ERR: usb_read interface was null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == handle->bulkIn) {
|
||||
DBG("ERR: bulkIn endpoint not assigned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result =
|
||||
(*handle->interface)->ReadPipe(handle->interface,
|
||||
handle->bulkIn, buf, &numBytes);
|
||||
|
||||
if (0 == result)
|
||||
return 0;
|
||||
else {
|
||||
DBG("ERR: usb_read failed with status %d\n", result);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *handle)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *handle)
|
||||
{
|
||||
/* release the interface */
|
||||
if (handle->interface)
|
||||
{
|
||||
(*handle->interface)->USBInterfaceClose(handle->interface);
|
||||
(*handle->interface)->Release(handle->interface);
|
||||
handle->interface = 0;
|
||||
}
|
||||
}
|
513
adb/usb_windows.c
Normal file
513
adb/usb_windows.c
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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 <windows.h>
|
||||
#include <winerror.h>
|
||||
#include <errno.h>
|
||||
#include <usb100.h>
|
||||
#include <adb_api.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sysdeps.h"
|
||||
|
||||
#define TRACE_TAG TRACE_USB
|
||||
#include "adb.h"
|
||||
|
||||
/** Structure usb_handle describes our connection to the usb device via
|
||||
AdbWinApi.dll. This structure is returned from usb_open() routine and
|
||||
is expected in each subsequent call that is accessing the device.
|
||||
*/
|
||||
struct usb_handle {
|
||||
/// Previous entry in the list of opened usb handles
|
||||
usb_handle *prev;
|
||||
|
||||
/// Next entry in the list of opened usb handles
|
||||
usb_handle *next;
|
||||
|
||||
/// Handle to USB interface
|
||||
ADBAPIHANDLE adb_interface;
|
||||
|
||||
/// Handle to USB read pipe (endpoint)
|
||||
ADBAPIHANDLE adb_read_pipe;
|
||||
|
||||
/// Handle to USB write pipe (endpoint)
|
||||
ADBAPIHANDLE adb_write_pipe;
|
||||
|
||||
/// Interface name
|
||||
char* interface_name;
|
||||
|
||||
/// Mask for determining when to use zero length packets
|
||||
unsigned zero_mask;
|
||||
};
|
||||
|
||||
/// Class ID assigned to the device by androidusb.sys
|
||||
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
|
||||
|
||||
/// List of opened usb handles
|
||||
static usb_handle handle_list = {
|
||||
.prev = &handle_list,
|
||||
.next = &handle_list,
|
||||
};
|
||||
|
||||
/// Locker for the list of opened usb handles
|
||||
ADB_MUTEX_DEFINE( usb_lock );
|
||||
|
||||
/// Checks if there is opened usb handle in handle_list for this device.
|
||||
int known_device(const char* dev_name);
|
||||
|
||||
/// Checks if there is opened usb handle in handle_list for this device.
|
||||
/// usb_lock mutex must be held before calling this routine.
|
||||
int known_device_locked(const char* dev_name);
|
||||
|
||||
/// Registers opened usb handle (adds it to handle_list).
|
||||
int register_new_device(usb_handle* handle);
|
||||
|
||||
/// Checks if interface (device) matches certain criteria
|
||||
int recognized_device(usb_handle* handle);
|
||||
|
||||
/// Enumerates present and available interfaces (devices), opens new ones and
|
||||
/// registers usb transport for them.
|
||||
void find_devices();
|
||||
|
||||
/// Entry point for thread that polls (every second) for new usb interfaces.
|
||||
/// This routine calls find_devices in infinite loop.
|
||||
void* device_poll_thread(void* unused);
|
||||
|
||||
/// Initializes this module
|
||||
void usb_init();
|
||||
|
||||
/// Cleans up this module
|
||||
void usb_cleanup();
|
||||
|
||||
/// Opens usb interface (device) by interface (device) name.
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name);
|
||||
|
||||
/// Writes data to the opened usb handle
|
||||
int usb_write(usb_handle* handle, const void* data, int len);
|
||||
|
||||
/// Reads data using the opened usb handle
|
||||
int usb_read(usb_handle *handle, void* data, int len);
|
||||
|
||||
/// Cleans up opened usb handle
|
||||
void usb_cleanup_handle(usb_handle* handle);
|
||||
|
||||
/// Cleans up (but don't close) opened usb handle
|
||||
void usb_kick(usb_handle* handle);
|
||||
|
||||
/// Closes opened usb handle
|
||||
int usb_close(usb_handle* handle);
|
||||
|
||||
/// Gets interface (device) name for an opened usb handle
|
||||
const char *usb_name(usb_handle* handle);
|
||||
|
||||
int known_device_locked(const char* dev_name) {
|
||||
usb_handle* usb;
|
||||
|
||||
if (NULL != dev_name) {
|
||||
// Iterate through the list looking for the name match.
|
||||
for(usb = handle_list.next; usb != &handle_list; usb = usb->next) {
|
||||
// In Windows names are not case sensetive!
|
||||
if((NULL != usb->interface_name) &&
|
||||
(0 == stricmp(usb->interface_name, dev_name))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int known_device(const char* dev_name) {
|
||||
int ret = 0;
|
||||
|
||||
if (NULL != dev_name) {
|
||||
adb_mutex_lock(&usb_lock);
|
||||
ret = known_device_locked(dev_name);
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int register_new_device(usb_handle* handle) {
|
||||
if (NULL == handle)
|
||||
return 0;
|
||||
|
||||
adb_mutex_lock(&usb_lock);
|
||||
|
||||
// Check if device is already in the list
|
||||
if (known_device_locked(handle->interface_name)) {
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Not in the list. Add this handle to the list.
|
||||
handle->next = &handle_list;
|
||||
handle->prev = handle_list.prev;
|
||||
handle->prev->next = handle;
|
||||
handle->next->prev = handle;
|
||||
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void* device_poll_thread(void* unused) {
|
||||
D("Created device thread\n");
|
||||
|
||||
while(1) {
|
||||
find_devices();
|
||||
adb_sleep_ms(1000);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
adb_thread_t tid;
|
||||
|
||||
if(adb_thread_create(&tid, device_poll_thread, NULL)) {
|
||||
fatal_errno("cannot create input thread");
|
||||
}
|
||||
}
|
||||
|
||||
void usb_cleanup() {
|
||||
}
|
||||
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name) {
|
||||
// Allocate our handle
|
||||
usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
|
||||
if (NULL == ret)
|
||||
return NULL;
|
||||
|
||||
// Set linkers back to the handle
|
||||
ret->next = ret;
|
||||
ret->prev = ret;
|
||||
|
||||
// Create interface.
|
||||
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
|
||||
|
||||
if (NULL == ret->adb_interface) {
|
||||
free(ret);
|
||||
errno = GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open read pipe (endpoint)
|
||||
ret->adb_read_pipe =
|
||||
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
|
||||
AdbOpenAccessTypeReadWrite,
|
||||
AdbOpenSharingModeReadWrite);
|
||||
if (NULL != ret->adb_read_pipe) {
|
||||
// Open write pipe (endpoint)
|
||||
ret->adb_write_pipe =
|
||||
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
|
||||
AdbOpenAccessTypeReadWrite,
|
||||
AdbOpenSharingModeReadWrite);
|
||||
if (NULL != ret->adb_write_pipe) {
|
||||
// Save interface name
|
||||
unsigned long name_len = 0;
|
||||
|
||||
// First get expected name length
|
||||
AdbGetInterfaceName(ret->adb_interface,
|
||||
NULL,
|
||||
&name_len,
|
||||
true);
|
||||
if (0 != name_len) {
|
||||
ret->interface_name = (char*)malloc(name_len);
|
||||
|
||||
if (NULL != ret->interface_name) {
|
||||
// Now save the name
|
||||
if (AdbGetInterfaceName(ret->adb_interface,
|
||||
ret->interface_name,
|
||||
&name_len,
|
||||
true)) {
|
||||
// We're done at this point
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Something went wrong.
|
||||
errno = GetLastError();
|
||||
usb_cleanup_handle(ret);
|
||||
free(ret);
|
||||
SetLastError(errno);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* handle, const void* data, int len) {
|
||||
unsigned long time_out = 500 + len * 8;
|
||||
unsigned long written = 0;
|
||||
int ret;
|
||||
|
||||
D("usb_write %d\n", len);
|
||||
if (NULL != handle) {
|
||||
// Perform write
|
||||
ret = AdbWriteEndpointSync(handle->adb_write_pipe,
|
||||
(void*)data,
|
||||
(unsigned long)len,
|
||||
&written,
|
||||
time_out);
|
||||
errno = GetLastError();
|
||||
|
||||
if (ret) {
|
||||
// Make sure that we've written what we were asked to write
|
||||
D("usb_write got: %ld, expected: %d\n", written, len);
|
||||
if (written == (unsigned long)len) {
|
||||
if(handle->zero_mask && (len & handle->zero_mask) == 0) {
|
||||
// Send a zero length packet
|
||||
AdbWriteEndpointSync(handle->adb_write_pipe,
|
||||
(void*)data,
|
||||
0,
|
||||
&written,
|
||||
time_out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// assume ERROR_INVALID_HANDLE indicates we are disconnected
|
||||
if (errno == ERROR_INVALID_HANDLE)
|
||||
usb_kick(handle);
|
||||
}
|
||||
} else {
|
||||
D("usb_write NULL handle\n");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
D("usb_write failed: %d\n", errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *handle, void* data, int len) {
|
||||
unsigned long time_out = 500 + len * 8;
|
||||
unsigned long read = 0;
|
||||
int ret;
|
||||
|
||||
D("usb_read %d\n", len);
|
||||
if (NULL != handle) {
|
||||
while (len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
ret = AdbReadEndpointSync(handle->adb_read_pipe,
|
||||
(void*)data,
|
||||
(unsigned long)xfer,
|
||||
&read,
|
||||
time_out);
|
||||
errno = GetLastError();
|
||||
D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
|
||||
if (ret) {
|
||||
data += read;
|
||||
len -= read;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
} else if (errno != ERROR_SEM_TIMEOUT) {
|
||||
// assume ERROR_INVALID_HANDLE indicates we are disconnected
|
||||
if (errno == ERROR_INVALID_HANDLE)
|
||||
usb_kick(handle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
D("usb_read NULL handle\n");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
D("usb_read failed: %d\n", errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_cleanup_handle(usb_handle* handle) {
|
||||
if (NULL != handle) {
|
||||
if (NULL != handle->interface_name)
|
||||
free(handle->interface_name);
|
||||
if (NULL != handle->adb_write_pipe)
|
||||
AdbCloseHandle(handle->adb_write_pipe);
|
||||
if (NULL != handle->adb_read_pipe)
|
||||
AdbCloseHandle(handle->adb_read_pipe);
|
||||
if (NULL != handle->adb_interface)
|
||||
AdbCloseHandle(handle->adb_interface);
|
||||
|
||||
handle->interface_name = NULL;
|
||||
handle->adb_write_pipe = NULL;
|
||||
handle->adb_read_pipe = NULL;
|
||||
handle->adb_interface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* handle) {
|
||||
if (NULL != handle) {
|
||||
adb_mutex_lock(&usb_lock);
|
||||
|
||||
usb_cleanup_handle(handle);
|
||||
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
} else {
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
errno = ERROR_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* handle) {
|
||||
D("usb_close\n");
|
||||
|
||||
if (NULL != handle) {
|
||||
// Remove handle from the list
|
||||
adb_mutex_lock(&usb_lock);
|
||||
|
||||
if ((handle->next != handle) && (handle->prev != handle)) {
|
||||
handle->next->prev = handle->prev;
|
||||
handle->prev->next = handle->next;
|
||||
handle->prev = handle;
|
||||
handle->next = handle;
|
||||
}
|
||||
|
||||
adb_mutex_unlock(&usb_lock);
|
||||
|
||||
// Cleanup handle
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *usb_name(usb_handle* handle) {
|
||||
if (NULL == handle) {
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
errno = ERROR_INVALID_HANDLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const char*)handle->interface_name;
|
||||
}
|
||||
|
||||
int recognized_device(usb_handle* handle) {
|
||||
if (NULL == handle)
|
||||
return 0;
|
||||
|
||||
// Check vendor and product id first
|
||||
USB_DEVICE_DESCRIPTOR device_desc;
|
||||
|
||||
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
|
||||
&device_desc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Then check interface properties
|
||||
USB_INTERFACE_DESCRIPTOR interf_desc;
|
||||
|
||||
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
|
||||
&interf_desc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Must have two endpoints
|
||||
if (2 != interf_desc.bNumEndpoints) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_adb_interface(device_desc.idVendor, device_desc.idProduct,
|
||||
interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
|
||||
|
||||
if(interf_desc.bInterfaceProtocol == 0x01) {
|
||||
AdbEndpointInformation endpoint_info;
|
||||
// assuming zero is a valid bulk endpoint ID
|
||||
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
|
||||
handle->zero_mask = endpoint_info.max_packet_size - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void find_devices() {
|
||||
usb_handle* handle = NULL;
|
||||
char entry_buffer[2048];
|
||||
char interf_name[2048];
|
||||
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
|
||||
unsigned long entry_buffer_size = sizeof(entry_buffer);
|
||||
char* copy_name;
|
||||
|
||||
// Enumerate all present and active interfaces.
|
||||
ADBAPIHANDLE enum_handle =
|
||||
AdbEnumInterfaces(usb_class_id, true, true, true);
|
||||
|
||||
if (NULL == enum_handle)
|
||||
return;
|
||||
|
||||
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
|
||||
// TODO: FIXME - temp hack converting wchar_t into char.
|
||||
// It would be better to change AdbNextInterface so it will return
|
||||
// interface name as single char string.
|
||||
const wchar_t* wchar_name = next_interface->device_name;
|
||||
for(copy_name = interf_name;
|
||||
L'\0' != *wchar_name;
|
||||
wchar_name++, copy_name++) {
|
||||
*copy_name = (char)(*wchar_name);
|
||||
}
|
||||
*copy_name = '\0';
|
||||
|
||||
// Lets see if we already have this device in the list
|
||||
if (!known_device(interf_name)) {
|
||||
// This seems to be a new device. Open it!
|
||||
handle = do_usb_open(next_interface->device_name);
|
||||
if (NULL != handle) {
|
||||
// Lets see if this interface (device) belongs to us
|
||||
if (recognized_device(handle)) {
|
||||
D("adding a new device %s\n", interf_name);
|
||||
char serial_number[512];
|
||||
unsigned long serial_number_len = sizeof(serial_number);
|
||||
if (AdbGetSerialNumber(handle->adb_interface,
|
||||
serial_number,
|
||||
&serial_number_len,
|
||||
true)) {
|
||||
// Lets make sure that we don't duplicate this device
|
||||
if (register_new_device(handle)) {
|
||||
register_usb_transport(handle, serial_number);
|
||||
} else {
|
||||
D("register_new_device failed for %s\n", interf_name);
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
} else {
|
||||
D("cannot get serial number\n");
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
} else {
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entry_buffer_size = sizeof(entry_buffer);
|
||||
}
|
||||
|
||||
AdbCloseHandle(enum_handle);
|
||||
}
|
13
cpio/Android.mk
Normal file
13
cpio/Android.mk
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright 2005 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
mkbootfs.c
|
||||
|
||||
LOCAL_MODULE := mkbootfs
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE))
|
220
cpio/mkbootfs.c
Normal file
220
cpio/mkbootfs.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
/* NOTES
|
||||
**
|
||||
** - see buffer-format.txt from the linux kernel docs for
|
||||
** an explanation of this file format
|
||||
** - dotfiles are ignored
|
||||
** - directories named 'root' are ignored
|
||||
** - device notes, pipes, etc are not supported (error)
|
||||
*/
|
||||
|
||||
void die(const char *why, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, why);
|
||||
fprintf(stderr,"error: ");
|
||||
vfprintf(stderr, why, ap);
|
||||
fprintf(stderr,"\n");
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int verbose = 0;
|
||||
static int total_size = 0;
|
||||
|
||||
static void fix_stat(const char *path, struct stat *s)
|
||||
{
|
||||
fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
|
||||
}
|
||||
|
||||
static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
|
||||
{
|
||||
while(total_size & 3) {
|
||||
total_size++;
|
||||
putchar(0);
|
||||
}
|
||||
|
||||
fix_stat(out, s);
|
||||
// fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
|
||||
|
||||
printf("%06x%08x%08x%08x%08x%08x%08x"
|
||||
"%08x%08x%08x%08x%08x%08x%08x%s%c",
|
||||
0x070701,
|
||||
(unsigned) s->st_ino,
|
||||
s->st_mode,
|
||||
0, // s.st_uid,
|
||||
0, // s.st_gid,
|
||||
1, // s.st_nlink,
|
||||
(unsigned) s->st_mtime,
|
||||
datasize,
|
||||
0, // volmajor
|
||||
0, // volminor
|
||||
0, // devmajor
|
||||
0, // devminor,
|
||||
olen + 1,
|
||||
0,
|
||||
out,
|
||||
0
|
||||
);
|
||||
|
||||
total_size += 6 + 8*13 + olen + 1;
|
||||
|
||||
if(strlen(out) != olen) die("ACK!");
|
||||
|
||||
while(total_size & 3) {
|
||||
total_size++;
|
||||
putchar(0);
|
||||
}
|
||||
|
||||
if(datasize) {
|
||||
fwrite(data, datasize, 1, stdout);
|
||||
total_size += datasize;
|
||||
}
|
||||
}
|
||||
|
||||
static void _eject_trailer()
|
||||
{
|
||||
struct stat s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
_eject(&s, "TRAILER!!!", 10, 0, 0);
|
||||
|
||||
while(total_size & 0xff) {
|
||||
total_size++;
|
||||
putchar(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void _archive(char *in, char *out, int ilen, int olen);
|
||||
|
||||
static void _archive_dir(char *in, char *out, int ilen, int olen)
|
||||
{
|
||||
int t;
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
|
||||
if(verbose) {
|
||||
fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
|
||||
in, out, ilen, olen);
|
||||
}
|
||||
|
||||
d = opendir(in);
|
||||
if(d == 0) die("cannot open directory '%s'", in);
|
||||
|
||||
while((de = readdir(d)) != 0){
|
||||
/* xxx: feature? maybe some dotfiles are okay */
|
||||
if(de->d_name[0] == '.') continue;
|
||||
|
||||
/* xxx: hack. use a real exclude list */
|
||||
if(!strcmp(de->d_name, "root")) continue;
|
||||
|
||||
t = strlen(de->d_name);
|
||||
in[ilen] = '/';
|
||||
memcpy(in + ilen + 1, de->d_name, t + 1);
|
||||
|
||||
if(olen > 0) {
|
||||
out[olen] = '/';
|
||||
memcpy(out + olen + 1, de->d_name, t + 1);
|
||||
_archive(in, out, ilen + t + 1, olen + t + 1);
|
||||
} else {
|
||||
memcpy(out, de->d_name, t + 1);
|
||||
_archive(in, out, ilen + t + 1, t);
|
||||
}
|
||||
|
||||
in[ilen] = 0;
|
||||
out[olen] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void _archive(char *in, char *out, int ilen, int olen)
|
||||
{
|
||||
struct stat s;
|
||||
|
||||
if(verbose) {
|
||||
fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
|
||||
in, out, ilen, olen);
|
||||
}
|
||||
|
||||
if(lstat(in, &s)) die("could not stat '%s'\n", in);
|
||||
|
||||
if(S_ISREG(s.st_mode)){
|
||||
char *tmp;
|
||||
int fd;
|
||||
|
||||
fd = open(in, O_RDONLY);
|
||||
if(fd < 0) die("cannot open '%s' for read", in);
|
||||
|
||||
tmp = (char*) malloc(s.st_size);
|
||||
if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
|
||||
|
||||
if(read(fd, tmp, s.st_size) != s.st_size) {
|
||||
die("cannot read %d bytes", s.st_size);
|
||||
}
|
||||
|
||||
_eject(&s, out, olen, tmp, s.st_size);
|
||||
|
||||
free(tmp);
|
||||
close(fd);
|
||||
} else if(S_ISDIR(s.st_mode)) {
|
||||
_eject(&s, out, olen, 0, 0);
|
||||
_archive_dir(in, out, ilen, olen);
|
||||
} else if(S_ISLNK(s.st_mode)) {
|
||||
char buf[1024];
|
||||
int size;
|
||||
size = readlink(in, buf, 1024);
|
||||
if(size < 0) die("cannot read symlink '%s'", in);
|
||||
_eject(&s, out, olen, buf, size);
|
||||
} else {
|
||||
die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void archive(const char *start, const char *prefix)
|
||||
{
|
||||
char in[8192];
|
||||
char out[8192];
|
||||
|
||||
strcpy(in, start);
|
||||
strcpy(out, prefix);
|
||||
|
||||
_archive_dir(in, out, strlen(in), strlen(out));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if(argc == 0) die("no directories to process?!");
|
||||
|
||||
while(argc-- > 0){
|
||||
char *x = strchr(*argv, '=');
|
||||
if(x != 0) {
|
||||
*x++ = 0;
|
||||
} else {
|
||||
x = "";
|
||||
}
|
||||
|
||||
archive(*argv, x);
|
||||
|
||||
argv++;
|
||||
}
|
||||
|
||||
_eject_trailer();
|
||||
|
||||
return 0;
|
||||
}
|
22
debuggerd/Android.mk
Normal file
22
debuggerd/Android.mk
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Copyright 2005 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c
|
||||
LOCAL_CFLAGS := -Wall
|
||||
LOCAL_MODULE := debuggerd
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libcutils libc
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := crasher.c
|
||||
LOCAL_SRC_FILES += crashglue.S
|
||||
LOCAL_MODULE := crasher
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
|
||||
LOCAL_MODULE_TAGS := eng
|
||||
#LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_SHARED_LIBRARIES := libcutils libc
|
||||
include $(BUILD_EXECUTABLE)
|
0
debuggerd/MODULE_LICENSE_APACHE2
Normal file
0
debuggerd/MODULE_LICENSE_APACHE2
Normal file
190
debuggerd/NOTICE
Normal file
190
debuggerd/NOTICE
Normal file
|
@ -0,0 +1,190 @@
|
|||
|
||||
Copyright (c) 2005-2008, 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
105
debuggerd/crasher.c
Normal file
105
debuggerd/crasher.c
Normal file
|
@ -0,0 +1,105 @@
|
|||
|
||||
//#include <cutils/misc.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
void crash1(void);
|
||||
void crashnostack(void);
|
||||
|
||||
static void debuggerd_connect()
|
||||
{
|
||||
char tmp[1];
|
||||
int s;
|
||||
sprintf(tmp, "%d", gettid());
|
||||
s = socket_local_client("android:debuggerd",
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
|
||||
if(s >= 0) {
|
||||
read(s, tmp, 1);
|
||||
close(s);
|
||||
}
|
||||
}
|
||||
|
||||
void test_call1()
|
||||
{
|
||||
*((int*) 32) = 1;
|
||||
}
|
||||
|
||||
void *test_thread(void *x)
|
||||
{
|
||||
printf("crasher: thread pid=%d tid=%d\n", getpid(), gettid());
|
||||
|
||||
sleep(1);
|
||||
test_call1();
|
||||
printf("goodbye\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *noisy(void *x)
|
||||
{
|
||||
char c = (unsigned) x;
|
||||
for(;;) {
|
||||
usleep(250*1000);
|
||||
write(2, &c, 1);
|
||||
if(c == 'C') *((unsigned*) 0) = 42;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ctest()
|
||||
{
|
||||
pthread_t thr;
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&thr, &attr, noisy, (void*) 'A');
|
||||
pthread_create(&thr, &attr, noisy, (void*) 'B');
|
||||
pthread_create(&thr, &attr, noisy, (void*) 'C');
|
||||
for(;;) ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pthread_t thr;
|
||||
pthread_attr_t attr;
|
||||
|
||||
fprintf(stderr,"crasher: " __TIME__ "!@\n");
|
||||
fprintf(stderr,"crasher: init pid=%d tid=%d\n", getpid(), gettid());
|
||||
|
||||
if(argc > 1) {
|
||||
if(!strcmp(argv[1],"nostack")) crashnostack();
|
||||
if(!strcmp(argv[1],"ctest")) return ctest();
|
||||
if(!strcmp(argv[1],"exit")) exit(1);
|
||||
if(!strcmp(argv[1],"abort")) maybeabort();
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&thr, &attr, test_thread, 0);
|
||||
while(1) sleep(1);
|
||||
} else {
|
||||
crash1();
|
||||
// *((int*) 0) = 42;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void maybeabort()
|
||||
{
|
||||
if(time(0) != 42) abort();
|
||||
}
|
28
debuggerd/crashglue.S
Normal file
28
debuggerd/crashglue.S
Normal file
|
@ -0,0 +1,28 @@
|
|||
.globl crash1
|
||||
.globl crashnostack
|
||||
|
||||
crash1:
|
||||
ldr r0, =0xa5a50000
|
||||
ldr r1, =0xa5a50001
|
||||
ldr r2, =0xa5a50002
|
||||
ldr r3, =0xa5a50003
|
||||
ldr r4, =0xa5a50004
|
||||
ldr r5, =0xa5a50005
|
||||
ldr r6, =0xa5a50006
|
||||
ldr r7, =0xa5a50007
|
||||
ldr r8, =0xa5a50008
|
||||
ldr r9, =0xa5a50009
|
||||
ldr r10, =0xa5a50010
|
||||
ldr r11, =0xa5a50011
|
||||
ldr r12, =0xa5a50012
|
||||
|
||||
mov lr, #0
|
||||
ldr lr, [lr]
|
||||
b .
|
||||
|
||||
|
||||
crashnostack:
|
||||
mov sp, #0
|
||||
mov r0, #0
|
||||
ldr r0, [r0]
|
||||
b .
|
852
debuggerd/debuggerd.c
Normal file
852
debuggerd/debuggerd.c
Normal file
|
@ -0,0 +1,852 @@
|
|||
/* system/debuggerd/debuggerd.c
|
||||
**
|
||||
** Copyright 2006, 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <cutils/sockets.h>
|
||||
#include <cutils/logd.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
/* Main entry point to get the backtrace from the crashing process */
|
||||
extern int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
|
||||
unsigned int sp_list[],
|
||||
int *frame0_pc_sane,
|
||||
bool at_fault);
|
||||
|
||||
static char **process_name_ptr;
|
||||
|
||||
static int logsocket = -1;
|
||||
|
||||
#define ANDROID_LOG_INFO 4
|
||||
|
||||
/* Log information onto the tombstone */
|
||||
void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (tfd >= 0) {
|
||||
int len;
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
len = strlen(buf);
|
||||
if(tfd >= 0) write(tfd, buf, len);
|
||||
}
|
||||
|
||||
if (!in_tombstone_only)
|
||||
__android_log_vprint(ANDROID_LOG_INFO, "DEBUG", fmt, ap);
|
||||
}
|
||||
|
||||
#define LOG(fmt...) _LOG(-1, 0, fmt)
|
||||
#if 0
|
||||
#define XLOG(fmt...) _LOG(-1, 0, fmt)
|
||||
#else
|
||||
#define XLOG(fmt...) do {} while(0)
|
||||
#endif
|
||||
|
||||
// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
|
||||
// 012345678901234567890123456789012345678901234567890123456789
|
||||
// 0 1 2 3 4 5
|
||||
|
||||
mapinfo *parse_maps_line(char *line)
|
||||
{
|
||||
mapinfo *mi;
|
||||
int len = strlen(line);
|
||||
|
||||
if(len < 1) return 0;
|
||||
line[--len] = 0;
|
||||
|
||||
if(len < 50) return 0;
|
||||
if(line[20] != 'x') return 0;
|
||||
|
||||
mi = malloc(sizeof(mapinfo) + (len - 47));
|
||||
if(mi == 0) return 0;
|
||||
|
||||
mi->start = strtoul(line, 0, 16);
|
||||
mi->end = strtoul(line + 9, 0, 16);
|
||||
/* To be filled in parse_exidx_info if the mapped section starts with
|
||||
* elf_header
|
||||
*/
|
||||
mi->exidx_start = mi->exidx_end = 0;
|
||||
mi->next = 0;
|
||||
strcpy(mi->name, line + 49);
|
||||
|
||||
return mi;
|
||||
}
|
||||
|
||||
void dump_build_info(int tfd)
|
||||
{
|
||||
char fingerprint[PROPERTY_VALUE_MAX];
|
||||
|
||||
property_get("ro.build.fingerprint", fingerprint, "unknown");
|
||||
|
||||
_LOG(tfd, false, "Build fingerprint: '%s'\n", fingerprint);
|
||||
}
|
||||
|
||||
|
||||
void dump_stack_and_code(int tfd, int pid, mapinfo *map,
|
||||
int unwind_depth, unsigned int sp_list[],
|
||||
int frame0_pc_sane, bool at_fault)
|
||||
{
|
||||
unsigned int sp, pc, p, end, data;
|
||||
struct pt_regs r;
|
||||
int sp_depth;
|
||||
bool only_in_tombstone = !at_fault;
|
||||
|
||||
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return;
|
||||
sp = r.ARM_sp;
|
||||
pc = r.ARM_pc;
|
||||
|
||||
/* Died because calling the weeds - dump
|
||||
* the code around the PC in the next frame instead.
|
||||
*/
|
||||
if (frame0_pc_sane == 0) {
|
||||
pc = r.ARM_lr;
|
||||
}
|
||||
|
||||
_LOG(tfd, true, "code%s:\n", frame0_pc_sane ? "" : " (around frame #01)");
|
||||
|
||||
end = p = pc & ~3;
|
||||
p -= 16;
|
||||
|
||||
/* Dump the code as:
|
||||
* PC contents
|
||||
* 00008d34 fffffcd0 4c0eb530 b0934a0e 1c05447c
|
||||
* 00008d44 f7ff18a0 490ced94 68035860 d0012b00
|
||||
*/
|
||||
while (p <= end) {
|
||||
int i;
|
||||
|
||||
_LOG(tfd, true, " %08x ", p);
|
||||
for (i = 0; i < 4; i++) {
|
||||
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
|
||||
_LOG(tfd, true, " %08x", data);
|
||||
p += 4;
|
||||
}
|
||||
_LOG(tfd, true, "\n", p);
|
||||
}
|
||||
|
||||
p = sp - 64;
|
||||
p &= ~3;
|
||||
if (unwind_depth != 0) {
|
||||
if (unwind_depth < STACK_CONTENT_DEPTH) {
|
||||
end = sp_list[unwind_depth-1];
|
||||
}
|
||||
else {
|
||||
end = sp_list[STACK_CONTENT_DEPTH-1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
end = sp | 0x000000ff;
|
||||
end += 0xff;
|
||||
}
|
||||
|
||||
_LOG(tfd, only_in_tombstone, "stack:\n");
|
||||
|
||||
/* If the crash is due to PC == 0, there will be two frames that
|
||||
* have identical SP value.
|
||||
*/
|
||||
if (sp_list[0] == sp_list[1]) {
|
||||
sp_depth = 1;
|
||||
}
|
||||
else {
|
||||
sp_depth = 0;
|
||||
}
|
||||
|
||||
while (p <= end) {
|
||||
char *prompt;
|
||||
char level[16];
|
||||
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
|
||||
if (p == sp_list[sp_depth]) {
|
||||
sprintf(level, "#%02d", sp_depth++);
|
||||
prompt = level;
|
||||
}
|
||||
else {
|
||||
prompt = " ";
|
||||
}
|
||||
|
||||
/* Print the stack content in the log for the first 3 frames. For the
|
||||
* rest only print them in the tombstone file.
|
||||
*/
|
||||
_LOG(tfd, (sp_depth > 2) || only_in_tombstone,
|
||||
"%s %08x %08x %s\n", prompt, p, data,
|
||||
map_to_name(map, data, ""));
|
||||
p += 4;
|
||||
}
|
||||
/* print another 64-byte of stack data after the last frame */
|
||||
|
||||
end = p+64;
|
||||
while (p <= end) {
|
||||
data = ptrace(PTRACE_PEEKTEXT, pid, (void*)p, NULL);
|
||||
_LOG(tfd, (sp_depth > 2) || only_in_tombstone,
|
||||
" %08x %08x %s\n", p, data,
|
||||
map_to_name(map, data, ""));
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void dump_pc_and_lr(int tfd, int pid, mapinfo *map, int unwound_level,
|
||||
bool at_fault)
|
||||
{
|
||||
struct pt_regs r;
|
||||
|
||||
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
|
||||
_LOG(tfd, !at_fault, "tid %d not responding!\n", pid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unwound_level == 0) {
|
||||
_LOG(tfd, !at_fault, " #%02d pc %08x %s\n", 0, r.ARM_pc,
|
||||
map_to_name(map, r.ARM_pc, "<unknown>"));
|
||||
}
|
||||
_LOG(tfd, !at_fault, " #%02d lr %08x %s\n", 1, r.ARM_lr,
|
||||
map_to_name(map, r.ARM_lr, "<unknown>"));
|
||||
}
|
||||
|
||||
void dump_registers(int tfd, int pid, bool at_fault)
|
||||
{
|
||||
struct pt_regs r;
|
||||
bool only_in_tombstone = !at_fault;
|
||||
|
||||
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) {
|
||||
_LOG(tfd, only_in_tombstone,
|
||||
"cannot get registers: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG(tfd, only_in_tombstone, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
|
||||
r.ARM_r0, r.ARM_r1, r.ARM_r2, r.ARM_r3);
|
||||
_LOG(tfd, only_in_tombstone, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
|
||||
r.ARM_r4, r.ARM_r5, r.ARM_r6, r.ARM_r7);
|
||||
_LOG(tfd, only_in_tombstone, " r8 %08x r9 %08x 10 %08x fp %08x\n",
|
||||
r.ARM_r8, r.ARM_r9, r.ARM_r10, r.ARM_fp);
|
||||
_LOG(tfd, only_in_tombstone,
|
||||
" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
|
||||
r.ARM_ip, r.ARM_sp, r.ARM_lr, r.ARM_pc, r.ARM_cpsr);
|
||||
}
|
||||
|
||||
const char *get_signame(int sig)
|
||||
{
|
||||
switch(sig) {
|
||||
case SIGILL: return "SIGILL";
|
||||
case SIGABRT: return "SIGABRT";
|
||||
case SIGBUS: return "SIGBUS";
|
||||
case SIGFPE: return "SIGFPE";
|
||||
case SIGSEGV: return "SIGSEGV";
|
||||
case SIGSTKFLT: return "SIGSTKFLT";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
void dump_fault_addr(int tfd, int pid, int sig)
|
||||
{
|
||||
siginfo_t si;
|
||||
|
||||
memset(&si, 0, sizeof(si));
|
||||
if(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)){
|
||||
_LOG(tfd, false, "cannot get siginfo: %s\n", strerror(errno));
|
||||
} else {
|
||||
_LOG(tfd, false, "signal %d (%s), fault addr %08x\n",
|
||||
sig, get_signame(sig), si.si_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void dump_crash_banner(int tfd, unsigned pid, unsigned tid, int sig)
|
||||
{
|
||||
char data[1024];
|
||||
char *x = 0;
|
||||
FILE *fp;
|
||||
|
||||
sprintf(data, "/proc/%d/cmdline", pid);
|
||||
fp = fopen(data, "r");
|
||||
if(fp) {
|
||||
x = fgets(data, 1024, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
_LOG(tfd, false,
|
||||
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
|
||||
dump_build_info(tfd);
|
||||
_LOG(tfd, false, "pid: %d, tid: %d >>> %s <<<\n",
|
||||
pid, tid, x ? x : "UNKNOWN");
|
||||
|
||||
if(sig) dump_fault_addr(tfd, tid, sig);
|
||||
}
|
||||
|
||||
static void parse_exidx_info(mapinfo *milist, pid_t pid)
|
||||
{
|
||||
mapinfo *mi;
|
||||
for (mi = milist; mi != NULL; mi = mi->next) {
|
||||
Elf32_Ehdr ehdr;
|
||||
|
||||
memset(&ehdr, 0, sizeof(Elf32_Ehdr));
|
||||
/* Read in sizeof(Elf32_Ehdr) worth of data from the beginning of
|
||||
* mapped section.
|
||||
*/
|
||||
get_remote_struct(pid, (void *) (mi->start), &ehdr,
|
||||
sizeof(Elf32_Ehdr));
|
||||
/* Check if it has the matching magic words */
|
||||
if (IS_ELF(ehdr)) {
|
||||
Elf32_Phdr phdr;
|
||||
Elf32_Phdr *ptr;
|
||||
int i;
|
||||
|
||||
ptr = (Elf32_Phdr *) (mi->start + ehdr.e_phoff);
|
||||
for (i = 0; i < ehdr.e_phnum; i++) {
|
||||
/* Parse the program header */
|
||||
get_remote_struct(pid, (void *) ptr+i, &phdr,
|
||||
sizeof(Elf32_Phdr));
|
||||
/* Found a EXIDX segment? */
|
||||
if (phdr.p_type == PT_ARM_EXIDX) {
|
||||
mi->exidx_start = mi->start + phdr.p_offset;
|
||||
mi->exidx_end = mi->exidx_start + phdr.p_filesz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_crash_report(int tfd, unsigned pid, unsigned tid, bool at_fault)
|
||||
{
|
||||
char data[1024];
|
||||
FILE *fp;
|
||||
mapinfo *milist = 0;
|
||||
unsigned int sp_list[STACK_CONTENT_DEPTH];
|
||||
int stack_depth;
|
||||
int frame0_pc_sane = 1;
|
||||
|
||||
if (!at_fault) {
|
||||
_LOG(tfd, true,
|
||||
"--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
|
||||
_LOG(tfd, true, "pid: %d, tid: %d\n", pid, tid);
|
||||
}
|
||||
|
||||
dump_registers(tfd, tid, at_fault);
|
||||
|
||||
/* Clear stack pointer records */
|
||||
memset(sp_list, 0, sizeof(sp_list));
|
||||
|
||||
sprintf(data, "/proc/%d/maps", pid);
|
||||
fp = fopen(data, "r");
|
||||
if(fp) {
|
||||
while(fgets(data, 1024, fp)) {
|
||||
mapinfo *mi = parse_maps_line(data);
|
||||
if(mi) {
|
||||
mi->next = milist;
|
||||
milist = mi;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
parse_exidx_info(milist, tid);
|
||||
|
||||
/* If stack unwinder fails, use the default solution to dump the stack
|
||||
* content.
|
||||
*/
|
||||
stack_depth = unwind_backtrace_with_ptrace(tfd, tid, milist, sp_list,
|
||||
&frame0_pc_sane, at_fault);
|
||||
|
||||
/* The stack unwinder should at least unwind two levels of stack. If less
|
||||
* level is seen we make sure at lease pc and lr are dumped.
|
||||
*/
|
||||
if (stack_depth < 2) {
|
||||
dump_pc_and_lr(tfd, tid, milist, stack_depth, at_fault);
|
||||
}
|
||||
|
||||
dump_stack_and_code(tfd, tid, milist, stack_depth, sp_list, frame0_pc_sane,
|
||||
at_fault);
|
||||
|
||||
while(milist) {
|
||||
mapinfo *next = milist->next;
|
||||
free(milist);
|
||||
milist = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: unused: use it or lose it*/
|
||||
#if 0
|
||||
static
|
||||
void start_gdbserver_vs(int pid, int port)
|
||||
{
|
||||
pid_t p;
|
||||
char *args[5];
|
||||
char commspec[16];
|
||||
char pidspec[16];
|
||||
|
||||
p = fork();
|
||||
if(p < 0) {
|
||||
LOG("could not fork()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(p == 0) {
|
||||
sprintf(commspec, ":%d", port);
|
||||
sprintf(pidspec, "%d", pid);
|
||||
args[0] = "/system/bin/gdbserver";
|
||||
args[1] = commspec;
|
||||
args[2] = "--attach";
|
||||
args[3] = pidspec;
|
||||
args[4] = 0;
|
||||
exit(execv(args[0], args));
|
||||
} else {
|
||||
LOG("gdbserver pid=%d port=%d targetpid=%d\n",
|
||||
p, port, pid);
|
||||
|
||||
sleep(5);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MAX_TOMBSTONES 10
|
||||
|
||||
#define typecheck(x,y) { \
|
||||
typeof(x) __dummy1; \
|
||||
typeof(y) __dummy2; \
|
||||
(void)(&__dummy1 == &__dummy2); }
|
||||
|
||||
#define TOMBSTONE_DIR "/data/tombstones"
|
||||
|
||||
/*
|
||||
* find_and_open_tombstone - find an available tombstone slot, if any, of the
|
||||
* form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
|
||||
* file is available, we reuse the least-recently-modified file.
|
||||
*/
|
||||
static int find_and_open_tombstone(void)
|
||||
{
|
||||
unsigned long mtime = ULONG_MAX;
|
||||
struct stat sb;
|
||||
char path[128];
|
||||
int fd, i, oldest = 0;
|
||||
|
||||
/*
|
||||
* XXX: Our stat.st_mtime isn't time_t. If it changes, as it probably ought
|
||||
* to, our logic breaks. This check will generate a warning if that happens.
|
||||
*/
|
||||
typecheck(mtime, sb.st_mtime);
|
||||
|
||||
/*
|
||||
* In a single wolf-like pass, find an available slot and, in case none
|
||||
* exist, find and record the least-recently-modified file.
|
||||
*/
|
||||
for (i = 0; i < MAX_TOMBSTONES; i++) {
|
||||
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", i);
|
||||
|
||||
if (!stat(path, &sb)) {
|
||||
if (sb.st_mtime < mtime) {
|
||||
oldest = i;
|
||||
mtime = sb.st_mtime;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
continue;
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0600);
|
||||
if (fd < 0)
|
||||
continue; /* raced ? */
|
||||
|
||||
fchown(fd, AID_SYSTEM, AID_SYSTEM);
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* we didn't find an available file, so we clobber the oldest one */
|
||||
snprintf(path, sizeof(path), TOMBSTONE_DIR"/tombstone_%02d", oldest);
|
||||
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
|
||||
fchown(fd, AID_SYSTEM, AID_SYSTEM);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Return true if some thread is not detached cleanly */
|
||||
static bool dump_sibling_thread_report(int tfd, unsigned pid, unsigned tid)
|
||||
{
|
||||
char task_path[1024];
|
||||
|
||||
sprintf(task_path, "/proc/%d/task", pid);
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
int need_cleanup = 0;
|
||||
|
||||
d = opendir(task_path);
|
||||
/* Bail early if cannot open the task directory */
|
||||
if (d == NULL) {
|
||||
XLOG("Cannot open /proc/%d/task\n", pid);
|
||||
return false;
|
||||
}
|
||||
while ((de = readdir(d)) != NULL) {
|
||||
unsigned new_tid;
|
||||
/* Ignore "." and ".." */
|
||||
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
|
||||
continue;
|
||||
new_tid = atoi(de->d_name);
|
||||
/* The main thread at fault has been handled individually */
|
||||
if (new_tid == tid)
|
||||
continue;
|
||||
|
||||
/* Skip this thread if cannot ptrace it */
|
||||
if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0)
|
||||
continue;
|
||||
|
||||
dump_crash_report(tfd, pid, new_tid, false);
|
||||
need_cleanup |= ptrace(PTRACE_DETACH, new_tid, 0, 0);
|
||||
}
|
||||
closedir(d);
|
||||
return need_cleanup != 0;
|
||||
}
|
||||
|
||||
/* Return true if some thread is not detached cleanly */
|
||||
static bool engrave_tombstone(unsigned pid, unsigned tid, int debug_uid,
|
||||
int signal)
|
||||
{
|
||||
int fd;
|
||||
bool need_cleanup = false;
|
||||
|
||||
mkdir(TOMBSTONE_DIR, 0755);
|
||||
chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM);
|
||||
|
||||
fd = find_and_open_tombstone();
|
||||
if (fd < 0)
|
||||
return need_cleanup;
|
||||
|
||||
dump_crash_banner(fd, pid, tid, signal);
|
||||
dump_crash_report(fd, pid, tid, true);
|
||||
/*
|
||||
* If the user has requested to attach gdb, don't collect the per-thread
|
||||
* information as it increases the chance to lose track of the process.
|
||||
*/
|
||||
if ((signed)pid > debug_uid) {
|
||||
need_cleanup = dump_sibling_thread_report(fd, pid, tid);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return need_cleanup;
|
||||
}
|
||||
|
||||
static int
|
||||
write_string(const char* file, const char* string)
|
||||
{
|
||||
int len;
|
||||
int fd;
|
||||
ssize_t amt;
|
||||
fd = open(file, O_RDWR);
|
||||
len = strlen(string);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
amt = write(fd, string, len);
|
||||
close(fd);
|
||||
return amt >= 0 ? 0 : -errno;
|
||||
}
|
||||
|
||||
static
|
||||
void init_debug_led(void)
|
||||
{
|
||||
// trout leds
|
||||
write_string("/sys/class/leds/red/brightness", "0");
|
||||
write_string("/sys/class/leds/green/brightness", "0");
|
||||
write_string("/sys/class/leds/blue/brightness", "0");
|
||||
write_string("/sys/class/leds/red/device/blink", "0");
|
||||
// sardine leds
|
||||
write_string("/sys/class/leds/left/cadence", "0,0");
|
||||
}
|
||||
|
||||
static
|
||||
void enable_debug_led(void)
|
||||
{
|
||||
// trout leds
|
||||
write_string("/sys/class/leds/red/brightness", "255");
|
||||
// sardine leds
|
||||
write_string("/sys/class/leds/left/cadence", "1,0");
|
||||
}
|
||||
|
||||
static
|
||||
void disable_debug_led(void)
|
||||
{
|
||||
// trout leds
|
||||
write_string("/sys/class/leds/red/brightness", "0");
|
||||
// sardine leds
|
||||
write_string("/sys/class/leds/left/cadence", "0,0");
|
||||
}
|
||||
|
||||
extern int init_getevent();
|
||||
extern void uninit_getevent();
|
||||
extern int get_event(struct input_event* event, int timeout);
|
||||
|
||||
static void wait_for_user_action(unsigned tid, struct ucred* cr)
|
||||
{
|
||||
(void)tid;
|
||||
/* First log a helpful message */
|
||||
LOG( "********************************************************\n"
|
||||
"* process %d crashed. debuggerd waiting for gdbserver \n"
|
||||
"* \n"
|
||||
"* adb shell gdbserver :port --attach %d & \n"
|
||||
"* \n"
|
||||
"* and press the HOME key. \n"
|
||||
"********************************************************\n",
|
||||
cr->pid, cr->pid);
|
||||
|
||||
/* wait for HOME key */
|
||||
if (init_getevent() == 0) {
|
||||
int ms = 1200 / 10;
|
||||
int dit = 1;
|
||||
int dah = 3*dit;
|
||||
int _ = -dit;
|
||||
int ___ = 3*_;
|
||||
int _______ = 7*_;
|
||||
const signed char codes[] = {
|
||||
dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______
|
||||
};
|
||||
size_t s = 0;
|
||||
struct input_event e;
|
||||
int home = 0;
|
||||
init_debug_led();
|
||||
enable_debug_led();
|
||||
do {
|
||||
int timeout = abs((int)(codes[s])) * ms;
|
||||
int res = get_event(&e, timeout);
|
||||
if (res == 0) {
|
||||
if (e.type==EV_KEY && e.code==KEY_HOME && e.value==0)
|
||||
home = 1;
|
||||
} else if (res == 1) {
|
||||
if (++s >= sizeof(codes)/sizeof(*codes))
|
||||
s = 0;
|
||||
if (codes[s] > 0) {
|
||||
enable_debug_led();
|
||||
} else {
|
||||
disable_debug_led();
|
||||
}
|
||||
}
|
||||
} while (!home);
|
||||
uninit_getevent();
|
||||
}
|
||||
|
||||
/* don't forget to turn debug led off */
|
||||
disable_debug_led();
|
||||
|
||||
/* close filedescriptor */
|
||||
LOG("debuggerd resuming process %d", cr->pid);
|
||||
}
|
||||
|
||||
static void handle_crashing_process(int fd)
|
||||
{
|
||||
char buf[64];
|
||||
struct stat s;
|
||||
unsigned tid;
|
||||
struct ucred cr;
|
||||
int n, len, status;
|
||||
int tid_attach_status = -1;
|
||||
unsigned retry = 30;
|
||||
bool need_cleanup = false;
|
||||
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("debug.db.uid", value, "-1");
|
||||
int debug_uid = atoi(value);
|
||||
|
||||
XLOG("handle_crashing_process(%d)\n", fd);
|
||||
|
||||
len = sizeof(cr);
|
||||
n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
|
||||
if(n != 0) {
|
||||
LOG("cannot get credentials\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
XLOG("reading tid\n");
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
while((n = read(fd, &tid, sizeof(unsigned))) != sizeof(unsigned)) {
|
||||
if(errno == EINTR) continue;
|
||||
if(errno == EWOULDBLOCK) {
|
||||
if(retry-- > 0) {
|
||||
usleep(100 * 1000);
|
||||
continue;
|
||||
}
|
||||
LOG("timed out reading tid\n");
|
||||
goto done;
|
||||
}
|
||||
LOG("read failure? %s\n", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
sprintf(buf,"/proc/%d/task/%d", cr.pid, tid);
|
||||
if(stat(buf, &s)) {
|
||||
LOG("tid %d does not exist in pid %d. ignorning debug request\n",
|
||||
tid, cr.pid);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", cr.pid, cr.uid, cr.gid, tid);
|
||||
|
||||
tid_attach_status = ptrace(PTRACE_ATTACH, tid, 0, 0);
|
||||
if(tid_attach_status < 0) {
|
||||
LOG("ptrace attach failed: %s\n", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
for(;;) {
|
||||
n = waitpid(tid, &status, __WALL);
|
||||
|
||||
if(n < 0) {
|
||||
if(errno == EAGAIN) continue;
|
||||
LOG("waitpid failed: %s\n", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
XLOG("waitpid: n=%d status=%08x\n", n, status);
|
||||
|
||||
if(WIFSTOPPED(status)){
|
||||
n = WSTOPSIG(status);
|
||||
switch(n) {
|
||||
case SIGSTOP:
|
||||
XLOG("stopped -- continuing\n");
|
||||
n = ptrace(PTRACE_CONT, tid, 0, 0);
|
||||
if(n) {
|
||||
LOG("ptrace failed: %s\n", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
continue;
|
||||
|
||||
case SIGILL:
|
||||
case SIGABRT:
|
||||
case SIGBUS:
|
||||
case SIGFPE:
|
||||
case SIGSEGV:
|
||||
case SIGSTKFLT: {
|
||||
XLOG("stopped -- fatal signal\n");
|
||||
need_cleanup = engrave_tombstone(cr.pid, tid, debug_uid, n);
|
||||
kill(tid, SIGSTOP);
|
||||
goto done;
|
||||
}
|
||||
|
||||
default:
|
||||
XLOG("stopped -- unexpected signal\n");
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
XLOG("unexpected waitpid response\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
XLOG("detaching\n");
|
||||
|
||||
/* stop the process so we can debug */
|
||||
kill(cr.pid, SIGSTOP);
|
||||
|
||||
/*
|
||||
* If a thread has been attached by ptrace, make sure it is detached
|
||||
* successfully otherwise we will get a zombie.
|
||||
*/
|
||||
if (tid_attach_status == 0) {
|
||||
int detach_status;
|
||||
/* detach so we can attach gdbserver */
|
||||
detach_status = ptrace(PTRACE_DETACH, tid, 0, 0);
|
||||
need_cleanup |= (detach_status != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* if debug.db.uid is set, its value indicates if we should wait
|
||||
* for user action for the crashing process.
|
||||
* in this case, we log a message and turn the debug LED on
|
||||
* waiting for a gdb connection (for instance)
|
||||
*/
|
||||
|
||||
if ((signed)cr.uid <= debug_uid) {
|
||||
wait_for_user_action(tid, &cr);
|
||||
}
|
||||
|
||||
/* resume stopped process (so it can crash in peace) */
|
||||
kill(cr.pid, SIGCONT);
|
||||
|
||||
if (need_cleanup) {
|
||||
LOG("debuggerd committing suicide to free the zombie!\n");
|
||||
kill(getpid(), SIGKILL);
|
||||
}
|
||||
|
||||
if(fd != -1) close(fd);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int s;
|
||||
struct sigaction act;
|
||||
|
||||
process_name_ptr = argv;
|
||||
|
||||
logsocket = socket_local_client("logd",
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM);
|
||||
if(logsocket < 0) {
|
||||
logsocket = -1;
|
||||
} else {
|
||||
fcntl(logsocket, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaddset(&act.sa_mask,SIGCHLD);
|
||||
act.sa_flags = SA_NOCLDWAIT;
|
||||
sigaction(SIGCHLD, &act, 0);
|
||||
|
||||
s = socket_local_server("android:debuggerd",
|
||||
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
|
||||
if(s < 0) return -1;
|
||||
fcntl(s, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
LOG("debuggerd: " __DATE__ " " __TIME__ "\n");
|
||||
|
||||
for(;;) {
|
||||
struct sockaddr addr;
|
||||
socklen_t alen;
|
||||
int fd;
|
||||
|
||||
alen = sizeof(addr);
|
||||
fd = accept(s, &addr, &alen);
|
||||
if(fd < 0) continue;
|
||||
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
handle_crashing_process(fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
219
debuggerd/getevent.c
Normal file
219
debuggerd/getevent.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/limits.h>
|
||||
#include <sys/poll.h>
|
||||
#include <linux/input.h>
|
||||
#include <errno.h>
|
||||
#include <cutils/log.h>
|
||||
|
||||
static struct pollfd *ufds;
|
||||
static char **device_names;
|
||||
static int nfds;
|
||||
|
||||
static int open_device(const char *device)
|
||||
{
|
||||
int version;
|
||||
int fd;
|
||||
struct pollfd *new_ufds;
|
||||
char **new_device_names;
|
||||
char name[80];
|
||||
char location[80];
|
||||
char idstr[80];
|
||||
struct input_id id;
|
||||
|
||||
fd = open(device, O_RDWR);
|
||||
if(fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(ioctl(fd, EVIOCGVERSION, &version)) {
|
||||
return -1;
|
||||
}
|
||||
if(ioctl(fd, EVIOCGID, &id)) {
|
||||
return -1;
|
||||
}
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
location[sizeof(location) - 1] = '\0';
|
||||
idstr[sizeof(idstr) - 1] = '\0';
|
||||
if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
|
||||
//fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
|
||||
name[0] = '\0';
|
||||
}
|
||||
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
|
||||
//fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
|
||||
location[0] = '\0';
|
||||
}
|
||||
if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
|
||||
//fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
|
||||
idstr[0] = '\0';
|
||||
}
|
||||
|
||||
new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
|
||||
if(new_ufds == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
ufds = new_ufds;
|
||||
new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
|
||||
if(new_device_names == NULL) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
device_names = new_device_names;
|
||||
ufds[nfds].fd = fd;
|
||||
ufds[nfds].events = POLLIN;
|
||||
device_names[nfds] = strdup(device);
|
||||
nfds++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_device(const char *device)
|
||||
{
|
||||
int i;
|
||||
for(i = 1; i < nfds; i++) {
|
||||
if(strcmp(device_names[i], device) == 0) {
|
||||
int count = nfds - i - 1;
|
||||
free(device_names[i]);
|
||||
memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
|
||||
memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
|
||||
nfds--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int read_notify(const char *dirname, int nfd)
|
||||
{
|
||||
int res;
|
||||
char devname[PATH_MAX];
|
||||
char *filename;
|
||||
char event_buf[512];
|
||||
int event_size;
|
||||
int event_pos = 0;
|
||||
struct inotify_event *event;
|
||||
|
||||
res = read(nfd, event_buf, sizeof(event_buf));
|
||||
if(res < (int)sizeof(*event)) {
|
||||
if(errno == EINTR)
|
||||
return 0;
|
||||
fprintf(stderr, "could not get event, %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
//printf("got %d bytes of event information\n", res);
|
||||
|
||||
strcpy(devname, dirname);
|
||||
filename = devname + strlen(devname);
|
||||
*filename++ = '/';
|
||||
|
||||
while(res >= (int)sizeof(*event)) {
|
||||
event = (struct inotify_event *)(event_buf + event_pos);
|
||||
//printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
|
||||
if(event->len) {
|
||||
strcpy(filename, event->name);
|
||||
if(event->mask & IN_CREATE) {
|
||||
open_device(devname);
|
||||
}
|
||||
else {
|
||||
close_device(devname);
|
||||
}
|
||||
}
|
||||
event_size = sizeof(*event) + event->len;
|
||||
res -= event_size;
|
||||
event_pos += event_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scan_dir(const char *dirname)
|
||||
{
|
||||
char devname[PATH_MAX];
|
||||
char *filename;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
dir = opendir(dirname);
|
||||
if(dir == NULL)
|
||||
return -1;
|
||||
strcpy(devname, dirname);
|
||||
filename = devname + strlen(devname);
|
||||
*filename++ = '/';
|
||||
while((de = readdir(dir))) {
|
||||
if(de->d_name[0] == '.' &&
|
||||
(de->d_name[1] == '\0' ||
|
||||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
|
||||
continue;
|
||||
strcpy(filename, de->d_name);
|
||||
open_device(devname);
|
||||
}
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_getevent()
|
||||
{
|
||||
int res;
|
||||
const char *device_path = "/dev/input";
|
||||
|
||||
nfds = 1;
|
||||
ufds = calloc(1, sizeof(ufds[0]));
|
||||
ufds[0].fd = inotify_init();
|
||||
ufds[0].events = POLLIN;
|
||||
|
||||
res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
|
||||
if(res < 0) {
|
||||
return 1;
|
||||
}
|
||||
res = scan_dir(device_path);
|
||||
if(res < 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uninit_getevent()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < nfds; i++) {
|
||||
close(ufds[i].fd);
|
||||
}
|
||||
free(ufds);
|
||||
ufds = 0;
|
||||
nfds = 0;
|
||||
}
|
||||
|
||||
int get_event(struct input_event* event, int timeout)
|
||||
{
|
||||
int res;
|
||||
int i;
|
||||
int pollres;
|
||||
const char *device_path = "/dev/input";
|
||||
while(1) {
|
||||
pollres = poll(ufds, nfds, timeout);
|
||||
if (pollres == 0) {
|
||||
return 1;
|
||||
}
|
||||
if(ufds[0].revents & POLLIN) {
|
||||
read_notify(device_path, ufds[0].fd);
|
||||
}
|
||||
for(i = 1; i < nfds; i++) {
|
||||
if(ufds[i].revents) {
|
||||
if(ufds[i].revents & POLLIN) {
|
||||
res = read(ufds[i].fd, event, sizeof(*event));
|
||||
if(res < (int)sizeof(event)) {
|
||||
fprintf(stderr, "could not get event\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
345
debuggerd/pr-support.c
Normal file
345
debuggerd/pr-support.c
Normal file
|
@ -0,0 +1,345 @@
|
|||
/* ARM EABI compliant unwinding routines
|
||||
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||||
Contributed by Paul Brook
|
||||
|
||||
This file is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
In addition to the permissions in the GNU General Public License, the
|
||||
Free Software Foundation gives you unlimited permission to link the
|
||||
compiled version of this file into combinations with other programs,
|
||||
and to distribute those combinations without any restriction coming
|
||||
from the use of this file. (The General Public License restrictions
|
||||
do apply in other respects; for example, they cover modification of
|
||||
the file, and distribution when not linked into a combine
|
||||
executable.)
|
||||
|
||||
This file is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA. */
|
||||
|
||||
/****************************************************************************
|
||||
* The functions here are derived from gcc/config/arm/pr-support.c from the
|
||||
* 4.3.x release. The main changes here involve the use of ptrace to retrieve
|
||||
* memory/processor states from a remote process.
|
||||
****************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
/* We add a prototype for abort here to avoid creating a dependency on
|
||||
target headers. */
|
||||
extern void abort (void);
|
||||
|
||||
/* Derived from _Unwind_VRS_Pop to use ptrace */
|
||||
extern _Unwind_VRS_Result
|
||||
unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
|
||||
_Unwind_VRS_RegClass regclass,
|
||||
_uw discriminator,
|
||||
_Unwind_VRS_DataRepresentation representation,
|
||||
pid_t pid);
|
||||
|
||||
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
|
||||
|
||||
/* Misc constants. */
|
||||
#define R_IP 12
|
||||
#define R_SP 13
|
||||
#define R_LR 14
|
||||
#define R_PC 15
|
||||
|
||||
#define uint32_highbit (((_uw) 1) << 31)
|
||||
|
||||
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
|
||||
|
||||
/* Unwind descriptors. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
_uw16 length;
|
||||
_uw16 offset;
|
||||
} EHT16;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
_uw length;
|
||||
_uw offset;
|
||||
} EHT32;
|
||||
|
||||
/* Personality routine helper functions. */
|
||||
|
||||
#define CODE_FINISH (0xb0)
|
||||
|
||||
/* Derived from next_unwind_byte to use ptrace */
|
||||
/* Return the next byte of unwinding information, or CODE_FINISH if there is
|
||||
no data remaining. */
|
||||
static inline _uw8
|
||||
next_unwind_byte_with_ptrace (__gnu_unwind_state * uws, pid_t pid)
|
||||
{
|
||||
_uw8 b;
|
||||
|
||||
if (uws->bytes_left == 0)
|
||||
{
|
||||
/* Load another word */
|
||||
if (uws->words_left == 0)
|
||||
return CODE_FINISH; /* Nothing left. */
|
||||
uws->words_left--;
|
||||
uws->data = get_remote_word(pid, uws->next);
|
||||
uws->next++;
|
||||
uws->bytes_left = 3;
|
||||
}
|
||||
else
|
||||
uws->bytes_left--;
|
||||
|
||||
/* Extract the most significant byte. */
|
||||
b = (uws->data >> 24) & 0xff;
|
||||
uws->data <<= 8;
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Execute the unwinding instructions described by UWS. */
|
||||
_Unwind_Reason_Code
|
||||
unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
|
||||
pid_t pid)
|
||||
{
|
||||
_uw op;
|
||||
int set_pc;
|
||||
_uw reg;
|
||||
|
||||
set_pc = 0;
|
||||
for (;;)
|
||||
{
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
if (op == CODE_FINISH)
|
||||
{
|
||||
/* If we haven't already set pc then copy it from lr. */
|
||||
if (!set_pc)
|
||||
{
|
||||
_Unwind_VRS_Get (context, _UVRSC_CORE, R_LR, _UVRSD_UINT32,
|
||||
®);
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_PC, _UVRSD_UINT32,
|
||||
®);
|
||||
set_pc = 1;
|
||||
}
|
||||
/* Drop out of the loop. */
|
||||
break;
|
||||
}
|
||||
if ((op & 0x80) == 0)
|
||||
{
|
||||
/* vsp = vsp +- (imm6 << 2 + 4). */
|
||||
_uw offset;
|
||||
|
||||
offset = ((op & 0x3f) << 2) + 4;
|
||||
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
|
||||
if (op & 0x40)
|
||||
reg -= offset;
|
||||
else
|
||||
reg += offset;
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((op & 0xf0) == 0x80)
|
||||
{
|
||||
op = (op << 8) | next_unwind_byte_with_ptrace (uws, pid);
|
||||
if (op == 0x8000)
|
||||
{
|
||||
/* Refuse to unwind. */
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
/* Pop r4-r15 under mask. */
|
||||
op = (op << 4) & 0xfff0;
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op, _UVRSD_UINT32,
|
||||
pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
if (op & (1 << R_PC))
|
||||
set_pc = 1;
|
||||
continue;
|
||||
}
|
||||
if ((op & 0xf0) == 0x90)
|
||||
{
|
||||
op &= 0xf;
|
||||
if (op == 13 || op == 15)
|
||||
/* Reserved. */
|
||||
return _URC_FAILURE;
|
||||
/* vsp = r[nnnn]. */
|
||||
_Unwind_VRS_Get (context, _UVRSC_CORE, op, _UVRSD_UINT32, ®);
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32, ®);
|
||||
continue;
|
||||
}
|
||||
if ((op & 0xf0) == 0xa0)
|
||||
{
|
||||
/* Pop r4-r[4+nnn], [lr]. */
|
||||
_uw mask;
|
||||
|
||||
mask = (0xff0 >> (7 - (op & 7))) & 0xff0;
|
||||
if (op & 8)
|
||||
mask |= (1 << R_LR);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, mask, _UVRSD_UINT32,
|
||||
pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if ((op & 0xf0) == 0xb0)
|
||||
{
|
||||
/* op == 0xb0 already handled. */
|
||||
if (op == 0xb1)
|
||||
{
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
if (op == 0 || ((op & 0xf0) != 0))
|
||||
/* Spare. */
|
||||
return _URC_FAILURE;
|
||||
/* Pop r0-r4 under mask. */
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_CORE, op,
|
||||
_UVRSD_UINT32, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if (op == 0xb2)
|
||||
{
|
||||
/* vsp = vsp + 0x204 + (uleb128 << 2). */
|
||||
int shift;
|
||||
|
||||
_Unwind_VRS_Get (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
|
||||
®);
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
shift = 2;
|
||||
while (op & 0x80)
|
||||
{
|
||||
reg += ((op & 0x7f) << shift);
|
||||
shift += 7;
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
}
|
||||
reg += ((op & 0x7f) << shift) + 0x204;
|
||||
_Unwind_VRS_Set (context, _UVRSC_CORE, R_SP, _UVRSD_UINT32,
|
||||
®);
|
||||
continue;
|
||||
}
|
||||
if (op == 0xb3)
|
||||
{
|
||||
/* Pop VFP registers with fldmx. */
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX,
|
||||
pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if ((op & 0xfc) == 0xb4)
|
||||
{
|
||||
/* Pop FPA E[4]-E[4+nn]. */
|
||||
op = 0x40000 | ((op & 3) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
|
||||
pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
/* op & 0xf8 == 0xb8. */
|
||||
/* Pop VFP D[8]-D[8+nnn] with fldmx. */
|
||||
op = 0x80000 | ((op & 7) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_VFPX, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if ((op & 0xf0) == 0xc0)
|
||||
{
|
||||
if (op == 0xc6)
|
||||
{
|
||||
/* Pop iWMMXt D registers. */
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
|
||||
_UVRSD_UINT64, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if (op == 0xc7)
|
||||
{
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
if (op == 0 || (op & 0xf0) != 0)
|
||||
/* Spare. */
|
||||
return _URC_FAILURE;
|
||||
/* Pop iWMMXt wCGR{3,2,1,0} under mask. */
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXC, op,
|
||||
_UVRSD_UINT32, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if ((op & 0xf8) == 0xc0)
|
||||
{
|
||||
/* Pop iWMMXt wR[10]-wR[10+nnn]. */
|
||||
op = 0xa0000 | ((op & 0xf) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_WMMXD, op,
|
||||
_UVRSD_UINT64, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if (op == 0xc8)
|
||||
{
|
||||
#ifndef __VFP_FP__
|
||||
/* Pop FPA registers. */
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_FPA, op, _UVRSD_FPAX,
|
||||
pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
#else
|
||||
/* Pop VFPv3 registers D[16+ssss]-D[16+ssss+cccc] with vldm. */
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
op = (((op & 0xf0) + 16) << 12) | ((op & 0xf) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
|
||||
_UVRSD_DOUBLE, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
if (op == 0xc9)
|
||||
{
|
||||
/* Pop VFP registers with fldmd. */
|
||||
op = next_unwind_byte_with_ptrace (uws, pid);
|
||||
op = ((op & 0xf0) << 12) | ((op & 0xf) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op,
|
||||
_UVRSD_DOUBLE, pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
/* Spare. */
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
if ((op & 0xf8) == 0xd0)
|
||||
{
|
||||
/* Pop VFP D[8]-D[8+nnn] with fldmd. */
|
||||
op = 0x80000 | ((op & 7) + 1);
|
||||
if (unwind_VRS_Pop_with_ptrace (context, _UVRSC_VFP, op, _UVRSD_DOUBLE,
|
||||
pid)
|
||||
!= _UVRSR_OK)
|
||||
return _URC_FAILURE;
|
||||
continue;
|
||||
}
|
||||
/* Spare. */
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
return _URC_OK;
|
||||
}
|
618
debuggerd/unwind-arm.c
Normal file
618
debuggerd/unwind-arm.c
Normal file
|
@ -0,0 +1,618 @@
|
|||
/* ARM EABI compliant unwinding routines.
|
||||
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||||
Contributed by Paul Brook
|
||||
|
||||
This file is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
In addition to the permissions in the GNU General Public License, the
|
||||
Free Software Foundation gives you unlimited permission to link the
|
||||
compiled version of this file into combinations with other programs,
|
||||
and to distribute those combinations without any restriction coming
|
||||
from the use of this file. (The General Public License restrictions
|
||||
do apply in other respects; for example, they cover modification of
|
||||
the file, and distribution when not linked into a combine
|
||||
executable.)
|
||||
|
||||
This file is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA. */
|
||||
|
||||
/****************************************************************************
|
||||
* The functions here are derived from gcc/config/arm/unwind-arm.c from the
|
||||
* 4.3.x release. The main changes here involve the use of ptrace to retrieve
|
||||
* memory/processor states from a remote process.
|
||||
****************************************************************************/
|
||||
|
||||
#include <cutils/logd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <unwind.h>
|
||||
#include "utility.h"
|
||||
|
||||
typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
|
||||
|
||||
void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
|
||||
bool __attribute__((weak)) __cxa_begin_cleanup(_Unwind_Control_Block *ucbp);
|
||||
bool __attribute__((weak)) __cxa_type_match(_Unwind_Control_Block *ucbp,
|
||||
const type_info *rttip,
|
||||
bool is_reference,
|
||||
void **matched_object);
|
||||
|
||||
/* Misc constants. */
|
||||
#define R_IP 12
|
||||
#define R_SP 13
|
||||
#define R_LR 14
|
||||
#define R_PC 15
|
||||
|
||||
#define EXIDX_CANTUNWIND 1
|
||||
#define uint32_highbit (((_uw) 1) << 31)
|
||||
|
||||
#define UCB_FORCED_STOP_FN(ucbp) ((ucbp)->unwinder_cache.reserved1)
|
||||
#define UCB_PR_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved2)
|
||||
#define UCB_SAVED_CALLSITE_ADDR(ucbp) ((ucbp)->unwinder_cache.reserved3)
|
||||
#define UCB_FORCED_STOP_ARG(ucbp) ((ucbp)->unwinder_cache.reserved4)
|
||||
|
||||
struct core_regs
|
||||
{
|
||||
_uw r[16];
|
||||
};
|
||||
|
||||
/* We use normal integer types here to avoid the compiler generating
|
||||
coprocessor instructions. */
|
||||
struct vfp_regs
|
||||
{
|
||||
_uw64 d[16];
|
||||
_uw pad;
|
||||
};
|
||||
|
||||
struct vfpv3_regs
|
||||
{
|
||||
/* Always populated via VSTM, so no need for the "pad" field from
|
||||
vfp_regs (which is used to store the format word for FSTMX). */
|
||||
_uw64 d[16];
|
||||
};
|
||||
|
||||
struct fpa_reg
|
||||
{
|
||||
_uw w[3];
|
||||
};
|
||||
|
||||
struct fpa_regs
|
||||
{
|
||||
struct fpa_reg f[8];
|
||||
};
|
||||
|
||||
struct wmmxd_regs
|
||||
{
|
||||
_uw64 wd[16];
|
||||
};
|
||||
|
||||
struct wmmxc_regs
|
||||
{
|
||||
_uw wc[4];
|
||||
};
|
||||
|
||||
/* Unwind descriptors. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
_uw16 length;
|
||||
_uw16 offset;
|
||||
} EHT16;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
_uw length;
|
||||
_uw offset;
|
||||
} EHT32;
|
||||
|
||||
/* The ABI specifies that the unwind routines may only use core registers,
|
||||
except when actually manipulating coprocessor state. This allows
|
||||
us to write one implementation that works on all platforms by
|
||||
demand-saving coprocessor registers.
|
||||
|
||||
During unwinding we hold the coprocessor state in the actual hardware
|
||||
registers and allocate demand-save areas for use during phase1
|
||||
unwinding. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* The first fields must be the same as a phase2_vrs. */
|
||||
_uw demand_save_flags;
|
||||
struct core_regs core;
|
||||
_uw prev_sp; /* Only valid during forced unwinding. */
|
||||
struct vfp_regs vfp;
|
||||
struct vfpv3_regs vfp_regs_16_to_31;
|
||||
struct fpa_regs fpa;
|
||||
struct wmmxd_regs wmmxd;
|
||||
struct wmmxc_regs wmmxc;
|
||||
} phase1_vrs;
|
||||
|
||||
/* This must match the structure created by the assembly wrappers. */
|
||||
typedef struct
|
||||
{
|
||||
_uw demand_save_flags;
|
||||
struct core_regs core;
|
||||
} phase2_vrs;
|
||||
|
||||
|
||||
/* An exception index table entry. */
|
||||
|
||||
typedef struct __EIT_entry
|
||||
{
|
||||
_uw fnoffset;
|
||||
_uw content;
|
||||
} __EIT_entry;
|
||||
|
||||
/* Derived version to use ptrace */
|
||||
typedef _Unwind_Reason_Code (*personality_routine_with_ptrace)
|
||||
(_Unwind_State,
|
||||
_Unwind_Control_Block *,
|
||||
_Unwind_Context *,
|
||||
pid_t);
|
||||
|
||||
/* Derived version to use ptrace */
|
||||
/* ABI defined personality routines. */
|
||||
static _Unwind_Reason_Code unwind_cpp_pr0_with_ptrace (_Unwind_State,
|
||||
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
|
||||
static _Unwind_Reason_Code unwind_cpp_pr1_with_ptrace (_Unwind_State,
|
||||
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
|
||||
static _Unwind_Reason_Code unwind_cpp_pr2_with_ptrace (_Unwind_State,
|
||||
_Unwind_Control_Block *, _Unwind_Context *, pid_t);
|
||||
|
||||
/* Execute the unwinding instructions described by UWS. */
|
||||
extern _Unwind_Reason_Code
|
||||
unwind_execute_with_ptrace(_Unwind_Context * context, __gnu_unwind_state * uws,
|
||||
pid_t pid);
|
||||
|
||||
/* Derived version to use ptrace. Only handles core registers. Disregards
|
||||
* FP and others.
|
||||
*/
|
||||
/* ABI defined function to pop registers off the stack. */
|
||||
|
||||
_Unwind_VRS_Result unwind_VRS_Pop_with_ptrace (_Unwind_Context *context,
|
||||
_Unwind_VRS_RegClass regclass,
|
||||
_uw discriminator,
|
||||
_Unwind_VRS_DataRepresentation representation,
|
||||
pid_t pid)
|
||||
{
|
||||
phase1_vrs *vrs = (phase1_vrs *) context;
|
||||
|
||||
switch (regclass)
|
||||
{
|
||||
case _UVRSC_CORE:
|
||||
{
|
||||
_uw *ptr;
|
||||
_uw mask;
|
||||
int i;
|
||||
|
||||
if (representation != _UVRSD_UINT32)
|
||||
return _UVRSR_FAILED;
|
||||
|
||||
mask = discriminator & 0xffff;
|
||||
ptr = (_uw *) vrs->core.r[R_SP];
|
||||
/* Pop the requested registers. */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
if (mask & (1 << i)) {
|
||||
vrs->core.r[i] = get_remote_word(pid, ptr);
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
/* Writeback the stack pointer value if it wasn't restored. */
|
||||
if ((mask & (1 << R_SP)) == 0)
|
||||
vrs->core.r[R_SP] = (_uw) ptr;
|
||||
}
|
||||
return _UVRSR_OK;
|
||||
|
||||
default:
|
||||
return _UVRSR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Core unwinding functions. */
|
||||
|
||||
/* Calculate the address encoded by a 31-bit self-relative offset at address
|
||||
P. */
|
||||
static inline _uw
|
||||
selfrel_offset31 (const _uw *p, pid_t pid)
|
||||
{
|
||||
_uw offset = get_remote_word(pid, (void*)p);
|
||||
|
||||
//offset = *p;
|
||||
/* Sign extend to 32 bits. */
|
||||
if (offset & (1 << 30))
|
||||
offset |= 1u << 31;
|
||||
else
|
||||
offset &= ~(1u << 31);
|
||||
|
||||
return offset + (_uw) p;
|
||||
}
|
||||
|
||||
|
||||
/* Perform a binary search for RETURN_ADDRESS in TABLE. The table contains
|
||||
NREC entries. */
|
||||
|
||||
static const __EIT_entry *
|
||||
search_EIT_table (const __EIT_entry * table, int nrec, _uw return_address,
|
||||
pid_t pid)
|
||||
{
|
||||
_uw next_fn;
|
||||
_uw this_fn;
|
||||
int n, left, right;
|
||||
|
||||
if (nrec == 0)
|
||||
return (__EIT_entry *) 0;
|
||||
|
||||
left = 0;
|
||||
right = nrec - 1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
n = (left + right) / 2;
|
||||
this_fn = selfrel_offset31 (&table[n].fnoffset, pid);
|
||||
if (n != nrec - 1)
|
||||
next_fn = selfrel_offset31 (&table[n + 1].fnoffset, pid) - 1;
|
||||
else
|
||||
next_fn = (_uw)0 - 1;
|
||||
|
||||
if (return_address < this_fn)
|
||||
{
|
||||
if (n == left)
|
||||
return (__EIT_entry *) 0;
|
||||
right = n - 1;
|
||||
}
|
||||
else if (return_address <= next_fn)
|
||||
return &table[n];
|
||||
else
|
||||
left = n + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the exception index table eintry for the given address. */
|
||||
static const __EIT_entry*
|
||||
get_eitp(_uw return_address, pid_t pid, mapinfo *map, mapinfo **containing_map)
|
||||
{
|
||||
const __EIT_entry *eitp = NULL;
|
||||
int nrec;
|
||||
mapinfo *mi;
|
||||
|
||||
/* The return address is the address of the instruction following the
|
||||
call instruction (plus one in thumb mode). If this was the last
|
||||
instruction in the function the address will lie in the following
|
||||
function. Subtract 2 from the address so that it points within the call
|
||||
instruction itself. */
|
||||
if (return_address >= 2)
|
||||
return_address -= 2;
|
||||
|
||||
for (mi = map; mi != NULL; mi = mi->next) {
|
||||
if (return_address >= mi->start && return_address <= mi->end) break;
|
||||
}
|
||||
|
||||
if (mi) {
|
||||
if (containing_map) *containing_map = mi;
|
||||
eitp = (__EIT_entry *) mi->exidx_start;
|
||||
nrec = (mi->exidx_end - mi->exidx_start)/sizeof(__EIT_entry);
|
||||
eitp = search_EIT_table (eitp, nrec, return_address, pid);
|
||||
}
|
||||
return eitp;
|
||||
}
|
||||
|
||||
/* Find the exception index table eintry for the given address.
|
||||
Fill in the relevant fields of the UCB.
|
||||
Returns _URC_FAILURE if an error occurred, _URC_OK on success. */
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
get_eit_entry (_Unwind_Control_Block *ucbp, _uw return_address, pid_t pid,
|
||||
mapinfo *map, mapinfo **containing_map)
|
||||
{
|
||||
const __EIT_entry *eitp;
|
||||
|
||||
eitp = get_eitp(return_address, pid, map, containing_map);
|
||||
|
||||
if (!eitp)
|
||||
{
|
||||
UCB_PR_ADDR (ucbp) = 0;
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
ucbp->pr_cache.fnstart = selfrel_offset31 (&eitp->fnoffset, pid);
|
||||
|
||||
_uw eitp_content = get_remote_word(pid, (void *)&eitp->content);
|
||||
|
||||
/* Can this frame be unwound at all? */
|
||||
if (eitp_content == EXIDX_CANTUNWIND)
|
||||
{
|
||||
UCB_PR_ADDR (ucbp) = 0;
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
/* Obtain the address of the "real" __EHT_Header word. */
|
||||
|
||||
if (eitp_content & uint32_highbit)
|
||||
{
|
||||
/* It is immediate data. */
|
||||
ucbp->pr_cache.ehtp = (_Unwind_EHT_Header *)&eitp->content;
|
||||
ucbp->pr_cache.additional = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The low 31 bits of the content field are a self-relative
|
||||
offset to an _Unwind_EHT_Entry structure. */
|
||||
ucbp->pr_cache.ehtp =
|
||||
(_Unwind_EHT_Header *) selfrel_offset31 (&eitp->content, pid);
|
||||
ucbp->pr_cache.additional = 0;
|
||||
}
|
||||
|
||||
/* Discover the personality routine address. */
|
||||
if (get_remote_word(pid, ucbp->pr_cache.ehtp) & (1u << 31))
|
||||
{
|
||||
/* One of the predefined standard routines. */
|
||||
_uw idx = (get_remote_word(pid, ucbp->pr_cache.ehtp) >> 24) & 0xf;
|
||||
if (idx == 0)
|
||||
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr0_with_ptrace;
|
||||
else if (idx == 1)
|
||||
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr1_with_ptrace;
|
||||
else if (idx == 2)
|
||||
UCB_PR_ADDR (ucbp) = (_uw) &unwind_cpp_pr2_with_ptrace;
|
||||
else
|
||||
{ /* Failed */
|
||||
UCB_PR_ADDR (ucbp) = 0;
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Execute region offset to PR */
|
||||
UCB_PR_ADDR (ucbp) = selfrel_offset31 (ucbp->pr_cache.ehtp, pid);
|
||||
/* Since we are unwinding the stack from a different process, it is
|
||||
* impossible to execute the personality routine in debuggerd. Punt here.
|
||||
*/
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
return _URC_OK;
|
||||
}
|
||||
|
||||
/* Print out the current call level, pc, and module name in the crash log */
|
||||
static _Unwind_Reason_Code log_function(_Unwind_Context *context, int tfd,
|
||||
int stack_level,
|
||||
mapinfo *map,
|
||||
unsigned int sp_list[],
|
||||
bool at_fault)
|
||||
{
|
||||
_uw pc;
|
||||
phase2_vrs *vrs = (phase2_vrs*) context;
|
||||
const mapinfo *mi;
|
||||
bool only_in_tombstone = !at_fault;
|
||||
|
||||
if (stack_level < STACK_CONTENT_DEPTH) {
|
||||
sp_list[stack_level] = vrs->core.r[R_SP];
|
||||
}
|
||||
pc = vrs->core.r[R_PC];
|
||||
|
||||
// Top level frame
|
||||
if (stack_level == 0) {
|
||||
pc &= ~1;
|
||||
}
|
||||
// For deeper framers, rollback pc by one instruction
|
||||
else {
|
||||
pc = vrs->core.r[R_PC];
|
||||
// Thumb mode
|
||||
if (pc & 1) {
|
||||
pc = (pc & ~1) - 2;
|
||||
}
|
||||
else {
|
||||
pc -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
mi = pc_to_mapinfo(map, pc);
|
||||
|
||||
_LOG(tfd, only_in_tombstone,
|
||||
" #%02d pc %08x %s\n", stack_level, pc,
|
||||
mi ? mi->name : "");
|
||||
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
/* Derived from __gnu_Unwind_Backtrace to use ptrace */
|
||||
/* Perform stack backtrace through unwind data. Return the level of stack it
|
||||
* unwinds.
|
||||
*/
|
||||
int unwind_backtrace_with_ptrace(int tfd, pid_t pid, mapinfo *map,
|
||||
unsigned int sp_list[], int *frame0_pc_sane,
|
||||
bool at_fault)
|
||||
{
|
||||
phase1_vrs saved_vrs;
|
||||
_Unwind_Reason_Code code = _URC_OK;
|
||||
struct pt_regs r;
|
||||
int i;
|
||||
int stack_level = 0;
|
||||
|
||||
_Unwind_Control_Block ucb;
|
||||
_Unwind_Control_Block *ucbp = &ucb;
|
||||
|
||||
if(ptrace(PTRACE_GETREGS, pid, 0, &r)) return 0;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
saved_vrs.core.r[i] = r.uregs[i];
|
||||
/*
|
||||
_LOG(tfd, "r[%d] = 0x%x\n", i, saved_vrs.core.r[i]);
|
||||
*/
|
||||
}
|
||||
|
||||
/* Set demand-save flags. */
|
||||
saved_vrs.demand_save_flags = ~(_uw) 0;
|
||||
|
||||
/*
|
||||
* If the app crashes because of calling the weeds, we cannot pass the PC
|
||||
* to the usual unwinding code as the EXIDX mapping will fail.
|
||||
* Instead, we simply print out the 0 as the top frame, and resume the
|
||||
* unwinding process with the value stored in LR.
|
||||
*/
|
||||
if (get_eitp(saved_vrs.core.r[R_PC], pid, map, NULL) == NULL) {
|
||||
*frame0_pc_sane = 0;
|
||||
log_function ((_Unwind_Context *) &saved_vrs, tfd, stack_level,
|
||||
map, sp_list, at_fault);
|
||||
saved_vrs.core.r[R_PC] = saved_vrs.core.r[R_LR];
|
||||
stack_level++;
|
||||
}
|
||||
|
||||
do {
|
||||
mapinfo *this_map = NULL;
|
||||
/* Find the entry for this routine. */
|
||||
if (get_eit_entry(ucbp, saved_vrs.core.r[R_PC], pid, map, &this_map)
|
||||
!= _URC_OK) {
|
||||
/* Uncomment the code below to study why the unwinder failed */
|
||||
#if 0
|
||||
/* Shed more debugging info for stack unwinder improvement */
|
||||
if (this_map) {
|
||||
_LOG(tfd, 1,
|
||||
"Relative PC=%#x from %s not contained in EXIDX\n",
|
||||
saved_vrs.core.r[R_PC] - this_map->start, this_map->name);
|
||||
}
|
||||
_LOG(tfd, 1, "PC=%#x SP=%#x\n",
|
||||
saved_vrs.core.r[R_PC], saved_vrs.core.r[R_SP]);
|
||||
#endif
|
||||
code = _URC_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* The dwarf unwinder assumes the context structure holds things
|
||||
like the function and LSDA pointers. The ARM implementation
|
||||
caches these in the exception header (UCB). To avoid
|
||||
rewriting everything we make the virtual IP register point at
|
||||
the UCB. */
|
||||
_Unwind_SetGR((_Unwind_Context *)&saved_vrs, 12, (_Unwind_Ptr) ucbp);
|
||||
|
||||
/* Call log function. */
|
||||
if (log_function ((_Unwind_Context *) &saved_vrs, tfd, stack_level,
|
||||
map, sp_list, at_fault) != _URC_NO_REASON) {
|
||||
code = _URC_FAILURE;
|
||||
break;
|
||||
}
|
||||
stack_level++;
|
||||
|
||||
/* Call the pr to decide what to do. */
|
||||
code = ((personality_routine_with_ptrace) UCB_PR_ADDR (ucbp))(
|
||||
_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, ucbp,
|
||||
(void *) &saved_vrs, pid);
|
||||
/*
|
||||
* In theory the unwinding process will stop when the end of stack is
|
||||
* reached or there is no unwinding information for the code address.
|
||||
* To add another level of guarantee that the unwinding process
|
||||
* will terminate we will stop it when the STACK_CONTENT_DEPTH is reached.
|
||||
*/
|
||||
} while (code != _URC_END_OF_STACK && code != _URC_FAILURE &&
|
||||
stack_level < STACK_CONTENT_DEPTH);
|
||||
return stack_level;
|
||||
}
|
||||
|
||||
|
||||
/* Derived version to use ptrace */
|
||||
/* Common implementation for ARM ABI defined personality routines.
|
||||
ID is the index of the personality routine, other arguments are as defined
|
||||
by __aeabi_unwind_cpp_pr{0,1,2}. */
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_pr_common_with_ptrace (_Unwind_State state,
|
||||
_Unwind_Control_Block *ucbp,
|
||||
_Unwind_Context *context,
|
||||
int id,
|
||||
pid_t pid)
|
||||
{
|
||||
__gnu_unwind_state uws;
|
||||
_uw *data;
|
||||
int phase2_call_unexpected_after_unwind = 0;
|
||||
|
||||
state &= _US_ACTION_MASK;
|
||||
|
||||
data = (_uw *) ucbp->pr_cache.ehtp;
|
||||
uws.data = get_remote_word(pid, data);
|
||||
data++;
|
||||
uws.next = data;
|
||||
if (id == 0)
|
||||
{
|
||||
uws.data <<= 8;
|
||||
uws.words_left = 0;
|
||||
uws.bytes_left = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
uws.words_left = (uws.data >> 16) & 0xff;
|
||||
uws.data <<= 16;
|
||||
uws.bytes_left = 2;
|
||||
data += uws.words_left;
|
||||
}
|
||||
|
||||
/* Restore the saved pointer. */
|
||||
if (state == _US_UNWIND_FRAME_RESUME)
|
||||
data = (_uw *) ucbp->cleanup_cache.bitpattern[0];
|
||||
|
||||
if ((ucbp->pr_cache.additional & 1) == 0)
|
||||
{
|
||||
/* Process descriptors. */
|
||||
while (get_remote_word(pid, data)) {
|
||||
/**********************************************************************
|
||||
* The original code here seems to deal with exceptions that are not
|
||||
* applicable in our toolchain, thus there is no way to test it for now.
|
||||
* Instead of leaving it here and causing potential instability in
|
||||
* debuggerd, we'd better punt here and leave the stack unwound.
|
||||
* In the future when we discover cases where the stack should be unwound
|
||||
* further but is not, we can revisit the code here.
|
||||
**********************************************************************/
|
||||
return _URC_FAILURE;
|
||||
}
|
||||
/* Finished processing this descriptor. */
|
||||
}
|
||||
|
||||
if (unwind_execute_with_ptrace (context, &uws, pid) != _URC_OK)
|
||||
return _URC_FAILURE;
|
||||
|
||||
if (phase2_call_unexpected_after_unwind)
|
||||
{
|
||||
/* Enter __cxa_unexpected as if called from the call site. */
|
||||
_Unwind_SetGR (context, R_LR, _Unwind_GetGR (context, R_PC));
|
||||
_Unwind_SetGR (context, R_PC, (_uw) &__cxa_call_unexpected);
|
||||
return _URC_INSTALL_CONTEXT;
|
||||
}
|
||||
|
||||
return _URC_CONTINUE_UNWIND;
|
||||
}
|
||||
|
||||
|
||||
/* ABI defined personality routine entry points. */
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_cpp_pr0_with_ptrace (_Unwind_State state,
|
||||
_Unwind_Control_Block *ucbp,
|
||||
_Unwind_Context *context,
|
||||
pid_t pid)
|
||||
{
|
||||
return unwind_pr_common_with_ptrace (state, ucbp, context, 0, pid);
|
||||
}
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_cpp_pr1_with_ptrace (_Unwind_State state,
|
||||
_Unwind_Control_Block *ucbp,
|
||||
_Unwind_Context *context,
|
||||
pid_t pid)
|
||||
{
|
||||
return unwind_pr_common_with_ptrace (state, ucbp, context, 1, pid);
|
||||
}
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_cpp_pr2_with_ptrace (_Unwind_State state,
|
||||
_Unwind_Control_Block *ucbp,
|
||||
_Unwind_Context *context,
|
||||
pid_t pid)
|
||||
{
|
||||
return unwind_pr_common_with_ptrace (state, ucbp, context, 2, pid);
|
||||
}
|
78
debuggerd/utility.c
Normal file
78
debuggerd/utility.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* system/debuggerd/utility.c
|
||||
**
|
||||
** Copyright 2008, 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 <sys/ptrace.h>
|
||||
#include <sys/exec_elf.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
/* Get a word from pid using ptrace. The result is the return value. */
|
||||
int get_remote_word(int pid, void *src)
|
||||
{
|
||||
return ptrace(PTRACE_PEEKTEXT, pid, src, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Handy routine to read aggregated data from pid using ptrace. The read
|
||||
* values are written to the dest locations directly.
|
||||
*/
|
||||
void get_remote_struct(int pid, void *src, void *dst, size_t size)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i+4 <= size; i+=4) {
|
||||
*(int *)(dst+i) = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL);
|
||||
}
|
||||
|
||||
if (i < size) {
|
||||
int val;
|
||||
|
||||
assert((size - i) < 4);
|
||||
val = ptrace(PTRACE_PEEKTEXT, pid, src+i, NULL);
|
||||
while (i < size) {
|
||||
((unsigned char *)dst)[i] = val & 0xff;
|
||||
i++;
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Map a pc address to the name of the containing ELF file */
|
||||
const char *map_to_name(mapinfo *mi, unsigned pc, const char* def)
|
||||
{
|
||||
while(mi) {
|
||||
if((pc >= mi->start) && (pc < mi->end)){
|
||||
return mi->name;
|
||||
}
|
||||
mi = mi->next;
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/* Find the containing map info for the pc */
|
||||
const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc)
|
||||
{
|
||||
while(mi) {
|
||||
if((pc >= mi->start) && (pc < mi->end)){
|
||||
return mi;
|
||||
}
|
||||
mi = mi->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
56
debuggerd/utility.h
Normal file
56
debuggerd/utility.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* system/debuggerd/utility.h
|
||||
**
|
||||
** Copyright 2008, 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.
|
||||
*/
|
||||
|
||||
#ifndef __utility_h
|
||||
#define __utility_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef PT_ARM_EXIDX
|
||||
#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
|
||||
#endif
|
||||
|
||||
#define STACK_CONTENT_DEPTH 32
|
||||
|
||||
typedef struct mapinfo {
|
||||
struct mapinfo *next;
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
unsigned exidx_start;
|
||||
unsigned exidx_end;
|
||||
char name[];
|
||||
} mapinfo;
|
||||
|
||||
/* Get a word from pid using ptrace. The result is the return value. */
|
||||
extern int get_remote_word(int pid, void *src);
|
||||
|
||||
/* Handy routine to read aggregated data from pid using ptrace. The read
|
||||
* values are written to the dest locations directly.
|
||||
*/
|
||||
extern void get_remote_struct(int pid, void *src, void *dst, size_t size);
|
||||
|
||||
/* Find the containing map for the pc */
|
||||
const mapinfo *pc_to_mapinfo (mapinfo *mi, unsigned pc);
|
||||
|
||||
/* Map a pc address to the name of the containing ELF file */
|
||||
const char *map_to_name(mapinfo *mi, unsigned pc, const char* def);
|
||||
|
||||
/* Log information onto the tombstone */
|
||||
extern void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...);
|
||||
|
||||
#endif
|
57
fastboot/Android.mk
Normal file
57
fastboot/Android.mk
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Copyright (C) 2007 Google Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg
|
||||
LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c
|
||||
LOCAL_MODULE := fastboot
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
LOCAL_SRC_FILES += usb_linux.c util_linux.c
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),darwin)
|
||||
LOCAL_SRC_FILES += usb_osx.c util_osx.c
|
||||
LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit \
|
||||
-framework Carbon
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
LOCAL_SRC_FILES += usb_windows.c util_windows.c
|
||||
EXTRA_STATIC_LIBS := AdbWinApi
|
||||
LOCAL_C_INCLUDES += /usr/include/w32api/ddk development/host/windows/usb/api
|
||||
ifeq ($(strip $(USE_CYGWIN)),)
|
||||
LOCAL_LDLIBS += -lws2_32
|
||||
USE_SYSDEPS_WIN32 := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
$(call dist-for-goals,user userdebug droid,$(LOCAL_BUILT_MODULE))
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := usbtest.c usb_linux.c
|
||||
LOCAL_MODULE := usbtest
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
endif
|
||||
|
||||
ifeq ($(HOST_OS),windows)
|
||||
$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
|
||||
endif
|
85
fastboot/bootimg.c
Normal file
85
fastboot/bootimg.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <bootimg.h>
|
||||
|
||||
void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
|
||||
{
|
||||
strcpy((char*) h->cmdline, cmdline);
|
||||
}
|
||||
|
||||
boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size,
|
||||
void *ramdisk, unsigned ramdisk_size,
|
||||
void *second, unsigned second_size,
|
||||
unsigned page_size,
|
||||
unsigned *bootimg_size)
|
||||
{
|
||||
unsigned kernel_actual;
|
||||
unsigned ramdisk_actual;
|
||||
unsigned second_actual;
|
||||
unsigned page_mask;
|
||||
boot_img_hdr *hdr;
|
||||
|
||||
page_mask = page_size - 1;
|
||||
|
||||
kernel_actual = (kernel_size + page_mask) & (~page_mask);
|
||||
ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
|
||||
second_actual = (second_size + page_mask) & (~page_mask);
|
||||
|
||||
*bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
|
||||
|
||||
hdr = calloc(*bootimg_size, 1);
|
||||
|
||||
if(hdr == 0) {
|
||||
return hdr;
|
||||
}
|
||||
|
||||
memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
|
||||
|
||||
hdr->kernel_size = kernel_size;
|
||||
hdr->kernel_addr = 0x10008000;
|
||||
hdr->ramdisk_size = ramdisk_size;
|
||||
hdr->ramdisk_addr = 0x11000000;
|
||||
hdr->second_size = second_size;
|
||||
hdr->second_addr = 0x10F00000;
|
||||
|
||||
hdr->tags_addr = 0x10000100;
|
||||
hdr->page_size = page_size;
|
||||
|
||||
memcpy(hdr->magic + page_size,
|
||||
kernel, kernel_size);
|
||||
memcpy(hdr->magic + page_size + kernel_actual,
|
||||
ramdisk, ramdisk_size);
|
||||
memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
|
||||
second, second_size);
|
||||
return hdr;
|
||||
}
|
289
fastboot/engine.c
Normal file
289
fastboot/engine.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fastboot.h"
|
||||
|
||||
char *mkmsg(const char *fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
char *s;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf(buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
s = strdup(buf);
|
||||
if (s == 0) die("out of memory");
|
||||
return s;
|
||||
}
|
||||
|
||||
#define OP_DOWNLOAD 1
|
||||
#define OP_COMMAND 2
|
||||
#define OP_QUERY 3
|
||||
#define OP_NOTICE 4
|
||||
|
||||
typedef struct Action Action;
|
||||
|
||||
struct Action
|
||||
{
|
||||
unsigned op;
|
||||
Action *next;
|
||||
|
||||
char cmd[64];
|
||||
void *data;
|
||||
unsigned size;
|
||||
|
||||
const char *msg;
|
||||
int (*func)(Action *a, int status, char *resp);
|
||||
};
|
||||
|
||||
static Action *action_list = 0;
|
||||
static Action *action_last = 0;
|
||||
|
||||
static int cb_default(Action *a, int status, char *resp)
|
||||
{
|
||||
if (status) {
|
||||
fprintf(stderr,"FAILED (%s)\n", resp);
|
||||
} else {
|
||||
fprintf(stderr,"OKAY\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static Action *queue_action(unsigned op, const char *fmt, ...)
|
||||
{
|
||||
Action *a;
|
||||
va_list ap;
|
||||
|
||||
a = calloc(1, sizeof(Action));
|
||||
if (a == 0) die("out of memory");
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsprintf(a->cmd, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (action_last) {
|
||||
action_last->next = a;
|
||||
} else {
|
||||
action_list = a;
|
||||
}
|
||||
action_last = a;
|
||||
a->op = op;
|
||||
a->func = cb_default;
|
||||
return a;
|
||||
}
|
||||
|
||||
void fb_queue_erase(const char *ptn)
|
||||
{
|
||||
Action *a;
|
||||
a = queue_action(OP_COMMAND, "erase:%s", ptn);
|
||||
a->msg = mkmsg("erasing '%s'", ptn);
|
||||
}
|
||||
|
||||
void fb_queue_flash(const char *ptn, void *data, unsigned sz)
|
||||
{
|
||||
Action *a;
|
||||
|
||||
a = queue_action(OP_DOWNLOAD, "");
|
||||
a->data = data;
|
||||
a->size = sz;
|
||||
a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
|
||||
|
||||
a = queue_action(OP_COMMAND, "flash:%s", ptn);
|
||||
a->msg = mkmsg("writing '%s'", ptn);
|
||||
}
|
||||
|
||||
static int match(char *str, const char **value, unsigned count)
|
||||
{
|
||||
const char *val;
|
||||
unsigned n;
|
||||
int len;
|
||||
|
||||
for (n = 0; n < count; n++) {
|
||||
const char *val = value[n];
|
||||
int len = strlen(val);
|
||||
int match;
|
||||
|
||||
if ((len > 1) && (val[len-1] == '*')) {
|
||||
len--;
|
||||
match = !strncmp(val, str, len);
|
||||
} else {
|
||||
match = !strcmp(val, str);
|
||||
}
|
||||
|
||||
if (match) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int cb_check(Action *a, int status, char *resp, int invert)
|
||||
{
|
||||
const char **value = a->data;
|
||||
unsigned count = a->size;
|
||||
unsigned n;
|
||||
int yes;
|
||||
|
||||
if (status) {
|
||||
fprintf(stderr,"FAILED (%s)\n", resp);
|
||||
return status;
|
||||
}
|
||||
|
||||
yes = match(resp, value, count);
|
||||
if (invert) yes = !yes;
|
||||
|
||||
if (yes) {
|
||||
fprintf(stderr,"OKAY\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,"FAILED\n\n");
|
||||
fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
|
||||
fprintf(stderr,"Update %s '%s'",
|
||||
invert ? "rejects" : "requires", value[0]);
|
||||
for (n = 1; n < count; n++) {
|
||||
fprintf(stderr," or '%s'", value[n]);
|
||||
}
|
||||
fprintf(stderr,".\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int cb_require(Action *a, int status, char *resp)
|
||||
{
|
||||
return cb_check(a, status, resp, 0);
|
||||
}
|
||||
|
||||
static int cb_reject(Action *a, int status, char *resp)
|
||||
{
|
||||
return cb_check(a, status, resp, 1);
|
||||
}
|
||||
|
||||
void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value)
|
||||
{
|
||||
Action *a;
|
||||
a = queue_action(OP_QUERY, "getvar:%s", var);
|
||||
a->data = value;
|
||||
a->size = nvalues;
|
||||
a->msg = mkmsg("checking %s", var);
|
||||
a->func = invert ? cb_reject : cb_require;
|
||||
if (a->data == 0) die("out of memory");
|
||||
}
|
||||
|
||||
static int cb_display(Action *a, int status, char *resp)
|
||||
{
|
||||
if (status) {
|
||||
fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
|
||||
return status;
|
||||
}
|
||||
fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fb_queue_display(const char *var, const char *prettyname)
|
||||
{
|
||||
Action *a;
|
||||
a = queue_action(OP_QUERY, "getvar:%s", var);
|
||||
a->data = strdup(prettyname);
|
||||
if (a->data == 0) die("out of memory");
|
||||
a->func = cb_display;
|
||||
}
|
||||
|
||||
static int cb_do_nothing(Action *a, int status, char *resp)
|
||||
{
|
||||
fprintf(stderr,"\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fb_queue_reboot(void)
|
||||
{
|
||||
Action *a = queue_action(OP_COMMAND, "reboot");
|
||||
a->func = cb_do_nothing;
|
||||
a->msg = "rebooting";
|
||||
}
|
||||
|
||||
void fb_queue_command(const char *cmd, const char *msg)
|
||||
{
|
||||
Action *a = queue_action(OP_COMMAND, cmd);
|
||||
a->msg = msg;
|
||||
}
|
||||
|
||||
void fb_queue_download(const char *name, void *data, unsigned size)
|
||||
{
|
||||
Action *a = queue_action(OP_DOWNLOAD, "");
|
||||
a->data = data;
|
||||
a->size = size;
|
||||
a->msg = mkmsg("downloading '%s'", name);
|
||||
}
|
||||
|
||||
void fb_queue_notice(const char *notice)
|
||||
{
|
||||
Action *a = queue_action(OP_NOTICE, "");
|
||||
a->data = (void*) notice;
|
||||
}
|
||||
|
||||
void fb_execute_queue(usb_handle *usb)
|
||||
{
|
||||
Action *a;
|
||||
char resp[FB_RESPONSE_SZ+1];
|
||||
int status;
|
||||
|
||||
a = action_list;
|
||||
resp[FB_RESPONSE_SZ] = 0;
|
||||
|
||||
for (a = action_list; a; a = a->next) {
|
||||
if (a->msg) {
|
||||
fprintf(stderr,"%s... ",a->msg);
|
||||
}
|
||||
if (a->op == OP_DOWNLOAD) {
|
||||
status = fb_download_data(usb, a->data, a->size);
|
||||
status = a->func(a, status, status ? fb_get_error() : "");
|
||||
if (status) break;
|
||||
} else if (a->op == OP_COMMAND) {
|
||||
status = fb_command(usb, a->cmd);
|
||||
status = a->func(a, status, status ? fb_get_error() : "");
|
||||
if (status) break;
|
||||
} else if (a->op == OP_QUERY) {
|
||||
status = fb_command_response(usb, a->cmd, resp);
|
||||
status = a->func(a, status, status ? fb_get_error() : resp);
|
||||
if (status) break;
|
||||
} else if (a->op == OP_NOTICE) {
|
||||
fprintf(stderr,"%s\n",(char*)a->data);
|
||||
} else {
|
||||
die("bogus action");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
fastboot/engineering_key.p12
Normal file
BIN
fastboot/engineering_key.p12
Normal file
Binary file not shown.
657
fastboot/fastboot.c
Normal file
657
fastboot/fastboot.c
Normal file
|
@ -0,0 +1,657 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <bootimg.h>
|
||||
#include <zipfile/zipfile.h>
|
||||
|
||||
#include "fastboot.h"
|
||||
|
||||
static usb_handle *usb = 0;
|
||||
static const char *serial = 0;
|
||||
static const char *product = 0;
|
||||
static const char *cmdline = 0;
|
||||
static int wipe_data = 0;
|
||||
|
||||
void die(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr,"error: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr,"\n");
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void get_my_path(char *path);
|
||||
|
||||
char *find_item(const char *item, const char *product)
|
||||
{
|
||||
char *dir;
|
||||
char *fn;
|
||||
char path[PATH_MAX + 128];
|
||||
|
||||
if(!strcmp(item,"boot")) {
|
||||
fn = "boot.img";
|
||||
} else if(!strcmp(item,"recovery")) {
|
||||
fn = "recovery.img";
|
||||
} else if(!strcmp(item,"system")) {
|
||||
fn = "system.img";
|
||||
} else if(!strcmp(item,"userdata")) {
|
||||
fn = "userdata.img";
|
||||
} else if(!strcmp(item,"info")) {
|
||||
fn = "android-info.txt";
|
||||
} else {
|
||||
fprintf(stderr,"unknown partition '%s'\n", item);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(product) {
|
||||
get_my_path(path);
|
||||
sprintf(path + strlen(path),
|
||||
"../../../target/product/%s/%s", product, fn);
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
dir = getenv("ANDROID_PRODUCT_OUT");
|
||||
if((dir == 0) || (dir[0] == 0)) {
|
||||
die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(path, "%s/%s", dir, fn);
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void *load_file(const char *fn, unsigned *_sz);
|
||||
#else
|
||||
void *load_file(const char *fn, unsigned *_sz)
|
||||
{
|
||||
char *data;
|
||||
int sz;
|
||||
int fd;
|
||||
|
||||
data = 0;
|
||||
fd = open(fn, O_RDONLY);
|
||||
if(fd < 0) return 0;
|
||||
|
||||
sz = lseek(fd, 0, SEEK_END);
|
||||
if(sz < 0) goto oops;
|
||||
|
||||
if(lseek(fd, 0, SEEK_SET) != 0) goto oops;
|
||||
|
||||
data = (char*) malloc(sz);
|
||||
if(data == 0) goto oops;
|
||||
|
||||
if(read(fd, data, sz) != sz) goto oops;
|
||||
close(fd);
|
||||
|
||||
if(_sz) *_sz = sz;
|
||||
return data;
|
||||
|
||||
oops:
|
||||
close(fd);
|
||||
if(data != 0) free(data);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int match_fastboot(usb_ifc_info *info)
|
||||
{
|
||||
if((info->dev_vendor != 0x18d1) &&
|
||||
(info->dev_vendor != 0x0bb4)) return -1;
|
||||
if(info->ifc_class != 0xff) return -1;
|
||||
if(info->ifc_subclass != 0x42) return -1;
|
||||
if(info->ifc_protocol != 0x03) return -1;
|
||||
// require matching serial number if a serial number is specified
|
||||
// at the command line with the -s option.
|
||||
if (serial && strcmp(serial, info->serial_number) != 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int list_devices_callback(usb_ifc_info *info)
|
||||
{
|
||||
if (match_fastboot(info) == 0) {
|
||||
char* serial = info->serial_number;
|
||||
if (!serial[0]) {
|
||||
serial = "????????????";
|
||||
}
|
||||
// output compatible with "adb devices"
|
||||
printf("%s\tfastboot\n", serial);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
usb_handle *open_device(void)
|
||||
{
|
||||
static usb_handle *usb = 0;
|
||||
int announce = 1;
|
||||
|
||||
if(usb) return usb;
|
||||
|
||||
for(;;) {
|
||||
usb = usb_open(match_fastboot);
|
||||
if(usb) return usb;
|
||||
if(announce) {
|
||||
announce = 0;
|
||||
fprintf(stderr,"< waiting for device >\n");
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void list_devices(void) {
|
||||
// We don't actually open a USB device here,
|
||||
// just getting our callback called so we can
|
||||
// list all the connected devices.
|
||||
usb_open(list_devices_callback);
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */
|
||||
"usage: fastboot [ <option> ] <command>\n"
|
||||
"\n"
|
||||
"commands:\n"
|
||||
" update <filename> reflash device from update.zip\n"
|
||||
" flashall 'flash boot' + 'flash system'\n"
|
||||
" flash <partition> [ <filename> ] write a file to a flash partition\n"
|
||||
" erase <partition> erase a flash partition\n"
|
||||
" getvar <variable> display a bootloader variable\n"
|
||||
" boot <kernel> [ <ramdisk> ] download and boot kernel\n"
|
||||
" flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n"
|
||||
" devices list all connected devices\n"
|
||||
" reboot reboot device normally\n"
|
||||
" reboot-bootloader reboot device into bootloader\n"
|
||||
"\n"
|
||||
"options:\n"
|
||||
" -w erase userdata and cache\n"
|
||||
" -s <serial number> specify device serial number\n"
|
||||
" -p <product> specify product name\n"
|
||||
" -c <cmdline> override kernel commandline\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void *load_bootable_image(const char *kernel, const char *ramdisk,
|
||||
unsigned *sz, const char *cmdline)
|
||||
{
|
||||
void *kdata = 0, *rdata = 0;
|
||||
unsigned ksize = 0, rsize = 0;
|
||||
void *bdata;
|
||||
unsigned bsize;
|
||||
|
||||
if(kernel == 0) {
|
||||
fprintf(stderr, "no image specified\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
kdata = load_file(kernel, &ksize);
|
||||
if(kdata == 0) {
|
||||
fprintf(stderr, "cannot load '%s'\n", kernel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* is this actually a boot image? */
|
||||
if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
|
||||
if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
|
||||
|
||||
if(ramdisk) {
|
||||
fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*sz = ksize;
|
||||
return kdata;
|
||||
}
|
||||
|
||||
if(ramdisk) {
|
||||
rdata = load_file(ramdisk, &rsize);
|
||||
if(rdata == 0) {
|
||||
fprintf(stderr,"cannot load '%s'\n", ramdisk);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr,"creating boot image...\n");
|
||||
bdata = mkbootimg(kdata, ksize, rdata, rsize, 0, 0, 2048, &bsize);
|
||||
if(bdata == 0) {
|
||||
fprintf(stderr,"failed to create boot.img\n");
|
||||
return 0;
|
||||
}
|
||||
if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
|
||||
fprintf(stderr,"creating boot image - %d bytes\n", bsize);
|
||||
*sz = bsize;
|
||||
|
||||
return bdata;
|
||||
}
|
||||
|
||||
void *unzip_file(zipfile_t zip, const char *name, unsigned *sz)
|
||||
{
|
||||
void *data;
|
||||
zipentry_t entry;
|
||||
unsigned datasz;
|
||||
|
||||
entry = lookup_zipentry(zip, name);
|
||||
if (entry == NULL) {
|
||||
fprintf(stderr, "archive does not contain '%s'\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*sz = get_zipentry_size(entry);
|
||||
|
||||
datasz = *sz * 1.001;
|
||||
data = malloc(datasz);
|
||||
|
||||
if(data == 0) {
|
||||
fprintf(stderr, "failed to allocate %d bytes\n", *sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (decompress_zipentry(entry, data, datasz)) {
|
||||
fprintf(stderr, "failed to unzip '%s' from archive\n", name);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static char *strip(char *s)
|
||||
{
|
||||
int n;
|
||||
while(*s && isspace(*s)) s++;
|
||||
n = strlen(s);
|
||||
while(n-- > 0) {
|
||||
if(!isspace(s[n])) break;
|
||||
s[n] = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#define MAX_OPTIONS 32
|
||||
static int setup_requirement_line(char *name)
|
||||
{
|
||||
char *val[MAX_OPTIONS];
|
||||
const char **out;
|
||||
unsigned n, count;
|
||||
char *x;
|
||||
int invert = 0;
|
||||
|
||||
if (!strncmp(name, "reject ", 7)) {
|
||||
name += 7;
|
||||
invert = 1;
|
||||
} else if (!strncmp(name, "require ", 8)) {
|
||||
name += 8;
|
||||
invert = 0;
|
||||
}
|
||||
|
||||
x = strchr(name, '=');
|
||||
if (x == 0) return 0;
|
||||
*x = 0;
|
||||
val[0] = x + 1;
|
||||
|
||||
for(count = 1; count < MAX_OPTIONS; count++) {
|
||||
x = strchr(val[count - 1],'|');
|
||||
if (x == 0) break;
|
||||
*x = 0;
|
||||
val[count] = x + 1;
|
||||
}
|
||||
|
||||
name = strip(name);
|
||||
for(n = 0; n < count; n++) val[n] = strip(val[n]);
|
||||
|
||||
name = strip(name);
|
||||
if (name == 0) return -1;
|
||||
|
||||
/* work around an unfortunate name mismatch */
|
||||
if (!strcmp(name,"board")) name = "product";
|
||||
|
||||
out = malloc(sizeof(char*) * count);
|
||||
if (out == 0) return -1;
|
||||
|
||||
for(n = 0; n < count; n++) {
|
||||
out[n] = strdup(strip(val[n]));
|
||||
if (out[n] == 0) return -1;
|
||||
}
|
||||
|
||||
fb_queue_require(name, invert, n, out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_requirements(char *data, unsigned sz)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = data;
|
||||
while (sz-- > 0) {
|
||||
if(*s == '\n') {
|
||||
*s++ = 0;
|
||||
if (setup_requirement_line(data)) {
|
||||
die("out of memory");
|
||||
}
|
||||
data = s;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queue_info_dump(void)
|
||||
{
|
||||
fb_queue_notice("--------------------------------------------");
|
||||
fb_queue_display("version-bootloader", "Bootloader Version...");
|
||||
fb_queue_display("version-baseband", "Baseband Version.....");
|
||||
fb_queue_display("serialno", "Serial Number........");
|
||||
fb_queue_notice("--------------------------------------------");
|
||||
}
|
||||
|
||||
void do_update_signature(zipfile_t zip, char *fn)
|
||||
{
|
||||
void *data;
|
||||
unsigned sz;
|
||||
data = unzip_file(zip, fn, &sz);
|
||||
if (data == 0) return;
|
||||
fb_queue_download("signature", data, sz);
|
||||
fb_queue_command("signature", "installing signature");
|
||||
}
|
||||
|
||||
void do_update(char *fn)
|
||||
{
|
||||
void *zdata;
|
||||
unsigned zsize;
|
||||
void *data;
|
||||
unsigned sz;
|
||||
zipfile_t zip;
|
||||
|
||||
queue_info_dump();
|
||||
|
||||
zdata = load_file(fn, &zsize);
|
||||
if (zdata == 0) die("failed to load '%s'", fn);
|
||||
|
||||
zip = init_zipfile(zdata, zsize);
|
||||
if(zip == 0) die("failed to access zipdata in '%s'");
|
||||
|
||||
data = unzip_file(zip, "android-info.txt", &sz);
|
||||
if (data == 0) {
|
||||
char *tmp;
|
||||
/* fallback for older zipfiles */
|
||||
data = unzip_file(zip, "android-product.txt", &sz);
|
||||
if ((data == 0) || (sz < 1)) {
|
||||
die("update package has no android-info.txt or android-product.txt");
|
||||
}
|
||||
tmp = malloc(sz + 128);
|
||||
if (tmp == 0) die("out of memory");
|
||||
sprintf(tmp,"board=%sversion-baseband=0.66.04.19\n",(char*)data);
|
||||
data = tmp;
|
||||
sz = strlen(tmp);
|
||||
}
|
||||
|
||||
setup_requirements(data, sz);
|
||||
|
||||
data = unzip_file(zip, "boot.img", &sz);
|
||||
if (data == 0) die("update package missing boot.img");
|
||||
do_update_signature(zip, "boot.sig");
|
||||
fb_queue_flash("boot", data, sz);
|
||||
|
||||
data = unzip_file(zip, "recovery.img", &sz);
|
||||
if (data != 0) {
|
||||
do_update_signature(zip, "recovery.sig");
|
||||
fb_queue_flash("recovery", data, sz);
|
||||
}
|
||||
|
||||
data = unzip_file(zip, "system.img", &sz);
|
||||
if (data == 0) die("update package missing system.img");
|
||||
do_update_signature(zip, "system.sig");
|
||||
fb_queue_flash("system", data, sz);
|
||||
}
|
||||
|
||||
void do_send_signature(char *fn)
|
||||
{
|
||||
void *data;
|
||||
unsigned sz;
|
||||
char *xtn;
|
||||
|
||||
xtn = strrchr(fn, '.');
|
||||
if (!xtn) return;
|
||||
if (strcmp(xtn, ".img")) return;
|
||||
|
||||
strcpy(xtn,".sig");
|
||||
data = load_file(fn, &sz);
|
||||
strcpy(xtn,".img");
|
||||
if (data == 0) return;
|
||||
fb_queue_download("signature", data, sz);
|
||||
fb_queue_command("signature", "installing signature");
|
||||
}
|
||||
|
||||
void do_flashall(void)
|
||||
{
|
||||
char *fname;
|
||||
void *data;
|
||||
unsigned sz;
|
||||
|
||||
queue_info_dump();
|
||||
|
||||
fname = find_item("info", product);
|
||||
if (fname == 0) die("cannot find android-info.txt");
|
||||
data = load_file(fname, &sz);
|
||||
if (data == 0) die("could not load android-info.txt");
|
||||
setup_requirements(data, sz);
|
||||
|
||||
fname = find_item("boot", product);
|
||||
data = load_file(fname, &sz);
|
||||
if (data == 0) die("could not load boot.img");
|
||||
do_send_signature(fname);
|
||||
fb_queue_flash("boot", data, sz);
|
||||
|
||||
fname = find_item("recovery", product);
|
||||
data = load_file(fname, &sz);
|
||||
if (data != 0) {
|
||||
do_send_signature(fname);
|
||||
fb_queue_flash("recovery", data, sz);
|
||||
}
|
||||
|
||||
fname = find_item("system", product);
|
||||
data = load_file(fname, &sz);
|
||||
if (data == 0) die("could not load system.img");
|
||||
do_send_signature(fname);
|
||||
fb_queue_flash("system", data, sz);
|
||||
}
|
||||
|
||||
#define skip(n) do { argc -= (n); argv += (n); } while (0)
|
||||
#define require(n) do { if (argc < (n)) usage(); } while (0)
|
||||
|
||||
int do_oem_command(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
char command[256];
|
||||
if (argc <= 1) return 0;
|
||||
|
||||
command[0] = 0;
|
||||
while(1) {
|
||||
strcat(command,*argv);
|
||||
skip(1);
|
||||
if(argc == 0) break;
|
||||
strcat(command," ");
|
||||
}
|
||||
|
||||
fb_queue_command(command,"");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int wants_wipe = 0;
|
||||
int wants_reboot = 0;
|
||||
int wants_reboot_bootloader = 0;
|
||||
void *data;
|
||||
unsigned sz;
|
||||
|
||||
skip(1);
|
||||
if (argc == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(*argv, "devices")) {
|
||||
list_devices();
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (argc > 0) {
|
||||
if(!strcmp(*argv, "-w")) {
|
||||
wants_wipe = 1;
|
||||
skip(1);
|
||||
} else if(!strcmp(*argv, "-s")) {
|
||||
require(2);
|
||||
serial = argv[1];
|
||||
skip(2);
|
||||
} else if(!strcmp(*argv, "-p")) {
|
||||
require(2);
|
||||
product = argv[1];
|
||||
skip(2);
|
||||
} else if(!strcmp(*argv, "-c")) {
|
||||
require(2);
|
||||
cmdline = argv[1];
|
||||
skip(2);
|
||||
} else if(!strcmp(*argv, "getvar")) {
|
||||
require(2);
|
||||
fb_queue_display(argv[1], argv[1]);
|
||||
skip(2);
|
||||
} else if(!strcmp(*argv, "erase")) {
|
||||
require(2);
|
||||
fb_queue_erase(argv[1]);
|
||||
skip(2);
|
||||
} else if(!strcmp(*argv, "signature")) {
|
||||
require(2);
|
||||
data = load_file(argv[1], &sz);
|
||||
if (data == 0) die("could not load '%s'", argv[1]);
|
||||
if (sz != 256) die("signature must be 256 bytes");
|
||||
fb_queue_download("signature", data, sz);
|
||||
fb_queue_command("signature", "installing signature");
|
||||
skip(2);
|
||||
} else if(!strcmp(*argv, "reboot")) {
|
||||
wants_reboot = 1;
|
||||
skip(1);
|
||||
} else if(!strcmp(*argv, "reboot-bootloader")) {
|
||||
wants_reboot_bootloader = 1;
|
||||
skip(1);
|
||||
} else if(!strcmp(*argv, "boot")) {
|
||||
char *kname = 0;
|
||||
char *rname = 0;
|
||||
skip(1);
|
||||
if (argc > 0) {
|
||||
kname = argv[0];
|
||||
skip(1);
|
||||
}
|
||||
if (argc > 0) {
|
||||
rname = argv[0];
|
||||
skip(1);
|
||||
}
|
||||
data = load_bootable_image(kname, rname, &sz, cmdline);
|
||||
if (data == 0) return 1;
|
||||
fb_queue_download("boot.img", data, sz);
|
||||
fb_queue_command("boot", "booting");
|
||||
} else if(!strcmp(*argv, "flash")) {
|
||||
char *pname = argv[1];
|
||||
char *fname = 0;
|
||||
require(2);
|
||||
if (argc > 2) {
|
||||
fname = argv[2];
|
||||
skip(3);
|
||||
} else {
|
||||
fname = find_item(pname, product);
|
||||
skip(2);
|
||||
}
|
||||
if (fname == 0) die("cannot determine image filename for '%s'", pname);
|
||||
data = load_file(fname, &sz);
|
||||
if (data == 0) die("cannot load '%s'\n", fname);
|
||||
fb_queue_flash(pname, data, sz);
|
||||
} else if(!strcmp(*argv, "flash:raw")) {
|
||||
char *pname = argv[1];
|
||||
char *kname = argv[2];
|
||||
char *rname = 0;
|
||||
require(3);
|
||||
if(argc > 3) {
|
||||
rname = argv[3];
|
||||
skip(4);
|
||||
} else {
|
||||
skip(3);
|
||||
}
|
||||
data = load_bootable_image(kname, rname, &sz, cmdline);
|
||||
if (data == 0) die("cannot load bootable image");
|
||||
fb_queue_flash(pname, data, sz);
|
||||
} else if(!strcmp(*argv, "flashall")) {
|
||||
skip(1);
|
||||
do_flashall();
|
||||
wants_reboot = 1;
|
||||
} else if(!strcmp(*argv, "update")) {
|
||||
if (argc > 1) {
|
||||
do_update(argv[1]);
|
||||
skip(2);
|
||||
} else {
|
||||
do_update("update.zip");
|
||||
skip(1);
|
||||
}
|
||||
wants_reboot = 1;
|
||||
} else if(!strcmp(*argv, "oem")) {
|
||||
argc = do_oem_command(argc, argv);
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (wants_wipe) {
|
||||
fb_queue_erase("userdata");
|
||||
fb_queue_erase("cache");
|
||||
}
|
||||
if (wants_reboot) {
|
||||
fb_queue_reboot();
|
||||
} else if (wants_reboot_bootloader) {
|
||||
fb_queue_command("reboot-bootloader", "rebooting into bootloader");
|
||||
}
|
||||
|
||||
usb = open_device();
|
||||
|
||||
fb_execute_queue(usb);
|
||||
return 0;
|
||||
}
|
57
fastboot/fastboot.h
Normal file
57
fastboot/fastboot.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _FASTBOOT_H_
|
||||
#define _FASTBOOT_H_
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
/* protocol.c - fastboot protocol */
|
||||
int fb_command(usb_handle *usb, const char *cmd);
|
||||
int fb_command_response(usb_handle *usb, const char *cmd, char *response);
|
||||
int fb_download_data(usb_handle *usb, const void *data, unsigned size);
|
||||
char *fb_get_error(void);
|
||||
|
||||
#define FB_COMMAND_SZ 64
|
||||
#define FB_RESPONSE_SZ 64
|
||||
|
||||
/* engine.c - high level command queue engine */
|
||||
void fb_queue_flash(const char *ptn, void *data, unsigned sz);;
|
||||
void fb_queue_erase(const char *ptn);
|
||||
void fb_queue_require(const char *var, int invert, unsigned nvalues, const char **value);
|
||||
void fb_queue_display(const char *var, const char *prettyname);
|
||||
void fb_queue_reboot(void);
|
||||
void fb_queue_command(const char *cmd, const char *msg);
|
||||
void fb_queue_download(const char *name, void *data, unsigned size);
|
||||
void fb_queue_notice(const char *notice);
|
||||
void fb_execute_queue(usb_handle *usb);
|
||||
|
||||
/* util stuff */
|
||||
void die(const char *fmt, ...);
|
||||
|
||||
#endif
|
25
fastboot/genkey.sh
Executable file
25
fastboot/genkey.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 2 ]
|
||||
then
|
||||
echo "Usage: $0 alias \"pass phrase\""
|
||||
exit -1
|
||||
fi
|
||||
|
||||
# Generate a 2048 bit RSA key with public exponent 3.
|
||||
# Encrypt private key with provided password.
|
||||
openssl genrsa -3 -out $1.pem -passout pass:"$2" 2048
|
||||
|
||||
# Create a self-signed cert for this key.
|
||||
openssl req -new -x509 -key $1.pem -passin pass:"$2" \
|
||||
-out $1-cert.pem \
|
||||
-batch -days 10000
|
||||
|
||||
# Create a PKCS12 store containing the generated private key.
|
||||
# Protect the keystore and the private key with the provided password.
|
||||
openssl pkcs12 -export -in $1-cert.pem -inkey $1.pem -passin pass:"$2" \
|
||||
-out $1.p12 -name $1 -passout pass:"$2"
|
||||
|
||||
rm $1.pem
|
||||
rm $1-cert.pem
|
||||
|
9
fastboot/p12topem.sh
Executable file
9
fastboot/p12topem.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 2 ]
|
||||
then
|
||||
echo "Usage: $0 alias passphrase"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem
|
181
fastboot/protocol.c
Normal file
181
fastboot/protocol.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "fastboot.h"
|
||||
|
||||
static char ERROR[128];
|
||||
|
||||
char *fb_get_error(void)
|
||||
{
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
static int check_response(usb_handle *usb, unsigned size,
|
||||
unsigned data_okay, char *response)
|
||||
{
|
||||
unsigned char status[65];
|
||||
int r;
|
||||
|
||||
for(;;) {
|
||||
r = usb_read(usb, status, 64);
|
||||
if(r < 0) {
|
||||
sprintf(ERROR, "status read failed (%s)", strerror(errno));
|
||||
usb_close(usb);
|
||||
return -1;
|
||||
}
|
||||
status[r] = 0;
|
||||
|
||||
if(r < 4) {
|
||||
sprintf(ERROR, "status malformed (%d bytes)", r);
|
||||
usb_close(usb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!memcmp(status, "INFO", 4)) {
|
||||
fprintf(stderr,"%s\n", status);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!memcmp(status, "OKAY", 4)) {
|
||||
if(response) {
|
||||
strcpy(response, (char*) status + 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!memcmp(status, "FAIL", 4)) {
|
||||
if(r > 4) {
|
||||
sprintf(ERROR, "remote: %s", status + 4);
|
||||
} else {
|
||||
strcpy(ERROR, "remote failure");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!memcmp(status, "DATA", 4) && data_okay){
|
||||
unsigned dsize = strtoul((char*) status + 4, 0, 16);
|
||||
if(dsize > size) {
|
||||
strcpy(ERROR, "data size too large");
|
||||
usb_close(usb);
|
||||
return -1;
|
||||
}
|
||||
return dsize;
|
||||
}
|
||||
|
||||
strcpy(ERROR,"unknown status code");
|
||||
usb_close(usb);
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _command_send(usb_handle *usb, const char *cmd,
|
||||
const void *data, unsigned size,
|
||||
char *response)
|
||||
{
|
||||
int cmdsize = strlen(cmd);
|
||||
int r;
|
||||
|
||||
if(response) {
|
||||
response[0] = 0;
|
||||
}
|
||||
|
||||
if(cmdsize > 64) {
|
||||
sprintf(ERROR,"command too large");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(usb_write(usb, cmd, cmdsize) != cmdsize) {
|
||||
sprintf(ERROR,"command write failed (%s)", strerror(errno));
|
||||
usb_close(usb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(data == 0) {
|
||||
return check_response(usb, size, 0, response);
|
||||
}
|
||||
|
||||
r = check_response(usb, size, 1, 0);
|
||||
if(r < 0) {
|
||||
return -1;
|
||||
}
|
||||
size = r;
|
||||
|
||||
if(size) {
|
||||
r = usb_write(usb, data, size);
|
||||
if(r < 0) {
|
||||
sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
|
||||
usb_close(usb);
|
||||
return -1;
|
||||
}
|
||||
if(r != ((int) size)) {
|
||||
sprintf(ERROR, "data transfer failure (short transfer)");
|
||||
usb_close(usb);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
r = check_response(usb, 0, 0, 0);
|
||||
if(r < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
int fb_command(usb_handle *usb, const char *cmd)
|
||||
{
|
||||
return _command_send(usb, cmd, 0, 0, 0);
|
||||
}
|
||||
|
||||
int fb_command_response(usb_handle *usb, const char *cmd, char *response)
|
||||
{
|
||||
return _command_send(usb, cmd, 0, 0, response);
|
||||
}
|
||||
|
||||
int fb_download_data(usb_handle *usb, const void *data, unsigned size)
|
||||
{
|
||||
char cmd[64];
|
||||
int r;
|
||||
|
||||
sprintf(cmd, "download:%08x", size);
|
||||
r = _command_send(usb, cmd, data, size, 0);
|
||||
|
||||
if(r < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
10
fastboot/signfile.sh
Executable file
10
fastboot/signfile.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 3 ]
|
||||
then
|
||||
echo "Usage: $0 alias filename passpharse"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign
|
||||
|
64
fastboot/usb.h
Normal file
64
fastboot/usb.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _USB_H_
|
||||
#define _USB_H_
|
||||
|
||||
typedef struct usb_handle usb_handle;
|
||||
|
||||
typedef struct usb_ifc_info usb_ifc_info;
|
||||
|
||||
struct usb_ifc_info
|
||||
{
|
||||
/* from device descriptor */
|
||||
unsigned short dev_vendor;
|
||||
unsigned short dev_product;
|
||||
|
||||
unsigned char dev_class;
|
||||
unsigned char dev_subclass;
|
||||
unsigned char dev_protocol;
|
||||
|
||||
unsigned char ifc_class;
|
||||
unsigned char ifc_subclass;
|
||||
unsigned char ifc_protocol;
|
||||
|
||||
unsigned char has_bulk_in;
|
||||
unsigned char has_bulk_out;
|
||||
|
||||
char serial_number[256];
|
||||
};
|
||||
|
||||
typedef int (*ifc_match_func)(usb_ifc_info *ifc);
|
||||
|
||||
usb_handle *usb_open(ifc_match_func callback);
|
||||
int usb_close(usb_handle *h);
|
||||
int usb_read(usb_handle *h, void *_data, int len);
|
||||
int usb_write(usb_handle *h, const void *_data, int len);
|
||||
|
||||
|
||||
#endif
|
373
fastboot/usb_linux.c
Normal file
373
fastboot/usb_linux.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
|
||||
#include <linux/usb/ch9.h>
|
||||
#else
|
||||
#include <linux/usb_ch9.h>
|
||||
#endif
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
#if TRACE_USB
|
||||
#define DBG1(x...) fprintf(stderr, x)
|
||||
#define DBG(x...) fprintf(stderr, x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#define DBG1(x...)
|
||||
#endif
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
char fname[64];
|
||||
int desc;
|
||||
unsigned char ep_in;
|
||||
unsigned char ep_out;
|
||||
};
|
||||
|
||||
static inline int badname(const char *name)
|
||||
{
|
||||
while(*name) {
|
||||
if(!isdigit(*name++)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check(void *_desc, int len, unsigned type, int size)
|
||||
{
|
||||
unsigned char *desc = _desc;
|
||||
|
||||
if(len < size) return -1;
|
||||
if(desc[0] < size) return -1;
|
||||
if(desc[0] > len) return -1;
|
||||
if(desc[1] != type) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filter_usb_device(int fd, char *ptr, int len, ifc_match_func callback,
|
||||
int *ept_in_id, int *ept_out_id, int *ifc_id)
|
||||
{
|
||||
struct usb_device_descriptor *dev;
|
||||
struct usb_config_descriptor *cfg;
|
||||
struct usb_interface_descriptor *ifc;
|
||||
struct usb_endpoint_descriptor *ept;
|
||||
struct usb_ifc_info info;
|
||||
|
||||
int in, out;
|
||||
unsigned i;
|
||||
unsigned e;
|
||||
|
||||
if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
|
||||
return -1;
|
||||
dev = (void*) ptr;
|
||||
len -= dev->bLength;
|
||||
ptr += dev->bLength;
|
||||
|
||||
if(check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE))
|
||||
return -1;
|
||||
cfg = (void*) ptr;
|
||||
len -= cfg->bLength;
|
||||
ptr += cfg->bLength;
|
||||
|
||||
info.dev_vendor = dev->idVendor;
|
||||
info.dev_product = dev->idProduct;
|
||||
info.dev_class = dev->bDeviceClass;
|
||||
info.dev_subclass = dev->bDeviceSubClass;
|
||||
info.dev_protocol = dev->bDeviceProtocol;
|
||||
|
||||
// read device serial number (if there is one)
|
||||
info.serial_number[0] = 0;
|
||||
if (dev->iSerialNumber) {
|
||||
struct usbdevfs_ctrltransfer ctrl;
|
||||
__u16 buffer[128];
|
||||
int result;
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
|
||||
ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
|
||||
ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber;
|
||||
ctrl.wIndex = 0;
|
||||
ctrl.wLength = sizeof(buffer);
|
||||
ctrl.data = buffer;
|
||||
|
||||
result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
|
||||
if (result > 0) {
|
||||
int i;
|
||||
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
|
||||
result /= 2;
|
||||
for (i = 1; i < result; i++)
|
||||
info.serial_number[i - 1] = buffer[i];
|
||||
info.serial_number[i - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < cfg->bNumInterfaces; i++) {
|
||||
if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE))
|
||||
return -1;
|
||||
ifc = (void*) ptr;
|
||||
len -= ifc->bLength;
|
||||
ptr += ifc->bLength;
|
||||
|
||||
in = -1;
|
||||
out = -1;
|
||||
info.ifc_class = ifc->bInterfaceClass;
|
||||
info.ifc_subclass = ifc->bInterfaceSubClass;
|
||||
info.ifc_protocol = ifc->bInterfaceProtocol;
|
||||
|
||||
for(e = 0; e < ifc->bNumEndpoints; e++) {
|
||||
if(check(ptr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE))
|
||||
return -1;
|
||||
ept = (void*) ptr;
|
||||
len -= ept->bLength;
|
||||
ptr += ept->bLength;
|
||||
|
||||
if((ept->bmAttributes & 0x03) != 0x02)
|
||||
continue;
|
||||
|
||||
if(ept->bEndpointAddress & 0x80) {
|
||||
in = ept->bEndpointAddress;
|
||||
} else {
|
||||
out = ept->bEndpointAddress;
|
||||
}
|
||||
}
|
||||
|
||||
info.has_bulk_in = (in != -1);
|
||||
info.has_bulk_out = (out != -1);
|
||||
|
||||
if(callback(&info) == 0) {
|
||||
*ept_in_id = in;
|
||||
*ept_out_id = out;
|
||||
*ifc_id = ifc->bInterfaceNumber;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
|
||||
{
|
||||
usb_handle *usb = 0;
|
||||
char busname[64], devname[64];
|
||||
char desc[1024];
|
||||
int n, in, out, ifc;
|
||||
|
||||
DIR *busdir, *devdir;
|
||||
struct dirent *de;
|
||||
int fd;
|
||||
|
||||
busdir = opendir(base);
|
||||
if(busdir == 0) return 0;
|
||||
|
||||
while((de = readdir(busdir)) && (usb == 0)) {
|
||||
if(badname(de->d_name)) continue;
|
||||
|
||||
sprintf(busname, "%s/%s", base, de->d_name);
|
||||
devdir = opendir(busname);
|
||||
if(devdir == 0) continue;
|
||||
|
||||
// DBG("[ scanning %s ]\n", busname);
|
||||
while((de = readdir(devdir)) && (usb == 0)) {
|
||||
|
||||
if(badname(de->d_name)) continue;
|
||||
sprintf(devname, "%s/%s", busname, de->d_name);
|
||||
|
||||
// DBG("[ scanning %s ]\n", devname);
|
||||
if((fd = open(devname, O_RDWR)) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n = read(fd, desc, sizeof(desc));
|
||||
|
||||
if(filter_usb_device(fd, desc, n, callback, &in, &out, &ifc) == 0){
|
||||
usb = calloc(1, sizeof(usb_handle));
|
||||
strcpy(usb->fname, devname);
|
||||
usb->ep_in = in;
|
||||
usb->ep_out = out;
|
||||
usb->desc = fd;
|
||||
|
||||
n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
|
||||
if(n != 0) {
|
||||
close(fd);
|
||||
free(usb);
|
||||
usb = 0;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
closedir(devdir);
|
||||
}
|
||||
closedir(busdir);
|
||||
|
||||
return usb;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *h, const void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
unsigned count = 0;
|
||||
struct usbdevfs_bulktransfer bulk;
|
||||
int n;
|
||||
|
||||
if(h->ep_out == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(len == 0) {
|
||||
bulk.ep = h->ep_out;
|
||||
bulk.len = 0;
|
||||
bulk.data = data;
|
||||
bulk.timeout = 0;
|
||||
|
||||
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
|
||||
if(n != 0) {
|
||||
fprintf(stderr,"ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
while(len > 0) {
|
||||
int xfer;
|
||||
xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
bulk.ep = h->ep_out;
|
||||
bulk.len = xfer;
|
||||
bulk.data = data;
|
||||
bulk.timeout = 0;
|
||||
|
||||
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
|
||||
if(n != xfer) {
|
||||
DBG("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
count += xfer;
|
||||
len -= xfer;
|
||||
data += xfer;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *_data, int len)
|
||||
{
|
||||
unsigned char *data = (unsigned char*) _data;
|
||||
unsigned count = 0;
|
||||
struct usbdevfs_bulktransfer bulk;
|
||||
int n;
|
||||
|
||||
if(h->ep_in == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
bulk.ep = h->ep_in;
|
||||
bulk.len = xfer;
|
||||
bulk.data = data;
|
||||
bulk.timeout = 0;
|
||||
|
||||
DBG("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
|
||||
n = ioctl(h->desc, USBDEVFS_BULK, &bulk);
|
||||
DBG("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
|
||||
|
||||
if(n < 0) {
|
||||
DBG1("ERROR: n = %d, errno = %d (%s)\n",
|
||||
n, errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
count += n;
|
||||
len -= n;
|
||||
data += n;
|
||||
|
||||
if(n < xfer) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *h)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = h->desc;
|
||||
h->desc = -1;
|
||||
if(fd >= 0) {
|
||||
close(fd);
|
||||
DBG("[ usb closed %d ]\n", fd);
|
||||
}
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *h)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = h->desc;
|
||||
h->desc = -1;
|
||||
if(fd >= 0) {
|
||||
close(fd);
|
||||
DBG("[ usb closed %d ]\n", fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
usb_handle *usb_open(ifc_match_func callback)
|
||||
{
|
||||
return find_usb_device("/dev/bus/usb", callback);
|
||||
}
|
||||
|
||||
|
538
fastboot/usb_osx.c
Normal file
538
fastboot/usb_osx.c
Normal file
|
@ -0,0 +1,538 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/IOMessage.h>
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
|
||||
/*
|
||||
* Internal helper functions and associated definitions.
|
||||
*/
|
||||
|
||||
#if TRACE_USB
|
||||
#define WARN(x...) fprintf(stderr, x)
|
||||
#else
|
||||
#define WARN(x...)
|
||||
#endif
|
||||
|
||||
#define ERR(x...) fprintf(stderr, "ERROR: " x)
|
||||
|
||||
/** An open usb device */
|
||||
struct usb_handle
|
||||
{
|
||||
int success;
|
||||
ifc_match_func callback;
|
||||
usb_ifc_info info;
|
||||
|
||||
UInt8 bulkIn;
|
||||
UInt8 bulkOut;
|
||||
IOUSBInterfaceInterface190 **interface;
|
||||
unsigned int zero_mask;
|
||||
};
|
||||
|
||||
/** Try out all the interfaces and see if there's a match. Returns 0 on
|
||||
* success, -1 on failure. */
|
||||
static int try_interfaces(IOUSBDeviceInterface **dev, usb_handle *handle) {
|
||||
IOReturn kr;
|
||||
IOUSBFindInterfaceRequest request;
|
||||
io_iterator_t iterator;
|
||||
io_service_t usbInterface;
|
||||
IOCFPlugInInterface **plugInInterface;
|
||||
IOUSBInterfaceInterface190 **interface = NULL;
|
||||
HRESULT result;
|
||||
SInt32 score;
|
||||
UInt8 interfaceNumEndpoints;
|
||||
UInt8 endpoint;
|
||||
UInt8 configuration;
|
||||
|
||||
// Placing the constant KIOUSBFindInterfaceDontCare into the following
|
||||
// fields of the IOUSBFindInterfaceRequest structure will allow us to
|
||||
// find all of the interfaces
|
||||
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
|
||||
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
|
||||
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
|
||||
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
|
||||
|
||||
// SetConfiguration will kill an existing UMS connection, so let's
|
||||
// not do this if not necessary.
|
||||
configuration = 0;
|
||||
(*dev)->GetConfiguration(dev, &configuration);
|
||||
if (configuration != 1)
|
||||
(*dev)->SetConfiguration(dev, 1);
|
||||
|
||||
// Get an iterator for the interfaces on the device
|
||||
kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);
|
||||
|
||||
if (kr != 0) {
|
||||
ERR("Couldn't create a device interface iterator: (%08x)\n", kr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((usbInterface = IOIteratorNext(iterator))) {
|
||||
// Create an intermediate plugin
|
||||
kr = IOCreatePlugInInterfaceForService(
|
||||
usbInterface,
|
||||
kIOUSBInterfaceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugInInterface,
|
||||
&score);
|
||||
|
||||
// No longer need the usbInterface object now that we have the plugin
|
||||
(void) IOObjectRelease(usbInterface);
|
||||
|
||||
if ((kr != 0) || (!plugInInterface)) {
|
||||
WARN("Unable to create plugin (%08x)\n", kr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now create the interface interface for the interface
|
||||
result = (*plugInInterface)->QueryInterface(
|
||||
plugInInterface,
|
||||
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
|
||||
(LPVOID) &interface);
|
||||
|
||||
// No longer need the intermediate plugin
|
||||
(*plugInInterface)->Release(plugInInterface);
|
||||
|
||||
if (result || !interface) {
|
||||
ERR("Couldn't create interface interface: (%08x)\n",
|
||||
(unsigned int) result);
|
||||
// continue so we can try the next interface
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now open the interface. This will cause the pipes
|
||||
* associated with the endpoints in the interface descriptor
|
||||
* to be instantiated.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: Earlier comments here indicated that it was a bad
|
||||
* idea to just open any interface, because opening "mass
|
||||
* storage endpoints" is bad. However, the only way to find
|
||||
* out if an interface does bulk in or out is to open it, and
|
||||
* the framework in this application wants to be told about
|
||||
* bulk in / out before deciding whether it actually wants to
|
||||
* use the interface. Maybe something needs to be done about
|
||||
* this situation.
|
||||
*/
|
||||
|
||||
kr = (*interface)->USBInterfaceOpen(interface);
|
||||
|
||||
if (kr != 0) {
|
||||
WARN("Could not open interface: (%08x)\n", kr);
|
||||
(void) (*interface)->Release(interface);
|
||||
// continue so we can try the next interface
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the number of endpoints associated with this interface.
|
||||
kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
|
||||
|
||||
if (kr != 0) {
|
||||
ERR("Unable to get number of endpoints: (%08x)\n", kr);
|
||||
goto next_interface;
|
||||
}
|
||||
|
||||
// Get interface class, subclass and protocol
|
||||
if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||
|
||||
(*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||
|
||||
(*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)
|
||||
{
|
||||
ERR("Unable to get interface class, subclass and protocol\n");
|
||||
goto next_interface;
|
||||
}
|
||||
|
||||
handle->info.has_bulk_in = 0;
|
||||
handle->info.has_bulk_out = 0;
|
||||
|
||||
// Iterate over the endpoints for this interface and see if there
|
||||
// are any that do bulk in/out.
|
||||
for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
|
||||
UInt8 transferType;
|
||||
UInt16 maxPacketSize;
|
||||
UInt8 interval;
|
||||
UInt8 number;
|
||||
UInt8 direction;
|
||||
|
||||
kr = (*interface)->GetPipeProperties(interface, endpoint,
|
||||
&direction,
|
||||
&number, &transferType, &maxPacketSize, &interval);
|
||||
|
||||
if (kr == 0) {
|
||||
if (transferType != kUSBBulk) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (direction == kUSBIn) {
|
||||
handle->info.has_bulk_in = 1;
|
||||
handle->bulkIn = endpoint;
|
||||
} else if (direction == kUSBOut) {
|
||||
handle->info.has_bulk_out = 1;
|
||||
handle->bulkOut = endpoint;
|
||||
}
|
||||
|
||||
if (handle->info.ifc_protocol == 0x01) {
|
||||
handle->zero_mask = maxPacketSize - 1;
|
||||
}
|
||||
} else {
|
||||
ERR("could not get pipe properties\n");
|
||||
}
|
||||
|
||||
if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle->callback(&handle->info) == 0) {
|
||||
handle->interface = interface;
|
||||
handle->success = 1;
|
||||
|
||||
/*
|
||||
* Clear both the endpoints, because it has been observed
|
||||
* that the Mac may otherwise (incorrectly) start out with
|
||||
* them in bad state.
|
||||
*/
|
||||
|
||||
if (handle->info.has_bulk_in) {
|
||||
kr = (*interface)->ClearPipeStallBothEnds(interface,
|
||||
handle->bulkIn);
|
||||
if (kr != 0) {
|
||||
ERR("could not clear input pipe; result %d", kr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle->info.has_bulk_out) {
|
||||
kr = (*interface)->ClearPipeStallBothEnds(interface,
|
||||
handle->bulkOut);
|
||||
if (kr != 0) {
|
||||
ERR("could not clear output pipe; result %d", kr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
next_interface:
|
||||
(*interface)->USBInterfaceClose(interface);
|
||||
(*interface)->Release(interface);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Try out the given device and see if there's a match. Returns 0 on
|
||||
* success, -1 on failure.
|
||||
*/
|
||||
static int try_device(io_service_t device, usb_handle *handle) {
|
||||
kern_return_t kr;
|
||||
IOCFPlugInInterface **plugin = NULL;
|
||||
IOUSBDeviceInterface182 **dev = NULL;
|
||||
SInt32 score;
|
||||
HRESULT result;
|
||||
UInt8 serialIndex;
|
||||
|
||||
// Create an intermediate plugin.
|
||||
kr = IOCreatePlugInInterfaceForService(device,
|
||||
kIOUSBDeviceUserClientTypeID,
|
||||
kIOCFPlugInInterfaceID,
|
||||
&plugin, &score);
|
||||
|
||||
if ((kr != 0) || (plugin == NULL)) {
|
||||
ERR("Unable to create a plug-in (%08x)\n", kr);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Now create the device interface.
|
||||
result = (*plugin)->QueryInterface(plugin,
|
||||
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
|
||||
if ((result != 0) || (dev == NULL)) {
|
||||
ERR("Couldn't create a device interface (%08x)\n", (int) result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't need the intermediate interface after the device interface
|
||||
* is created.
|
||||
*/
|
||||
IODestroyPlugInInterface(plugin);
|
||||
|
||||
// So, we have a device, finally. Grab its vitals.
|
||||
|
||||
kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
|
||||
if (kr != 0) {
|
||||
ERR("GetDeviceVendor");
|
||||
goto error;
|
||||
}
|
||||
|
||||
kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);
|
||||
if (kr != 0) {
|
||||
ERR("GetDeviceProduct");
|
||||
goto error;
|
||||
}
|
||||
|
||||
kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);
|
||||
if (kr != 0) {
|
||||
ERR("GetDeviceClass");
|
||||
goto error;
|
||||
}
|
||||
|
||||
kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);
|
||||
if (kr != 0) {
|
||||
ERR("GetDeviceSubClass");
|
||||
goto error;
|
||||
}
|
||||
|
||||
kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);
|
||||
if (kr != 0) {
|
||||
ERR("GetDeviceProtocol");
|
||||
goto error;
|
||||
}
|
||||
|
||||
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
|
||||
|
||||
if (serialIndex > 0) {
|
||||
IOUSBDevRequest req;
|
||||
UInt16 buffer[256];
|
||||
|
||||
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
|
||||
req.bRequest = kUSBRqGetDescriptor;
|
||||
req.wValue = (kUSBStringDesc << 8) | serialIndex;
|
||||
req.wIndex = 0;
|
||||
req.pData = buffer;
|
||||
req.wLength = sizeof(buffer);
|
||||
kr = (*dev)->DeviceRequest(dev, &req);
|
||||
|
||||
if (kr == kIOReturnSuccess && req.wLenDone > 0) {
|
||||
int i, count;
|
||||
|
||||
// skip first word, and copy the rest to the serial string, changing shorts to bytes.
|
||||
count = (req.wLenDone - 1) / 2;
|
||||
for (i = 0; i < count; i++)
|
||||
handle->info.serial_number[i] = buffer[i + 1];
|
||||
handle->info.serial_number[i] = 0;
|
||||
}
|
||||
} else {
|
||||
// device has no serial number
|
||||
handle->info.serial_number[0] = 0;
|
||||
}
|
||||
|
||||
if (try_interfaces(dev, handle)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
(*dev)->Release(dev);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
||||
if (dev != NULL) {
|
||||
(*dev)->Release(dev);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/** Initializes the USB system. Returns 0 on success, -1 on error. */
|
||||
static int init_usb(ifc_match_func callback, usb_handle **handle) {
|
||||
int ret = -1;
|
||||
CFMutableDictionaryRef matchingDict;
|
||||
kern_return_t result;
|
||||
io_iterator_t iterator;
|
||||
usb_handle h;
|
||||
|
||||
h.success = 0;
|
||||
h.callback = callback;
|
||||
|
||||
/*
|
||||
* Create our matching dictionary to find appropriate devices.
|
||||
* IOServiceAddMatchingNotification consumes the reference, so we
|
||||
* do not need to release it.
|
||||
*/
|
||||
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||
|
||||
if (matchingDict == NULL) {
|
||||
ERR("Couldn't create USB matching dictionary.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = IOServiceGetMatchingServices(
|
||||
kIOMasterPortDefault, matchingDict, &iterator);
|
||||
|
||||
if (result != 0) {
|
||||
ERR("Could not create iterator.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (! IOIteratorIsValid(iterator)) {
|
||||
/*
|
||||
* Apple documentation advises resetting the iterator if
|
||||
* it should become invalid during iteration.
|
||||
*/
|
||||
IOIteratorReset(iterator);
|
||||
continue;
|
||||
}
|
||||
|
||||
io_service_t device = IOIteratorNext(iterator);
|
||||
|
||||
if (device == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
usb_ifc_info info;
|
||||
|
||||
if (try_device(device, &h) != 0) {
|
||||
IOObjectRelease(device);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (h.success) {
|
||||
*handle = calloc(1, sizeof(usb_handle));
|
||||
memcpy(*handle, &h, sizeof(usb_handle));
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
|
||||
IOObjectRelease(iterator);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Definitions of this file's public functions.
|
||||
*/
|
||||
|
||||
usb_handle *usb_open(ifc_match_func callback) {
|
||||
usb_handle *handle = NULL;
|
||||
|
||||
if (init_usb(callback, &handle) < 0) {
|
||||
/* Something went wrong initializing USB. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int usb_close(usb_handle *h) {
|
||||
/* TODO: Something better here? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *h, void *data, int len) {
|
||||
IOReturn result;
|
||||
UInt32 numBytes = len;
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (h == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h->interface == NULL) {
|
||||
ERR("usb_read interface was null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h->bulkIn == 0) {
|
||||
ERR("bulkIn endpoint not assigned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = (*h->interface)->ReadPipe(
|
||||
h->interface, h->bulkIn, data, &numBytes);
|
||||
|
||||
if (result == 0) {
|
||||
return (int) numBytes;
|
||||
} else {
|
||||
ERR("usb_read failed with status %x\n", result);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle *h, const void *data, int len) {
|
||||
IOReturn result;
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (h == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h->interface == NULL) {
|
||||
ERR("usb_write interface was null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (h->bulkOut == 0) {
|
||||
ERR("bulkOut endpoint not assigned\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = (*h->interface)->WritePipe(
|
||||
h->interface, h->bulkOut, (void *)data, len);
|
||||
|
||||
#if 0
|
||||
if ((result == 0) && (h->zero_mask)) {
|
||||
/* we need 0-markers and our transfer */
|
||||
if(!(len & h->zero_mask)) {
|
||||
result = (*h->interface)->WritePipe(
|
||||
h->interface, h->bulkOut, (void *)data, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (result != 0) {
|
||||
ERR("usb_write failed with status %x\n", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
375
fastboot/usb_windows.c
Normal file
375
fastboot/usb_windows.c
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <windows.h>
|
||||
#include <winerror.h>
|
||||
#include <errno.h>
|
||||
#include <usb100.h>
|
||||
#include <adb_api.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
//#define TRACE_USB 1
|
||||
#if TRACE_USB
|
||||
#define DBG(x...) fprintf(stderr, x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
|
||||
/** Structure usb_handle describes our connection to the usb device via
|
||||
AdbWinApi.dll. This structure is returned from usb_open() routine and
|
||||
is expected in each subsequent call that is accessing the device.
|
||||
*/
|
||||
struct usb_handle {
|
||||
/// Handle to USB interface
|
||||
ADBAPIHANDLE adb_interface;
|
||||
|
||||
/// Handle to USB read pipe (endpoint)
|
||||
ADBAPIHANDLE adb_read_pipe;
|
||||
|
||||
/// Handle to USB write pipe (endpoint)
|
||||
ADBAPIHANDLE adb_write_pipe;
|
||||
|
||||
/// Interface name
|
||||
char* interface_name;
|
||||
};
|
||||
|
||||
/// Class ID assigned to the device by androidusb.sys
|
||||
static const GUID usb_class_id = ANDROID_USB_CLASS_ID;
|
||||
|
||||
|
||||
/// Checks if interface (device) matches certain criteria
|
||||
int recognized_device(usb_handle* handle, ifc_match_func callback);
|
||||
|
||||
/// Opens usb interface (device) by interface (device) name.
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name);
|
||||
|
||||
/// Writes data to the opened usb handle
|
||||
int usb_write(usb_handle* handle, const void* data, int len);
|
||||
|
||||
/// Reads data using the opened usb handle
|
||||
int usb_read(usb_handle *handle, void* data, int len);
|
||||
|
||||
/// Cleans up opened usb handle
|
||||
void usb_cleanup_handle(usb_handle* handle);
|
||||
|
||||
/// Cleans up (but don't close) opened usb handle
|
||||
void usb_kick(usb_handle* handle);
|
||||
|
||||
/// Closes opened usb handle
|
||||
int usb_close(usb_handle* handle);
|
||||
|
||||
|
||||
usb_handle* do_usb_open(const wchar_t* interface_name) {
|
||||
// Allocate our handle
|
||||
usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
|
||||
if (NULL == ret)
|
||||
return NULL;
|
||||
|
||||
// Create interface.
|
||||
ret->adb_interface = AdbCreateInterfaceByName(interface_name);
|
||||
|
||||
if (NULL == ret->adb_interface) {
|
||||
free(ret);
|
||||
errno = GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Open read pipe (endpoint)
|
||||
ret->adb_read_pipe =
|
||||
AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,
|
||||
AdbOpenAccessTypeReadWrite,
|
||||
AdbOpenSharingModeReadWrite);
|
||||
if (NULL != ret->adb_read_pipe) {
|
||||
// Open write pipe (endpoint)
|
||||
ret->adb_write_pipe =
|
||||
AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
|
||||
AdbOpenAccessTypeReadWrite,
|
||||
AdbOpenSharingModeReadWrite);
|
||||
if (NULL != ret->adb_write_pipe) {
|
||||
// Save interface name
|
||||
unsigned long name_len = 0;
|
||||
|
||||
// First get expected name length
|
||||
AdbGetInterfaceName(ret->adb_interface,
|
||||
NULL,
|
||||
&name_len,
|
||||
true);
|
||||
if (0 != name_len) {
|
||||
ret->interface_name = (char*)malloc(name_len);
|
||||
|
||||
if (NULL != ret->interface_name) {
|
||||
// Now save the name
|
||||
if (AdbGetInterfaceName(ret->adb_interface,
|
||||
ret->interface_name,
|
||||
&name_len,
|
||||
true)) {
|
||||
// We're done at this point
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
SetLastError(ERROR_OUTOFMEMORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Something went wrong.
|
||||
errno = GetLastError();
|
||||
usb_cleanup_handle(ret);
|
||||
free(ret);
|
||||
SetLastError(errno);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int usb_write(usb_handle* handle, const void* data, int len) {
|
||||
unsigned long time_out = 500 + len * 8;
|
||||
unsigned long written = 0;
|
||||
unsigned count = 0;
|
||||
int ret;
|
||||
|
||||
DBG("usb_write %d\n", len);
|
||||
if (NULL != handle) {
|
||||
// Perform write
|
||||
while(len > 0) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
ret = AdbWriteEndpointSync(handle->adb_write_pipe,
|
||||
(void*)data,
|
||||
(unsigned long)xfer,
|
||||
&written,
|
||||
time_out);
|
||||
errno = GetLastError();
|
||||
DBG("AdbWriteEndpointSync returned %d, errno: %d\n", ret, errno);
|
||||
if (ret == 0) {
|
||||
// assume ERROR_INVALID_HANDLE indicates we are disconnected
|
||||
if (errno == ERROR_INVALID_HANDLE)
|
||||
usb_kick(handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
count += written;
|
||||
len -= written;
|
||||
data += written;
|
||||
|
||||
if (len == 0)
|
||||
return count;
|
||||
}
|
||||
} else {
|
||||
DBG("usb_write NULL handle\n");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
DBG("usb_write failed: %d\n", errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int usb_read(usb_handle *handle, void* data, int len) {
|
||||
unsigned long time_out = 500 + len * 8;
|
||||
unsigned long read = 0;
|
||||
int ret;
|
||||
|
||||
DBG("usb_read %d\n", len);
|
||||
if (NULL != handle) {
|
||||
while (1) {
|
||||
int xfer = (len > 4096) ? 4096 : len;
|
||||
|
||||
ret = AdbReadEndpointSync(handle->adb_read_pipe,
|
||||
(void*)data,
|
||||
(unsigned long)xfer,
|
||||
&read,
|
||||
time_out);
|
||||
errno = GetLastError();
|
||||
DBG("usb_read got: %ld, expected: %d, errno: %d\n", read, xfer, errno);
|
||||
if (ret) {
|
||||
return read;
|
||||
} else if (errno != ERROR_SEM_TIMEOUT) {
|
||||
// assume ERROR_INVALID_HANDLE indicates we are disconnected
|
||||
if (errno == ERROR_INVALID_HANDLE)
|
||||
usb_kick(handle);
|
||||
break;
|
||||
}
|
||||
// else we timed out - try again
|
||||
}
|
||||
} else {
|
||||
DBG("usb_read NULL handle\n");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
DBG("usb_read failed: %d\n", errno);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_cleanup_handle(usb_handle* handle) {
|
||||
if (NULL != handle) {
|
||||
if (NULL != handle->interface_name)
|
||||
free(handle->interface_name);
|
||||
if (NULL != handle->adb_write_pipe)
|
||||
AdbCloseHandle(handle->adb_write_pipe);
|
||||
if (NULL != handle->adb_read_pipe)
|
||||
AdbCloseHandle(handle->adb_read_pipe);
|
||||
if (NULL != handle->adb_interface)
|
||||
AdbCloseHandle(handle->adb_interface);
|
||||
|
||||
handle->interface_name = NULL;
|
||||
handle->adb_write_pipe = NULL;
|
||||
handle->adb_read_pipe = NULL;
|
||||
handle->adb_interface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle* handle) {
|
||||
if (NULL != handle) {
|
||||
usb_cleanup_handle(handle);
|
||||
} else {
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
errno = ERROR_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_close(usb_handle* handle) {
|
||||
DBG("usb_close\n");
|
||||
|
||||
if (NULL != handle) {
|
||||
// Cleanup handle
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int recognized_device(usb_handle* handle, ifc_match_func callback) {
|
||||
struct usb_ifc_info info;
|
||||
USB_DEVICE_DESCRIPTOR device_desc;
|
||||
USB_INTERFACE_DESCRIPTOR interf_desc;
|
||||
|
||||
if (NULL == handle)
|
||||
return 0;
|
||||
|
||||
// Check vendor and product id first
|
||||
if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
|
||||
&device_desc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Then check interface properties
|
||||
if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
|
||||
&interf_desc)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Must have two endpoints
|
||||
if (2 != interf_desc.bNumEndpoints) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
info.dev_vendor = device_desc.idVendor;
|
||||
info.dev_product = device_desc.idProduct;
|
||||
info.dev_class = device_desc.bDeviceClass;
|
||||
info.dev_subclass = device_desc.bDeviceSubClass;
|
||||
info.dev_protocol = device_desc.bDeviceProtocol;
|
||||
info.ifc_class = interf_desc.bInterfaceClass;
|
||||
info.ifc_subclass = interf_desc.bInterfaceSubClass;
|
||||
info.ifc_protocol = interf_desc.bInterfaceProtocol;
|
||||
|
||||
// read serial number (if there is one)
|
||||
unsigned long serial_number_len = sizeof(info.serial_number);
|
||||
if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number,
|
||||
&serial_number_len, true)) {
|
||||
info.serial_number[0] = 0;
|
||||
}
|
||||
|
||||
if (callback(&info) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static usb_handle *find_usb_device(ifc_match_func callback) {
|
||||
usb_handle* handle = NULL;
|
||||
char entry_buffer[2048];
|
||||
char interf_name[2048];
|
||||
AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);
|
||||
unsigned long entry_buffer_size = sizeof(entry_buffer);
|
||||
char* copy_name;
|
||||
|
||||
// Enumerate all present and active interfaces.
|
||||
ADBAPIHANDLE enum_handle =
|
||||
AdbEnumInterfaces(usb_class_id, true, true, true);
|
||||
|
||||
if (NULL == enum_handle)
|
||||
return NULL;
|
||||
|
||||
while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
|
||||
// TODO(vchtchetkine): FIXME - temp hack converting wchar_t into char.
|
||||
// It would be better to change AdbNextInterface so it will return
|
||||
// interface name as single char string.
|
||||
const wchar_t* wchar_name = next_interface->device_name;
|
||||
for(copy_name = interf_name;
|
||||
L'\0' != *wchar_name;
|
||||
wchar_name++, copy_name++) {
|
||||
*copy_name = (char)(*wchar_name);
|
||||
}
|
||||
*copy_name = '\0';
|
||||
|
||||
handle = do_usb_open(next_interface->device_name);
|
||||
if (NULL != handle) {
|
||||
// Lets see if this interface (device) belongs to us
|
||||
if (recognized_device(handle, callback)) {
|
||||
// found it!
|
||||
break;
|
||||
} else {
|
||||
usb_cleanup_handle(handle);
|
||||
free(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
entry_buffer_size = sizeof(entry_buffer);
|
||||
}
|
||||
|
||||
AdbCloseHandle(enum_handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
usb_handle *usb_open(ifc_match_func callback)
|
||||
{
|
||||
return find_usb_device(callback);
|
||||
}
|
||||
|
||||
// called from fastboot.c
|
||||
void sleep(int seconds)
|
||||
{
|
||||
Sleep(seconds * 1000);
|
||||
}
|
212
fastboot/usbtest.c
Normal file
212
fastboot/usbtest.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
static unsigned arg_size = 4096;
|
||||
static unsigned arg_count = 4096;
|
||||
|
||||
long long NOW(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
|
||||
return (((long long) tv.tv_sec) * ((long long) 1000000)) +
|
||||
(((long long) tv.tv_usec));
|
||||
}
|
||||
|
||||
int printifc(usb_ifc_info *info)
|
||||
{
|
||||
printf("dev: csp=%02x/%02x/%02x v=%04x p=%04x ",
|
||||
info->dev_class, info->dev_subclass, info->dev_protocol,
|
||||
info->dev_vendor, info->dev_product);
|
||||
printf("ifc: csp=%02x/%02x/%02x%s%s\n",
|
||||
info->ifc_class, info->ifc_subclass, info->ifc_protocol,
|
||||
info->has_bulk_in ? " in" : "",
|
||||
info->has_bulk_out ? " out" : "");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int match_null(usb_ifc_info *info)
|
||||
{
|
||||
if(info->dev_vendor != 0x18d1) return -1;
|
||||
if(info->ifc_class != 0xff) return -1;
|
||||
if(info->ifc_subclass != 0xfe) return -1;
|
||||
if(info->ifc_protocol != 0x01) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int match_zero(usb_ifc_info *info)
|
||||
{
|
||||
if(info->dev_vendor != 0x18d1) return -1;
|
||||
if(info->ifc_class != 0xff) return -1;
|
||||
if(info->ifc_subclass != 0xfe) return -1;
|
||||
if(info->ifc_protocol != 0x02) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int match_loop(usb_ifc_info *info)
|
||||
{
|
||||
if(info->dev_vendor != 0x18d1) return -1;
|
||||
if(info->ifc_class != 0xff) return -1;
|
||||
if(info->ifc_subclass != 0xfe) return -1;
|
||||
if(info->ifc_protocol != 0x03) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_null(usb_handle *usb)
|
||||
{
|
||||
int i;
|
||||
unsigned char buf[4096];
|
||||
memset(buf, 0xee, 4096);
|
||||
long long t0, t1;
|
||||
|
||||
t0 = NOW();
|
||||
for(i = 0; i < arg_count; i++) {
|
||||
if(usb_write(usb, buf, arg_size) != arg_size) {
|
||||
fprintf(stderr,"write failed (%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
t1 = NOW();
|
||||
fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_zero(usb_handle *usb)
|
||||
{
|
||||
int i;
|
||||
unsigned char buf[4096];
|
||||
long long t0, t1;
|
||||
|
||||
t0 = NOW();
|
||||
for(i = 0; i < arg_count; i++) {
|
||||
if(usb_read(usb, buf, arg_size) != arg_size) {
|
||||
fprintf(stderr,"read failed (%s)\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
t1 = NOW();
|
||||
fprintf(stderr,"%d bytes in %lld uS\n", arg_count * arg_size, (t1 - t0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
const char *cmd;
|
||||
ifc_match_func match;
|
||||
int (*test)(usb_handle *usb);
|
||||
const char *help;
|
||||
} tests[] = {
|
||||
{ "list", printifc, 0, "list interfaces" },
|
||||
{ "send", match_null, test_null, "send to null interface" },
|
||||
{ "recv", match_zero, test_zero, "recv from zero interface" },
|
||||
{ "loop", match_loop, 0, "exercise loopback interface" },
|
||||
{},
|
||||
};
|
||||
|
||||
int usage(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(stderr,"usage: usbtest <testname>\n\navailable tests:\n");
|
||||
for(i = 0; tests[i].cmd; i++) {
|
||||
fprintf(stderr," %-8s %s\n", tests[i].cmd, tests[i].help);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int process_args(int argc, char **argv)
|
||||
{
|
||||
while(argc-- > 0) {
|
||||
char *arg = *argv++;
|
||||
if(!strncmp(arg,"count=",6)) {
|
||||
arg_count = atoi(arg + 6);
|
||||
} else if(!strncmp(arg,"size=",5)) {
|
||||
arg_size = atoi(arg + 5);
|
||||
} else {
|
||||
fprintf(stderr,"unknown argument: %s\n", arg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(arg_count == 0) {
|
||||
fprintf(stderr,"count may not be zero\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(arg_size > 4096) {
|
||||
fprintf(stderr,"size may not be greater than 4096\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
usb_handle *usb;
|
||||
int i;
|
||||
|
||||
if(argc < 2)
|
||||
return usage();
|
||||
|
||||
if(argc > 2) {
|
||||
if(process_args(argc - 2, argv + 2))
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(i = 0; tests[i].cmd; i++) {
|
||||
if(!strcmp(argv[1], tests[i].cmd)) {
|
||||
usb = usb_open(tests[i].match);
|
||||
if(tests[i].test) {
|
||||
if(usb == 0) {
|
||||
fprintf(stderr,"usbtest: %s: could not find interface\n",
|
||||
tests[i].cmd);
|
||||
return -1;
|
||||
}
|
||||
if(tests[i].test(usb)) {
|
||||
fprintf(stderr,"usbtest: %s: FAIL\n", tests[i].cmd);
|
||||
return -1;
|
||||
} else {
|
||||
fprintf(stderr,"usbtest: %s: OKAY\n", tests[i].cmd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return usage();
|
||||
}
|
52
fastboot/util_linux.c
Normal file
52
fastboot/util_linux.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
void get_my_path(char *path)
|
||||
{
|
||||
char proc[64];
|
||||
char *x;
|
||||
|
||||
sprintf(proc, "/proc/%d/exe", getpid());
|
||||
int err = readlink(proc, path, PATH_MAX - 1);
|
||||
|
||||
if(err <= 0) {
|
||||
path[0] = 0;
|
||||
} else {
|
||||
path[err] = 0;
|
||||
x = strrchr(path,'/');
|
||||
if(x) x[1] = 0;
|
||||
}
|
||||
}
|
47
fastboot/util_osx.c
Normal file
47
fastboot/util_osx.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <utils/executablepath.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void get_my_path(char s[PATH_MAX])
|
||||
{
|
||||
char *x;
|
||||
ProcessSerialNumber psn;
|
||||
GetCurrentProcess(&psn);
|
||||
CFDictionaryRef dict;
|
||||
dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
|
||||
CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
|
||||
CFSTR("CFBundleExecutable"));
|
||||
CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
|
||||
x = strrchr(s, '/');
|
||||
if(x) x[1] = 0;
|
||||
}
|
||||
|
||||
|
93
fastboot/util_windows.c
Normal file
93
fastboot/util_windows.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* 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.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void get_my_path(char exe[PATH_MAX])
|
||||
{
|
||||
char* r;
|
||||
|
||||
GetModuleFileName( NULL, exe, PATH_MAX-1 );
|
||||
exe[PATH_MAX-1] = 0;
|
||||
r = strrchr( exe, '\\' );
|
||||
if (r)
|
||||
*r = 0;
|
||||
}
|
||||
|
||||
|
||||
void *load_file(const char *fn, unsigned *_sz)
|
||||
{
|
||||
HANDLE file;
|
||||
char *data;
|
||||
DWORD file_size;
|
||||
|
||||
file = CreateFile( fn,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL );
|
||||
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
file_size = GetFileSize( file, NULL );
|
||||
data = NULL;
|
||||
|
||||
if (file_size > 0) {
|
||||
data = (char*) malloc( file_size );
|
||||
if (data == NULL) {
|
||||
fprintf(stderr, "load_file: could not allocate %ld bytes\n", file_size );
|
||||
file_size = 0;
|
||||
} else {
|
||||
DWORD out_bytes;
|
||||
|
||||
if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
|
||||
out_bytes != file_size )
|
||||
{
|
||||
fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", file_size, fn);
|
||||
free(data);
|
||||
data = NULL;
|
||||
file_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle( file );
|
||||
|
||||
*_sz = (unsigned) file_size;
|
||||
return data;
|
||||
}
|
260
include/arch/darwin-x86/AndroidConfig.h
Normal file
260
include/arch/darwin-x86/AndroidConfig.h
Normal file
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Android config -- "Darwin". Used for PPC Mac OS X.
|
||||
*
|
||||
* TODO: split this into "x86" and "ppc" versions
|
||||
*/
|
||||
#ifndef _ANDROID_CONFIG_H
|
||||
#define _ANDROID_CONFIG_H
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* !!! IMPORTANT !!!
|
||||
* ===========================================================================
|
||||
*
|
||||
* This file is included by ALL C/C++ source files. Don't put anything in
|
||||
* here unless you are absolutely certain it can't go anywhere else.
|
||||
*
|
||||
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
|
||||
* comments.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Threading model. Choose one:
|
||||
*
|
||||
* HAVE_PTHREADS - use the pthreads library.
|
||||
* HAVE_WIN32_THREADS - use Win32 thread primitives.
|
||||
* -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
|
||||
*/
|
||||
#define HAVE_PTHREADS
|
||||
|
||||
/*
|
||||
* Do we have the futex syscall?
|
||||
*/
|
||||
|
||||
/* #define HAVE_FUTEX */
|
||||
|
||||
/*
|
||||
* Process creation model. Choose one:
|
||||
*
|
||||
* HAVE_FORKEXEC - use fork() and exec()
|
||||
* HAVE_WIN32_PROC - use CreateProcess()
|
||||
*/
|
||||
#define HAVE_FORKEXEC
|
||||
|
||||
/*
|
||||
* Process out-of-memory adjustment. Set if running on Linux,
|
||||
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
|
||||
* badness adjustment.
|
||||
*/
|
||||
/* #define HAVE_OOM_ADJ */
|
||||
|
||||
/*
|
||||
* IPC model. Choose one:
|
||||
*
|
||||
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
|
||||
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
|
||||
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
|
||||
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
|
||||
*/
|
||||
#define HAVE_MACOSX_IPC
|
||||
|
||||
/*
|
||||
* Memory-mapping model. Choose one:
|
||||
*
|
||||
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
|
||||
* HAVE_WIN32_FILEMAP - use Win32 filemaps
|
||||
*/
|
||||
#define HAVE_POSIX_FILEMAP
|
||||
|
||||
/*
|
||||
* Define this if you have <termio.h>
|
||||
*/
|
||||
#define HAVE_TERMIO_H
|
||||
|
||||
/*
|
||||
* Define this if you build against MSVCRT.DLL
|
||||
*/
|
||||
/* #define HAVE_MS_C_RUNTIME */
|
||||
|
||||
/*
|
||||
* Define this if you have sys/uio.h
|
||||
*/
|
||||
#define HAVE_SYS_UIO_H
|
||||
|
||||
/*
|
||||
* Define this if your platforms implements symbolic links
|
||||
* in its filesystems
|
||||
*/
|
||||
#define HAVE_SYMLINKS
|
||||
|
||||
/*
|
||||
* Define this if we have localtime_r().
|
||||
*/
|
||||
#define HAVE_LOCALTIME_R
|
||||
|
||||
/*
|
||||
* Define this if we have gethostbyname_r().
|
||||
*/
|
||||
/* #define HAVE_GETHOSTBYNAME_R */
|
||||
|
||||
/*
|
||||
* Define this if we have ioctl().
|
||||
*/
|
||||
/* #define HAVE_IOCTL */
|
||||
|
||||
/*
|
||||
* Define this if we want to use WinSock.
|
||||
*/
|
||||
/* #define HAVE_WINSOCK */
|
||||
|
||||
/*
|
||||
* Define this if have clock_gettime() and friends
|
||||
*/
|
||||
/* #define HAVE_POSIX_CLOCKS */
|
||||
|
||||
/*
|
||||
* Define this if we have pthread_cond_timedwait_monotonic() and
|
||||
* clock_gettime(CLOCK_MONOTONIC).
|
||||
*/
|
||||
/* #define HAVE_TIMEDWAIT_MONOTONIC */
|
||||
|
||||
/*
|
||||
* Endianness of the target machine. Choose one:
|
||||
*
|
||||
* HAVE_ENDIAN_H -- have endian.h header we can include.
|
||||
* HAVE_LITTLE_ENDIAN -- we are little endian.
|
||||
* HAVE_BIG_ENDIAN -- we are big endian.
|
||||
*/
|
||||
#if (defined(__ppc__) || defined(__ppc64__))
|
||||
# define HAVE_BIG_ENDIAN
|
||||
#elif defined(__i386__)
|
||||
# define HAVE_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We need to choose between 32-bit and 64-bit off_t. All of our code should
|
||||
* agree on the same size. For desktop systems, use 64-bit values,
|
||||
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE_SOURCE 1
|
||||
|
||||
/*
|
||||
* Defined if we have the backtrace() call for retrieving a stack trace.
|
||||
* Needed for CallStack to operate; if not defined, CallStack is
|
||||
* non-functional.
|
||||
*/
|
||||
#define HAVE_BACKTRACE 0
|
||||
|
||||
/*
|
||||
* Defined if we have the dladdr() call for retrieving the symbol associated
|
||||
* with a memory address. If not defined, stack crawls will not have symbolic
|
||||
* information.
|
||||
*/
|
||||
#define HAVE_DLADDR 0
|
||||
|
||||
/*
|
||||
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
|
||||
* not defined, stack crawls will be displayed with raw mangled symbols
|
||||
*/
|
||||
#define HAVE_CXXABI 0
|
||||
|
||||
/*
|
||||
* Defined if we have the gettid() system call.
|
||||
*/
|
||||
/* #define HAVE_GETTID */
|
||||
|
||||
|
||||
/*
|
||||
* Add any extra platform-specific defines here.
|
||||
*/
|
||||
#define _THREAD_SAFE
|
||||
|
||||
/*
|
||||
* Define if we have <malloc.h> header
|
||||
*/
|
||||
/* #define HAVE_MALLOC_H */
|
||||
|
||||
/*
|
||||
* Define if tm struct has tm_gmtoff field
|
||||
*/
|
||||
#define HAVE_TM_GMTOFF 1
|
||||
|
||||
/*
|
||||
* Define if dirent struct has d_type field
|
||||
*/
|
||||
#define HAVE_DIRENT_D_TYPE 1
|
||||
|
||||
/*
|
||||
* Define if we have madvise() in <sys/mman.h>
|
||||
*/
|
||||
#define HAVE_MADVISE 1
|
||||
|
||||
/*
|
||||
* Define if we include <sys/mount.h> for statfs()
|
||||
*/
|
||||
#define INCLUDE_SYS_MOUNT_FOR_STATFS 1
|
||||
|
||||
/*
|
||||
* What CPU architecture does this platform use?
|
||||
*/
|
||||
#if (defined(__ppc__) || defined(__ppc64__))
|
||||
# define ARCH_PPC
|
||||
#elif defined(__i386__)
|
||||
# define ARCH_X86
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sprintf() format string for shared library naming.
|
||||
*/
|
||||
#define OS_SHARED_LIB_FORMAT_STR "lib%s.dylib"
|
||||
|
||||
/*
|
||||
* type for the third argument to mincore().
|
||||
*/
|
||||
#define MINCORE_POINTER_TYPE char *
|
||||
|
||||
/*
|
||||
* The default path separator for the platform
|
||||
*/
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
|
||||
/*
|
||||
* Is the filesystem case sensitive?
|
||||
*
|
||||
* For tools apps, we'll treat is as not case sensitive.
|
||||
*/
|
||||
/* #define OS_CASE_SENSITIVE */
|
||||
|
||||
/*
|
||||
* Define if <sys/socket.h> exists.
|
||||
*/
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/*
|
||||
* Define if the strlcpy() function exists on the system.
|
||||
*/
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
/*
|
||||
* Define if writev() exists
|
||||
*/
|
||||
#define HAVE_WRITEV 1
|
||||
|
||||
#endif /*_ANDROID_CONFIG_H*/
|
294
include/arch/linux-arm/AndroidConfig.h
Normal file
294
include/arch/linux-arm/AndroidConfig.h
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Android config -- "android-arm". Used for ARM device builds.
|
||||
*/
|
||||
#ifndef _ANDROID_CONFIG_H
|
||||
#define _ANDROID_CONFIG_H
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* !!! IMPORTANT !!!
|
||||
* ===========================================================================
|
||||
*
|
||||
* This file is included by ALL C/C++ source files. Don't put anything in
|
||||
* here unless you are absolutely certain it can't go anywhere else.
|
||||
*
|
||||
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
|
||||
* comments.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Threading model. Choose one:
|
||||
*
|
||||
* HAVE_PTHREADS - use the pthreads library.
|
||||
* HAVE_WIN32_THREADS - use Win32 thread primitives.
|
||||
* -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
|
||||
*/
|
||||
#define HAVE_PTHREADS
|
||||
|
||||
/*
|
||||
* Do we have the futex syscall?
|
||||
*/
|
||||
|
||||
#define HAVE_FUTEX
|
||||
|
||||
/*
|
||||
* Process creation model. Choose one:
|
||||
*
|
||||
* HAVE_FORKEXEC - use fork() and exec()
|
||||
* HAVE_WIN32_PROC - use CreateProcess()
|
||||
*/
|
||||
#define HAVE_FORKEXEC
|
||||
|
||||
/*
|
||||
* Process out-of-memory adjustment. Set if running on Linux,
|
||||
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
|
||||
* badness adjustment.
|
||||
*/
|
||||
#define HAVE_OOM_ADJ
|
||||
|
||||
/*
|
||||
* IPC model. Choose one:
|
||||
*
|
||||
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
|
||||
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
|
||||
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
|
||||
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
|
||||
*/
|
||||
#define HAVE_ANDROID_IPC
|
||||
|
||||
/*
|
||||
* Memory-mapping model. Choose one:
|
||||
*
|
||||
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
|
||||
* HAVE_WIN32_FILEMAP - use Win32 filemaps
|
||||
*/
|
||||
#define HAVE_POSIX_FILEMAP
|
||||
|
||||
/*
|
||||
* Define this if you have <termio.h>
|
||||
*/
|
||||
#define HAVE_TERMIO_H
|
||||
|
||||
/*
|
||||
* Define this if you build against MSVCRT.DLL
|
||||
*/
|
||||
/* #define HAVE_MS_C_RUNTIME */
|
||||
|
||||
/*
|
||||
* Define this if you have sys/uio.h
|
||||
*/
|
||||
#define HAVE_SYS_UIO_H
|
||||
|
||||
/*
|
||||
* Define this if your platforms implements symbolic links
|
||||
* in its filesystems
|
||||
*/
|
||||
#define HAVE_SYMLINKS
|
||||
|
||||
/*
|
||||
* Define this if we have localtime_r().
|
||||
*/
|
||||
/* #define HAVE_LOCALTIME_R */
|
||||
|
||||
/*
|
||||
* Define this if we have gethostbyname_r().
|
||||
*/
|
||||
/* #define HAVE_GETHOSTBYNAME_R */
|
||||
|
||||
/*
|
||||
* Define this if we have ioctl().
|
||||
*/
|
||||
#define HAVE_IOCTL
|
||||
|
||||
/*
|
||||
* Define this if we want to use WinSock.
|
||||
*/
|
||||
/* #define HAVE_WINSOCK */
|
||||
|
||||
/*
|
||||
* Define this if have clock_gettime() and friends
|
||||
*/
|
||||
#define HAVE_POSIX_CLOCKS
|
||||
|
||||
/*
|
||||
* Define this if we have pthread_cond_timedwait_monotonic() and
|
||||
* clock_gettime(CLOCK_MONOTONIC).
|
||||
*/
|
||||
#define HAVE_TIMEDWAIT_MONOTONIC
|
||||
|
||||
/*
|
||||
* Define this if we have linux style epoll()
|
||||
*/
|
||||
#define HAVE_EPOLL
|
||||
|
||||
/*
|
||||
* Endianness of the target machine. Choose one:
|
||||
*
|
||||
* HAVE_ENDIAN_H -- have endian.h header we can include.
|
||||
* HAVE_LITTLE_ENDIAN -- we are little endian.
|
||||
* HAVE_BIG_ENDIAN -- we are big endian.
|
||||
*/
|
||||
#define HAVE_ENDIAN_H
|
||||
#define HAVE_LITTLE_ENDIAN
|
||||
|
||||
/*
|
||||
* We need to choose between 32-bit and 64-bit off_t. All of our code should
|
||||
* agree on the same size. For desktop systems, use 64-bit values,
|
||||
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
|
||||
*/
|
||||
/* #define _FILE_OFFSET_BITS 64 */
|
||||
/* #define _LARGEFILE_SOURCE 1 */
|
||||
|
||||
/*
|
||||
* Defined if we have the backtrace() call for retrieving a stack trace.
|
||||
* Needed for CallStack to operate; if not defined, CallStack is
|
||||
* non-functional.
|
||||
*/
|
||||
#define HAVE_BACKTRACE 0
|
||||
|
||||
/*
|
||||
* Defined if we have the dladdr() call for retrieving the symbol associated
|
||||
* with a memory address. If not defined, stack crawls will not have symbolic
|
||||
* information.
|
||||
*/
|
||||
#define HAVE_DLADDR 0
|
||||
|
||||
/*
|
||||
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
|
||||
* not defined, stack crawls will be displayed with raw mangled symbols
|
||||
*/
|
||||
#define HAVE_CXXABI 0
|
||||
|
||||
/*
|
||||
* Defined if we have the gettid() system call.
|
||||
*/
|
||||
#define HAVE_GETTID
|
||||
|
||||
/*
|
||||
* Defined if we have the sched_setscheduler() call
|
||||
*/
|
||||
#define HAVE_SCHED_SETSCHEDULER
|
||||
|
||||
/*
|
||||
* Add any extra platform-specific defines here.
|
||||
*/
|
||||
#define __linux__
|
||||
|
||||
/*
|
||||
* Define if we have <malloc.h> header
|
||||
*/
|
||||
#define HAVE_MALLOC_H
|
||||
|
||||
/*
|
||||
* Define if we're running on *our* linux on device or emulator.
|
||||
*/
|
||||
#define HAVE_ANDROID_OS 1
|
||||
|
||||
/*
|
||||
* Define if we have Linux-style non-filesystem Unix Domain Sockets
|
||||
*/
|
||||
#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE 1
|
||||
|
||||
/*
|
||||
* Define if we have Linux's inotify in <sys/inotify.h>.
|
||||
*/
|
||||
#define HAVE_INOTIFY 1
|
||||
|
||||
/*
|
||||
* Define if we have madvise() in <sys/mman.h>
|
||||
*/
|
||||
#define HAVE_MADVISE 1
|
||||
|
||||
/*
|
||||
* Define if tm struct has tm_gmtoff field
|
||||
*/
|
||||
#define HAVE_TM_GMTOFF 1
|
||||
|
||||
/*
|
||||
* Define if dirent struct has d_type field
|
||||
*/
|
||||
#define HAVE_DIRENT_D_TYPE 1
|
||||
|
||||
/*
|
||||
* Define if libc includes Android system properties implementation.
|
||||
*/
|
||||
#define HAVE_LIBC_SYSTEM_PROPERTIES 1
|
||||
|
||||
/*
|
||||
* Define if system provides a system property server (should be
|
||||
* mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
|
||||
*/
|
||||
/* #define HAVE_SYSTEM_PROPERTY_SERVER */
|
||||
|
||||
/*
|
||||
* What CPU architecture does this platform use?
|
||||
*/
|
||||
#define ARCH_ARM
|
||||
|
||||
/*
|
||||
* sprintf() format string for shared library naming.
|
||||
*/
|
||||
#define OS_SHARED_LIB_FORMAT_STR "lib%s.so"
|
||||
|
||||
/*
|
||||
* Do we have __memcmp16()?
|
||||
*/
|
||||
#define HAVE__MEMCMP16 1
|
||||
|
||||
/*
|
||||
* type for the third argument to mincore().
|
||||
*/
|
||||
#define MINCORE_POINTER_TYPE unsigned char *
|
||||
|
||||
/*
|
||||
* Do we have the sigaction flag SA_NOCLDWAIT?
|
||||
*/
|
||||
#define HAVE_SA_NOCLDWAIT
|
||||
|
||||
/*
|
||||
* The default path separator for the platform
|
||||
*/
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
|
||||
/*
|
||||
* Is the filesystem case sensitive?
|
||||
*/
|
||||
#define OS_CASE_SENSITIVE
|
||||
|
||||
/*
|
||||
* Define if <sys/socket.h> exists.
|
||||
*/
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/*
|
||||
* Define if the strlcpy() function exists on the system.
|
||||
*/
|
||||
#define HAVE_STRLCPY 1
|
||||
|
||||
/*
|
||||
* Define if prctl() exists
|
||||
*/
|
||||
#define HAVE_PRCTL 1
|
||||
|
||||
/*
|
||||
* Define if writev() exists
|
||||
*/
|
||||
#define HAVE_WRITEV 1
|
||||
|
||||
#endif /* _ANDROID_CONFIG_H */
|
286
include/arch/linux-x86/AndroidConfig.h
Normal file
286
include/arch/linux-x86/AndroidConfig.h
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Android config -- "Linux". Used for desktop x86 Linux.
|
||||
*/
|
||||
#ifndef _ANDROID_CONFIG_H
|
||||
#define _ANDROID_CONFIG_H
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* !!! IMPORTANT !!!
|
||||
* ===========================================================================
|
||||
*
|
||||
* This file is included by ALL C/C++ source files. Don't put anything in
|
||||
* here unless you are absolutely certain it can't go anywhere else.
|
||||
*
|
||||
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
|
||||
* comments.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Threading model. Choose one:
|
||||
*
|
||||
* HAVE_PTHREADS - use the pthreads library.
|
||||
* HAVE_WIN32_THREADS - use Win32 thread primitives.
|
||||
* -- combine HAVE_CREATETHREAD, HAVE_CREATEMUTEX, and HAVE__BEGINTHREADEX
|
||||
*/
|
||||
#define HAVE_PTHREADS
|
||||
|
||||
/*
|
||||
* Do we have the futex syscall?
|
||||
*/
|
||||
|
||||
#define HAVE_FUTEX
|
||||
|
||||
/*
|
||||
* Process creation model. Choose one:
|
||||
*
|
||||
* HAVE_FORKEXEC - use fork() and exec()
|
||||
* HAVE_WIN32_PROC - use CreateProcess()
|
||||
*/
|
||||
#define HAVE_FORKEXEC
|
||||
|
||||
/*
|
||||
* Process out-of-memory adjustment. Set if running on Linux,
|
||||
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
|
||||
* badness adjustment.
|
||||
*/
|
||||
#define HAVE_OOM_ADJ
|
||||
|
||||
/*
|
||||
* IPC model. Choose one:
|
||||
*
|
||||
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
|
||||
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
|
||||
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
|
||||
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
|
||||
*/
|
||||
#define HAVE_SYSV_IPC
|
||||
|
||||
/*
|
||||
* Memory-mapping model. Choose one:
|
||||
*
|
||||
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
|
||||
* HAVE_WIN32_FILEMAP - use Win32 filemaps
|
||||
*/
|
||||
#define HAVE_POSIX_FILEMAP
|
||||
|
||||
/*
|
||||
* Define this if you have <termio.h>
|
||||
*/
|
||||
#define HAVE_TERMIO_H
|
||||
|
||||
/*
|
||||
* Define this if you build against MSVCRT.DLL
|
||||
*/
|
||||
/* #define HAVE_MS_C_RUNTIME */
|
||||
|
||||
/*
|
||||
* Define this if you have sys/uio.h
|
||||
*/
|
||||
#define HAVE_SYS_UIO_H
|
||||
|
||||
/*
|
||||
* Define this if your platforms implements symbolic links
|
||||
* in its filesystems
|
||||
*/
|
||||
#define HAVE_SYMLINKS
|
||||
|
||||
/*
|
||||
* Define this if we have localtime_r().
|
||||
*/
|
||||
#define HAVE_LOCALTIME_R
|
||||
|
||||
/*
|
||||
* Define this if we have gethostbyname_r().
|
||||
*/
|
||||
#define HAVE_GETHOSTBYNAME_R
|
||||
|
||||
/*
|
||||
* Define this if we have ioctl().
|
||||
*/
|
||||
#define HAVE_IOCTL
|
||||
|
||||
/*
|
||||
* Define this if we want to use WinSock.
|
||||
*/
|
||||
/* #define HAVE_WINSOCK */
|
||||
|
||||
/*
|
||||
* Define this if have clock_gettime() and friends
|
||||
*
|
||||
* Desktop Linux has this in librt, but it's broken in goobuntu, yielding
|
||||
* mildly or wildly inaccurate results.
|
||||
*/
|
||||
/*#define HAVE_POSIX_CLOCKS*/
|
||||
|
||||
/*
|
||||
* Define this if we have pthread_cond_timedwait_monotonic() and
|
||||
* clock_gettime(CLOCK_MONOTONIC).
|
||||
*/
|
||||
/* #define HAVE_TIMEDWAIT_MONOTONIC */
|
||||
|
||||
/*
|
||||
* Define this if we have linux style epoll()
|
||||
*/
|
||||
#define HAVE_EPOLL
|
||||
|
||||
/*
|
||||
* Endianness of the target machine. Choose one:
|
||||
*
|
||||
* HAVE_ENDIAN_H -- have endian.h header we can include.
|
||||
* HAVE_LITTLE_ENDIAN -- we are little endian.
|
||||
* HAVE_BIG_ENDIAN -- we are big endian.
|
||||
*/
|
||||
#define HAVE_ENDIAN_H
|
||||
#define HAVE_LITTLE_ENDIAN
|
||||
|
||||
/*
|
||||
* We need to choose between 32-bit and 64-bit off_t. All of our code should
|
||||
* agree on the same size. For desktop systems, use 64-bit values,
|
||||
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE_SOURCE 1
|
||||
|
||||
/*
|
||||
* Defined if we have the backtrace() call for retrieving a stack trace.
|
||||
* Needed for CallStack to operate; if not defined, CallStack is
|
||||
* non-functional.
|
||||
*/
|
||||
#define HAVE_BACKTRACE 1
|
||||
|
||||
/*
|
||||
* Defined if we have the dladdr() call for retrieving the symbol associated
|
||||
* with a memory address. If not defined, stack crawls will not have symbolic
|
||||
* information.
|
||||
*/
|
||||
#define HAVE_DLADDR 1
|
||||
|
||||
/*
|
||||
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
|
||||
* not defined, stack crawls will be displayed with raw mangled symbols
|
||||
*/
|
||||
#define HAVE_CXXABI 0
|
||||
|
||||
/*
|
||||
* Defined if we have the gettid() system call.
|
||||
*/
|
||||
/* #define HAVE_GETTID */
|
||||
|
||||
/*
|
||||
* Defined if we have the sched_setscheduler() call
|
||||
*/
|
||||
#define HAVE_SCHED_SETSCHEDULER
|
||||
|
||||
/*
|
||||
* Add any extra platform-specific defines here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define if we have <malloc.h> header
|
||||
*/
|
||||
#define HAVE_MALLOC_H
|
||||
|
||||
/*
|
||||
* Define if we have Linux-style non-filesystem Unix Domain Sockets
|
||||
*/
|
||||
|
||||
/*
|
||||
* What CPU architecture does this platform use?
|
||||
*/
|
||||
#define ARCH_X86
|
||||
|
||||
|
||||
/*
|
||||
* Define if we have Linux's inotify in <sys/inotify.h>.
|
||||
*/
|
||||
/*#define HAVE_INOTIFY 1*/
|
||||
|
||||
/*
|
||||
* Define if we have madvise() in <sys/mman.h>
|
||||
*/
|
||||
#define HAVE_MADVISE 1
|
||||
|
||||
/*
|
||||
* Define if tm struct has tm_gmtoff field
|
||||
*/
|
||||
#define HAVE_TM_GMTOFF 1
|
||||
|
||||
/*
|
||||
* Define if dirent struct has d_type field
|
||||
*/
|
||||
#define HAVE_DIRENT_D_TYPE 1
|
||||
|
||||
/*
|
||||
* Define if libc includes Android system properties implementation.
|
||||
*/
|
||||
/* #define HAVE_LIBC_SYSTEM_PROPERTIES */
|
||||
|
||||
/*
|
||||
* Define if system provides a system property server (should be
|
||||
* mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
|
||||
*/
|
||||
#define HAVE_SYSTEM_PROPERTY_SERVER
|
||||
|
||||
/*
|
||||
* sprintf() format string for shared library naming.
|
||||
*/
|
||||
#define OS_SHARED_LIB_FORMAT_STR "lib%s.so"
|
||||
|
||||
/*
|
||||
* type for the third argument to mincore().
|
||||
*/
|
||||
#define MINCORE_POINTER_TYPE unsigned char *
|
||||
|
||||
/*
|
||||
* Do we have the sigaction flag SA_NOCLDWAIT?
|
||||
*/
|
||||
#define HAVE_SA_NOCLDWAIT
|
||||
|
||||
/*
|
||||
* The default path separator for the platform
|
||||
*/
|
||||
#define OS_PATH_SEPARATOR '/'
|
||||
|
||||
/*
|
||||
* Is the filesystem case sensitive?
|
||||
*/
|
||||
#define OS_CASE_SENSITIVE
|
||||
|
||||
/*
|
||||
* Define if <sys/socket.h> exists.
|
||||
*/
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
|
||||
/*
|
||||
* Define if the strlcpy() function exists on the system.
|
||||
*/
|
||||
/* #define HAVE_STRLCPY 1 */
|
||||
|
||||
/*
|
||||
* Define if prctl() exists
|
||||
*/
|
||||
#define HAVE_PRCTL 1
|
||||
|
||||
/*
|
||||
* Define if writev() exists
|
||||
*/
|
||||
#define HAVE_WRITEV 1
|
||||
|
||||
#endif /*_ANDROID_CONFIG_H*/
|
290
include/arch/windows/AndroidConfig.h
Normal file
290
include/arch/windows/AndroidConfig.h
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Android config -- "CYGWIN_NT-5.1".
|
||||
*
|
||||
* Cygwin has pthreads, but GDB seems to get confused if you use it to
|
||||
* create threads. By "confused", I mean it freezes up the first time the
|
||||
* debugged process creates a thread, even if you use CreateThread. The
|
||||
* mere presence of pthreads linkage seems to cause problems.
|
||||
*/
|
||||
#ifndef _ANDROID_CONFIG_H
|
||||
#define _ANDROID_CONFIG_H
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* !!! IMPORTANT !!!
|
||||
* ===========================================================================
|
||||
*
|
||||
* This file is included by ALL C/C++ source files. Don't put anything in
|
||||
* here unless you are absolutely certain it can't go anywhere else.
|
||||
*
|
||||
* Any C++ stuff must be wrapped with "#ifdef __cplusplus". Do not use "//"
|
||||
* comments.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Threading model. Choose one:
|
||||
*
|
||||
* HAVE_PTHREADS - use the pthreads library.
|
||||
* HAVE_WIN32_THREADS - use Win32 thread primitives.
|
||||
*/
|
||||
#define HAVE_WIN32_THREADS
|
||||
|
||||
/*
|
||||
* Do we have the futex syscall?
|
||||
*/
|
||||
|
||||
/* #define HAVE_FUTEX */
|
||||
|
||||
|
||||
/*
|
||||
* Process creation model. Choose one:
|
||||
*
|
||||
* HAVE_FORKEXEC - use fork() and exec()
|
||||
* HAVE_WIN32_PROC - use CreateProcess()
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
# define HAVE_FORKEXEC
|
||||
#else
|
||||
# define HAVE_WIN32_PROC
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Process out-of-memory adjustment. Set if running on Linux,
|
||||
* where we can write to /proc/<pid>/oom_adj to modify the out-of-memory
|
||||
* badness adjustment.
|
||||
*/
|
||||
/* #define HAVE_OOM_ADJ */
|
||||
|
||||
/*
|
||||
* IPC model. Choose one:
|
||||
*
|
||||
* HAVE_SYSV_IPC - use the classic SysV IPC mechanisms (semget, shmget).
|
||||
* HAVE_MACOSX_IPC - use Macintosh IPC mechanisms (sem_open, mmap).
|
||||
* HAVE_WIN32_IPC - use Win32 IPC (CreateSemaphore, CreateFileMapping).
|
||||
* HAVE_ANDROID_IPC - use Android versions (?, mmap).
|
||||
*/
|
||||
#define HAVE_WIN32_IPC
|
||||
|
||||
/*
|
||||
* Memory-mapping model. Choose one:
|
||||
*
|
||||
* HAVE_POSIX_FILEMAP - use the Posix sys/mmap.h
|
||||
* HAVE_WIN32_FILEMAP - use Win32 filemaps
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
#define HAVE_POSIX_FILEMAP
|
||||
#else
|
||||
#define HAVE_WIN32_FILEMAP
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define this if you have <termio.h>
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
# define HAVE_TERMIO_H
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define this if you build against MSVCRT.DLL
|
||||
*/
|
||||
#ifndef __CYGWIN__
|
||||
# define HAVE_MS_C_RUNTIME
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define this if you have sys/uio.h
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
#define HAVE_SYS_UIO_H
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Define this if we have localtime_r().
|
||||
*/
|
||||
/* #define HAVE_LOCALTIME_R */
|
||||
|
||||
/*
|
||||
* Define this if we have gethostbyname_r().
|
||||
*/
|
||||
/* #define HAVE_GETHOSTBYNAME_R */
|
||||
|
||||
/*
|
||||
* Define this if we have ioctl().
|
||||
*/
|
||||
/* #define HAVE_IOCTL */
|
||||
|
||||
/*
|
||||
* Define this if we want to use WinSock.
|
||||
*/
|
||||
#ifndef __CYGWIN__
|
||||
#define HAVE_WINSOCK
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define this if your platforms implements symbolic links
|
||||
* in its filesystems
|
||||
*/
|
||||
/* #define HAVE_SYMLINKS */
|
||||
|
||||
/*
|
||||
* Define this if have clock_gettime() and friends
|
||||
*/
|
||||
/* #define HAVE_POSIX_CLOCKS */
|
||||
|
||||
/*
|
||||
* Endianness of the target machine. Choose one:
|
||||
*
|
||||
* HAVE_ENDIAN_H -- have endian.h header we can include.
|
||||
* HAVE_LITTLE_ENDIAN -- we are little endian.
|
||||
* HAVE_BIG_ENDIAN -- we are big endian.
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
#define HAVE_ENDIAN_H
|
||||
#endif
|
||||
|
||||
#define HAVE_LITTLE_ENDIAN
|
||||
|
||||
/*
|
||||
* We need to choose between 32-bit and 64-bit off_t. All of our code should
|
||||
* agree on the same size. For desktop systems, use 64-bit values,
|
||||
* because some of our libraries (e.g. wxWidgets) expect to be built that way.
|
||||
*/
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE_SOURCE 1
|
||||
|
||||
/*
|
||||
* Defined if we have the backtrace() call for retrieving a stack trace.
|
||||
* Needed for CallStack to operate; if not defined, CallStack is
|
||||
* non-functional.
|
||||
*/
|
||||
#define HAVE_BACKTRACE 0
|
||||
|
||||
/*
|
||||
* Defined if we have the dladdr() call for retrieving the symbol associated
|
||||
* with a memory address. If not defined, stack crawls will not have symbolic
|
||||
* information.
|
||||
*/
|
||||
#define HAVE_DLADDR 0
|
||||
|
||||
/*
|
||||
* Defined if we have the cxxabi.h header for demangling C++ symbols. If
|
||||
* not defined, stack crawls will be displayed with raw mangled symbols
|
||||
*/
|
||||
#define HAVE_CXXABI 0
|
||||
|
||||
/*
|
||||
* Define if tm struct has tm_gmtoff field
|
||||
*/
|
||||
/* #define HAVE_TM_GMTOFF 1 */
|
||||
|
||||
/*
|
||||
* Define if dirent struct has d_type field
|
||||
*/
|
||||
/* #define HAVE_DIRENT_D_TYPE 1 */
|
||||
|
||||
/*
|
||||
* Define if libc includes Android system properties implementation.
|
||||
*/
|
||||
/* #define HAVE_LIBC_SYSTEM_PROPERTIES */
|
||||
|
||||
/*
|
||||
* Define if system provides a system property server (should be
|
||||
* mutually exclusive with HAVE_LIBC_SYSTEM_PROPERTIES).
|
||||
*/
|
||||
/* #define HAVE_SYSTEM_PROPERTY_SERVER */
|
||||
|
||||
/*
|
||||
* Define if we have madvise() in <sys/mman.h>
|
||||
*/
|
||||
/*#define HAVE_MADVISE 1*/
|
||||
|
||||
/*
|
||||
* Add any extra platform-specific defines here.
|
||||
*/
|
||||
#define WIN32 1 /* stock Cygwin doesn't define these */
|
||||
#define _WIN32 1
|
||||
#define _WIN32_WINNT 0x0500 /* admit to using >= Win2K */
|
||||
|
||||
#define HAVE_WINDOWS_PATHS /* needed by simulator */
|
||||
|
||||
/*
|
||||
* What CPU architecture does this platform use?
|
||||
*/
|
||||
#define ARCH_X86
|
||||
|
||||
/*
|
||||
* sprintf() format string for shared library naming.
|
||||
*/
|
||||
#define OS_SHARED_LIB_FORMAT_STR "lib%s.dll"
|
||||
|
||||
/*
|
||||
* type for the third argument to mincore().
|
||||
*/
|
||||
#define MINCORE_POINTER_TYPE unsigned char *
|
||||
|
||||
/*
|
||||
* The default path separator for the platform
|
||||
*/
|
||||
#define OS_PATH_SEPARATOR '\\'
|
||||
|
||||
/*
|
||||
* Is the filesystem case sensitive?
|
||||
*/
|
||||
/* #define OS_CASE_SENSITIVE */
|
||||
|
||||
/*
|
||||
* Define if <sys/socket.h> exists.
|
||||
* Cygwin has it, but not MinGW.
|
||||
*/
|
||||
#ifdef USE_MINGW
|
||||
/* #define HAVE_SYS_SOCKET_H */
|
||||
#else
|
||||
#define HAVE_SYS_SOCKET_H 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define if the strlcpy() function exists on the system.
|
||||
*/
|
||||
/* #define HAVE_STRLCPY 1 */
|
||||
|
||||
/*
|
||||
* Define if <winsock2.h> exists.
|
||||
* Only MinGW has it.
|
||||
*/
|
||||
#ifdef USE_MINGW
|
||||
#define HAVE_WINSOCK2_H 1
|
||||
#else
|
||||
/* #define HAVE_WINSOCK2_H */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Various definitions missing in MinGW
|
||||
*/
|
||||
#ifdef USE_MINGW
|
||||
#define S_IRGRP 0
|
||||
#define sleep _sleep
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define if writev() exists.
|
||||
*/
|
||||
/* #define HAVE_WRITEV */
|
||||
|
||||
#endif /*_ANDROID_CONFIG_H*/
|
70
include/ctest/ctest.h
Normal file
70
include/ctest/ctest.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Very simple unit testing framework.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_TEST_H
|
||||
#define __CUTILS_TEST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Adds a test to the test suite.
|
||||
*/
|
||||
#define addTest(test) addNamedTest(#test, &test)
|
||||
|
||||
/**
|
||||
* Asserts that a condition is true. The test fails if it isn't.
|
||||
*/
|
||||
#define assertTrue(value, message) assertTrueWithSource(value, __FILE__, __LINE__, message);
|
||||
|
||||
/**
|
||||
* Asserts that a condition is false. The test fails if the value is true.
|
||||
*/
|
||||
#define assertFalse(value, message) assertTrueWithSource(!value, __FILE__, __LINE__, message);
|
||||
|
||||
/** Fails a test with the given message. */
|
||||
#define fail(message) assertTrueWithSource(0, __FILE__, __LINE__, message);
|
||||
|
||||
/**
|
||||
* Asserts that two values are ==.
|
||||
*/
|
||||
#define assertSame(a, b) assertTrueWithSource(a == b, __FILE__, __LINE__, "Expected same value.");
|
||||
|
||||
/**
|
||||
* Asserts that two values are !=.
|
||||
*/
|
||||
#define assertNotSame(a, b) assertTrueWithSource(a != b, __FILE__, __LINE__,\
|
||||
"Expected different values");
|
||||
|
||||
/**
|
||||
* Runs a test suite.
|
||||
*/
|
||||
void runTests(void);
|
||||
|
||||
// Do not call these functions directly. Use macros above instead.
|
||||
void addNamedTest(const char* name, void (*test)(void));
|
||||
void assertTrueWithSource(int value, const char* file, int line, char* message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CUTILS_TEST_H */
|
35
include/cutils/adb_networking.h
Executable file
35
include/cutils/adb_networking.h
Executable file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _ADB_NETWORKING_H
|
||||
#define _ADB_NETWORKING_H 1
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int adb_networking_connect_fd(int fd, struct sockaddr_in *p_address);
|
||||
extern int adb_networking_gethostbyname(const char *name, struct in_addr *p_out_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*_ADB_NETWORKING_H*/
|
||||
|
67
include/cutils/array.h
Normal file
67
include/cutils/array.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A pointer array which intelligently expands its capacity ad needed.
|
||||
*/
|
||||
|
||||
#ifndef __ARRAY_H
|
||||
#define __ARRAY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/** An array. */
|
||||
typedef struct Array Array;
|
||||
|
||||
/** Constructs a new array. Returns NULL if we ran out of memory. */
|
||||
Array* arrayCreate();
|
||||
|
||||
/** Frees an array. Does not free elements themselves. */
|
||||
void arrayFree(Array* array);
|
||||
|
||||
/** Adds a pointer. Returns 0 is successful, < 0 otherwise. */
|
||||
int arrayAdd(Array* array, void* pointer);
|
||||
|
||||
/** Gets the pointer at the specified index. */
|
||||
void* arrayGet(Array* array, int index);
|
||||
|
||||
/** Removes the pointer at the given index and returns it. */
|
||||
void* arrayRemove(Array* array, int index);
|
||||
|
||||
/** Sets pointer at the given index. Returns old pointer. */
|
||||
void* arraySet(Array* array, int index, void* pointer);
|
||||
|
||||
/** Sets the array size. Sets new pointers to NULL. Returns 0 if successful, < 0 otherwise . */
|
||||
int arraySetSize(Array* array, int size);
|
||||
|
||||
/** Returns the size of the given array. */
|
||||
int arraySize(Array* array);
|
||||
|
||||
/**
|
||||
* Returns a pointer to a C-style array which will be valid until this array
|
||||
* changes.
|
||||
*/
|
||||
const void** arrayUnwrap(Array* array);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ARRAY_H */
|
42
include/cutils/ashmem.h
Normal file
42
include/cutils/ashmem.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* cutils/ashmem.h
|
||||
**
|
||||
** Copyright 2008 The Android Open Source Project
|
||||
**
|
||||
** This file is dual licensed. It may be redistributed and/or modified
|
||||
** under the terms of the Apache 2.0 License OR version 2 of the GNU
|
||||
** General Public License.
|
||||
*/
|
||||
|
||||
#ifndef _CUTILS_ASHMEM_H
|
||||
#define _CUTILS_ASHMEM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ashmem_create_region(const char *name, size_t size);
|
||||
int ashmem_set_prot_region(int fd, int prot);
|
||||
int ashmem_pin_region(int fd, size_t offset, size_t len);
|
||||
int ashmem_unpin_region(int fd, size_t offset, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __ASHMEMIOC /* in case someone included <linux/ashmem.h> too */
|
||||
|
||||
#define ASHMEM_NAME_LEN 256
|
||||
|
||||
#define ASHMEM_NAME_DEF "dev/ashmem"
|
||||
|
||||
/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
|
||||
#define ASHMEM_NOT_PURGED 0
|
||||
#define ASHMEM_WAS_PURGED 1
|
||||
|
||||
/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */
|
||||
#define ASHMEM_IS_UNPINNED 0
|
||||
#define ASHMEM_IS_PINNED 1
|
||||
|
||||
#endif /* ! __ASHMEMIOC */
|
||||
|
||||
#endif /* _CUTILS_ASHMEM_H */
|
79
include/cutils/atomic.h
Normal file
79
include/cutils/atomic.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_CUTILS_ATOMIC_H
|
||||
#define ANDROID_CUTILS_ATOMIC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NOTE: memory shared between threads is synchronized by all atomic operations
|
||||
* below, this means that no explicit memory barrier is required: all reads or
|
||||
* writes issued before android_atomic_* operations are guaranteed to complete
|
||||
* before the atomic operation takes place.
|
||||
*/
|
||||
|
||||
void android_atomic_write(int32_t value, volatile int32_t* addr);
|
||||
|
||||
/*
|
||||
* all these atomic operations return the previous value
|
||||
*/
|
||||
|
||||
|
||||
int32_t android_atomic_inc(volatile int32_t* addr);
|
||||
int32_t android_atomic_dec(volatile int32_t* addr);
|
||||
|
||||
int32_t android_atomic_add(int32_t value, volatile int32_t* addr);
|
||||
int32_t android_atomic_and(int32_t value, volatile int32_t* addr);
|
||||
int32_t android_atomic_or(int32_t value, volatile int32_t* addr);
|
||||
|
||||
int32_t android_atomic_swap(int32_t value, volatile int32_t* addr);
|
||||
|
||||
/*
|
||||
* NOTE: Two "quasiatomic" operations on the exact same memory address
|
||||
* are guaranteed to operate atomically with respect to each other,
|
||||
* but no guarantees are made about quasiatomic operations mixed with
|
||||
* non-quasiatomic operations on the same address, nor about
|
||||
* quasiatomic operations that are performed on partially-overlapping
|
||||
* memory.
|
||||
*/
|
||||
|
||||
int64_t android_quasiatomic_swap_64(int64_t value, volatile int64_t* addr);
|
||||
int64_t android_quasiatomic_read_64(volatile int64_t* addr);
|
||||
|
||||
/*
|
||||
* cmpxchg return a non zero value if the exchange was NOT performed,
|
||||
* in other words if oldvalue != *addr
|
||||
*/
|
||||
|
||||
int android_atomic_cmpxchg(int32_t oldvalue, int32_t newvalue,
|
||||
volatile int32_t* addr);
|
||||
|
||||
int android_quasiatomic_cmpxchg_64(int64_t oldvalue, int64_t newvalue,
|
||||
volatile int64_t* addr);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // ANDROID_CUTILS_ATOMIC_H
|
61
include/cutils/config_utils.h
Normal file
61
include/cutils/config_utils.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_CONFIG_UTILS_H
|
||||
#define __CUTILS_CONFIG_UTILS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct cnode cnode;
|
||||
|
||||
|
||||
struct cnode
|
||||
{
|
||||
cnode *next;
|
||||
cnode *first_child;
|
||||
cnode *last_child;
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
/* parse a text string into a config node tree */
|
||||
void config_load(cnode *root, char *data);
|
||||
|
||||
/* parse a file into a config node tree */
|
||||
void config_load_file(cnode *root, const char *fn);
|
||||
|
||||
/* create a single config node */
|
||||
cnode* config_node(const char *name, const char *value);
|
||||
|
||||
/* locate a named child of a config node */
|
||||
cnode* config_find(cnode *root, const char *name);
|
||||
|
||||
/* look up a child by name and return the boolean value */
|
||||
int config_bool(cnode *root, const char *name, int _default);
|
||||
|
||||
/* look up a child by name and return the string value */
|
||||
const char* config_str(cnode *root, const char *name, const char *_default);
|
||||
|
||||
/* add a named child to a config node (or modify it if it already exists) */
|
||||
void config_set(cnode *root, const char *name, const char *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
34
include/cutils/cpu_info.h
Normal file
34
include/cutils/cpu_info.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_CPU_INFO_H
|
||||
#define __CUTILS_CPU_INFO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* returns a string contiaining an ASCII representation of the CPU serial number,
|
||||
** or NULL if cpu info not available.
|
||||
** The string is a static variable, so don't call free() on it.
|
||||
*/
|
||||
extern const char* get_cpu_serial_number(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CUTILS_CPU_INFO_H */
|
26
include/cutils/dir_hash.h
Normal file
26
include/cutils/dir_hash.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
SHA_1,
|
||||
} HashAlgorithm;
|
||||
|
||||
int get_file_hash(HashAlgorithm algorithm, const char *path,
|
||||
char *output_string, size_t max_output_string);
|
||||
|
||||
int get_recursive_hash_manifest(HashAlgorithm algorithm,
|
||||
const char *directory_path,
|
||||
char **output_string);
|
50
include/cutils/event_tag_map.h
Normal file
50
include/cutils/event_tag_map.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBS_CUTILS_EVENTTAGMAP_H
|
||||
#define _LIBS_CUTILS_EVENTTAGMAP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"
|
||||
|
||||
struct EventTagMap;
|
||||
typedef struct EventTagMap EventTagMap;
|
||||
|
||||
/*
|
||||
* Open the specified file as an event log tag map.
|
||||
*
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
EventTagMap* android_openEventTagMap(const char* fileName);
|
||||
|
||||
/*
|
||||
* Close the map.
|
||||
*/
|
||||
void android_closeEventTagMap(EventTagMap* map);
|
||||
|
||||
/*
|
||||
* Look up a tag by index. Returns the tag string, or NULL if not found.
|
||||
*/
|
||||
const char* android_lookupEventTag(const EventTagMap* map, int tag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/
|
74
include/cutils/fdevent.h
Normal file
74
include/cutils/fdevent.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef __FDEVENT_H
|
||||
#define __FDEVENT_H
|
||||
|
||||
/* events that may be observed */
|
||||
#define FDE_READ 0x0001
|
||||
#define FDE_WRITE 0x0002
|
||||
#define FDE_ERROR 0x0004
|
||||
|
||||
/* features that may be set (via the events set/add/del interface) */
|
||||
#define FDE_DONT_CLOSE 0x0080
|
||||
|
||||
typedef struct fdevent fdevent;
|
||||
|
||||
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
|
||||
|
||||
/* Allocate and initialize a new fdevent object
|
||||
*/
|
||||
fdevent *fdevent_create(int fd, fd_func func, void *arg);
|
||||
|
||||
/* Uninitialize and deallocate an fdevent object that was
|
||||
** created by fdevent_create()
|
||||
*/
|
||||
void fdevent_destroy(fdevent *fde);
|
||||
|
||||
/* Initialize an fdevent object that was externally allocated
|
||||
*/
|
||||
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
|
||||
|
||||
/* Uninitialize an fdevent object that was initialized by
|
||||
** fdevent_install()
|
||||
*/
|
||||
void fdevent_remove(fdevent *item);
|
||||
|
||||
/* Change which events should cause notifications
|
||||
*/
|
||||
void fdevent_set(fdevent *fde, unsigned events);
|
||||
void fdevent_add(fdevent *fde, unsigned events);
|
||||
void fdevent_del(fdevent *fde, unsigned events);
|
||||
|
||||
/* loop forever, handling events.
|
||||
*/
|
||||
void fdevent_loop();
|
||||
|
||||
struct fdevent
|
||||
{
|
||||
fdevent *next;
|
||||
fdevent *prev;
|
||||
|
||||
int fd;
|
||||
unsigned short state;
|
||||
unsigned short events;
|
||||
|
||||
fd_func func;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
150
include/cutils/hashmap.h
Normal file
150
include/cutils/hashmap.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hash map.
|
||||
*/
|
||||
|
||||
#ifndef __HASHMAP_H
|
||||
#define __HASHMAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** A hash map. */
|
||||
typedef struct Hashmap Hashmap;
|
||||
|
||||
/**
|
||||
* Creates a new hash map. Returns NULL if memory allocation fails.
|
||||
*
|
||||
* @param initialCapacity number of expected entries
|
||||
* @param hash function which hashes keys
|
||||
* @param equals function which compares keys for equality
|
||||
*/
|
||||
Hashmap* hashmapCreate(size_t initialCapacity,
|
||||
int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB));
|
||||
|
||||
/**
|
||||
* Frees the hash map. Does not free the keys or values themselves.
|
||||
*/
|
||||
void hashmapFree(Hashmap* map);
|
||||
|
||||
/**
|
||||
* Hashes the memory pointed to by key with the given size. Useful for
|
||||
* implementing hash functions.
|
||||
*/
|
||||
int hashmapHash(void* key, size_t keySize);
|
||||
|
||||
/**
|
||||
* Puts value for the given key in the map. Returns pre-existing value if
|
||||
* any.
|
||||
*
|
||||
* If memory allocation fails, this function returns NULL, the map's size
|
||||
* does not increase, and errno is set to ENOMEM.
|
||||
*/
|
||||
void* hashmapPut(Hashmap* map, void* key, void* value);
|
||||
|
||||
/**
|
||||
* Gets a value from the map. Returns NULL if no entry for the given key is
|
||||
* found or if the value itself is NULL.
|
||||
*/
|
||||
void* hashmapGet(Hashmap* map, void* key);
|
||||
|
||||
/**
|
||||
* Returns true if the map contains an entry for the given key.
|
||||
*/
|
||||
bool hashmapContainsKey(Hashmap* map, void* key);
|
||||
|
||||
/**
|
||||
* Gets the value for a key. If a value is not found, this function gets a
|
||||
* value and creates an entry using the given callback.
|
||||
*
|
||||
* If memory allocation fails, the callback is not called, this function
|
||||
* returns NULL, and errno is set to ENOMEM.
|
||||
*/
|
||||
void* hashmapMemoize(Hashmap* map, void* key,
|
||||
void* (*initialValue)(void* key, void* context), void* context);
|
||||
|
||||
/**
|
||||
* Removes an entry from the map. Returns the removed value or NULL if no
|
||||
* entry was present.
|
||||
*/
|
||||
void* hashmapRemove(Hashmap* map, void* key);
|
||||
|
||||
/**
|
||||
* Gets the number of entries in this map.
|
||||
*/
|
||||
size_t hashmapSize(Hashmap* map);
|
||||
|
||||
/**
|
||||
* Invokes the given callback on each entry in the map. Stops iterating if
|
||||
* the callback returns false.
|
||||
*/
|
||||
void hashmapForEach(Hashmap* map,
|
||||
bool (*callback)(void* key, void* value, void* context),
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Concurrency support.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Locks the hash map so only the current thread can access it.
|
||||
*/
|
||||
void hashmapLock(Hashmap* map);
|
||||
|
||||
/**
|
||||
* Unlocks the hash map so other threads can access it.
|
||||
*/
|
||||
void hashmapUnlock(Hashmap* map);
|
||||
|
||||
/**
|
||||
* Key utilities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hashes int keys. 'key' is a pointer to int.
|
||||
*/
|
||||
int hashmapIntHash(void* key);
|
||||
|
||||
/**
|
||||
* Compares two int keys for equality.
|
||||
*/
|
||||
bool hashmapIntEquals(void* keyA, void* keyB);
|
||||
|
||||
/**
|
||||
* For debugging.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets current capacity.
|
||||
*/
|
||||
size_t hashmapCurrentCapacity(Hashmap* map);
|
||||
|
||||
/**
|
||||
* Counts the number of entry collisions.
|
||||
*/
|
||||
size_t hashmapCountCollisions(Hashmap* map);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __HASHMAP_H */
|
43
include/cutils/jstring.h
Normal file
43
include/cutils/jstring.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_STRING16_H
|
||||
#define __CUTILS_STRING16_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint16_t char16_t;
|
||||
|
||||
extern char * strndup16to8 (const char16_t* s, size_t n);
|
||||
extern size_t strnlen16to8 (const char16_t* s, size_t n);
|
||||
extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n);
|
||||
|
||||
extern char16_t * strdup8to16 (const char* s, size_t *out_len);
|
||||
extern size_t strlen8to16 (const char* utf8Str);
|
||||
extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len);
|
||||
extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length,
|
||||
size_t *out_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CUTILS_STRING16_H */
|
346
include/cutils/log.h
Normal file
346
include/cutils/log.h
Normal file
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
|
||||
//
|
||||
// C/C++ logging functions. See the logging documentation for API details.
|
||||
//
|
||||
// We'd like these to be available from C code (in case we import some from
|
||||
// somewhere), so this has a C interface.
|
||||
//
|
||||
// The output will be correct when the log file is shared between multiple
|
||||
// threads and/or multiple processes so long as the operating system
|
||||
// supports O_APPEND. These calls have mutex-protected data structures
|
||||
// and so are NOT reentrant. Do not use LOG in a signal handler.
|
||||
//
|
||||
#ifndef _LIBS_CUTILS_LOG_H
|
||||
#define _LIBS_CUTILS_LOG_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_PTHREADS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <cutils/uio.h>
|
||||
#include <cutils/logd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Normally we strip LOGV (VERBOSE messages) from release builds.
|
||||
* You can modify this (for example with "#define LOG_NDEBUG 0"
|
||||
* at the top of your source file) to change that behavior.
|
||||
*/
|
||||
#ifndef LOG_NDEBUG
|
||||
#ifdef NDEBUG
|
||||
#define LOG_NDEBUG 1
|
||||
#else
|
||||
#define LOG_NDEBUG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the local tag used for the following simplified
|
||||
* logging macros. You can change this preprocessor definition
|
||||
* before using the other macros to change the tag.
|
||||
*/
|
||||
#ifndef LOG_TAG
|
||||
#define LOG_TAG NULL
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Simplified macro to send a verbose log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef LOGV
|
||||
#if LOG_NDEBUG
|
||||
#define LOGV(...) ((void)0)
|
||||
#else
|
||||
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
|
||||
#ifndef LOGV_IF
|
||||
#if LOG_NDEBUG
|
||||
#define LOGV_IF(cond, ...) ((void)0)
|
||||
#else
|
||||
#define LOGV_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a debug log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef LOGD
|
||||
#define LOGD(...) ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef LOGD_IF
|
||||
#define LOGD_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an info log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef LOGI
|
||||
#define LOGI(...) ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef LOGI_IF
|
||||
#define LOGI_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)LOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send a warning log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef LOGW
|
||||
#define LOGW(...) ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef LOGW_IF
|
||||
#define LOGW_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)LOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Simplified macro to send an error log message using the current LOG_TAG.
|
||||
*/
|
||||
#ifndef LOGE
|
||||
#define LOGE(...) ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef LOGE_IF
|
||||
#define LOGE_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)LOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* verbose priority.
|
||||
*/
|
||||
#ifndef IF_LOGV
|
||||
#if LOG_NDEBUG
|
||||
#define IF_LOGV() if (false)
|
||||
#else
|
||||
#define IF_LOGV() IF_LOG(LOG_VERBOSE, LOG_TAG)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* debug priority.
|
||||
*/
|
||||
#ifndef IF_LOGD
|
||||
#define IF_LOGD() IF_LOG(LOG_DEBUG, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* info priority.
|
||||
*/
|
||||
#ifndef IF_LOGI
|
||||
#define IF_LOGI() IF_LOG(LOG_INFO, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* warn priority.
|
||||
*/
|
||||
#ifndef IF_LOGW
|
||||
#define IF_LOGW() IF_LOG(LOG_WARN, LOG_TAG)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional based on whether the current LOG_TAG is enabled at
|
||||
* error priority.
|
||||
*/
|
||||
#ifndef IF_LOGE
|
||||
#define IF_LOGE() IF_LOG(LOG_ERROR, LOG_TAG)
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Log a fatal error. If the given condition fails, this stops program
|
||||
* execution like a normal assertion, but also generating the given message.
|
||||
* It is NOT stripped from release builds. Note that the condition test
|
||||
* is -inverted- from the normal assert() semantics.
|
||||
*/
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
|
||||
#define LOG_ALWAYS_FATAL(...) \
|
||||
( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) )
|
||||
|
||||
/*
|
||||
* Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
|
||||
* are stripped out of release builds.
|
||||
*/
|
||||
#if LOG_NDEBUG
|
||||
|
||||
#define LOG_FATAL_IF(cond, ...) ((void)0)
|
||||
#define LOG_FATAL(...) ((void)0)
|
||||
|
||||
#else
|
||||
|
||||
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
|
||||
#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assertion that generates a log message when the assertion fails.
|
||||
* Stripped out of release builds. Uses the current LOG_TAG.
|
||||
*/
|
||||
#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__)
|
||||
//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Basic log message macro.
|
||||
*
|
||||
* Example:
|
||||
* LOG(LOG_WARN, NULL, "Failed with error %d", errno);
|
||||
*
|
||||
* The second argument may be NULL or "" to indicate the "global" tag.
|
||||
*/
|
||||
#ifndef LOG
|
||||
#define LOG(priority, tag, ...) \
|
||||
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to specify a number for the priority.
|
||||
*/
|
||||
#ifndef LOG_PRI
|
||||
#define LOG_PRI(priority, tag, ...) \
|
||||
android_printLog(priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Log macro that allows you to pass in a varargs ("args" is a va_list).
|
||||
*/
|
||||
#ifndef LOG_PRI_VA
|
||||
#define LOG_PRI_VA(priority, tag, fmt, args) \
|
||||
android_vprintLog(priority, NULL, tag, fmt, args)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conditional given a desired logging priority and tag.
|
||||
*/
|
||||
#ifndef IF_LOG
|
||||
#define IF_LOG(priority, tag) \
|
||||
if (android_testLog(ANDROID_##priority, tag))
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Event logging.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Event log entry types. These must match up with the declarations in
|
||||
* java/android/android/util/EventLog.java.
|
||||
*/
|
||||
typedef enum {
|
||||
EVENT_TYPE_INT = 0,
|
||||
EVENT_TYPE_LONG = 1,
|
||||
EVENT_TYPE_STRING = 2,
|
||||
EVENT_TYPE_LIST = 3,
|
||||
} AndroidEventLogType;
|
||||
|
||||
|
||||
#define LOG_EVENT_INT(_tag, _value) { \
|
||||
int intBuf = _value; \
|
||||
(void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \
|
||||
sizeof(intBuf)); \
|
||||
}
|
||||
#define LOG_EVENT_LONG(_tag, _value) { \
|
||||
long long longBuf = _value; \
|
||||
(void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \
|
||||
sizeof(longBuf)); \
|
||||
}
|
||||
#define LOG_EVENT_STRING(_tag, _value) \
|
||||
((void) 0) /* not implemented -- must combine len with string */
|
||||
/* TODO: something for LIST */
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
*
|
||||
* The stuff in the rest of this file should not be used directly.
|
||||
*/
|
||||
|
||||
#define android_printLog(prio, tag, fmt...) \
|
||||
__android_log_print(prio, tag, fmt)
|
||||
|
||||
#define android_vprintLog(prio, cond, tag, fmt...) \
|
||||
__android_log_vprint(prio, tag, fmt)
|
||||
|
||||
#define android_printAssert(cond, tag, fmt...) \
|
||||
__android_log_assert(cond, tag, fmt)
|
||||
|
||||
#define android_writeLog(prio, tag, text) \
|
||||
__android_log_write(prio, tag, text)
|
||||
|
||||
#define android_bWriteLog(tag, payload, len) \
|
||||
__android_log_bwrite(tag, payload, len)
|
||||
#define android_btWriteLog(tag, type, payload, len) \
|
||||
__android_log_btwrite(tag, type, payload, len)
|
||||
|
||||
// TODO: remove these prototypes and their users
|
||||
#define android_testLog(prio, tag) (1)
|
||||
#define android_writevLog(vec,num) do{}while(0)
|
||||
#define android_write1Log(str,len) do{}while (0)
|
||||
#define android_setMinPriority(tag, prio) do{}while(0)
|
||||
//#define android_logToCallback(func) do{}while(0)
|
||||
#define android_logToFile(tag, file) (0)
|
||||
#define android_logToFd(tag, fd) (0)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _LIBS_CUTILS_LOG_H
|
78
include/cutils/logd.h
Normal file
78
include/cutils/logd.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_CUTILS_LOGD_H
|
||||
#define _ANDROID_CUTILS_LOGD_H
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef HAVE_PTHREADS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <cutils/uio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Priority values, in ascending priority order.
|
||||
*/
|
||||
typedef enum android_LogPriority {
|
||||
ANDROID_LOG_UNKNOWN = 0,
|
||||
ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
|
||||
ANDROID_LOG_VERBOSE,
|
||||
ANDROID_LOG_DEBUG,
|
||||
ANDROID_LOG_INFO,
|
||||
ANDROID_LOG_WARN,
|
||||
ANDROID_LOG_ERROR,
|
||||
ANDROID_LOG_FATAL,
|
||||
ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
|
||||
} android_LogPriority;
|
||||
|
||||
int __android_log_write(int prio, const char *tag, const char *text);
|
||||
|
||||
int __android_log_vprint(int prio, const char *tag,
|
||||
const char *fmt, va_list ap);
|
||||
|
||||
int __android_log_bwrite(int32_t tag, const void *payload, size_t len);
|
||||
int __android_log_btwrite(int32_t tag, char type, const void *payload,
|
||||
size_t len);
|
||||
|
||||
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
|
||||
#if defined(__GNUC__)
|
||||
__attribute__ ((format(printf, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
void __android_log_assert(const char *cond, const char *tag,
|
||||
const char *fmt, ...)
|
||||
#if defined(__GNUC__)
|
||||
__attribute__ ((noreturn))
|
||||
__attribute__ ((format(printf, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LOGD_H */
|
156
include/cutils/logprint.h
Normal file
156
include/cutils/logprint.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _LOGPRINT_H
|
||||
#define _LOGPRINT_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/logger.h>
|
||||
#include <cutils/event_tag_map.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
FORMAT_OFF = 0,
|
||||
FORMAT_BRIEF,
|
||||
FORMAT_PROCESS,
|
||||
FORMAT_TAG,
|
||||
FORMAT_THREAD,
|
||||
FORMAT_RAW,
|
||||
FORMAT_TIME,
|
||||
FORMAT_THREADTIME,
|
||||
FORMAT_LONG,
|
||||
} AndroidLogPrintFormat;
|
||||
|
||||
typedef struct AndroidLogFormat_t AndroidLogFormat;
|
||||
|
||||
typedef struct AndroidLogEntry_t {
|
||||
time_t tv_sec;
|
||||
long tv_nsec;
|
||||
android_LogPriority priority;
|
||||
pid_t pid;
|
||||
pthread_t tid;
|
||||
const char * tag;
|
||||
size_t messageLen;
|
||||
const char * message;
|
||||
} AndroidLogEntry;
|
||||
|
||||
AndroidLogFormat *android_log_format_new();
|
||||
|
||||
void android_log_format_free(AndroidLogFormat *p_format);
|
||||
|
||||
void android_log_setPrintFormat(AndroidLogFormat *p_format,
|
||||
AndroidLogPrintFormat format);
|
||||
|
||||
/**
|
||||
* Returns FORMAT_OFF on invalid string
|
||||
*/
|
||||
AndroidLogPrintFormat android_log_formatFromString(const char *s);
|
||||
|
||||
/**
|
||||
* filterExpression: a single filter expression
|
||||
* eg "AT:d"
|
||||
*
|
||||
* returns 0 on success and -1 on invalid expression
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
|
||||
int android_log_addFilterRule(AndroidLogFormat *p_format,
|
||||
const char *filterExpression);
|
||||
|
||||
|
||||
/**
|
||||
* filterString: a whitespace-separated set of filter expressions
|
||||
* eg "AT:d *:i"
|
||||
*
|
||||
* returns 0 on success and -1 on invalid expression
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
|
||||
int android_log_addFilterString(AndroidLogFormat *p_format,
|
||||
const char *filterString);
|
||||
|
||||
|
||||
/**
|
||||
* returns 1 if this log line should be printed based on its priority
|
||||
* and tag, and 0 if it should not
|
||||
*/
|
||||
int android_log_shouldPrintLine (
|
||||
AndroidLogFormat *p_format, const char *tag, android_LogPriority pri);
|
||||
|
||||
|
||||
/**
|
||||
* Splits a wire-format buffer into an AndroidLogEntry
|
||||
* entry allocated by caller. Pointers will point directly into buf
|
||||
*
|
||||
* Returns 0 on success and -1 on invalid wire format (entry will be
|
||||
* in unspecified state)
|
||||
*/
|
||||
int android_log_processLogBuffer(struct logger_entry *buf,
|
||||
AndroidLogEntry *entry);
|
||||
|
||||
/**
|
||||
* Like android_log_processLogBuffer, but for binary logs.
|
||||
*
|
||||
* If "map" is non-NULL, it will be used to convert the log tag number
|
||||
* into a string.
|
||||
*/
|
||||
int android_log_processBinaryLogBuffer(struct logger_entry *buf,
|
||||
AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
|
||||
int messageBufLen);
|
||||
|
||||
|
||||
/**
|
||||
* Formats a log message into a buffer
|
||||
*
|
||||
* Uses defaultBuffer if it can, otherwise malloc()'s a new buffer
|
||||
* If return value != defaultBuffer, caller must call free()
|
||||
* Returns NULL on malloc error
|
||||
*/
|
||||
|
||||
char *android_log_formatLogLine (
|
||||
AndroidLogFormat *p_format,
|
||||
char *defaultBuffer,
|
||||
size_t defaultBufferSize,
|
||||
const AndroidLogEntry *p_line,
|
||||
size_t *p_outLength);
|
||||
|
||||
|
||||
/**
|
||||
* Either print or do not print log line, based on filter
|
||||
*
|
||||
* Assumes single threaded execution
|
||||
*
|
||||
*/
|
||||
int android_log_filterAndPrintLogLine(
|
||||
AndroidLogFormat *p_format,
|
||||
int fd,
|
||||
const AndroidLogEntry *entry);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*_LOGPRINT_H*/
|
42
include/cutils/memory.h
Normal file
42
include/cutils/memory.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_CUTILS_MEMORY_H
|
||||
#define ANDROID_CUTILS_MEMORY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* size is given in bytes and must be multiple of 2 */
|
||||
void android_memset16(uint16_t* dst, uint16_t value, size_t size);
|
||||
|
||||
/* size is given in bytes and must be multiple of 4 */
|
||||
void android_memset32(uint32_t* dst, uint32_t value, size_t size);
|
||||
|
||||
#if !HAVE_STRLCPY
|
||||
/* Declaration of strlcpy() for platforms that don't already have it. */
|
||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // ANDROID_CUTILS_MEMORY_H
|
48
include/cutils/misc.h
Normal file
48
include/cutils/misc.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_MISC_H
|
||||
#define __CUTILS_MISC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Load an entire file into a malloc'd chunk of memory
|
||||
* that is length_of_file + 1 (null terminator). If
|
||||
* sz is non-zero, return the size of the file via sz.
|
||||
* Returns 0 on failure.
|
||||
*/
|
||||
extern void *load_file(const char *fn, unsigned *sz);
|
||||
|
||||
/* Connects your process to the system debugger daemon
|
||||
* so that on a crash it may be logged or interactively
|
||||
* debugged (depending on system settings).
|
||||
*/
|
||||
extern void debuggerd_connect(void);
|
||||
|
||||
|
||||
/* This is the range of UIDs (and GIDs) that are reserved
|
||||
* for assigning to applications.
|
||||
*/
|
||||
#define FIRST_APPLICATION_UID 10000
|
||||
#define LAST_APPLICATION_UID 99999
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CUTILS_MISC_H */
|
124
include/cutils/mq.h
Normal file
124
include/cutils/mq.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* IPC messaging library.
|
||||
*/
|
||||
|
||||
#ifndef __MQ_H
|
||||
#define __MQ_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** A message. */
|
||||
typedef struct MqMessage MqMessage;
|
||||
|
||||
/** A destination to which messages can be sent. */
|
||||
typedef struct MqDestination MqDestination;
|
||||
|
||||
/* Array of bytes. */
|
||||
typedef struct MqBytes MqBytes;
|
||||
|
||||
/**
|
||||
* Hears messages.
|
||||
*
|
||||
* @param destination to which the message was sent
|
||||
* @param message the message to hear
|
||||
*/
|
||||
typedef void MqMessageListener(MqDestination* destination, MqMessage* message);
|
||||
|
||||
/**
|
||||
* Hears a destination close.
|
||||
*
|
||||
* @param destination that closed
|
||||
*/
|
||||
typedef void MqCloseListener(MqDestination* destination);
|
||||
|
||||
/** Message functions. */
|
||||
|
||||
/**
|
||||
* Creates a new Message.
|
||||
*
|
||||
* @param header as defined by user
|
||||
* @param body as defined by user
|
||||
* @param replyTo destination to which replies should be sent, NULL if none
|
||||
*/
|
||||
MqMessage* mqCreateMessage(MqBytes header, MqBytes body,
|
||||
MqDestination* replyTo);
|
||||
|
||||
/** Sends a message to a destination. */
|
||||
void mqSendMessage(MqMessage* message, MqDestination* destination);
|
||||
|
||||
/** Destination functions. */
|
||||
|
||||
/**
|
||||
* Creates a new destination. Acquires a reference implicitly.
|
||||
*
|
||||
* @param messageListener function to call when a message is recieved
|
||||
* @param closeListener function to call when the destination closes
|
||||
* @param userData user-specific data to associate with the destination.
|
||||
* Retrieve using mqGetDestinationUserData().
|
||||
*/
|
||||
MqDestination* mqCreateDestination(MqMessageListener* messageListener,
|
||||
MqCloseListener* closeListener, void* userData);
|
||||
|
||||
/**
|
||||
* Gets user data which was associated with the given destination at
|
||||
* construction time.
|
||||
*
|
||||
* It is only valid to call this function in the same process that the
|
||||
* given destination was created in.
|
||||
* This function returns a null pointer if you call it on a destination
|
||||
* created in a remote process.
|
||||
*/
|
||||
void* mqGetUserData(MqDestination* destination);
|
||||
|
||||
/**
|
||||
* Returns 1 if the destination was created in this process, or 0 if
|
||||
* the destination was created in a different process, in which case you have
|
||||
* a remote stub.
|
||||
*/
|
||||
int mqIsDestinationLocal(MqDestination* destination);
|
||||
|
||||
/**
|
||||
* Increments the destination's reference count.
|
||||
*/
|
||||
void mqKeepDestination(MqDesintation* destination);
|
||||
|
||||
/**
|
||||
* Decrements the destination's reference count.
|
||||
*/
|
||||
void mqFreeDestination(MqDestination* desintation);
|
||||
|
||||
/** Registry API. */
|
||||
|
||||
/**
|
||||
* Gets the destination bound to a name.
|
||||
*/
|
||||
MqDestination* mqGetDestination(char* name);
|
||||
|
||||
/**
|
||||
* Binds a destination to a name.
|
||||
*/
|
||||
void mqPutDestination(char* name, MqDestination* desintation);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __MQ_H */
|
117
include/cutils/mspace.h
Normal file
117
include/cutils/mspace.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
/* A wrapper file for dlmalloc.h that defines prototypes for the
|
||||
* mspace_*() functions, which provide an interface for creating
|
||||
* multiple heaps.
|
||||
*/
|
||||
|
||||
#ifndef MSPACE_H_
|
||||
#define MSPACE_H_
|
||||
|
||||
/* It's a pain getting the mallinfo stuff to work
|
||||
* with Linux, OSX, and klibc, so just turn it off
|
||||
* for now.
|
||||
* TODO: make mallinfo work
|
||||
*/
|
||||
#define NO_MALLINFO 1
|
||||
|
||||
/* Allow setting the maximum heap footprint.
|
||||
*/
|
||||
#define USE_MAX_ALLOWED_FOOTPRINT 1
|
||||
|
||||
#define USE_CONTIGUOUS_MSPACES 1
|
||||
#if USE_CONTIGUOUS_MSPACES
|
||||
#define HAVE_MMAP 0
|
||||
#define HAVE_MORECORE 1
|
||||
#define MORECORE_CONTIGUOUS 0
|
||||
#endif
|
||||
|
||||
#define MSPACES 1
|
||||
#define ONLY_MSPACES 1
|
||||
#include "../../../../bionic/libc/bionic/dlmalloc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
mspace_usable_size(void* p);
|
||||
|
||||
Returns the number of bytes you can actually use in
|
||||
an allocated chunk, which may be more than you requested (although
|
||||
often not) due to alignment and minimum size constraints.
|
||||
You can use this many bytes without worrying about
|
||||
overwriting other allocated objects. This is not a particularly great
|
||||
programming practice. mspace_usable_size can be more useful in
|
||||
debugging and assertions, for example:
|
||||
|
||||
p = mspace_malloc(msp, n);
|
||||
assert(mspace_usable_size(msp, p) >= 256);
|
||||
*/
|
||||
size_t mspace_usable_size(mspace, const void*);
|
||||
|
||||
#if USE_CONTIGUOUS_MSPACES
|
||||
/*
|
||||
Similar to create_mspace(), but the underlying memory is
|
||||
guaranteed to be contiguous. No more than max_capacity
|
||||
bytes is ever allocated to the mspace.
|
||||
*/
|
||||
mspace create_contiguous_mspace(size_t starting_capacity, size_t max_capacity,
|
||||
int locked);
|
||||
|
||||
/*
|
||||
Identical to create_contiguous_mspace, but labels the mapping 'mspace/name'
|
||||
instead of 'mspace'
|
||||
*/
|
||||
mspace create_contiguous_mspace_with_name(size_t starting_capacity,
|
||||
size_t max_capacity, int locked, const char *name);
|
||||
|
||||
size_t destroy_contiguous_mspace(mspace msp);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Call the handler for each block in the specified mspace.
|
||||
chunkptr and chunklen refer to the heap-level chunk including
|
||||
the chunk overhead, and userptr and userlen refer to the
|
||||
user-usable part of the chunk. If the chunk is free, userptr
|
||||
will be NULL and userlen will be 0. userlen is not guaranteed
|
||||
to be the same value passed into malloc() for a given chunk;
|
||||
it is >= the requested size.
|
||||
*/
|
||||
void mspace_walk_heap(mspace msp,
|
||||
void(*handler)(const void *chunkptr, size_t chunklen,
|
||||
const void *userptr, size_t userlen, void *arg), void *harg);
|
||||
|
||||
/*
|
||||
mspace_walk_free_pages(handler, harg)
|
||||
|
||||
Calls the provided handler on each free region in the specified
|
||||
mspace. The memory between start and end are guaranteed not to
|
||||
contain any important data, so the handler is free to alter the
|
||||
contents in any way. This can be used to advise the OS that large
|
||||
free regions may be swapped out.
|
||||
|
||||
The value in harg will be passed to each call of the handler.
|
||||
*/
|
||||
void mspace_walk_free_pages(mspace msp,
|
||||
void(*handler)(void *start, void *end, void *arg), void *harg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}; /* end of extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* MSPACE_H_ */
|
42
include/cutils/process_name.h
Normal file
42
include/cutils/process_name.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2008 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gives the current process a name.
|
||||
*/
|
||||
|
||||
#ifndef __PROCESS_NAME_H
|
||||
#define __PROCESS_NAME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets the current process name.
|
||||
*
|
||||
* Warning: This leaks a string every time you call it. Use judiciously!
|
||||
*/
|
||||
void set_process_name(const char* process_name);
|
||||
|
||||
/** Gets the current process name. */
|
||||
const char* get_process_name(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PROCESS_NAME_H */
|
70
include/cutils/properties.h
Normal file
70
include/cutils/properties.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_PROPERTIES_H
|
||||
#define __CUTILS_PROPERTIES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* System properties are *small* name value pairs managed by the
|
||||
** property service. If your data doesn't fit in the provided
|
||||
** space it is not appropriate for a system property.
|
||||
**
|
||||
** WARNING: system/bionic/include/sys/system_properties.h also defines
|
||||
** these, but with different names. (TODO: fix that)
|
||||
*/
|
||||
#define PROPERTY_KEY_MAX 32
|
||||
#define PROPERTY_VALUE_MAX 92
|
||||
|
||||
/* property_get: returns the length of the value which will never be
|
||||
** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.
|
||||
** (the length does not include the terminating zero).
|
||||
**
|
||||
** If the property read fails or returns an empty value, the default
|
||||
** value is used (if nonnull).
|
||||
*/
|
||||
int property_get(const char *key, char *value, const char *default_value);
|
||||
|
||||
/* property_set: returns 0 on success, < 0 on failure
|
||||
*/
|
||||
int property_set(const char *key, const char *value);
|
||||
|
||||
int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
|
||||
|
||||
|
||||
#ifdef HAVE_SYSTEM_PROPERTY_SERVER
|
||||
/*
|
||||
* We have an external property server instead of built-in libc support.
|
||||
* Used by the simulator.
|
||||
*/
|
||||
#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop"
|
||||
|
||||
enum {
|
||||
kSystemPropertyUnknown = 0,
|
||||
kSystemPropertyGet,
|
||||
kSystemPropertySet,
|
||||
kSystemPropertyList
|
||||
};
|
||||
#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
43
include/cutils/record_stream.h
Normal file
43
include/cutils/record_stream.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A simple utility for reading fixed records out of a stream fd
|
||||
*/
|
||||
|
||||
#ifndef _CUTILS_RECORD_STREAM_H
|
||||
#define _CUTILS_RECORD_STREAM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct RecordStream RecordStream;
|
||||
|
||||
extern RecordStream *record_stream_new(int fd, size_t maxRecordLen);
|
||||
extern void record_stream_free(RecordStream *p_rs);
|
||||
|
||||
extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
|
||||
size_t *p_outRecordLen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /*_CUTILS_RECORD_STREAM_H*/
|
||||
|
130
include/cutils/selector.h
Normal file
130
include/cutils/selector.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Framework for multiplexing I/O. A selector manages a set of file
|
||||
* descriptors and calls out to user-provided callback functions to read and
|
||||
* write data and handle errors.
|
||||
*/
|
||||
|
||||
#ifndef __SELECTOR_H
|
||||
#define __SELECTOR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Manages SelectableFds and invokes their callbacks at appropriate times.
|
||||
*/
|
||||
typedef struct Selector Selector;
|
||||
|
||||
/**
|
||||
* A selectable descriptor. Contains callbacks which the selector can invoke
|
||||
* before calling select(), when the descriptor is readable or writable, and
|
||||
* when the descriptor contains out-of-band data. Simply set a callback to
|
||||
* NULL if you're not interested in that particular event.
|
||||
*
|
||||
* A selectable descriptor can indicate that it needs to be removed from the
|
||||
* selector by setting the 'remove' flag. The selector will remove the
|
||||
* descriptor at a later time and invoke the onRemove() callback.
|
||||
*
|
||||
* SelectableFd fields should only be modified from the selector loop.
|
||||
*/
|
||||
typedef struct SelectableFd SelectableFd;
|
||||
struct SelectableFd {
|
||||
|
||||
/** The file descriptor itself. */
|
||||
int fd;
|
||||
|
||||
/** Pointer to user-specific data. Can be NULL. */
|
||||
void* data;
|
||||
|
||||
/**
|
||||
* Set this flag when you no longer wish to be selected. The selector
|
||||
* will invoke onRemove() when the descriptor is actually removed.
|
||||
*/
|
||||
bool remove;
|
||||
|
||||
/**
|
||||
* Invoked by the selector before calling select. You can set up other
|
||||
* callbacks from here as necessary.
|
||||
*/
|
||||
void (*beforeSelect)(SelectableFd* self);
|
||||
|
||||
/**
|
||||
* Invoked by the selector when the descriptor has data available. Set to
|
||||
* NULL to indicate that you're not interested in reading.
|
||||
*/
|
||||
void (*onReadable)(SelectableFd* self);
|
||||
|
||||
/**
|
||||
* Invoked by the selector when the descriptor can accept data. Set to
|
||||
* NULL to indicate that you're not interested in writing.
|
||||
*/
|
||||
void (*onWritable)(SelectableFd* self);
|
||||
|
||||
/**
|
||||
* Invoked by the selector when out-of-band (OOB) data is available. Set to
|
||||
* NULL to indicate that you're not interested in OOB data.
|
||||
*/
|
||||
void (*onExcept)(SelectableFd* self);
|
||||
|
||||
/**
|
||||
* Invoked by the selector after the descriptor is removed from the
|
||||
* selector but before the selector frees the SelectableFd memory.
|
||||
*/
|
||||
void (*onRemove)(SelectableFd* self);
|
||||
|
||||
/**
|
||||
* The selector which selected this fd. Set by the selector itself.
|
||||
*/
|
||||
Selector* selector;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new selector.
|
||||
*/
|
||||
Selector* selectorCreate(void);
|
||||
|
||||
/**
|
||||
* Creates a new selectable fd, adds it to the given selector and returns a
|
||||
* pointer. Outside of 'selector' and 'fd', all fields are set to 0 or NULL
|
||||
* by default.
|
||||
*
|
||||
* The selectable fd should only be modified from the selector loop thread.
|
||||
*/
|
||||
SelectableFd* selectorAdd(Selector* selector, int fd);
|
||||
|
||||
/**
|
||||
* Wakes up the selector even though no I/O events occurred. Use this
|
||||
* to indicate that you're ready to write to a descriptor.
|
||||
*/
|
||||
void selectorWakeUp(Selector* selector);
|
||||
|
||||
/**
|
||||
* Loops continuously selecting file descriptors and firing events.
|
||||
* Does not return.
|
||||
*/
|
||||
void selectorLoop(Selector* selector);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SELECTOR_H */
|
100
include/cutils/sockets.h
Normal file
100
include/cutils/sockets.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef __CUTILS_SOCKETS_H
|
||||
#define __CUTILS_SOCKETS_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_WINSOCK
|
||||
#include <winsock2.h>
|
||||
typedef int socklen_t;
|
||||
#elif HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"
|
||||
#define ANDROID_SOCKET_DIR "/dev/socket"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* android_get_control_socket - simple helper function to get the file
|
||||
* descriptor of our init-managed Unix domain socket. `name' is the name of the
|
||||
* socket, as given in init.rc. Returns -1 on error.
|
||||
*
|
||||
* This is inline and not in libcutils proper because we want to use this in
|
||||
* third-party daemons with minimal modification.
|
||||
*/
|
||||
static inline int android_get_control_socket(const char *name)
|
||||
{
|
||||
char key[64] = ANDROID_SOCKET_ENV_PREFIX;
|
||||
const char *val;
|
||||
int fd;
|
||||
|
||||
/* build our environment variable, counting cycles like a wolf ... */
|
||||
#if HAVE_STRLCPY
|
||||
strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
|
||||
name,
|
||||
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
|
||||
#else /* for the host, which may lack the almightly strncpy ... */
|
||||
strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
|
||||
name,
|
||||
sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
|
||||
key[sizeof(key)-1] = '\0';
|
||||
#endif
|
||||
|
||||
val = getenv(key);
|
||||
if (!val)
|
||||
return -1;
|
||||
|
||||
errno = 0;
|
||||
fd = strtol(val, NULL, 10);
|
||||
if (errno)
|
||||
return -1;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* See also android.os.LocalSocketAddress.Namespace
|
||||
*/
|
||||
// Linux "abstract" (non-filesystem) namespace
|
||||
#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
|
||||
// Android "reserved" (/dev/socket) namespace
|
||||
#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
|
||||
// Normal filesystem namespace
|
||||
#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
|
||||
|
||||
extern int socket_loopback_client(int port, int type);
|
||||
extern int socket_network_client(const char *host, int port, int type);
|
||||
extern int socket_loopback_server(int port, int type);
|
||||
extern int socket_local_server(const char *name, int namespaceId, int type);
|
||||
extern int socket_local_server_bind(int s, const char *name, int namespaceId);
|
||||
extern int socket_local_client_connect(int fd,
|
||||
const char *name, int namespaceId, int type);
|
||||
extern int socket_local_client(const char *name, int namespaceId, int type);
|
||||
extern int socket_inaddr_any_server(int port, int type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CUTILS_SOCKETS_H */
|
146
include/cutils/threads.h
Normal file
146
include/cutils/threads.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (C) 2007 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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBS_CUTILS_THREADS_H
|
||||
#define _LIBS_CUTILS_THREADS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/***********************************************************************/
|
||||
/***********************************************************************/
|
||||
/***** *****/
|
||||
/***** local thread storage *****/
|
||||
/***** *****/
|
||||
/***********************************************************************/
|
||||
/***********************************************************************/
|
||||
|
||||
#ifdef HAVE_PTHREADS
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
pthread_mutex_t lock;
|
||||
int has_tls;
|
||||
pthread_key_t tls;
|
||||
|
||||
} thread_store_t;
|
||||
|
||||
#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
|
||||
|
||||
#elif defined HAVE_WIN32_THREADS
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef struct {
|
||||
int lock_init;
|
||||
int has_tls;
|
||||
DWORD tls;
|
||||
CRITICAL_SECTION lock;
|
||||
|
||||
} thread_store_t;
|
||||
|
||||
#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} }
|
||||
|
||||
#else
|
||||
# error "no thread_store_t implementation for your platform !!"
|
||||
#endif
|
||||
|
||||
typedef void (*thread_store_destruct_t)(void* value);
|
||||
|
||||
extern void* thread_store_get(thread_store_t* store);
|
||||
|
||||
extern void thread_store_set(thread_store_t* store,
|
||||
void* value,
|
||||
thread_store_destruct_t destroy);
|
||||
|
||||
/***********************************************************************/
|
||||
/***********************************************************************/
|
||||
/***** *****/
|
||||
/***** mutexes *****/
|
||||
/***** *****/
|
||||
/***********************************************************************/
|
||||
/***********************************************************************/
|
||||
|
||||
#ifdef HAVE_PTHREADS
|
||||
|
||||
typedef pthread_mutex_t mutex_t;
|
||||
|
||||
#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
|
||||
|
||||
static __inline__ void mutex_lock(mutex_t* lock)
|
||||
{
|
||||
pthread_mutex_lock(lock);
|
||||
}
|
||||
static __inline__ void mutex_unlock(mutex_t* lock)
|
||||
{
|
||||
pthread_mutex_unlock(lock);
|
||||
}
|
||||
static __inline__ int mutex_init(mutex_t* lock)
|
||||
{
|
||||
return pthread_mutex_init(lock, NULL);
|
||||
}
|
||||
static __inline__ void mutex_destroy(mutex_t* lock)
|
||||
{
|
||||
pthread_mutex_destroy(lock);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WIN32_THREADS
|
||||
typedef struct {
|
||||
int init;
|
||||
CRITICAL_SECTION lock[1];
|
||||
} mutex_t;
|
||||
|
||||
#define MUTEX_INITIALIZER { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} }
|
||||
|
||||
static __inline__ void mutex_lock(mutex_t* lock)
|
||||
{
|
||||
if (!lock->init) {
|
||||
lock->init = 1;
|
||||
InitializeCriticalSection( lock->lock );
|
||||
lock->init = 2;
|
||||
} else while (lock->init != 2)
|
||||
Sleep(10);
|
||||
|
||||
EnterCriticalSection(lock->lock);
|
||||
}
|
||||
|
||||
static __inline__ void mutex_unlock(mutex_t* lock)
|
||||
{
|
||||
LeaveCriticalSection(lock->lock);
|
||||
}
|
||||
static __inline__ int mutex_init(mutex_t* lock)
|
||||
{
|
||||
InitializeCriticalSection(lock->lock);
|
||||
lock->init = 2;
|
||||
return 0;
|
||||
}
|
||||
static __inline__ void mutex_destroy(mutex_t* lock)
|
||||
{
|
||||
if (lock->init) {
|
||||
lock->init = 0;
|
||||
DeleteCriticalSection(lock->lock);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBS_CUTILS_THREADS_H */
|
32
include/cutils/tztime.h
Normal file
32
include/cutils/tztime.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2006 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.
|
||||
*/
|
||||
|
||||
#ifndef _CUTILS_TZTIME_H
|
||||
#define _CUTILS_TZTIME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
time_t mktime_tz(struct tm * const tmp, char const * tz);
|
||||
void localtime_tz(const time_t * const timep, struct tm * tmp, const char* tz);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CUTILS_TZTIME_H */
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue