summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHyungKyu Song <hk76.song@samsung.com>2013-02-16 00:41:57 +0900
committerHyungKyu Song <hk76.song@samsung.com>2013-02-16 00:41:57 +0900
commitaa44aabb66da71d4380b74481f609f6daac2c269 (patch)
treed0ffbd64e7371bded3c7ffd472464774f908e941
parent041e6080ec2c7e6db643943434e7ddff49ffb68a (diff)
downloadsdb-tizen_2.0.tar.gz
sdb-tizen_2.0.tar.bz2
sdb-tizen_2.0.zip
-rw-r--r--AUTHORS4
-rw-r--r--LICENSE.APLv2204
-rw-r--r--Makefile139
-rw-r--r--Makefile.win48
-rw-r--r--NOTICE3
-rw-r--r--SdbWinApi.bat10
-rwxr-xr-xansicon/.gitignore3
-rwxr-xr-xansicon/ANSI-LLW.c22
-rwxr-xr-xansicon/ANSI.c1825
-rwxr-xr-xansicon/COPYING.MinGW-w64-runtime.txt240
-rwxr-xr-xansicon/G1.bat18
-rwxr-xr-xansicon/G1.txtbin0 -> 404 bytes
-rwxr-xr-xansicon/ansi.rc42
-rwxr-xr-xansicon/ansicon.c686
-rwxr-xr-xansicon/ansicon.h47
-rwxr-xr-xansicon/ansicon.rc36
-rwxr-xr-xansicon/injdll32.c123
-rwxr-xr-xansicon/injdll64.c96
-rwxr-xr-xansicon/makefile82
-rwxr-xr-xansicon/makefile.vc88
-rwxr-xr-xansicon/proctype.c105
-rwxr-xr-xansicon/readme.txt439
-rwxr-xr-xansicon/util.c128
-rwxr-xr-xansicon/version.h9
-rwxr-xr-xansicon/wow64.h88
-rw-r--r--debian/changelog7
-rw-r--r--debian/compat1
-rw-r--r--debian/control10
-rwxr-xr-xdebian/rules44
-rw-r--r--debian/sdbd.install3
-rwxr-xr-xpackage/build.linux76
-rwxr-xr-xpackage/build.macos-6439
-rw-r--r--package/build.windows57
-rw-r--r--package/changelog42
-rw-r--r--package/pkginfo.manifest23
-rwxr-xr-xpackage/sdb-run.bat25
-rwxr-xr-xpackage/sdb.install.darwin18
-rwxr-xr-xpackage/sdb.install.linux35
-rwxr-xr-xpackage/sdb.remove.darwin22
-rwxr-xr-xpackage/sdb.remove.linux25
-rw-r--r--package/sdb.remove.windows10
-rwxr-xr-xpackage/usb-connection-for-ssh.install.linux8
-rwxr-xr-xscript/S06sdbd2
-rwxr-xr-xscript/sdbd39
-rw-r--r--src/Android.mk154
-rw-r--r--src/MODULE_LICENSE_APACHE20
-rw-r--r--src/NOTICE191
-rw-r--r--src/OVERVIEW.TXT139
-rw-r--r--src/SERVICES.TXT236
-rw-r--r--src/TizenConfig.h336
-rw-r--r--src/android_reboot.c133
-rw-r--r--src/android_reboot.h35
-rw-r--r--src/clear_vars.mk107
-rwxr-xr-xsrc/commandline.c1676
-rw-r--r--src/console.c61
-rw-r--r--src/fdevent.c698
-rw-r--r--src/fdevent.h83
-rwxr-xr-xsrc/file_sync_client.c1052
-rw-r--r--src/file_sync_service.c428
-rw-r--r--src/file_sync_service.h87
-rw-r--r--src/framebuffer_service.c181
-rw-r--r--src/get_my_path_darwin.c30
-rw-r--r--src/get_my_path_freebsd.c33
-rw-r--r--src/get_my_path_linux.c33
-rw-r--r--src/get_my_path_windows.c34
-rw-r--r--src/jdwp_service.c750
-rw-r--r--src/mutex_list.h41
-rw-r--r--src/properties.c475
-rw-r--r--src/properties.h69
-rw-r--r--src/protocol.txt252
-rw-r--r--src/qemu_pipe.h91
-rw-r--r--src/remount_service.c111
-rwxr-xr-xsrc/sdb.c1477
-rwxr-xr-xsrc/sdb.h479
-rw-r--r--src/sdb_client.c343
-rw-r--r--src/sdb_client.h70
-rw-r--r--src/sdbwinapi/BUILDME.TXT19
-rw-r--r--src/sdbwinapi/MAKEFILE22
-rw-r--r--src/sdbwinapi/MAKEFILE.org22
-rw-r--r--src/sdbwinapi/Resource.h34
-rw-r--r--src/sdbwinapi/SOURCES96
-rw-r--r--src/sdbwinapi/SdbWinApi.cpp112
-rw-r--r--src/sdbwinapi/SdbWinApi.def18
-rw-r--r--src/sdbwinapi/SdbWinApi.rc111
-rw-r--r--src/sdbwinapi/sdb_api.cpp537
-rw-r--r--src/sdbwinapi/sdb_api.h622
-rw-r--r--src/sdbwinapi/sdb_api_instance.cpp47
-rw-r--r--src/sdbwinapi/sdb_api_instance.h113
-rw-r--r--src/sdbwinapi/sdb_api_legacy.h190
-rw-r--r--src/sdbwinapi/sdb_api_private_defines.h114
-rw-r--r--src/sdbwinapi/sdb_endpoint_object.cpp96
-rw-r--r--src/sdbwinapi/sdb_endpoint_object.h222
-rw-r--r--src/sdbwinapi/sdb_helper_routines.cpp276
-rw-r--r--src/sdbwinapi/sdb_helper_routines.h137
-rw-r--r--src/sdbwinapi/sdb_interface.cpp117
-rw-r--r--src/sdbwinapi/sdb_interface.h203
-rw-r--r--src/sdbwinapi/sdb_interface_enum.cpp103
-rw-r--r--src/sdbwinapi/sdb_interface_enum.h98
-rw-r--r--src/sdbwinapi/sdb_io_completion.cpp50
-rw-r--r--src/sdbwinapi/sdb_io_completion.h133
-rw-r--r--src/sdbwinapi/sdb_legacy_endpoint_object.cpp237
-rw-r--r--src/sdbwinapi/sdb_legacy_endpoint_object.h142
-rw-r--r--src/sdbwinapi/sdb_legacy_interface.cpp324
-rw-r--r--src/sdbwinapi/sdb_legacy_interface.h190
-rw-r--r--src/sdbwinapi/sdb_legacy_io_completion.cpp90
-rw-r--r--src/sdbwinapi/sdb_legacy_io_completion.h115
-rw-r--r--src/sdbwinapi/sdb_object_handle.cpp167
-rw-r--r--src/sdbwinapi/sdb_object_handle.h212
-rw-r--r--src/sdbwinapi/sdb_winusb_api.h48
-rw-r--r--src/sdbwinapi/stdafx.cpp21
-rw-r--r--src/sdbwinapi/stdafx.h79
-rw-r--r--src/sdbwinusbapi/BUILDME.TXT7
-rw-r--r--src/sdbwinusbapi/MAKEFILE22
-rw-r--r--src/sdbwinusbapi/Resource.h34
-rw-r--r--src/sdbwinusbapi/SOURCES91
-rw-r--r--src/sdbwinusbapi/SdbWinUsbApi.cpp62
-rw-r--r--src/sdbwinusbapi/SdbWinUsbApi.def5
-rw-r--r--src/sdbwinusbapi/SdbWinUsbApi.rc111
-rw-r--r--src/sdbwinusbapi/sdb_winusb_endpoint_object.cpp169
-rw-r--r--src/sdbwinusbapi/sdb_winusb_endpoint_object.h155
-rw-r--r--src/sdbwinusbapi/sdb_winusb_interface.cpp340
-rw-r--r--src/sdbwinusbapi/sdb_winusb_interface.h205
-rw-r--r--src/sdbwinusbapi/sdb_winusb_io_completion.cpp98
-rw-r--r--src/sdbwinusbapi/sdb_winusb_io_completion.h118
-rw-r--r--src/sdbwinusbapi/stdafx.cpp21
-rw-r--r--src/sdbwinusbapi/stdafx.h78
-rwxr-xr-xsrc/services.c623
-rw-r--r--src/socket_inaddr_any_server.c69
-rw-r--r--src/socket_local.h39
-rw-r--r--src/socket_local_client.c166
-rw-r--r--src/socket_local_server.c124
-rw-r--r--src/socket_loopback_client.c58
-rw-r--r--src/socket_loopback_server.c138
-rw-r--r--src/socket_network_client.c64
-rw-r--r--src/sockets.c857
-rw-r--r--src/sockets.diabin0 -> 2333 bytes
-rw-r--r--src/sockets.h100
-rw-r--r--src/sysdeps.h529
-rw-r--r--src/sysdeps_win32.c1982
-rw-r--r--src/threads.h146
-rwxr-xr-xsrc/transport.c1217
-rw-r--r--src/transport.h26
-rw-r--r--src/transport_local.c537
-rw-r--r--src/transport_usb.c148
-rw-r--r--src/usb_libusb.c654
-rwxr-xr-xsrc/usb_linux.c714
-rw-r--r--src/usb_linux_client.c160
-rw-r--r--src/usb_osx.c537
-rw-r--r--src/usb_vendors.c266
-rw-r--r--src/usb_vendors.h25
-rw-r--r--src/usb_windows.c515
-rw-r--r--src/utils.c126
-rw-r--r--src/utils.h70
-rwxr-xr-xtest/install_udev.sh14
-rwxr-xr-xtest/test_inoti_service.c115
-rwxr-xr-xtest/test_track_devices.c113
-rw-r--r--test/test_track_jdwp.c113
157 files changed, 31734 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..9e88574
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+Kangho Kim <kh5325.kim@samsung.com>
+Ho Namkoong <ho.namkoong@samsung.com>
+Yoonki Park <yoonki.park@samsung.com>
+HyunGoo Kang <hyungoo1.kang@samsung.com>
diff --git a/LICENSE.APLv2 b/LICENSE.APLv2
new file mode 100644
index 0000000..5554685
--- /dev/null
+++ b/LICENSE.APLv2
@@ -0,0 +1,204 @@
+ 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
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..60389b8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,139 @@
+#
+#
+# Makefile for sdb
+#
+
+#
+HOST_OS := $(shell uname -s | tr A-Z a-z)
+
+# sdb host tool
+# =========================================================
+
+# 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
+ SDB_EXTRA_CFLAGS := -mmacosx-version-min=10.4
+endif
+
+ifeq ($(HOST_OS),freebsd)
+ USB_SRCS := usb_libusb.c
+ EXTRA_SRCS := get_my_path_freebsd.c
+ LOCAL_LDLIBS += -lpthread -lusb
+endif
+
+
+
+SDB_SRC_FILES := \
+ src/sdb.c \
+ src/console.c \
+ src/transport.c \
+ src/transport_local.c \
+ src/transport_usb.c \
+ src/commandline.c \
+ src/sdb_client.c \
+ src/sockets.c \
+ src/services.c \
+ src/file_sync_client.c \
+ src/$(EXTRA_SRCS) \
+ src/$(USB_SRCS) \
+ src/utils.c \
+ src/usb_vendors.c \
+ src/fdevent.c \
+ src/socket_inaddr_any_server.c \
+ src/socket_local_client.c \
+ src/socket_local_server.c \
+ src/socket_loopback_client.c \
+ src/socket_loopback_server.c \
+ src/socket_network_client.c
+
+SDB_CFLAGS := -O2 -g -DSDB_HOST=1 -DSDB_HOST_ON_TARGET=1 -Wall -Wno-unused-parameter
+SDB_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+SDB_CFLAGS += -DHAVE_FORKEXEC -DHAVE_TERMIO_H -DHAVE_SYMLINKS
+SDB_LFLAGS := $(LOCAL_LDLIBS)
+
+SDBD_SRC_FILES := \
+ src/sdb.c \
+ src/fdevent.c \
+ src/transport.c \
+ src/transport_local.c \
+ src/transport_usb.c \
+ src/sockets.c \
+ src/services.c \
+ src/file_sync_service.c \
+ src/framebuffer_service.c \
+ src/remount_service.c \
+ src/usb_linux_client.c \
+ src/utils.c \
+ src/socket_inaddr_any_server.c \
+ src/socket_local_client.c \
+ src/socket_local_server.c \
+ src/socket_loopback_client.c \
+ src/socket_loopback_server.c \
+ src/socket_network_client.c \
+ src/properties.c \
+ src/android_reboot.c
+
+SDBD_CFLAGS := -O2 -g -DSDB_HOST=0 -Wall -Wno-unused-parameter
+SDBD_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+SDBD_CFLAGS += -DHAVE_FORKEXEC -fPIE
+
+IFLAGS := -Iinclude -Isrc
+OBJDIR := bin
+INSTALLDIR := usr/sbin
+INITSCRIPTDIR := etc/init.d
+RCSCRIPTDIR := etc/rc.d/rc3.d
+
+UNAME := $(shell uname -sm)
+ifneq (,$(findstring 86,$(UNAME)))
+ HOST_ARCH := x86
+endif
+
+TARGET_ARCH = $(HOST_ARCH)
+ifeq ($(TARGET_ARCH),)
+ TARGET_ARCH := arm
+endif
+
+ifeq ($(TARGET_ARCH),arm)
+ MODULE := sdbd
+ SDBD_CFLAGS += -DANDROID_GADGET=1
+else
+ifeq ($(TARGET_HOST),true)
+ MODULE := sdb
+else
+ MODULE := sdbd
+endif
+endif
+
+all : $(MODULE)
+
+sdb : $(SDB_SRC_FILES)
+ mkdir -p $(OBJDIR)
+ $(CC) -pthread -o $(OBJDIR)/$(MODULE) $(SDB_CFLAGS) $(SDB_EXTRA_CFLAGS) $(SDB_LFLAGS) $(IFLAGS) $(SDB_SRC_FILES)
+
+sdbd : $(SDBD_SRC_FILES)
+ mkdir -p $(OBJDIR)
+ $(CC) -pthread -o $(OBJDIR)/$(MODULE) $(SDBD_CFLAGS) $(IFLAGS) $(SDBD_SRC_FILES)
+
+install :
+ mkdir -p $(DESTDIR)/$(INSTALLDIR)
+ install $(OBJDIR)/$(MODULE) $(DESTDIR)/$(INSTALLDIR)/$(MODULE)
+ifeq ($(MODULE),sdbd)
+ mkdir -p $(DESTDIR)/$(INITSCRIPTDIR)
+ install script/sdbd $(DESTDIR)/$(INITSCRIPTDIR)/sdbd
+endif
+ mkdir -p $(DESTDIR)/$(RCSCRIPTDIR)
+ install script/S06sdbd $(DESTDIR)/$(RCSCRIPTDIR)/S06sdbd
+
+clean :
+ rm -rf $(OBJDIR)/*
diff --git a/Makefile.win b/Makefile.win
new file mode 100644
index 0000000..99de9ca
--- /dev/null
+++ b/Makefile.win
@@ -0,0 +1,48 @@
+CC := gcc
+SRC_FILES := \
+ src/sdb.c \
+ src/console.c \
+ src/transport.c \
+ src/transport_local.c \
+ src/transport_usb.c \
+ src/commandline.c \
+ src/sdb_client.c \
+ src/sockets.c \
+ src/services.c \
+ src/file_sync_client.c \
+ src/get_my_path_windows.c \
+ src/usb_windows.c \
+ src/utils.c \
+ src/usb_vendors.c \
+ src/socket_local_client.c \
+ src/sysdeps_win32.c
+INCS := \
+ -I/mingw/include/ddk \
+ -Isrc/sdbwinapi \
+ -Isrc
+
+DIRECT_INCS := \
+ -include src/TizenConfig.h
+
+LIB_PATH := \
+ -Lprebuilt
+
+LIBS := -lws2_32 \
+ prebuilt/SdbWinApi.a
+
+CFLAGS := \
+ -O2 -DSDB_HOST=1 -Wall -Wno-unused-parameter \
+ -D_XOPEN_SOURCE -D_GNU_SOURCE
+
+OBJDIR := bin
+PREBUILTDIR := prebuilt
+
+all : sdb
+
+sdb : $(SRC_FILES)
+ rm -rf $(OBJDIR)
+ mkdir $(OBJDIR)
+ $(CC) $(CFLAGS) $(INCS) -o $(OBJDIR)/$@ $(SRC_FILES) $(DIRECT_INCS) $(LIB_PATH) $(LIBS)
+ cp $(PREBUILTDIR)/SdbWinApi.dll $(OBJDIR)
+ cp $(PREBUILTDIR)/SdbWinUsbApi.dll $(OBJDIR)
+
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..3b7387a
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,3 @@
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+Except as noted, this software is licensed under Apache License, Version 2.
+Please, see the LICENSE.APLv2 file for Apache License terms and conditions.
diff --git a/SdbWinApi.bat b/SdbWinApi.bat
new file mode 100644
index 0000000..3bc86c2
--- /dev/null
+++ b/SdbWinApi.bat
@@ -0,0 +1,10 @@
+md prebuilt
+cd src\sdbwinapi
+build -cbeEIFZ
+copy objfre_win7_x86\i386\SdbWinApi.dll ..\..\prebuilt\
+copy SdbWinApi.def ..\..\prebuilt\
+cd ..\..\prebuilt
+dlltool --def SdbWInApi.def --dllname SdbWinApi.dll --output-lib SdbWinApi.a
+cd ..\src\sdbwinusbapi
+build -cbeEIFZ
+copy objfre_win7_x86\i386\SdbWinUsbApi.dll ..\..\prebuilt\
diff --git a/ansicon/.gitignore b/ansicon/.gitignore
new file mode 100755
index 0000000..a45c74f
--- /dev/null
+++ b/ansicon/.gitignore
@@ -0,0 +1,3 @@
+*.zip
+/x86
+/x64
diff --git a/ansicon/ANSI-LLW.c b/ansicon/ANSI-LLW.c
new file mode 100755
index 0000000..28991f0
--- /dev/null
+++ b/ansicon/ANSI-LLW.c
@@ -0,0 +1,22 @@
+/*
+ ANSI-LLW.c - Output the 32-bit address of LoadLibraryW.
+
+ Jason Hood, 13 November, 2010 (LLA version 5 September, 2010).
+
+ I don't know of a method to retrieve the 32-bit address of a function in
+ 64-bit code, so this is a simple workaround.
+
+ 18 December, 2010: Initially I used GetProcAddress, but then I thought that
+ was silly, why don't I just return LoadLibraryW directly? That worked fine
+ for TDM64 and VC, but MinGW32 would return the address of the jump to the
+ function, not the function itself. Not so silly after all.
+*/
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+int main( void )
+{
+ return (DWORD)GetProcAddress( GetModuleHandle( "kernel32.dll" ),
+ "LoadLibraryW" );
+}
diff --git a/ansicon/ANSI.c b/ansicon/ANSI.c
new file mode 100755
index 0000000..3907252
--- /dev/null
+++ b/ansicon/ANSI.c
@@ -0,0 +1,1825 @@
+/*
+ ANSI.c - ANSI escape sequence console driver.
+
+ Jason Hood, 21 & 22 October, 2005.
+
+ Derived from ANSI.xs by Jean-Louis Morel, from his Perl package
+ Win32::Console::ANSI. I removed the codepage conversion ("\e(") and added
+ WriteConsole hooking.
+
+ v1.01, 11 & 12 March, 2006:
+ disable when console has disabled processed output;
+ \e[5m (blink) is the same as \e[4m (underline);
+ do not conceal control characters (0 to 31);
+ \e[m will restore original color.
+
+ v1.10, 22 February, 2009:
+ fix MyWriteConsoleW for strings longer than the buffer;
+ initialise attributes to current;
+ hook into child processes.
+
+ v1.11, 28 February, 2009:
+ fix hooking into child processes (only do console executables).
+
+ v1.12, 9 March, 2009:
+ really fix hooking (I didn't realise MinGW didn't generate relocations).
+
+ v1.13, 21 & 27 March, 2009:
+ alternate injection method, to work with DEP;
+ use Unicode and the current output code page (not OEMCP).
+
+ v1.14, 3 April, 2009:
+ fix test for empty import section.
+
+ v1.15, 17 May, 2009:
+ properly update lpNumberOfCharsWritten in MyWriteConsoleA.
+
+ v1.20, 26 & 29 May, 17 to 21 June, 2009:
+ create an ANSICON environment variable;
+ hook GetEnvironmentVariable to create ANSICON dynamically;
+ use another injection method.
+
+ v1.22, 5 October, 2009:
+ hook LoadLibrary to intercept the newly loaded functions.
+
+ v1.23, 11 November, 2009:
+ unload gracefully;
+ conceal characters by making foreground same as background;
+ reverse the bold/underline attributes, too.
+
+ v1.25, 15, 20 & 21 July, 2010:
+ hook LoadLibraryEx (now cscript works);
+ Win7 support.
+
+ v1.30, 3 August to 7 September, 2010:
+ x64 support.
+
+ v1.31, 13 & 19 November, 2010:
+ fix multibyte conversion problems.
+
+ v1.32, 4 to 22 December, 2010:
+ test for lpNumberOfCharsWritten/lpNumberOfBytesWritten being NULL;
+ recognise DSR and xterm window title;
+ ignore sequences starting with \e[? & \e[>;
+ close the handles opened by CreateProcess.
+
+ v1.40, 25 & 26 February, 1 March, 2011:
+ hook GetProcAddress, addresses issues with .NET (work with PowerShell);
+ implement SO & SI to use the DEC Special Graphics Character Set (enables
+ line drawing via ASCII); ignore \e(X & \e)X (where X is any character);
+ add \e[?25h & \e[?25l to show/hide the cursor (DECTCEM).
+
+ v1.50, 7 to 14 December, 2011:
+ added dynamic environment variable ANSICON_VER to return version;
+ read ANSICON_EXC environment variable to exclude selected modules;
+ read ANSICON_GUI environment variable to hook selected GUI programs;
+ read ANSICON_DEF environment variable to set the default GR;
+ transfer current GR to child, read it on exit.
+
+ v1.51, 15 January, 5, 22 & 24 February, 2012:
+ added log mask 16 to log all the imported modules of imported modules;
+ ignore the version within the core API DLL names;
+ fix 32-bit process trying to identify 64-bit process;
+ hook _lwrite & _hwrite.
+
+ v1.52, 10 April, 1 & 2 June, 2012:
+ use ansicon.exe to enable 32-bit to inject into 64-bit;
+ implement \e[39m & \e[49m (only setting color, nothing else);
+ added the character/line equivalents (keaj`) of the cursor movement
+ sequences (ABCDG), as well as vertical absolute (d) and erase characters
+ (X).
+
+ v1.53, 12 June, 2012:
+ fixed Update_GRM when running multiple processes (e.g. "cl /MP").
+*/
+
+#include "ansicon.h"
+#include "version.h"
+#include <tlhelp32.h>
+
+#define isdigit(c) ('0' <= (c) && (c) <= '9')
+
+#ifdef __GNUC__
+#define SHARED __attribute__((shared, section(".share")))
+#else
+#pragma section(".shared", read,write,shared)
+#define SHARED __declspec(allocate(".shared"))
+#endif
+
+
+// ========== Global variables and constants
+
+HANDLE hConOut; // handle to CONOUT$
+
+#define ESC '\x1B' // ESCape character
+#define BEL '\x07'
+#define SO '\x0E' // Shift Out
+#define SI '\x0F' // Shift In
+
+#define MAX_ARG 16 // max number of args in an escape sequence
+int state; // automata state
+TCHAR prefix; // escape sequence prefix ( '[', ']' or '(' );
+TCHAR prefix2; // secondary prefix ( '?' or '>' );
+TCHAR suffix; // escape sequence suffix
+int es_argc; // escape sequence args count
+int es_argv[MAX_ARG]; // escape sequence args
+TCHAR Pt_arg[MAX_PATH*2]; // text parameter for Operating System Command
+int Pt_len;
+BOOL shifted;
+
+
+// DEC Special Graphics Character Set from
+// http://vt100.net/docs/vt220-rm/table2-4.html
+// Some of these may not look right, depending on the font and code page (in
+// particular, the Control Pictures probably won't work at all).
+const WCHAR G1[] =
+{
+ ' ', // _ - blank
+ L'\x2666', // ` - Black Diamond Suit
+ L'\x2592', // a - Medium Shade
+ L'\x2409', // b - HT
+ L'\x240c', // c - FF
+ L'\x240d', // d - CR
+ L'\x240a', // e - LF
+ L'\x00b0', // f - Degree Sign
+ L'\x00b1', // g - Plus-Minus Sign
+ L'\x2424', // h - NL
+ L'\x240b', // i - VT
+ L'\x2518', // j - Box Drawings Light Up And Left
+ L'\x2510', // k - Box Drawings Light Down And Left
+ L'\x250c', // l - Box Drawings Light Down And Right
+ L'\x2514', // m - Box Drawings Light Up And Right
+ L'\x253c', // n - Box Drawings Light Vertical And Horizontal
+ L'\x00af', // o - SCAN 1 - Macron
+ L'\x25ac', // p - SCAN 3 - Black Rectangle
+ L'\x2500', // q - SCAN 5 - Box Drawings Light Horizontal
+ L'_', // r - SCAN 7 - Low Line
+ L'_', // s - SCAN 9 - Low Line
+ L'\x251c', // t - Box Drawings Light Vertical And Right
+ L'\x2524', // u - Box Drawings Light Vertical And Left
+ L'\x2534', // v - Box Drawings Light Up And Horizontal
+ L'\x252c', // w - Box Drawings Light Down And Horizontal
+ L'\x2502', // x - Box Drawings Light Vertical
+ L'\x2264', // y - Less-Than Or Equal To
+ L'\x2265', // z - Greater-Than Or Equal To
+ L'\x03c0', // { - Greek Small Letter Pi
+ L'\x2260', // | - Not Equal To
+ L'\x00a3', // } - Pound Sign
+ L'\x00b7', // ~ - Middle Dot
+};
+
+#define FIRST_G1 '_'
+#define LAST_G1 '~'
+
+
+// color constants
+
+#define FOREGROUND_BLACK 0
+#define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
+
+#define BACKGROUND_BLACK 0
+#define BACKGROUND_WHITE BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE
+
+const BYTE foregroundcolor[8] =
+{
+ FOREGROUND_BLACK, // black foreground
+ FOREGROUND_RED, // red foreground
+ FOREGROUND_GREEN, // green foreground
+ FOREGROUND_RED | FOREGROUND_GREEN, // yellow foreground
+ FOREGROUND_BLUE, // blue foreground
+ FOREGROUND_BLUE | FOREGROUND_RED, // magenta foreground
+ FOREGROUND_BLUE | FOREGROUND_GREEN, // cyan foreground
+ FOREGROUND_WHITE // white foreground
+};
+
+const BYTE backgroundcolor[8] =
+{
+ BACKGROUND_BLACK, // black background
+ BACKGROUND_RED, // red background
+ BACKGROUND_GREEN, // green background
+ BACKGROUND_RED | BACKGROUND_GREEN, // yellow background
+ BACKGROUND_BLUE, // blue background
+ BACKGROUND_BLUE | BACKGROUND_RED, // magenta background
+ BACKGROUND_BLUE | BACKGROUND_GREEN, // cyan background
+ BACKGROUND_WHITE, // white background
+};
+
+const BYTE attr2ansi[8] = // map console attribute to ANSI number
+{
+ 0, // black
+ 4, // blue
+ 2, // green
+ 6, // cyan
+ 1, // red
+ 5, // magenta
+ 3, // yellow
+ 7 // white
+};
+
+GRM grm;
+
+// saved cursor position
+COORD SavePos;
+
+// Variables to enable copying attributes between processes.
+SHARED DWORD s_pid;
+SHARED GRM s_grm;
+SHARED DWORD s_flag;
+#define GRM_INIT 1
+#define GRM_EXIT 2
+
+
+
+// Wait for the child process to finish, then update our GRM to the child's.
+DWORD WINAPI UpdateGRM( LPVOID child_pi )
+{
+ DWORD pid = ((LPPROCESS_INFORMATION)child_pi)->dwProcessId;
+ HANDLE proc = ((LPPROCESS_INFORMATION)child_pi)->hProcess;
+ free( child_pi );
+
+ WaitForSingleObject( proc, INFINITE );
+ CloseHandle( proc );
+
+ if (s_flag == GRM_EXIT && s_pid == pid)
+ {
+ s_flag = 0;
+ grm = s_grm;
+ }
+
+ return 0;
+}
+
+
+// Search an environment variable for a string.
+BOOL search_env( LPCTSTR var, LPCTSTR val )
+{
+ static LPTSTR env;
+ static DWORD env_len;
+ DWORD len;
+ BOOL not;
+
+ len = GetEnvironmentVariable( var, env, env_len );
+ if (len == 0)
+ return FALSE;
+
+ if (len > env_len)
+ {
+ LPTSTR tmp = realloc( env, TSIZE(len) );
+ if (tmp == NULL)
+ return FALSE;
+ env = tmp;
+ env_len = len;
+ GetEnvironmentVariable( var, env, env_len );
+ }
+
+ not = (*env == '!');
+ if (not && env[1] == '\0')
+ return TRUE;
+
+ for (var = wcstok( env + not, L";" ); var; var = wcstok( NULL, L";" ))
+ if (_wcsicmp( val, var ) == 0)
+ return !not;
+
+ return not;
+}
+
+
+// ========== Print Buffer functions
+
+#define BUFFER_SIZE 2048
+
+int nCharInBuffer;
+WCHAR ChBuffer[BUFFER_SIZE];
+
+//-----------------------------------------------------------------------------
+// FlushBuffer()
+// Writes the buffer to the console and empties it.
+//-----------------------------------------------------------------------------
+
+void FlushBuffer( void )
+{
+ DWORD nWritten;
+ if (nCharInBuffer <= 0) return;
+ WriteConsole( hConOut, ChBuffer, nCharInBuffer, &nWritten, NULL );
+ nCharInBuffer = 0;
+}
+
+//-----------------------------------------------------------------------------
+// PushBuffer( WCHAR c )
+// Adds a character in the buffer.
+//-----------------------------------------------------------------------------
+
+void PushBuffer( WCHAR c )
+{
+ if (shifted && c >= FIRST_G1 && c <= LAST_G1)
+ c = G1[c-FIRST_G1];
+ ChBuffer[nCharInBuffer] = c;
+ if (++nCharInBuffer == BUFFER_SIZE)
+ FlushBuffer();
+}
+
+//-----------------------------------------------------------------------------
+// SendSequence( LPTSTR seq )
+// Send the string to the input buffer.
+//-----------------------------------------------------------------------------
+
+void SendSequence( LPTSTR seq )
+{
+ DWORD out;
+ INPUT_RECORD in;
+ HANDLE hStdIn = GetStdHandle( STD_INPUT_HANDLE );
+
+ in.EventType = KEY_EVENT;
+ in.Event.KeyEvent.bKeyDown = TRUE;
+ in.Event.KeyEvent.wRepeatCount = 1;
+ in.Event.KeyEvent.wVirtualKeyCode = 0;
+ in.Event.KeyEvent.wVirtualScanCode = 0;
+ in.Event.KeyEvent.dwControlKeyState = 0;
+ for (; *seq; ++seq)
+ {
+ in.Event.KeyEvent.uChar.UnicodeChar = *seq;
+ WriteConsoleInput( hStdIn, &in, 1, &out );
+ }
+}
+
+// ========== Print functions
+
+//-----------------------------------------------------------------------------
+// InterpretEscSeq()
+// Interprets the last escape sequence scanned by ParseAndPrintString
+// prefix escape sequence prefix
+// es_argc escape sequence args count
+// es_argv[] escape sequence args array
+// suffix escape sequence suffix
+//
+// for instance, with \e[33;45;1m we have
+// prefix = '[',
+// es_argc = 3, es_argv[0] = 33, es_argv[1] = 45, es_argv[2] = 1
+// suffix = 'm'
+//-----------------------------------------------------------------------------
+
+void InterpretEscSeq( void )
+{
+ int i;
+ WORD attribut;
+ CONSOLE_SCREEN_BUFFER_INFO Info;
+ CONSOLE_CURSOR_INFO CursInfo;
+ DWORD len, NumberOfCharsWritten;
+ COORD Pos;
+ SMALL_RECT Rect;
+ CHAR_INFO CharInfo;
+
+ if (prefix == '[')
+ {
+ if (prefix2 == '?' && (suffix == 'h' || suffix == 'l'))
+ {
+ if (es_argc == 1 && es_argv[0] == 25)
+ {
+ GetConsoleCursorInfo( hConOut, &CursInfo );
+ CursInfo.bVisible = (suffix == 'h');
+ SetConsoleCursorInfo( hConOut, &CursInfo );
+ return;
+ }
+ }
+ // Ignore any other \e[? or \e[> sequences.
+ if (prefix2 != 0)
+ return;
+
+ GetConsoleScreenBufferInfo( hConOut, &Info );
+ switch (suffix)
+ {
+ case 'm':
+ if (es_argc == 0) es_argv[es_argc++] = 0;
+ for (i = 0; i < es_argc; i++)
+ {
+ if (30 <= es_argv[i] && es_argv[i] <= 37)
+ grm.foreground = es_argv[i] - 30;
+ else if (40 <= es_argv[i] && es_argv[i] <= 47)
+ grm.background = es_argv[i] - 40;
+ else switch (es_argv[i])
+ {
+ case 0:
+ case 39:
+ case 49:
+ {
+ TCHAR def[4];
+ int a;
+ *def = '7'; def[1] = '\0';
+ GetEnvironmentVariable( L"ANSICON_DEF", def, lenof(def) );
+ a = wcstol( def, NULL, 16 );
+ grm.reverse = FALSE;
+ if (a < 0)
+ {
+ grm.reverse = TRUE;
+ a = -a;
+ }
+ if (es_argv[i] != 49)
+ grm.foreground = attr2ansi[a & 7];
+ if (es_argv[i] != 39)
+ grm.background = attr2ansi[(a >> 4) & 7];
+ if (es_argv[i] == 0)
+ {
+ if (es_argc == 1)
+ {
+ grm.bold = a & FOREGROUND_INTENSITY;
+ grm.underline = a & BACKGROUND_INTENSITY;
+ }
+ else
+ {
+ grm.bold = 0;
+ grm.underline = 0;
+ }
+ grm.rvideo = 0;
+ grm.concealed = 0;
+ }
+ }
+ break;
+
+ case 1: grm.bold = FOREGROUND_INTENSITY; break;
+ case 5: // blink
+ case 4: grm.underline = BACKGROUND_INTENSITY; break;
+ case 7: grm.rvideo = 1; break;
+ case 8: grm.concealed = 1; break;
+ case 21: // oops, this actually turns on double underline
+ case 22: grm.bold = 0; break;
+ case 25:
+ case 24: grm.underline = 0; break;
+ case 27: grm.rvideo = 0; break;
+ case 28: grm.concealed = 0; break;
+ }
+ }
+ if (grm.concealed)
+ {
+ if (grm.rvideo)
+ {
+ attribut = foregroundcolor[grm.foreground]
+ | backgroundcolor[grm.foreground];
+ if (grm.bold)
+ attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
+ }
+ else
+ {
+ attribut = foregroundcolor[grm.background]
+ | backgroundcolor[grm.background];
+ if (grm.underline)
+ attribut |= FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
+ }
+ }
+ else if (grm.rvideo)
+ {
+ attribut = foregroundcolor[grm.background]
+ | backgroundcolor[grm.foreground];
+ if (grm.bold)
+ attribut |= BACKGROUND_INTENSITY;
+ if (grm.underline)
+ attribut |= FOREGROUND_INTENSITY;
+ }
+ else
+ attribut = foregroundcolor[grm.foreground] | grm.bold
+ | backgroundcolor[grm.background] | grm.underline;
+ if (grm.reverse)
+ attribut = ((attribut >> 4) & 15) | ((attribut & 15) << 4);
+ SetConsoleTextAttribute( hConOut, attribut );
+ return;
+
+ case 'J':
+ if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[J == ESC[0J
+ if (es_argc != 1) return;
+ switch (es_argv[0])
+ {
+ case 0: // ESC[0J erase from cursor to end of display
+ len = (Info.dwSize.Y - Info.dwCursorPosition.Y - 1) * Info.dwSize.X
+ + Info.dwSize.X - Info.dwCursorPosition.X - 1;
+ FillConsoleOutputCharacter( hConOut, ' ', len,
+ Info.dwCursorPosition,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes, len,
+ Info.dwCursorPosition,
+ &NumberOfCharsWritten );
+ return;
+
+ case 1: // ESC[1J erase from start to cursor.
+ Pos.X = 0;
+ Pos.Y = 0;
+ len = Info.dwCursorPosition.Y * Info.dwSize.X
+ + Info.dwCursorPosition.X + 1;
+ FillConsoleOutputCharacter( hConOut, ' ', len, Pos,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, Pos,
+ &NumberOfCharsWritten );
+ return;
+
+ case 2: // ESC[2J Clear screen and home cursor
+ Pos.X = 0;
+ Pos.Y = 0;
+ len = Info.dwSize.X * Info.dwSize.Y;
+ FillConsoleOutputCharacter( hConOut, ' ', len, Pos,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes, len, Pos,
+ &NumberOfCharsWritten );
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ default:
+ return;
+ }
+
+ case 'K':
+ if (es_argc == 0) es_argv[es_argc++] = 0; // ESC[K == ESC[0K
+ if (es_argc != 1) return;
+ switch (es_argv[0])
+ {
+ case 0: // ESC[0K Clear to end of line
+ len = Info.srWindow.Right - Info.dwCursorPosition.X + 1;
+ FillConsoleOutputCharacter( hConOut, ' ', len,
+ Info.dwCursorPosition,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes, len,
+ Info.dwCursorPosition,
+ &NumberOfCharsWritten );
+ return;
+
+ case 1: // ESC[1K Clear from start of line to cursor
+ Pos.X = 0;
+ Pos.Y = Info.dwCursorPosition.Y;
+ FillConsoleOutputCharacter( hConOut, ' ',
+ Info.dwCursorPosition.X + 1, Pos,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes,
+ Info.dwCursorPosition.X + 1, Pos,
+ &NumberOfCharsWritten );
+ return;
+
+ case 2: // ESC[2K Clear whole line.
+ Pos.X = 0;
+ Pos.Y = Info.dwCursorPosition.Y;
+ FillConsoleOutputCharacter( hConOut, ' ', Info.dwSize.X, Pos,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes,
+ Info.dwSize.X, Pos,
+ &NumberOfCharsWritten );
+ return;
+
+ default:
+ return;
+ }
+
+ case 'X': // ESC[#X Erase # characters.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[X == ESC[1X
+ if (es_argc != 1) return;
+ FillConsoleOutputCharacter( hConOut, ' ', es_argv[0],
+ Info.dwCursorPosition,
+ &NumberOfCharsWritten );
+ FillConsoleOutputAttribute( hConOut, Info.wAttributes, es_argv[0],
+ Info.dwCursorPosition,
+ &NumberOfCharsWritten );
+ return;
+
+ case 'L': // ESC[#L Insert # blank lines.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[L == ESC[1L
+ if (es_argc != 1) return;
+ Rect.Left = 0;
+ Rect.Top = Info.dwCursorPosition.Y;
+ Rect.Right = Info.dwSize.X - 1;
+ Rect.Bottom = Info.dwSize.Y - 1;
+ Pos.X = 0;
+ Pos.Y = Info.dwCursorPosition.Y + es_argv[0];
+ CharInfo.Char.UnicodeChar = ' ';
+ CharInfo.Attributes = Info.wAttributes;
+ ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo );
+ return;
+
+ case 'M': // ESC[#M Delete # lines.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[M == ESC[1M
+ if (es_argc != 1) return;
+ if (es_argv[0] > Info.dwSize.Y - Info.dwCursorPosition.Y)
+ es_argv[0] = Info.dwSize.Y - Info.dwCursorPosition.Y;
+ Rect.Left = 0;
+ Rect.Top = Info.dwCursorPosition.Y + es_argv[0];
+ Rect.Right = Info.dwSize.X - 1;
+ Rect.Bottom = Info.dwSize.Y - 1;
+ Pos.X = 0;
+ Pos.Y = Info.dwCursorPosition.Y;
+ CharInfo.Char.UnicodeChar = ' ';
+ CharInfo.Attributes = Info.wAttributes;
+ ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo );
+ return;
+
+ case 'P': // ESC[#P Delete # characters.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[P == ESC[1P
+ if (es_argc != 1) return;
+ if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1)
+ es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X;
+ Rect.Left = Info.dwCursorPosition.X + es_argv[0];
+ Rect.Top = Info.dwCursorPosition.Y;
+ Rect.Right = Info.dwSize.X - 1;
+ Rect.Bottom = Info.dwCursorPosition.Y;
+ CharInfo.Char.UnicodeChar = ' ';
+ CharInfo.Attributes = Info.wAttributes;
+ ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Info.dwCursorPosition,
+ &CharInfo );
+ return;
+
+ case '@': // ESC[#@ Insert # blank characters.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[@ == ESC[1@
+ if (es_argc != 1) return;
+ if (Info.dwCursorPosition.X + es_argv[0] > Info.dwSize.X - 1)
+ es_argv[0] = Info.dwSize.X - Info.dwCursorPosition.X;
+ Rect.Left = Info.dwCursorPosition.X;
+ Rect.Top = Info.dwCursorPosition.Y;
+ Rect.Right = Info.dwSize.X - 1 - es_argv[0];
+ Rect.Bottom = Info.dwCursorPosition.Y;
+ Pos.X = Info.dwCursorPosition.X + es_argv[0];
+ Pos.Y = Info.dwCursorPosition.Y;
+ CharInfo.Char.UnicodeChar = ' ';
+ CharInfo.Attributes = Info.wAttributes;
+ ScrollConsoleScreenBuffer( hConOut, &Rect, NULL, Pos, &CharInfo );
+ return;
+
+ case 'k': // ESC[#k
+ case 'A': // ESC[#A Moves cursor up # lines
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[A == ESC[1A
+ if (es_argc != 1) return;
+ Pos.Y = Info.dwCursorPosition.Y - es_argv[0];
+ if (Pos.Y < 0) Pos.Y = 0;
+ Pos.X = Info.dwCursorPosition.X;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'e': // ESC[#e
+ case 'B': // ESC[#B Moves cursor down # lines
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[B == ESC[1B
+ if (es_argc != 1) return;
+ Pos.Y = Info.dwCursorPosition.Y + es_argv[0];
+ if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1;
+ Pos.X = Info.dwCursorPosition.X;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'a': // ESC[#a
+ case 'C': // ESC[#C Moves cursor forward # spaces
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[C == ESC[1C
+ if (es_argc != 1) return;
+ Pos.X = Info.dwCursorPosition.X + es_argv[0];
+ if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1;
+ Pos.Y = Info.dwCursorPosition.Y;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'j': // ESC[#j
+ case 'D': // ESC[#D Moves cursor back # spaces
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[D == ESC[1D
+ if (es_argc != 1) return;
+ Pos.X = Info.dwCursorPosition.X - es_argv[0];
+ if (Pos.X < 0) Pos.X = 0;
+ Pos.Y = Info.dwCursorPosition.Y;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'E': // ESC[#E Moves cursor down # lines, column 1.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[E == ESC[1E
+ if (es_argc != 1) return;
+ Pos.Y = Info.dwCursorPosition.Y + es_argv[0];
+ if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1;
+ Pos.X = 0;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'F': // ESC[#F Moves cursor up # lines, column 1.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[F == ESC[1F
+ if (es_argc != 1) return;
+ Pos.Y = Info.dwCursorPosition.Y - es_argv[0];
+ if (Pos.Y < 0) Pos.Y = 0;
+ Pos.X = 0;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case '`': // ESC[#`
+ case 'G': // ESC[#G Moves cursor column # in current row.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[G == ESC[1G
+ if (es_argc != 1) return;
+ Pos.X = es_argv[0] - 1;
+ if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1;
+ if (Pos.X < 0) Pos.X = 0;
+ Pos.Y = Info.dwCursorPosition.Y;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'd': // ESC[#d Moves cursor row #, current column.
+ if (es_argc == 0) es_argv[es_argc++] = 1; // ESC[d == ESC[1d
+ if (es_argc != 1) return;
+ Pos.Y = es_argv[0] - 1;
+ if (Pos.Y < 0) Pos.Y = 0;
+ if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 'f': // ESC[#;#f
+ case 'H': // ESC[#;#H Moves cursor to line #, column #
+ if (es_argc == 0)
+ es_argv[es_argc++] = 1; // ESC[H == ESC[1;1H
+ if (es_argc == 1)
+ es_argv[es_argc++] = 1; // ESC[#H == ESC[#;1H
+ if (es_argc > 2) return;
+ Pos.X = es_argv[1] - 1;
+ if (Pos.X < 0) Pos.X = 0;
+ if (Pos.X >= Info.dwSize.X) Pos.X = Info.dwSize.X - 1;
+ Pos.Y = es_argv[0] - 1;
+ if (Pos.Y < 0) Pos.Y = 0;
+ if (Pos.Y >= Info.dwSize.Y) Pos.Y = Info.dwSize.Y - 1;
+ SetConsoleCursorPosition( hConOut, Pos );
+ return;
+
+ case 's': // ESC[s Saves cursor position for recall later
+ if (es_argc != 0) return;
+ SavePos = Info.dwCursorPosition;
+ return;
+
+ case 'u': // ESC[u Return to saved cursor position
+ if (es_argc != 0) return;
+ SetConsoleCursorPosition( hConOut, SavePos );
+ return;
+
+ case 'n': // ESC[#n Device status report
+ if (es_argc != 1) return; // ESC[n == ESC[0n -> ignored
+ switch (es_argv[0])
+ {
+ case 5: // ESC[5n Report status
+ SendSequence( L"\33[0n" ); // "OK"
+ return;
+
+ case 6: // ESC[6n Report cursor position
+ {
+ TCHAR buf[32];
+ wsprintf( buf, L"\33[%d;%dR", Info.dwCursorPosition.Y + 1,
+ Info.dwCursorPosition.X + 1 );
+ SendSequence( buf );
+ }
+ return;
+
+ default:
+ return;
+ }
+
+ case 't': // ESC[#t Window manipulation
+ if (es_argc != 1) return;
+ if (es_argv[0] == 21) // ESC[21t Report xterm window's title
+ {
+ TCHAR buf[MAX_PATH*2];
+ DWORD len = GetConsoleTitle( buf+3, lenof(buf)-3-2 );
+ // Too bad if it's too big or fails.
+ buf[0] = ESC;
+ buf[1] = ']';
+ buf[2] = 'l';
+ buf[3+len] = ESC;
+ buf[3+len+1] = '\\';
+ buf[3+len+2] = '\0';
+ SendSequence( buf );
+ }
+ return;
+
+ default:
+ return;
+ }
+ }
+ else // (prefix == ']')
+ {
+ // Ignore any \e]? or \e]> sequences.
+ if (prefix2 != 0)
+ return;
+
+ if (es_argc == 1 && es_argv[0] == 0) // ESC]0;titleST
+ {
+ SetConsoleTitle( Pt_arg );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ParseAndPrintString(hDev, lpBuffer, nNumberOfBytesToWrite)
+// Parses the string lpBuffer, interprets the escapes sequences and prints the
+// characters in the device hDev (console).
+// The lexer is a three states automata.
+// If the number of arguments es_argc > MAX_ARG, only the MAX_ARG-1 firsts and
+// the last arguments are processed (no es_argv[] overflow).
+//-----------------------------------------------------------------------------
+
+BOOL
+ParseAndPrintString( HANDLE hDev,
+ LPCVOID lpBuffer,
+ DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten
+ )
+{
+ DWORD i;
+ LPCTSTR s;
+
+ if (hDev != hConOut) // reinit if device has changed
+ {
+ hConOut = hDev;
+ state = 1;
+ shifted = FALSE;
+ }
+ for (i = nNumberOfBytesToWrite, s = (LPCTSTR)lpBuffer; i > 0; i--, s++)
+ {
+ if (state == 1)
+ {
+ if (*s == ESC) state = 2;
+ else if (*s == SO) shifted = TRUE;
+ else if (*s == SI) shifted = FALSE;
+ else PushBuffer( *s );
+ }
+ else if (state == 2)
+ {
+ if (*s == ESC) ; // \e\e...\e == \e
+ else if ((*s == '[') || (*s == ']'))
+ {
+ FlushBuffer();
+ prefix = *s;
+ prefix2 = 0;
+ state = 3;
+ Pt_len = 0;
+ *Pt_arg = '\0';
+ }
+ else if (*s == ')' || *s == '(') state = 6;
+ else state = 1;
+ }
+ else if (state == 3)
+ {
+ if (isdigit( *s ))
+ {
+ es_argc = 0;
+ es_argv[0] = *s - '0';
+ state = 4;
+ }
+ else if (*s == ';')
+ {
+ es_argc = 1;
+ es_argv[0] = 0;
+ es_argv[1] = 0;
+ state = 4;
+ }
+ else if (*s == '?' || *s == '>')
+ {
+ prefix2 = *s;
+ }
+ else
+ {
+ es_argc = 0;
+ suffix = *s;
+ InterpretEscSeq();
+ state = 1;
+ }
+ }
+ else if (state == 4)
+ {
+ if (isdigit( *s ))
+ {
+ es_argv[es_argc] = 10 * es_argv[es_argc] + (*s - '0');
+ }
+ else if (*s == ';')
+ {
+ if (es_argc < MAX_ARG-1) es_argc++;
+ es_argv[es_argc] = 0;
+ if (prefix == ']')
+ state = 5;
+ }
+ else
+ {
+ es_argc++;
+ suffix = *s;
+ InterpretEscSeq();
+ state = 1;
+ }
+ }
+ else if (state == 5)
+ {
+ if (*s == BEL)
+ {
+ Pt_arg[Pt_len] = '\0';
+ InterpretEscSeq();
+ state = 1;
+ }
+ else if (*s == '\\' && Pt_len > 0 && Pt_arg[Pt_len-1] == ESC)
+ {
+ Pt_arg[--Pt_len] = '\0';
+ InterpretEscSeq();
+ state = 1;
+ }
+ else if (Pt_len < lenof(Pt_arg)-1)
+ Pt_arg[Pt_len++] = *s;
+ }
+ else if (state == 6)
+ {
+ // Ignore it (ESC ) 0 is implicit; nothing else is supported).
+ state = 1;
+ }
+ }
+ FlushBuffer();
+ if (lpNumberOfBytesWritten != NULL)
+ *lpNumberOfBytesWritten = nNumberOfBytesToWrite - i;
+ return (i == 0);
+}
+
+
+// ========== Hooking API functions
+//
+// References about API hooking (and dll injection):
+// - Matt Pietrek ~ Windows 95 System Programming Secrets.
+// - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed.
+
+// Macro for adding pointers/DWORDs together without C arithmetic interfering
+#define MakeVA( cast, offset ) (cast)((DWORD_PTR)(pDosHeader)+(DWORD)(offset))
+
+
+const char APIKernel[] = "kernel32.dll";
+const char APIConsole[] = "API-MS-Win-Core-Console-";
+const char APIProcessThreads[] = "API-MS-Win-Core-ProcessThreads-";
+const char APIProcessEnvironment[] = "API-MS-Win-Core-ProcessEnvironment-";
+const char APILibraryLoader[] = "API-MS-Win-Core-LibraryLoader-";
+const char APIFile[] = "API-MS-Win-Core-File-";
+
+typedef struct
+{
+ PCSTR name;
+ DWORD len;
+ HMODULE base;
+} API_DATA, *PAPI_DATA;
+
+API_DATA APIs[] =
+{
+ { APIConsole, sizeof(APIConsole) - 1, NULL },
+ { APIProcessThreads, sizeof(APIProcessThreads) - 1, NULL },
+ { APIProcessEnvironment, sizeof(APIProcessEnvironment) - 1, NULL },
+ { APILibraryLoader, sizeof(APILibraryLoader) - 1, NULL },
+ { APIFile, sizeof(APIFile) - 1, NULL },
+ { NULL, 0, NULL }
+};
+
+
+HMODULE hKernel; // Kernel32 module handle
+HINSTANCE hDllInstance; // Dll instance handle
+TCHAR hDllName[MAX_PATH]; // Dll file name
+#if defined(_WIN64) || defined(W32ON64)
+LPTSTR hDllNameType; // pointer to process type within above
+#endif
+
+typedef struct
+{
+ PCSTR lib;
+ PSTR name;
+ PROC newfunc;
+ PROC oldfunc;
+ PROC apifunc;
+} HookFn, *PHookFn;
+
+HookFn Hooks[];
+
+const WCHAR zIgnoring[] = L"Ignoring";
+const WCHAR zHooking[] = L"Hooking";
+const WCHAR zUnhooking[] = L"Unhooking";
+
+
+//-----------------------------------------------------------------------------
+// HookAPIOneMod
+// Substitute a new function in the Import Address Table (IAT) of the
+// specified module.
+// Return FALSE on error and TRUE on success.
+//-----------------------------------------------------------------------------
+
+BOOL HookAPIOneMod(
+ HMODULE hFromModule, // Handle of the module to intercept calls from
+ PHookFn Hooks, // Functions to replace
+ BOOL restore // Restore the original functions
+ )
+{
+ PIMAGE_DOS_HEADER pDosHeader;
+ PIMAGE_NT_HEADERS pNTHeader;
+ PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
+ PIMAGE_THUNK_DATA pThunk;
+ PHookFn hook;
+
+ // Tests to make sure we're looking at a module image (the 'MZ' header)
+ pDosHeader = (PIMAGE_DOS_HEADER)hFromModule;
+ if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
+ {
+ DEBUGSTR( 1, L"Image has no DOS header!" );
+ return FALSE;
+ }
+
+ // The MZ header has a pointer to the PE header
+ pNTHeader = MakeVA( PIMAGE_NT_HEADERS, pDosHeader->e_lfanew );
+
+ // One more test to make sure we're looking at a "PE" image
+ if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
+ {
+ DEBUGSTR( 1, L"Image has no NT header!" );
+ return FALSE;
+ }
+
+ // We now have a valid pointer to the module's PE header.
+ // Get a pointer to its imports section.
+ pImportDesc = MakeVA( PIMAGE_IMPORT_DESCRIPTOR,
+ pNTHeader->OptionalHeader.
+ DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
+ VirtualAddress );
+
+ // Bail out if the RVA of the imports section is 0 (it doesn't exist)
+ if (pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pDosHeader)
+ return TRUE;
+
+ // Iterate through the array of imported module descriptors, looking
+ // for the module whose name matches the pszFunctionModule parameter.
+ for (; pImportDesc->Name; pImportDesc++)
+ {
+ BOOL kernel = TRUE;
+ PSTR pszModName = MakeVA( PSTR, pImportDesc->Name );
+ if (_stricmp( pszModName, APIKernel ) != 0)
+ {
+ PAPI_DATA lib;
+ for (lib = APIs; lib->name; ++lib)
+ {
+ if (_strnicmp( pszModName, lib->name, lib->len ) == 0)
+ {
+ if (lib->base == NULL)
+ {
+ lib->base = GetModuleHandleA( pszModName );
+ for (hook = Hooks; hook->name; ++hook)
+ if (hook->lib == lib->name)
+ hook->apifunc = GetProcAddress( lib->base, hook->name );
+ }
+ break;
+ }
+ }
+ if (lib->name == NULL)
+ {
+ if (log_level & 16)
+ DEBUGSTR( 2, L" %s %S", zIgnoring, pszModName );
+ continue;
+ }
+ kernel = FALSE;
+ }
+ if (log_level & 16)
+ DEBUGSTR( 2, L" Scanning %S", pszModName );
+
+ // Get a pointer to the found module's import address table (IAT).
+ pThunk = MakeVA( PIMAGE_THUNK_DATA, pImportDesc->FirstThunk );
+
+ // Blast through the table of import addresses, looking for the ones
+ // that match the original addresses.
+ while (pThunk->u1.Function)
+ {
+ for (hook = Hooks; hook->name; ++hook)
+ {
+ PROC patch = 0;
+ if (restore)
+ {
+ if ((PROC)pThunk->u1.Function == hook->newfunc)
+ patch = (kernel) ? hook->oldfunc : hook->apifunc;
+ }
+ else if ((PROC)pThunk->u1.Function == hook->oldfunc ||
+ (PROC)pThunk->u1.Function == hook->apifunc)
+ {
+ patch = hook->newfunc;
+ }
+ if (patch)
+ {
+ DWORD flOldProtect, flNewProtect, flDummy;
+ MEMORY_BASIC_INFORMATION mbi;
+
+ DEBUGSTR( 3, L" %S", hook->name );
+ // Get the current protection attributes.
+ VirtualQuery( &pThunk->u1.Function, &mbi, sizeof(mbi) );
+ // Take the access protection flags.
+ flNewProtect = mbi.Protect;
+ // Remove ReadOnly and ExecuteRead flags.
+ flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
+ // Add on ReadWrite flag
+ flNewProtect |= (PAGE_READWRITE);
+ // Change the access protection on the region of committed pages in the
+ // virtual address space of the current process.
+ VirtualProtect( &pThunk->u1.Function, sizeof(PVOID),
+ flNewProtect, &flOldProtect );
+
+ // Overwrite the original address with the address of the new function.
+ if (!WriteProcessMemory( GetCurrentProcess(),
+ &pThunk->u1.Function,
+ &patch, sizeof(patch), NULL ))
+ {
+ DEBUGSTR( 1, L"Could not patch!" );
+ return FALSE;
+ }
+
+ // Put the page attributes back the way they were.
+ VirtualProtect( &pThunk->u1.Function, sizeof(PVOID),
+ flOldProtect, &flDummy );
+ }
+ }
+ pThunk++; // Advance to next imported function address
+ }
+ }
+
+ return TRUE; // Function not found
+}
+
+//-----------------------------------------------------------------------------
+// HookAPIAllMod
+// Substitute a new function in the Import Address Table (IAT) of all
+// the modules in the current process.
+// Return FALSE on error and TRUE on success.
+//-----------------------------------------------------------------------------
+
+BOOL HookAPIAllMod( PHookFn Hooks, BOOL restore )
+{
+ HANDLE hModuleSnap;
+ MODULEENTRY32 me;
+ BOOL fOk;
+
+ // Take a snapshot of all modules in the current process.
+ hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE,
+ GetCurrentProcessId() );
+
+ if (hModuleSnap == INVALID_HANDLE_VALUE)
+ {
+ DEBUGSTR( 1, L"Failed to create snapshot!" );
+ return FALSE;
+ }
+
+ // Fill the size of the structure before using it.
+ me.dwSize = sizeof(MODULEENTRY32);
+
+ // Walk the module list of the modules.
+ for (fOk = Module32First( hModuleSnap, &me ); fOk;
+ fOk = Module32Next( hModuleSnap, &me ))
+ {
+ // We don't hook functions in our own module.
+ if (me.hModule != hDllInstance && me.hModule != hKernel)
+ {
+ if (search_env( L"ANSICON_EXC", me.szModule ))
+ {
+ DEBUGSTR( 2, L"%s %s", zIgnoring, me.szModule );
+ continue;
+ }
+ DEBUGSTR( 2, L"%s %s", (restore) ? zUnhooking : zHooking, me.szModule );
+ // Hook this function in this module.
+ if (!HookAPIOneMod( me.hModule, Hooks, restore ))
+ {
+ CloseHandle( hModuleSnap );
+ return FALSE;
+ }
+ }
+ }
+ CloseHandle( hModuleSnap );
+ DEBUGSTR( 2, L"%s completed", (restore) ? zUnhooking : zHooking );
+ return TRUE;
+}
+
+
+// ========== Child process injection
+
+// Inject code into the target process to load our DLL.
+void Inject( DWORD dwCreationFlags, LPPROCESS_INFORMATION lpi,
+ LPPROCESS_INFORMATION child_pi,
+ BOOL wide, LPCVOID lpApp, LPCVOID lpCmd )
+{
+ int type;
+ BOOL gui;
+
+ type = ProcessType( child_pi, &gui );
+ if (gui)
+ {
+ TCHAR app[MAX_PATH];
+ LPTSTR name;
+ LPCTSTR term = L" \t";
+
+ app[MAX_PATH-1] = '\0';
+ if (lpApp == NULL)
+ {
+ // Extract the program from the command line. I would use
+ // GetModuleFileNameEx, but it doesn't work when a process is created
+ // suspended and setting up a delay until it does work sometimes
+ // prevents the process running at all. GetProcessImageFileName works,
+ // but it's not supported in 2K.
+ if (wide)
+ {
+ LPCTSTR pos;
+ for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ;
+ if (*pos == '"')
+ {
+ term = L"\"";
+ ++pos;
+ }
+ wcsncpy( app, pos, MAX_PATH-1 );
+ }
+ else
+ {
+ LPCSTR pos;
+ for (pos = lpCmd; *pos == ' ' || *pos == '\t'; ++pos) ;
+ if (*pos == '"')
+ {
+ term = L"\"";
+ ++pos;
+ }
+ MultiByteToWideChar( CP_ACP, 0, pos, -1, app, MAX_PATH );
+ }
+ // CreateProcess only works with surrounding quotes ('"a name"' works, but
+ // 'a" "name' fails), so that's all I'll test, too. However, it also
+ // tests for a file at each separator ('a name' tries "a.exe" before
+ // "a name.exe") which I won't do.
+ name = wcspbrk( app, term );
+ if (name)
+ *name = '\0';
+ }
+ else
+ {
+ if (wide)
+ wcsncpy( app, lpApp, MAX_PATH-1 );
+ else
+ MultiByteToWideChar( CP_ACP, 0, lpApp, -1, app, MAX_PATH );
+ }
+ name = get_program_name( app );
+ if (!search_env( L"ANSICON_GUI", name ))
+ {
+ DEBUGSTR( 1, L" %s", zIgnoring );
+ type = 0;
+ }
+ }
+ if (type != 0)
+ {
+#ifdef _WIN64
+ if (type == 32)
+ {
+ hDllNameType[0] = '3';
+ hDllNameType[1] = '2';
+ InjectDLL32( child_pi, hDllName );
+ }
+ else
+ {
+ hDllNameType[0] = '6';
+ hDllNameType[1] = '4';
+ InjectDLL64( child_pi, hDllName );
+ }
+#else
+#ifdef W32ON64
+ if (type == 64)
+ {
+ TCHAR args[64];
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ wcscpy( hDllNameType, L"CON.exe" );
+ wsprintf( args, L"ansicon -P%lu:%lu",
+ child_pi->dwProcessId, child_pi->dwThreadId );
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ if (CreateProcess( hDllName, args, NULL, NULL, FALSE, 0, NULL, NULL,
+ &si, &pi ))
+ {
+ WaitForSingleObject( pi.hProcess, INFINITE );
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+ }
+ else
+ DEBUGSTR( 1, L"Could not execute \"%s\"", hDllName );
+ wcscpy( hDllNameType, L"32.dll" );
+ }
+ else
+#endif
+ InjectDLL32( child_pi, hDllName );
+#endif
+ if (!gui && !(dwCreationFlags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)))
+ {
+ LPPROCESS_INFORMATION cpi;
+ s_pid = child_pi->dwProcessId;
+ s_grm = grm;
+ s_flag = GRM_INIT;
+ cpi = malloc( sizeof(*cpi) );
+ cpi->dwProcessId = child_pi->dwProcessId;
+ DuplicateHandle( GetCurrentProcess(), child_pi->hProcess,
+ GetCurrentProcess(), &cpi->hProcess, 0, FALSE,
+ DUPLICATE_SAME_ACCESS );
+ CloseHandle( CreateThread( NULL, 4096, UpdateGRM, cpi, 0, NULL ) );
+ }
+ }
+
+ if (!(dwCreationFlags & CREATE_SUSPENDED))
+ ResumeThread( child_pi->hThread );
+
+ if (lpi)
+ {
+ memcpy( lpi, child_pi, sizeof(PROCESS_INFORMATION) );
+ }
+ else
+ {
+ CloseHandle( child_pi->hThread );
+ CloseHandle( child_pi->hProcess );
+ }
+}
+
+
+BOOL WINAPI MyCreateProcessA( LPCSTR lpApplicationName,
+ LPSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCSTR lpCurrentDirectory,
+ LPSTARTUPINFOA lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation )
+{
+ PROCESS_INFORMATION child_pi;
+
+ if (!CreateProcessA( lpApplicationName,
+ lpCommandLine,
+ lpThreadAttributes,
+ lpProcessAttributes,
+ bInheritHandles,
+ dwCreationFlags | CREATE_SUSPENDED,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ &child_pi ))
+ return FALSE;
+
+ DEBUGSTR( 1, L"CreateProcessA: (%lu) \"%S\", \"%S\"",
+ child_pi.dwProcessId,
+ (lpApplicationName == NULL) ? "" : lpApplicationName,
+ (lpCommandLine == NULL) ? "" : lpCommandLine );
+ Inject( dwCreationFlags, lpProcessInformation, &child_pi,
+ FALSE, lpApplicationName, lpCommandLine );
+
+ return TRUE;
+}
+
+
+BOOL WINAPI MyCreateProcessW( LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation )
+{
+ PROCESS_INFORMATION child_pi;
+
+ if (!CreateProcessW( lpApplicationName,
+ lpCommandLine,
+ lpThreadAttributes,
+ lpProcessAttributes,
+ bInheritHandles,
+ dwCreationFlags | CREATE_SUSPENDED,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ &child_pi ))
+ return FALSE;
+
+ DEBUGSTR( 1, L"CreateProcessW: (%lu) \"%s\", \"%s\"",
+ child_pi.dwProcessId,
+ (lpApplicationName == NULL) ? L"" : lpApplicationName,
+ (lpCommandLine == NULL) ? L"" : lpCommandLine );
+ Inject( dwCreationFlags, lpProcessInformation, &child_pi,
+ TRUE, lpApplicationName, lpCommandLine );
+
+ return TRUE;
+}
+
+
+FARPROC WINAPI MyGetProcAddress( HMODULE hModule, LPCSTR lpProcName )
+{
+ PHookFn hook;
+ FARPROC proc;
+
+ proc = GetProcAddress( hModule, lpProcName );
+
+ if (proc)
+ {
+ if (hModule == hKernel)
+ {
+ // Ignore LoadLibrary so other hooks continue to work (our version
+ // might end up at a different address).
+ if (proc == Hooks[0].oldfunc || proc == Hooks[1].oldfunc)
+ {
+ DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName );
+ return proc;
+ }
+ for (hook = Hooks + 2; hook->name; ++hook)
+ {
+ if (proc == hook->oldfunc)
+ {
+ DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName );
+ return hook->newfunc;
+ }
+ }
+ }
+ else
+ {
+ PAPI_DATA api;
+ for (api = APIs; api->name; ++api)
+ {
+ if (hModule == api->base)
+ {
+ if (proc == Hooks[0].apifunc || proc == Hooks[1].apifunc)
+ {
+ DEBUGSTR( 3, L"GetProcAddress: %S (ignoring)", lpProcName );
+ return proc;
+ }
+ for (hook = Hooks + 2; hook->name; ++hook)
+ {
+ if (proc == hook->apifunc)
+ {
+ DEBUGSTR( 3, L"GetProcAddress: %S", lpProcName );
+ return hook->newfunc;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return proc;
+}
+
+
+void HookLibrary( HMODULE hMod, LPCVOID lpFileName, BOOL wide, LPCSTR funcName )
+{
+ LPCWSTR name;
+ WCHAR wname[MAX_PATH];
+
+ if (hMod && hMod != hKernel)
+ {
+ if (!wide)
+ {
+ MultiByteToWideChar( AreFileApisANSI() ? CP_ACP : CP_OEMCP, 0,
+ lpFileName, -1, wname, MAX_PATH );
+ lpFileName = wname;
+ }
+ name = wcsrchr( lpFileName, '\\' );
+ if (name == NULL)
+ name = lpFileName;
+ else
+ ++name;
+ if (search_env( L"ANSICON_EXC", name ))
+ DEBUGSTR( 2, L"%s %s (%S)", zIgnoring, lpFileName, funcName );
+ else
+ {
+ DEBUGSTR( 2, L"%s %s (%S)", zHooking, lpFileName, funcName );
+ HookAPIOneMod( hMod, Hooks, FALSE );
+ }
+ }
+}
+
+
+HMODULE WINAPI MyLoadLibraryA( LPCSTR lpFileName )
+{
+ HMODULE hMod = LoadLibraryA( lpFileName );
+ HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryA" );
+ return hMod;
+}
+
+
+HMODULE WINAPI MyLoadLibraryW( LPCWSTR lpFileName )
+{
+ HMODULE hMod = LoadLibraryW( lpFileName );
+ HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryW" );
+ return hMod;
+}
+
+
+HMODULE WINAPI MyLoadLibraryExA( LPCSTR lpFileName, HANDLE hFile,
+ DWORD dwFlags )
+{
+ HMODULE hMod = LoadLibraryExA( lpFileName, hFile, dwFlags );
+ if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE))
+ HookLibrary( hMod, lpFileName, FALSE, "LoadLibraryExA" );
+ return hMod;
+}
+
+
+HMODULE WINAPI MyLoadLibraryExW( LPCWSTR lpFileName, HANDLE hFile,
+ DWORD dwFlags )
+{
+ HMODULE hMod = LoadLibraryExW( lpFileName, hFile, dwFlags );
+ if (!(dwFlags & LOAD_LIBRARY_AS_DATAFILE))
+ HookLibrary( hMod, lpFileName, TRUE, "LoadLibraryExW" );
+ return hMod;
+}
+
+
+//-----------------------------------------------------------------------------
+// MyWrite...
+// It is the new function that must replace the original Write... function.
+// This function have exactly the same signature as the original one.
+//-----------------------------------------------------------------------------
+
+BOOL
+WINAPI MyWriteConsoleA( HANDLE hCon, LPCVOID lpBuffer,
+ DWORD nNumberOfCharsToWrite,
+ LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved )
+{
+ DWORD Mode;
+ LPWSTR buf;
+ DWORD len;
+ BOOL rc = TRUE;
+
+ // if we write in a console buffer with processed output
+ if (GetConsoleMode( hCon, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT))
+ {
+ UINT cp = GetConsoleOutputCP();
+ DEBUGSTR( 4, L"\33WriteConsoleA: %lu \"%.*S\"",
+ nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer );
+ len = MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, NULL,0 );
+ buf = malloc( TSIZE(len) );
+ if (buf == NULL)
+ {
+ if (lpNumberOfCharsWritten != NULL)
+ *lpNumberOfCharsWritten = 0;
+ return (nNumberOfCharsToWrite == 0);
+ }
+ MultiByteToWideChar( cp, 0, lpBuffer, nNumberOfCharsToWrite, buf, len );
+ rc = ParseAndPrintString( hCon, buf, len, lpNumberOfCharsWritten );
+ free( buf );
+ if (rc && lpNumberOfCharsWritten != NULL &&
+ *lpNumberOfCharsWritten != nNumberOfCharsToWrite)
+ {
+ // Converting a multibyte character to Unicode results in a different
+ // "character" count. This causes some programs to think not everything
+ // was written, so the difference is sent again. Fudge the (presumably)
+ // correct count.
+ if (search_env( L"ANSICON_API", prog ))
+ *lpNumberOfCharsWritten = nNumberOfCharsToWrite;
+ }
+ return rc;
+ }
+
+ return WriteConsoleA( hCon, lpBuffer, nNumberOfCharsToWrite,
+ lpNumberOfCharsWritten, lpReserved );
+
+}
+
+BOOL
+WINAPI MyWriteConsoleW( HANDLE hCon, LPCVOID lpBuffer,
+ DWORD nNumberOfCharsToWrite,
+ LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved )
+{
+ DWORD Mode;
+ if (GetConsoleMode( hCon, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT))
+ {
+ DEBUGSTR( 4, L"\33WriteConsoleW: %lu \"%.*s\"",
+ nNumberOfCharsToWrite, nNumberOfCharsToWrite, lpBuffer );
+ return ParseAndPrintString( hCon, lpBuffer,
+ nNumberOfCharsToWrite,
+ lpNumberOfCharsWritten );
+ }
+
+ return WriteConsoleW( hCon, lpBuffer, nNumberOfCharsToWrite,
+ lpNumberOfCharsWritten, lpReserved );
+}
+
+BOOL
+WINAPI MyWriteFile( HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
+ LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped )
+{
+ DWORD Mode;
+ if (GetConsoleMode( hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT))
+ {
+ DEBUGSTR( 4, L"WriteFile->" );
+ return MyWriteConsoleA( hFile, lpBuffer,
+ nNumberOfBytesToWrite,
+ lpNumberOfBytesWritten,
+ lpOverlapped );
+ }
+
+ // here, WriteFile is the old function (this module is not hooked)
+ return WriteFile( hFile, lpBuffer, nNumberOfBytesToWrite,
+ lpNumberOfBytesWritten, lpOverlapped );
+}
+
+
+#define HHFILE (HANDLE)(DWORD_PTR)
+
+UINT
+WINAPI My_lwrite( HFILE hFile, LPCSTR lpBuffer, UINT uBytes )
+{
+ DWORD Mode, written;
+ if (GetConsoleMode( HHFILE hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT))
+ {
+ DEBUGSTR( 4, L"_lwrite->" );
+ MyWriteConsoleA( HHFILE hFile, lpBuffer, uBytes, &written, NULL );
+ return written;
+ }
+
+ return _lwrite( hFile, lpBuffer, uBytes );
+}
+
+long
+WINAPI My_hwrite( HFILE hFile, LPCSTR lpBuffer, long lBytes )
+{
+ DWORD Mode, written;
+ if (GetConsoleMode( HHFILE hFile, &Mode ) && (Mode & ENABLE_PROCESSED_OUTPUT))
+ {
+ DEBUGSTR( 4, L"_hwrite->" );
+ MyWriteConsoleA( HHFILE hFile, lpBuffer, lBytes, &written, NULL );
+ return written;
+ }
+
+ return _hwrite( hFile, lpBuffer, lBytes );
+}
+
+
+// ========== Environment variable
+
+void set_ansicon( PCONSOLE_SCREEN_BUFFER_INFO pcsbi )
+{
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ TCHAR buf[64];
+
+ if (pcsbi == NULL)
+ {
+ HANDLE hConOut;
+ hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, 0 );
+ GetConsoleScreenBufferInfo( hConOut, &csbi );
+ CloseHandle( hConOut );
+ pcsbi = &csbi;
+ }
+
+ wsprintf( buf, L"%dx%d (%dx%d)",
+ pcsbi->dwSize.X, pcsbi->dwSize.Y,
+ pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1,
+ pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1 );
+ SetEnvironmentVariable( L"ANSICON", buf );
+}
+
+DWORD
+WINAPI MyGetEnvironmentVariableA( LPCSTR lpName, LPSTR lpBuffer, DWORD nSize )
+{
+ if (lstrcmpiA( lpName, "ANSICON_VER" ) == 0)
+ {
+ if (nSize < sizeof(PVEREA))
+ return sizeof(PVEREA);
+ memcpy( lpBuffer, PVEREA, sizeof(PVEREA) );
+ return sizeof(PVEREA) - 1;
+ }
+
+ if (lstrcmpiA( lpName, "ANSICON" ) == 0)
+ set_ansicon( NULL );
+
+ return GetEnvironmentVariableA( lpName, lpBuffer, nSize );
+}
+
+DWORD
+WINAPI MyGetEnvironmentVariableW( LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize )
+{
+ if (lstrcmpi( lpName, L"ANSICON_VER" ) == 0)
+ {
+ if (nSize < lenof(PVERE))
+ return lenof(PVERE);
+ memcpy( lpBuffer, PVERE, sizeof(PVERE) );
+ return lenof(PVERE) - 1;
+ }
+
+ if (lstrcmpi( lpName, L"ANSICON" ) == 0)
+ set_ansicon( NULL );
+
+ return GetEnvironmentVariableW( lpName, lpBuffer, nSize );
+}
+
+
+// ========== Initialisation
+
+HookFn Hooks[] = {
+ // These two are expected first!
+ { APILibraryLoader, "LoadLibraryA", (PROC)MyLoadLibraryA, NULL, NULL },
+ { APILibraryLoader, "LoadLibraryW", (PROC)MyLoadLibraryW, NULL, NULL },
+ { APIProcessThreads, "CreateProcessA", (PROC)MyCreateProcessA, NULL, NULL },
+ { APIProcessThreads, "CreateProcessW", (PROC)MyCreateProcessW, NULL, NULL },
+ { APIProcessEnvironment, "GetEnvironmentVariableA", (PROC)MyGetEnvironmentVariableA, NULL, NULL },
+ { APIProcessEnvironment, "GetEnvironmentVariableW", (PROC)MyGetEnvironmentVariableW, NULL, NULL },
+ { APILibraryLoader, "GetProcAddress", (PROC)MyGetProcAddress, NULL, NULL },
+ { APILibraryLoader, "LoadLibraryExA", (PROC)MyLoadLibraryExA, NULL, NULL },
+ { APILibraryLoader, "LoadLibraryExW", (PROC)MyLoadLibraryExW, NULL, NULL },
+ { APIConsole, "WriteConsoleA", (PROC)MyWriteConsoleA, NULL, NULL },
+ { APIConsole, "WriteConsoleW", (PROC)MyWriteConsoleW, NULL, NULL },
+ { APIFile, "WriteFile", (PROC)MyWriteFile, NULL, NULL },
+ { APIKernel, "_lwrite", (PROC)My_lwrite, NULL, NULL },
+ { APIKernel, "_hwrite", (PROC)My_hwrite, NULL, NULL },
+ { NULL, NULL, NULL, NULL, NULL }
+};
+
+//-----------------------------------------------------------------------------
+// OriginalAttr()
+// Determine the original attributes for use by \e[m.
+//-----------------------------------------------------------------------------
+void OriginalAttr( void )
+{
+ HANDLE hConOut;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, 0 );
+ if (!GetConsoleScreenBufferInfo( hConOut, &csbi ))
+ csbi.wAttributes = 7;
+ CloseHandle( hConOut );
+
+ if (s_flag == GRM_INIT && s_pid == GetCurrentProcessId())
+ {
+ s_flag = 0;
+ grm = s_grm;
+ }
+ else
+ {
+ if (GetEnvironmentVariable( L"ANSICON_REVERSE", NULL, 0 ))
+ {
+ SetEnvironmentVariable( L"ANSICON_REVERSE", NULL );
+ grm.reverse = TRUE;
+ grm.foreground = attr2ansi[(csbi.wAttributes >> 4) & 7];
+ grm.background = attr2ansi[csbi.wAttributes & 7];
+ grm.bold = (csbi.wAttributes & BACKGROUND_INTENSITY) >> 4;
+ grm.underline = (csbi.wAttributes & FOREGROUND_INTENSITY) << 4;
+ }
+ else
+ {
+ grm.foreground = attr2ansi[csbi.wAttributes & 7];
+ grm.background = attr2ansi[(csbi.wAttributes >> 4) & 7];
+ grm.bold = csbi.wAttributes & FOREGROUND_INTENSITY;
+ grm.underline = csbi.wAttributes & BACKGROUND_INTENSITY;
+ }
+ }
+ if (!GetEnvironmentVariable( L"ANSICON_DEF", NULL, 0 ))
+ {
+ TCHAR def[4];
+ LPTSTR a = def;
+ if (grm.reverse)
+ {
+ *a++ = '-';
+ csbi.wAttributes = ((csbi.wAttributes >> 4) & 15)
+ | ((csbi.wAttributes & 15) << 4);
+ }
+ wsprintf( a, L"%X", csbi.wAttributes & 255 );
+ SetEnvironmentVariable( L"ANSICON_DEF", def );
+ }
+ set_ansicon( &csbi );
+}
+
+
+//-----------------------------------------------------------------------------
+// DllMain()
+// Function called by the system when processes and threads are initialized
+// and terminated.
+//-----------------------------------------------------------------------------
+
+__declspec(dllexport) // to stop MinGW exporting everything
+BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
+{
+ BOOL bResult = TRUE;
+ PHookFn hook;
+ TCHAR logstr[4];
+
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ *logstr = '\0';
+ GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) );
+ log_level = _wtoi( logstr );
+ prog = get_program_name( NULL );
+#if defined(_WIN64) || defined(W32ON64)
+ hDllNameType = hDllName - 6 +
+#endif
+ GetModuleFileName( hInstance, hDllName, lenof(hDllName) );
+
+ hDllInstance = hInstance; // save Dll instance handle
+ DEBUGSTR( 1, L"hDllInstance = %p", hDllInstance );
+
+ // Get the entry points to the original functions.
+ hKernel = GetModuleHandleA( APIKernel );
+ for (hook = Hooks; hook->name; ++hook)
+ hook->oldfunc = GetProcAddress( hKernel, hook->name );
+
+ bResult = HookAPIAllMod( Hooks, FALSE );
+ OriginalAttr();
+ DisableThreadLibraryCalls( hInstance );
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ {
+ if (lpReserved == NULL)
+ {
+ DEBUGSTR( 1, L"Unloading" );
+ HookAPIAllMod( Hooks, TRUE );
+ }
+ else
+ {
+ DEBUGSTR( 1, L"Terminating" );
+ s_pid = GetCurrentProcessId();
+ s_grm = grm;
+ s_flag = GRM_EXIT;
+ }
+ }
+
+ return bResult;
+}
diff --git a/ansicon/COPYING.MinGW-w64-runtime.txt b/ansicon/COPYING.MinGW-w64-runtime.txt
new file mode 100755
index 0000000..3d11e87
--- /dev/null
+++ b/ansicon/COPYING.MinGW-w64-runtime.txt
@@ -0,0 +1,240 @@
+MinGW-w64 runtime licensing
+***************************
+
+This program or library was built using MinGW-w64 and statically
+linked against the MinGW-w64 runtime. Some parts of the runtime
+are under licenses which require that the copyright and license
+notices are included when distributing the code in binary form.
+These notices are listed below.
+
+
+========================
+Overall copyright notice
+========================
+
+Copyright (c) 2009, 2010 by the mingw-w64 project
+
+This license has been certified as open source. It has also been designated
+as GPL compatible by the Free Software Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions in source code must retain the accompanying copyright
+ notice, this list of conditions, and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+ 3. Names of the copyright holders must not be used to endorse or promote
+ products derived from this software without prior written permission
+ from the copyright holders.
+ 4. The right to distribute this software or to use it for any purpose does
+ not give you the right to use Servicemarks (sm) or Trademarks (tm) of
+ the copyright holders. Use of them is covered by separate agreement
+ with the copyright holders.
+ 5. If any files are modified, you must cause the modified files to carry
+ prominent notices stating that you changed the files and the date of
+ any change.
+
+Disclaimer
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
+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 HOLDERS 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.
+
+========================================
+getopt, getopt_long, and getop_long_only
+========================================
+
+Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Sponsored in part by the Defense Advanced Research Projects
+Agency (DARPA) and Air Force Research Laboratory, Air Force
+Materiel Command, USAF, under agreement number F39502-99-1-0512.
+
+ * * * * * * *
+
+Copyright (c) 2000 The NetBSD Foundation, Inc.
+All rights reserved.
+
+This code is derived from software contributed to The NetBSD Foundation
+by Dieter Baron and Thomas Klausner.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+
+
+===============================================================
+gdtoa: Converting between IEEE floating point numbers and ASCII
+===============================================================
+
+The author of this software is David M. Gay.
+
+Copyright (C) 1997, 1998, 1999, 2000, 2001 by Lucent Technologies
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name of Lucent or any of its entities
+not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+ * * * * * * *
+
+The author of this software is David M. Gay.
+
+Copyright (C) 2005 by David M. Gay
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that the copyright notice and this permission notice and warranty
+disclaimer appear in supporting documentation, and that the name of
+the author or any of his current or former employers not be used in
+advertising or publicity pertaining to distribution of the software
+without specific, written prior permission.
+
+THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
+NO EVENT SHALL THE AUTHOR OR ANY OF HIS CURRENT OR FORMER EMPLOYERS BE
+LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+ * * * * * * *
+
+The author of this software is David M. Gay.
+
+Copyright (C) 2004 by David M. Gay.
+All Rights Reserved
+Based on material in the rest of /netlib/fp/gdota.tar.gz,
+which is copyright (C) 1998, 2000 by Lucent Technologies.
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appear in all
+copies and that both that the copyright notice and this
+permission notice and warranty disclaimer appear in supporting
+documentation, and that the name of Lucent or any of its entities
+not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+THIS SOFTWARE.
+
+
+=========================
+Parts of the math library
+=========================
+
+Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+
+Developed at SunSoft, a Sun Microsystems, Inc. business.
+Permission to use, copy, modify, and distribute this
+software is freely granted, provided that this notice
+is preserved.
+
+ * * * * * * *
+
+Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+
+Developed at SunPro, a Sun Microsystems, Inc. business.
+Permission to use, copy, modify, and distribute this
+software is freely granted, provided that this notice
+is preserved.
+
+ * * * * * * *
+
+FIXME: Cephes math lib
+Copyright (C) 1984-1998 Stephen L. Moshier
+
+It sounds vague, but as to be found at
+<http://lists.debian.org/debian-legal/2004/12/msg00295.html>, it gives an
+impression that the author could be willing to give an explicit
+permission to distribute those files e.g. under a BSD style license. So
+probably there is no problem here, although it could be good to get a
+permission from the author and then add a license into the Cephes files
+in MinGW runtime. At least on follow-up it is marked that debian sees the
+version a-like BSD one. As MinGW.org (where those cephes parts are coming
+from) distributes them now over 6 years, it should be fine.
+
+===================================
+Headers and IDLs imported from Wine
+===================================
+
+Some header and IDL files were imported from the Wine project. These files
+are prominent maked in source. Their copyright belongs to contributors and
+they are distributed under LGPL license.
+
+Disclaimer
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
diff --git a/ansicon/G1.bat b/ansicon/G1.bat
new file mode 100755
index 0000000..37a6bb8
--- /dev/null
+++ b/ansicon/G1.bat
@@ -0,0 +1,18 @@
+@echo off & setlocal
+
+::Extract the current code page. Hopefully this method will work with other
+::languages. CHCP outputs: "Active code page: #". Take the last five
+::characters (the longest code) and delete up to and including the space.
+for /f "delims=" %%j in ('chcp') do set CP=%%j
+set CP=%CP:~-5%
+set CP=%CP:* =%
+
+x86\ansicon -e The DEC Special Graphics Character Set according to code page %CP%:^
+
+^
+
+ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~^
+
+^
+
+ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { ^| } ~
diff --git a/ansicon/G1.txt b/ansicon/G1.txt
new file mode 100755
index 0000000..a007b91
--- /dev/null
+++ b/ansicon/G1.txt
Binary files differ
diff --git a/ansicon/ansi.rc b/ansicon/ansi.rc
new file mode 100755
index 0000000..16a4585
--- /dev/null
+++ b/ansicon/ansi.rc
@@ -0,0 +1,42 @@
+/*
+ ansi.rc - Version resource for ANSI{32,64}.dll.
+
+ Jason Hood, 11 November, 2009.
+*/
+
+#include <winver.h>
+#include "version.h"
+
+#ifdef _WIN64
+# define BITS "64"
+#else
+# define BITS "32"
+#endif
+
+1 VERSIONINFO
+FILEVERSION PVERB
+PRODUCTVERSION PVERB
+FILEOS VOS_NT
+FILETYPE VFT_DLL
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904B0"
+ {
+ VALUE "Comments", "http://ansicon.adoxa.cjb.net/"
+ VALUE "CompanyName", "Jason Hood"
+ VALUE "FileDescription", "ANSI Console"
+ VALUE "FileVersion", PVERSA
+ VALUE "InternalName", "ANSI" BITS
+ VALUE "LegalCopyright", "Freeware"
+ VALUE "OriginalFilename", "ANSI" BITS ".dll"
+ VALUE "ProductName", "ANSICON"
+ VALUE "ProductVersion", PVERSA
+ }
+ }
+
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04B0
+ }
+}
diff --git a/ansicon/ansicon.c b/ansicon/ansicon.c
new file mode 100755
index 0000000..2b87abe
--- /dev/null
+++ b/ansicon/ansicon.c
@@ -0,0 +1,686 @@
+/*
+ ANSICON.c - ANSI escape sequence console driver.
+
+ Jason Hood, 21 to 23 October, 2005.
+
+ Original injection code was derived from Console Manager by Sergey Oblomov
+ (hoopoepg). Use of FlushInstructionCache came from www.catch22.net.
+ Additional information came from "Process-wide API spying - an ultimate hack",
+ Anton Bassov's article in "The Code Project" (use of OpenThread).
+
+ v1.01, 11 & 12 March, 2006:
+ -m option to set "monochrome" (grey on black);
+ restore original color on exit.
+
+ v1.10, 22 February, 2009:
+ ignore Ctrl+C/Ctrl+Break.
+
+ v1.13, 21 & 27 March, 2009:
+ alternate injection method, to work with DEP;
+ use Unicode.
+
+ v1.20, 17 to 21 June, 2009:
+ use a combination of the two injection methods;
+ test if ANSICON is already installed;
+ added -e (and -E) option to echo the command line (without newline);
+ added -t (and -T) option to type (display) files (with file name).
+
+ v1.21, 23 September, 2009:
+ added -i (and -u) to add (remove) ANSICON to AutoRun.
+
+ v1.24, 6 & 7 January, 2010:
+ no arguments to -t, or using "-" for the name, will read from stdin;
+ fix -t and -e when ANSICON was already loaded.
+
+ v1.25, 22 July, 2010:
+ added -IU for HKLM.
+
+ v1.30, 3 August to 7 September, 2010:
+ x64 support.
+
+ v1.31, 13 & 15 November, 2010:
+ use LLW to fix potential Unicode path problems;
+ VC compatibility (2008 Express for 32-bit, PSDK 2003 R2 for 64-bit);
+ explicitly use wide characters (stick with TCHAR, but not <tchar.h>).
+
+ v1.32, 4 to 22 December, 2010:
+ make -p more robust;
+ inject into GUI processes;
+ -i implies -p.
+
+ v1.50, 7 to 14 December, 2011:
+ -u does not imply -p;
+ add the PID to the debugging output;
+ use ANSICON_VER to test if already installed;
+ always place first in AutoRun;
+ logging is always available, controlled by ANSICON_LOG environment variable;
+ only restore the original color after program/echo/type;
+ return program's exit code.
+
+ 7 January, 2012:
+ fixed installing into a piped CMD.EXE;
+ added a log message indicating all imports have been processed.
+
+ v1.52, 10 April, 2012:
+ fixed running "cmd" if "ComSpec" is not defined;
+ pass process & thread identifiers on the command line (for x86->x64).
+*/
+
+#define PDATE L"12 June, 2012"
+
+#include "ansicon.h"
+#include "version.h"
+#include <tlhelp32.h>
+#include <ctype.h>
+#include <io.h>
+
+#ifdef __MINGW32__
+int _CRT_glob = 0;
+#endif
+
+
+#ifdef _WIN64
+# define BITS L"64"
+#else
+# define BITS L"32"
+#endif
+
+
+#define CMDKEY L"Software\\Microsoft\\Command Processor"
+#define AUTORUN L"AutoRun"
+
+
+void help( void );
+
+void display( LPCTSTR, BOOL );
+void print_error( LPCTSTR, ... );
+LPTSTR skip_spaces( LPTSTR );
+void get_arg( LPTSTR, LPTSTR*, LPTSTR* );
+
+void process_autorun( TCHAR );
+
+BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe );
+BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR );
+
+
+// Find the name of the DLL and inject it.
+BOOL Inject( LPPROCESS_INFORMATION ppi, BOOL* gui, LPCTSTR app )
+{
+ DWORD len;
+ WCHAR dll[MAX_PATH];
+ int type;
+
+ DEBUGSTR( 1, L"%s (%lu)", app, ppi->dwProcessId );
+ type = ProcessType( ppi, gui );
+ if (type == 0)
+ {
+ fwprintf( stderr, L"ANSICON: %s: unsupported process.\n", app );
+ return FALSE;
+ }
+
+ len = (DWORD)(prog - prog_path);
+ memcpy( dll, prog_path, TSIZE(len) );
+#ifdef _WIN64
+ wsprintf( dll + len, L"ANSI%d.dll", type );
+ if (type == 32)
+ InjectDLL32( ppi, dll );
+ else
+ InjectDLL64( ppi, dll );
+#else
+ wcscpy( dll + len, L"ANSI32.dll" );
+ InjectDLL32( ppi, dll );
+#endif
+ return TRUE;
+}
+
+
+static HANDLE hConOut;
+static CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+void get_original_attr( void )
+{
+ hConOut = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, 0 );
+ GetConsoleScreenBufferInfo( hConOut, &csbi );
+}
+
+
+void set_original_attr( void )
+{
+ SetConsoleTextAttribute( hConOut, csbi.wAttributes );
+ CloseHandle( hConOut );
+}
+
+
+DWORD CtrlHandler( DWORD event )
+{
+ return (event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT);
+}
+
+
+int main( void )
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ LPTSTR argv, arg, cmd;
+ TCHAR logstr[4];
+ BOOL installed;
+ BOOL shell, run, gui;
+ HMODULE ansi;
+ DWORD len;
+ int rc = 0;
+
+ argv = GetCommandLine();
+ len = (DWORD)wcslen( argv ) + 1;
+ if (len < MAX_PATH)
+ len = MAX_PATH;
+ arg = malloc( TSIZE(len) );
+ get_arg( arg, &argv, &cmd ); // skip the program name
+ get_arg( arg, &argv, &cmd );
+
+ if (*arg)
+ {
+ if (wcscmp( arg, L"/?" ) == 0 ||
+ wcscmp( arg, L"--help" ) == 0)
+ {
+ help();
+ return rc;
+ }
+ if (wcscmp( arg, L"--version" ) == 0)
+ {
+ _putws( L"ANSICON (" BITS L"-bit) version " PVERS L" (" PDATE L")." );
+ return rc;
+ }
+ }
+
+ prog = get_program_name( NULL );
+ *logstr = '\0';
+ GetEnvironmentVariable( L"ANSICON_LOG", logstr, lenof(logstr) );
+ log_level = _wtoi( logstr );
+
+#ifdef _WIN64
+ if (*arg == '-' && arg[1] == 'P')
+ {
+ swscanf( arg + 2, L"%u:%u", &pi.dwProcessId, &pi.dwThreadId );
+ pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
+ pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId );
+ Inject( &pi, &gui, arg );
+ CloseHandle( pi.hThread );
+ CloseHandle( pi.hProcess );
+ return 0;
+ }
+#endif
+
+ if (log_level && !(log_level & 8))
+ DEBUGSTR( 1, NULL ); // create a new file
+
+ installed = (GetEnvironmentVariable( L"ANSICON_VER", NULL, 0 ) != 0);
+ // If it's already installed, remove it. This serves two purposes: preserves
+ // the parent's GRM; and unconditionally injects into GUI, without having to
+ // worry about ANSICON_GUI.
+ if (installed)
+ {
+ fputws( L"\33[m", stdout );
+ FreeLibrary( GetModuleHandle( L"ANSI" BITS L".dll" ) );
+ }
+
+ shell = run = TRUE;
+ get_original_attr();
+
+ while (*arg == '-')
+ {
+ switch (arg[1])
+ {
+ case 'l':
+ SetEnvironmentVariable( L"ANSICON_LOG", arg + 2 );
+ log_level = _wtoi( arg + 2 );
+ if (!(log_level & 8)) // unless told otherwise
+ DEBUGSTR( 1, NULL ); // create a new file
+ break;
+
+ case 'i':
+ case 'I':
+ case 'u':
+ case 'U':
+ shell = FALSE;
+ process_autorun( arg[1] );
+ if (arg[1] == 'u' || arg[1] == 'U')
+ break;
+ // else fall through
+
+ case 'p':
+ shell = FALSE;
+ // If it's already installed, there's no need to do anything.
+ if (installed)
+ {
+ DEBUGSTR( 1, L"Already installed" );
+ }
+ else if (GetParentProcessInfo( &pi, arg ))
+ {
+ pi.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
+ pi.hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, pi.dwThreadId );
+ SuspendThread( pi.hThread );
+ if (!Inject( &pi, &gui, arg ))
+ rc = 1;
+ ResumeThread( pi.hThread );
+ CloseHandle( pi.hThread );
+ CloseHandle( pi.hProcess );
+ }
+ else
+ {
+ fputws( L"ANSICON: could not obtain the parent process.\n", stderr );
+ rc = 1;
+ }
+ break;
+
+ case 'm':
+ {
+ int a = wcstol( arg + 2, NULL, 16 );
+ if (a == 0)
+ a = (arg[2] == '-') ? -7 : 7;
+ if (a < 0)
+ {
+ SetEnvironmentVariable( L"ANSICON_REVERSE", L"1" );
+ a = -a;
+ a = ((a >> 4) & 15) | ((a & 15) << 4);
+ }
+ SetConsoleTextAttribute( hConOut, a );
+ SetEnvironmentVariable( L"ANSICON_DEF", NULL );
+ break;
+ }
+
+ case 'e':
+ case 'E':
+ case 't':
+ case 'T':
+ run = FALSE;
+ ++arg;
+ goto arg_out;
+ }
+ get_arg( arg, &argv, &cmd );
+ }
+arg_out:
+ if (run && *cmd == '\0')
+ {
+ if (!shell)
+ run = FALSE;
+ else if (!_isatty( 0 ))
+ {
+ *arg = 't';
+ run = FALSE;
+ }
+ }
+
+ if (run)
+ {
+ if (*cmd == '\0')
+ {
+ cmd = _wgetenv( L"ComSpec" );
+ if (cmd == NULL)
+ {
+ // CreateProcessW writes to the string, so can't simply point to "cmd".
+ static TCHAR cmdstr[] = L"cmd";
+ cmd = cmdstr;
+ }
+ arg = cmd;
+ }
+
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ if (CreateProcess( NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi ))
+ {
+ Inject( &pi, &gui, arg );
+ ResumeThread( pi.hThread );
+ if (!gui)
+ {
+ SetConsoleCtrlHandler( (PHANDLER_ROUTINE)CtrlHandler, TRUE );
+ WaitForSingleObject( pi.hProcess, INFINITE );
+ GetExitCodeProcess( pi.hProcess, (LPDWORD)(LPVOID)&rc );
+ }
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+ }
+ else
+ {
+ print_error( arg, arg );
+ rc = 1;
+ }
+ }
+ else if (*arg)
+ {
+ ansi = LoadLibrary( L"ANSI" BITS L".dll" );
+ if (ansi == NULL)
+ {
+ print_error( L"ANSI" BITS L".dll" );
+ rc = 1;
+ }
+
+ if (*arg == 'e' || *arg == 'E')
+ {
+ cmd += 2;
+ if (*cmd == ' ' || *cmd == '\t')
+ ++cmd;
+ fputws( cmd, stdout );
+ if (*arg == 'e')
+ putwchar( '\n' );
+ }
+ else // (*arg == 't' || *arg == 'T')
+ {
+ BOOL title = (*arg == 'T');
+ get_arg( arg, &argv, &cmd );
+ if (*arg == '\0')
+ wcscpy( arg, L"-" );
+ do
+ {
+ if (title)
+ {
+ wprintf( L"==> %s <==\n", arg );
+ display( arg, title );
+ putwchar( '\n' );
+ }
+ else
+ display( arg, title );
+ get_arg( arg, &argv, &cmd );
+ } while (*arg);
+ }
+
+ FreeLibrary( ansi );
+ }
+
+ if (run || *arg)
+ set_original_attr();
+ else
+ CloseHandle( hConOut );
+
+ return rc;
+}
+
+
+// Display a file.
+void display( LPCTSTR name, BOOL title )
+{
+ HANDLE in, out;
+ BOOL pipe;
+ char buf[8192];
+ DWORD len;
+
+ if (*name == '-' && name[1] == '\0')
+ {
+ pipe = TRUE;
+ in = GetStdHandle( STD_INPUT_HANDLE );
+ }
+ else
+ {
+ pipe = FALSE;
+ in = CreateFile( name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, NULL );
+ if (in == INVALID_HANDLE_VALUE)
+ {
+ print_error( name );
+ return;
+ }
+ }
+ if (title)
+ {
+ putwchar( '\n' );
+ // Need to flush, otherwise it's written *after* STD_OUTPUT_HANDLE should
+ // it be redirected.
+ fflush( stdout );
+ }
+ out = GetStdHandle( STD_OUTPUT_HANDLE );
+ for (;;)
+ {
+ if (!ReadFile( in, buf, sizeof(buf), &len, NULL ))
+ {
+ if (GetLastError() != ERROR_BROKEN_PIPE)
+ print_error( name );
+ break;
+ }
+ if (len == 0)
+ break;
+ WriteFile( out, buf, len, &len, NULL );
+ }
+ if (!pipe)
+ CloseHandle( in );
+}
+
+
+void print_error( LPCTSTR name, ... )
+{
+ LPTSTR errmsg = NULL;
+ DWORD err = GetLastError();
+ va_list arg;
+
+ if (err == ERROR_BAD_EXE_FORMAT)
+ {
+ // This error requires an argument, which is a duplicate of name.
+ va_start( arg, name );
+ FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, &arg );
+ va_end( arg );
+ fwprintf( stderr, L"ANSICON: %s", errmsg );
+ }
+ else
+ {
+ FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, err, 0, (LPTSTR)(LPVOID)&errmsg, 0, NULL );
+ // Just in case there are other messages requiring args...
+ if (errmsg == NULL)
+ fwprintf( stderr, L"ANSICON: %s: Error %lu.\n", name, err );
+ else
+ fwprintf( stderr, L"ANSICON: %s: %s", name, errmsg );
+ }
+ LocalFree( errmsg );
+}
+
+
+// Add or remove ANSICON to AutoRun.
+void process_autorun( TCHAR cmd )
+{
+ HKEY cmdkey;
+ TCHAR ansicon[MAX_PATH+80];
+ TCHAR logstr[80];
+ LPTSTR autorun, ansirun;
+ DWORD len, type, exist;
+ BOOL inst;
+
+ if (log_level)
+ _snwprintf( logstr, lenof(logstr), L"set ANSICON_LOG=%d&", log_level );
+ else
+ *logstr = '\0';
+ len = TSIZE(_snwprintf( ansicon, lenof(ansicon),
+ L"(if %%ANSICON_VER%%==^%%ANSICON_VER^%% %s\"%s\" -p)",
+ logstr, prog_path ) + 1);
+
+ inst = (towlower( cmd ) == 'i');
+ RegCreateKeyEx( (iswlower( cmd )) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE,
+ CMDKEY, 0, NULL,
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
+ &cmdkey, &exist );
+ exist = 0;
+ RegQueryValueEx( cmdkey, AUTORUN, NULL, NULL, NULL, &exist );
+ if (exist == 0)
+ {
+ if (inst)
+ RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len );
+ }
+ else
+ {
+ // Let's assume there's sufficient memory.
+ autorun = malloc( exist + len );
+ RegQueryValueEx( cmdkey, AUTORUN, NULL, &type, (PBYTE)autorun, &exist );
+ // Remove the existing command, if present.
+ ansirun = wcsstr( autorun, L"(if %ANSICON_VER%" );
+ if (ansirun != NULL)
+ {
+ LPTSTR tmp = wcschr( ansirun, '"' ); // opening quote
+ tmp = wcschr( tmp + 1, '"' ); // closing quote
+ tmp = wcschr( tmp + 1, ')' ); // closing bracket
+ if (*++tmp == '&')
+ ++tmp;
+ if (*tmp == '&')
+ ++tmp;
+ if (*tmp == '\0')
+ {
+ if (ansirun > autorun && ansirun[-1] == '&')
+ --ansirun;
+ if (ansirun > autorun && ansirun[-1] == '&')
+ --ansirun;
+ }
+ wcscpy( ansirun, tmp );
+ exist = TSIZE((DWORD)wcslen( autorun ) + 1);
+ }
+ if (inst)
+ {
+ if (exist == sizeof(TCHAR))
+ RegSetValueEx( cmdkey, AUTORUN, 0, REG_SZ, (PBYTE)ansicon, len );
+ else
+ {
+ memmove( (PBYTE)autorun + len, autorun, exist );
+ memcpy( autorun, ansicon, len );
+ ((PBYTE)autorun)[len-sizeof(TCHAR)] = '&';
+ RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist+len );
+ }
+ }
+ else
+ {
+ if (exist == sizeof(TCHAR))
+ RegDeleteValue( cmdkey, AUTORUN );
+ else
+ RegSetValueEx( cmdkey, AUTORUN, 0, type, (PBYTE)autorun, exist );
+ }
+ free( autorun );
+ }
+ RegCloseKey( cmdkey );
+}
+
+
+// Search each process in the snapshot for id.
+BOOL find_proc_id( HANDLE snap, DWORD id, LPPROCESSENTRY32 ppe )
+{
+ BOOL fOk;
+
+ ppe->dwSize = sizeof(PROCESSENTRY32);
+ for (fOk = Process32First( snap, ppe ); fOk; fOk = Process32Next( snap, ppe ))
+ if (ppe->th32ProcessID == id)
+ break;
+
+ return fOk;
+}
+
+
+// Obtain the process and thread identifiers of the parent process.
+BOOL GetParentProcessInfo( LPPROCESS_INFORMATION ppi, LPTSTR name )
+{
+ HANDLE hSnap;
+ PROCESSENTRY32 pe;
+ THREADENTRY32 te;
+ BOOL fOk;
+
+ hSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD, 0 );
+
+ if (hSnap == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ find_proc_id( hSnap, GetCurrentProcessId(), &pe );
+ if (!find_proc_id( hSnap, pe.th32ParentProcessID, &pe ))
+ {
+ CloseHandle( hSnap );
+ return FALSE;
+ }
+
+ te.dwSize = sizeof(te);
+ for (fOk = Thread32First( hSnap, &te ); fOk; fOk = Thread32Next( hSnap, &te ))
+ if (te.th32OwnerProcessID == pe.th32ProcessID)
+ break;
+
+ CloseHandle( hSnap );
+
+ ppi->dwProcessId = pe.th32ProcessID;
+ ppi->dwThreadId = te.th32ThreadID;
+ wcscpy( name, pe.szExeFile );
+
+ return fOk;
+}
+
+
+// Return the first non-space character from arg.
+LPTSTR skip_spaces( LPTSTR arg )
+{
+ while (*arg == ' ' || *arg == '\t')
+ ++arg;
+
+ return arg;
+}
+
+
+// Retrieve an argument from the command line. cmd gets the existing argv; argv
+// is ready for the next argument.
+void get_arg( LPTSTR arg, LPTSTR* argv, LPTSTR* cmd )
+{
+ LPTSTR line;
+
+ line = *cmd = skip_spaces( *argv );
+ while (*line != '\0')
+ {
+ if (*line == ' ' || *line == '\t')
+ {
+ ++line;
+ break;
+ }
+ if (*line == '"')
+ {
+ while (*++line != '\0')
+ {
+ if (*line == '"')
+ {
+ ++line;
+ break;
+ }
+ *arg++ = *line;
+ }
+ }
+ else
+ *arg++ = *line++;
+ }
+ *arg = '\0';
+ *argv = line;
+}
+
+
+void help( void )
+{
+ _putws(
+L"ANSICON by Jason Hood <jadoxa@yahoo.com.au>.\n"
+L"Version " PVERS L" (" PDATE L"). Freeware.\n"
+L"http://ansicon.adoxa.cjb.net/\n"
+L"\n"
+#ifdef _WIN64
+L"Process ANSI escape sequences in Windows console programs.\n"
+#else
+L"Process ANSI escape sequences in Win32 console programs.\n"
+#endif
+L"\n"
+L"ansicon [-l<level>] [-i] [-I] [-u] [-U] [-m[<attr>]] [-p]\n"
+L" [-e|E string | -t|T [file(s)] | program [args]]\n"
+L"\n"
+L" -l\t\tset the logging level (1=process, 2=module, 3=function,\n"
+L" \t\t +4=output, +8=append) for program (-p is unaffected)\n"
+L" -i\t\tinstall - add ANSICON to the AutoRun entry (also implies -p)\n"
+L" -u\t\tuninstall - remove ANSICON from the AutoRun entry\n"
+L" -I -U\t\tuse local machine instead of current user\n"
+L" -m\t\tuse grey on black (\"monochrome\") or <attr> as default color\n"
+L" -p\t\thook into the parent process\n"
+L" -e\t\techo string\n"
+L" -E\t\techo string, don't append newline\n"
+L" -t\t\tdisplay files (\"-\" for stdin), combined as a single stream\n"
+L" -T\t\tdisplay files, name first, blank line before and after\n"
+L" program\trun the specified program\n"
+L" nothing\trun a new command processor, or display stdin if redirected\n"
+L"\n"
+L"<attr> is one or two hexadecimal digits; please use \"COLOR /?\" for details.\n"
+L"It may start with '-' to reverse foreground and background (but not for -p)."
+ );
+}
diff --git a/ansicon/ansicon.h b/ansicon/ansicon.h
new file mode 100755
index 0000000..8819186
--- /dev/null
+++ b/ansicon/ansicon.h
@@ -0,0 +1,47 @@
+/*
+ ansicon.h - Header file for common definitions.
+
+ Jason Hood, 12 December, 2010 (originally injdll.h, 20 June, 2009).
+*/
+
+#ifndef ANSICON_H
+#define ANSICON_H
+
+#ifndef UNICODE
+# define UNICODE
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#define _WIN32_WINNT 0x0500 // MinGW wants this defined for OpenThread
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define lenof(array) (sizeof(array)/sizeof(*(array)))
+#define TSIZE(size) ((size) * sizeof(TCHAR))
+
+
+typedef struct
+{
+ BYTE foreground; // ANSI base color (0 to 7; add 30)
+ BYTE background; // ANSI base color (0 to 7; add 40)
+ BYTE bold; // console FOREGROUND_INTENSITY bit
+ BYTE underline; // console BACKGROUND_INTENSITY bit
+ BYTE rvideo; // swap foreground/bold & background/underline
+ BYTE concealed; // set foreground/bold to background/underline
+ BYTE reverse; // swap console foreground & background attributes
+} GRM, *PGRM; // Graphic Rendition Mode
+
+
+int ProcessType( LPPROCESS_INFORMATION, BOOL* );
+void InjectDLL32( LPPROCESS_INFORMATION, LPCTSTR );
+void InjectDLL64( LPPROCESS_INFORMATION, LPCTSTR );
+
+extern TCHAR prog_path[MAX_PATH];
+extern LPTSTR prog;
+LPTSTR get_program_name( LPTSTR );
+
+extern int log_level;
+void DEBUGSTR( int level, LPTSTR szFormat, ... );
+
+#endif
diff --git a/ansicon/ansicon.rc b/ansicon/ansicon.rc
new file mode 100755
index 0000000..09b03c1
--- /dev/null
+++ b/ansicon/ansicon.rc
@@ -0,0 +1,36 @@
+/*
+ ansicon.rc - Version resource for ansicon.exe.
+
+ Jason Hood, 11 November, 2009.
+*/
+
+#include <winver.h>
+#include "version.h"
+
+1 VERSIONINFO
+FILEVERSION PVERB
+PRODUCTVERSION PVERB
+FILEOS VOS_NT
+FILETYPE VFT_APP
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904B0"
+ {
+ VALUE "Comments", "http://ansicon.adoxa.cjb.net/"
+ VALUE "CompanyName", "Jason Hood"
+ VALUE "FileDescription", "ANSI Console"
+ VALUE "FileVersion", PVERSA
+ VALUE "InternalName", "ansicon"
+ VALUE "LegalCopyright", "Freeware"
+ VALUE "OriginalFilename", "ansicon.exe"
+ VALUE "ProductName", "ANSICON"
+ VALUE "ProductVersion", PVERSA
+ }
+ }
+
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04B0
+ }
+}
diff --git a/ansicon/injdll32.c b/ansicon/injdll32.c
new file mode 100755
index 0000000..25ed7ae
--- /dev/null
+++ b/ansicon/injdll32.c
@@ -0,0 +1,123 @@
+/*
+ Inject code into the target process to load our DLL. The target thread
+ should be suspended on entry; it remains suspended on exit.
+
+ Initially I used the "stack" method of injection. However, this fails
+ when DEP is active, since that doesn't allow code to execute in the stack.
+ To overcome this I used the "CreateRemoteThread" method. However, this
+ would fail with Wselect, a program to assist batch files. Wselect runs,
+ but it has no output. As it turns out, removing the suspended flag would
+ make Wselect work, but it caused problems with everything else. So now I
+ allocate a section of memory and change the context to run from there. At
+ first I had an event to signal when the library was loaded, then the memory
+ was released. However, that wouldn't work with -p and CMD.EXE (4NT v8
+ worked fine). Since it's possible the DLL might start a process suspended,
+ I've decided to simply keep the memory.
+*/
+
+#include "ansicon.h"
+
+#ifdef _WIN64
+#if defined(__MINGW64__) || (defined(_MSC_VER) && _MSC_VER <= 1400)
+#include "wow64.h"
+
+TWow64GetThreadContext Wow64GetThreadContext;
+TWow64SetThreadContext Wow64SetThreadContext;
+#endif
+
+#define CONTEXT WOW64_CONTEXT
+#undef CONTEXT_CONTROL
+#define CONTEXT_CONTROL WOW64_CONTEXT_CONTROL
+#define GetThreadContext Wow64GetThreadContext
+#define SetThreadContext Wow64SetThreadContext
+#endif
+
+
+DWORD LLW;
+
+
+void InjectDLL32( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
+{
+ CONTEXT context;
+ DWORD len;
+ LPVOID mem;
+ DWORD mem32;
+ #define CODESIZE 20
+ BYTE code[CODESIZE+TSIZE(MAX_PATH)];
+ union
+ {
+ PBYTE pB;
+ PDWORD pL;
+ } ip;
+
+ len = TSIZE(lstrlen( dll ) + 1);
+ if (len > TSIZE(MAX_PATH))
+ return;
+
+ if (LLW == 0)
+ {
+ HMODULE hKernel = GetModuleHandleA( "kernel32.dll" );
+#ifdef _WIN64
+#ifdef __MINGW64__
+ #define GETPROC( proc ) proc = (T##proc)GetProcAddress( hKernel, #proc )
+ GETPROC( Wow64GetThreadContext );
+ GETPROC( Wow64SetThreadContext );
+ // Assume if one is defined, so is the other.
+ if (Wow64GetThreadContext == 0)
+ {
+ DEBUGSTR( 1, L"Failed to get pointer to Wow64GetThreadContext.\n" );
+ return;
+ }
+#endif
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ ZeroMemory( &si, sizeof(si) );
+ si.cb = sizeof(si);
+ // ...ANSI32.dll\0
+ CopyMemory( code, dll, len - TSIZE(7) );
+ // ...ANSI-LLW.exe\0
+ CopyMemory( code + len - TSIZE(7), L"-LLW.exe", TSIZE(9) );
+ if (!CreateProcess( (LPCTSTR)code, NULL, NULL, NULL, FALSE, 0, NULL, NULL,
+ &si, &pi ))
+ {
+ DEBUGSTR( 1, L"Failed to execute \"%s\".\n", (LPCTSTR)code );
+ return;
+ }
+ WaitForSingleObject( pi.hProcess, INFINITE );
+ GetExitCodeProcess( pi.hProcess, &LLW );
+ CloseHandle( pi.hProcess );
+ CloseHandle( pi.hThread );
+#else
+ LLW = (DWORD)GetProcAddress( hKernel, "LoadLibraryW" );
+#endif
+ }
+
+ CopyMemory( code + CODESIZE, dll, len );
+ len += CODESIZE;
+
+ context.ContextFlags = CONTEXT_CONTROL;
+ GetThreadContext( ppi->hThread, &context );
+ mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE );
+ mem32 = (DWORD)(DWORD_PTR)mem;
+
+ ip.pB = code;
+
+ *ip.pB++ = 0x68; // push eip
+ *ip.pL++ = context.Eip;
+ *ip.pB++ = 0x9c; // pushf
+ *ip.pB++ = 0x60; // pusha
+ *ip.pB++ = 0x68; // push L"path\to\ANSI32.dll"
+ *ip.pL++ = mem32 + CODESIZE;
+ *ip.pB++ = 0xe8; // call LoadLibraryW
+ *ip.pL++ = LLW - (mem32 + (DWORD)(ip.pB+4 - code));
+ *ip.pB++ = 0x61; // popa
+ *ip.pB++ = 0x9d; // popf
+ *ip.pB++ = 0xc3; // ret
+
+ WriteProcessMemory( ppi->hProcess, mem, code, len, NULL );
+ FlushInstructionCache( ppi->hProcess, mem, len );
+ context.Eip = mem32;
+ SetThreadContext( ppi->hThread, &context );
+}
diff --git a/ansicon/injdll64.c b/ansicon/injdll64.c
new file mode 100755
index 0000000..c46bd46
--- /dev/null
+++ b/ansicon/injdll64.c
@@ -0,0 +1,96 @@
+/*
+ Inject code into the target process to load our DLL. The target thread
+ should be suspended on entry; it remains suspended on exit.
+
+ Initially I used the "stack" method of injection. However, this fails
+ when DEP is active, since that doesn't allow code to execute in the stack.
+ To overcome this I used the "CreateRemoteThread" method. However, this
+ would fail with Wselect, a program to assist batch files. Wselect runs,
+ but it has no output. As it turns out, removing the suspended flag would
+ make Wselect work, but it caused problems with everything else. So now I
+ allocate a section of memory and change the context to run from there. At
+ first I had an event to signal when the library was loaded, then the memory
+ was released. However, that wouldn't work with -p and CMD.EXE (4NT v8
+ worked fine). Since it's possible the DLL might start a process suspended,
+ I've decided to simply keep the memory.
+*/
+
+#include "ansicon.h"
+
+void InjectDLL64( LPPROCESS_INFORMATION ppi, LPCTSTR dll )
+{
+ CONTEXT context;
+ DWORD len;
+ LPVOID mem;
+ DWORD64 LLW;
+ union
+ {
+ PBYTE pB;
+ PDWORD64 pL;
+ } ip;
+ #define CODESIZE 92
+ static BYTE code[CODESIZE+TSIZE(MAX_PATH)] = {
+ 0,0,0,0,0,0,0,0, // original rip
+ 0,0,0,0,0,0,0,0, // LoadLibraryW
+ 0x9C, // pushfq
+ 0x50, // push rax
+ 0x51, // push rcx
+ 0x52, // push rdx
+ 0x53, // push rbx
+ 0x55, // push rbp
+ 0x56, // push rsi
+ 0x57, // push rdi
+ 0x41,0x50, // push r8
+ 0x41,0x51, // push r9
+ 0x41,0x52, // push r10
+ 0x41,0x53, // push r11
+ 0x41,0x54, // push r12
+ 0x41,0x55, // push r13
+ 0x41,0x56, // push r14
+ 0x41,0x57, // push r15
+ 0x48,0x83,0xEC,0x28, // sub rsp, 40
+ 0x48,0x8D,0x0D,41,0,0,0, // lea ecx, L"path\to\ANSI64.dll"
+ 0xFF,0x15,-49,-1,-1,-1, // call LoadLibraryW
+ 0x48,0x83,0xC4,0x28, // add rsp, 40
+ 0x41,0x5F, // pop r15
+ 0x41,0x5E, // pop r14
+ 0x41,0x5D, // pop r13
+ 0x41,0x5C, // pop r12
+ 0x41,0x5B, // pop r11
+ 0x41,0x5A, // pop r10
+ 0x41,0x59, // pop r9
+ 0x41,0x58, // pop r8
+ 0x5F, // pop rdi
+ 0x5E, // pop rsi
+ 0x5D, // pop rbp
+ 0x5B, // pop rbx
+ 0x5A, // pop rdx
+ 0x59, // pop rcx
+ 0x58, // pop rax
+ 0x9D, // popfq
+ 0xFF,0x25,-91,-1,-1,-1, // jmp original Rip
+ 0, // dword alignment for LLW, fwiw
+ };
+
+ len = TSIZE(lstrlen( dll ) + 1);
+ if (len > TSIZE(MAX_PATH))
+ return;
+ CopyMemory( code + CODESIZE, dll, len );
+ len += CODESIZE;
+
+ context.ContextFlags = CONTEXT_CONTROL;
+ GetThreadContext( ppi->hThread, &context );
+ mem = VirtualAllocEx( ppi->hProcess, NULL, len, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE );
+ LLW = (DWORD64)LoadLibraryW;
+
+ ip.pB = code;
+
+ *ip.pL++ = context.Rip;
+ *ip.pL++ = LLW;
+
+ WriteProcessMemory( ppi->hProcess, mem, code, len, NULL );
+ FlushInstructionCache( ppi->hProcess, mem, len );
+ context.Rip = (DWORD64)mem + 16;
+ SetThreadContext( ppi->hThread, &context );
+}
diff --git a/ansicon/makefile b/ansicon/makefile
new file mode 100755
index 0000000..0007bee
--- /dev/null
+++ b/ansicon/makefile
@@ -0,0 +1,82 @@
+# Makefile for ANSICON.
+# Jason Hood, 11 March, 2006. Updated 20 June, 2009.
+
+# I've used TDM64 (gcc 4.6.1), building the 32-bit version in the x86 directory
+# and the 64-bit version in the x64 directory. MinGW32 (gcc 3.4.5) will also
+# build the 32-bit version.
+
+# 19 November, 2010:
+# explicitly use 64-bit flags, in case the compiler isn't.
+#
+# 13 December, 2011:
+# use CMD for file operations, not programs from fileutils.
+
+CC = gcc
+CFLAGS = -O2 -Wall
+
+X86OBJS = x86/proctype.o x86/injdll32.o x86/util.o
+X64OBJS = x64/proctype.o x64/injdll64.o x64/injdll32.o x64/util.o
+
+x86/%.o: %.c ansicon.h
+ $(CC) -m32 -c $(CFLAGS) $< -o $@
+
+x86/%v.o: %.rc version.h
+ windres -U _WIN64 -F pe-i386 $< $@
+
+x64/%.o: %.c ansicon.h
+ $(CC) -m64 -c $(CFLAGS) $< -o $@
+
+x64/%v.o: %.rc version.h
+ windres -F pe-x86-64 $< $@
+
+all: ansicon32 ansicon64
+
+ansicon32: x86 x86/ansicon.exe x86/ANSI32.dll
+
+ansicon64: x64 x64/ansicon.exe x64/ANSI64.dll x64/ANSI32.dll x64/ANSI-LLW.exe
+
+x86:
+ cmd /c "mkdir x86"
+
+x86/ansicon.exe: x86/ansicon.o $(X86OBJS) x86/ansiconv.o
+ $(CC) -m32 $+ -s -o $@
+
+x86/ANSI32.dll: x86/ANSI.o $(X86OBJS) x86/ansiv.o
+ $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared
+
+x64:
+ cmd /c "mkdir x64"
+
+x64/ansicon.exe: x64/ansicon.o $(X64OBJS) x64/ansiconv.o
+ $(CC) -m64 $+ -s -o $@
+
+x64/ANSI64.dll: x64/ANSI.o $(X64OBJS) x64/ansiv.o
+ $(CC) -m64 $+ -s -o $@ -mdll -Wl,-shared
+
+x64/ANSI32.dll: x64/ANSI32.o x64/proctype32.o x86/injdll32.o x86/util.o x86/ansiv.o
+ $(CC) -m32 $+ -s -o $@ -mdll -Wl,-shared
+
+x64/ANSI-LLW.exe: ANSI-LLW.c
+ $(CC) -m32 $(CFLAGS) $< -s -o $@
+
+x86/ansicon.o: version.h
+x86/ANSI.o: version.h
+x64/ansicon.o: version.h
+x64/ANSI.o: version.h
+x86/util.o: version.h
+x64/util.o: version.h
+x86/ansiconv.o: ansicon.rc
+x86/ansiv.o: ansi.rc
+x64/ansiconv.o: ansicon.rc
+x64/ansiv.o: ansi.rc
+
+x64/ANSI32.o: ANSI.c
+ $(CC) -m32 -DW32ON64 $(CFLAGS) $< -c -o $@
+x64/proctype32.o: proctype.c
+ $(CC) -m32 -DW32ON64 $(CFLAGS) $< -c -o $@
+
+# Need two commands, because if the directory doesn't exist, it won't delete
+# anything at all.
+clean:
+ -cmd /c "del x86\*.o 2>nul"
+ -cmd /c "del x64\*.o 2>nul"
diff --git a/ansicon/makefile.vc b/ansicon/makefile.vc
new file mode 100755
index 0000000..da71872
--- /dev/null
+++ b/ansicon/makefile.vc
@@ -0,0 +1,88 @@
+# VC makefile for ANSICON.
+# Jason Hood, 15 November, 2010.
+
+# I've used Visual C++ 2008 Express for the 32-bit version and the 2003 R2
+# Platform SDK for the 64-bit version. Since these are entirely separate
+# environments, define BITS to decide which should be built. Note that the
+# 64-bit version still requires the 32-bit ANSI32.dll, so both environments
+# are required and you should build the 32-bit version before the 64-bit.
+
+#BITS = 32
+#BITS = 64
+
+!IFNDEF BITS
+BITS = 32
+!ENDIF
+
+!IF $(BITS) == 32
+DIR = x86
+!ELSE
+!IF $(BITS) == 64
+DIR = x64
+!ELSE
+!ERROR BITS should be defined to 32 or 64.
+!ENDIF
+!ENDIF
+
+CC = cl
+CFLAGS = /nologo /W3 /Ox /GF /D_CRT_SECURE_NO_WARNINGS
+LIBS = advapi32.lib user32.lib
+
+# This is required for the 2003 Platform SDK, but not for Visual Studio 2010.
+#LIBS64 = bufferoverflowu.lib
+
+X86OBJS = x86\proctype.obj x86\injdll32.obj x86\util.obj
+X64OBJS = x64\proctype.obj x64\injdll64.obj x64\injdll32.obj x64\util.obj
+
+{}.c{$(DIR)}.obj:
+ $(CC) /c $(CFLAGS) /Fo$@ $<
+
+{}.rc{$(DIR)}.res:
+ rc /fo$@ $<
+
+all: ansicon$(BITS)
+
+ansicon32: x86 x86\ansicon.exe x86\ANSI32.dll x64\ANSI32.dll
+
+ansicon64: x64 x64\ansicon.exe x64\ANSI64.dll x64\ANSI-LLW.exe
+
+x86:
+ mkdir x86
+
+x86\ansicon.exe: x86\ansicon.obj $(X86OBJS) x86\ansicon.res
+ $(CC) /nologo /Fe$@ $** $(LIBS)
+
+x86\ANSI32.dll: x86\ANSI.obj $(X86OBJS) x86\ansi.res
+ $(CC) /nologo /LD /Fe$@ $** $(LIBS)
+
+x64:
+ mkdir x64
+
+x64\ansicon.exe: x64\ansicon.obj $(X64OBJS) x64\ansicon.res
+ $(CC) /nologo /Fe$@ $** $(LIBS) $(LIBS64)
+
+x64\ANSI64.dll: x64\ANSI.obj $(X64OBJS) x64\ansi.res
+ $(CC) /nologo /LD /Fe$@ $** $(LIBS) $(LIBS64)
+
+x64\ANSI32.dll: x64\ANSI32.obj x64\proctype32.obj x86\injdll32.obj x86\util.obj x86\ansi.res
+ $(CC) /nologo /LD /Fe$@ $** $(LIBS)
+
+x64\ANSI-LLW.exe: ANSI-LLW.c
+ $(CC) $(CFLAGS) /Fe$@ /Fo$*.obj $? $(LIBS64)
+
+ansicon.c: ansicon.h version.h
+ansicon.rc: version.h
+ANSI.c: ansicon.h version.h
+ANSI.rc: version.h
+util.c: ansicon.h version.h
+injdll32.c: ansicon.h
+injdll64.c: ansicon.h
+proctype.c: ansicon.h
+
+x64\ANSI32.obj: ANSI.c
+ $(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?
+x64\proctype32.obj: proctype.c
+ $(CC) /DW32ON64 /c $(CFLAGS) /Fo$@ $?
+
+clean:
+ -del $(DIR)\*.obj $(DIR)\*.res $(DIR)\*.lib $(DIR)\*.exp
diff --git a/ansicon/proctype.c b/ansicon/proctype.c
new file mode 100755
index 0000000..27399cd
--- /dev/null
+++ b/ansicon/proctype.c
@@ -0,0 +1,105 @@
+/*
+ Test for a valid process. This may sometimes detect GUI, even for a console
+ process. I think this is due to a DLL being loaded in the address space
+ before the main image. Ideally I could just use the base address directly,
+ but that doesn't seem easy to do for another process - there doesn't seem to
+ be a GetModuleHandle for another process. The CreateRemoteThread trick won't
+ work with 64-bit (exit code is DWORD) and setting it up to make it work
+ hardly seems worth it. There's GetModuleInformation, but passing in NULL just
+ returns a base of NULL, so that's no help. Since 64/32 is sufficient, let
+ ansicon.exe handle the difference between console/GUI.
+
+ Update: ignore images characterised as DLL.
+*/
+
+#include "ansicon.h"
+
+
+int ProcessType( LPPROCESS_INFORMATION pinfo, BOOL* gui )
+{
+ char* ptr;
+ MEMORY_BASIC_INFORMATION minfo;
+ IMAGE_DOS_HEADER dos_header;
+ IMAGE_NT_HEADERS nt_header;
+ SIZE_T read;
+
+ *gui = FALSE;
+ for (ptr = NULL;
+ VirtualQueryEx( pinfo->hProcess, ptr, &minfo, sizeof(minfo) );
+ ptr += minfo.RegionSize)
+ {
+ if (minfo.BaseAddress == minfo.AllocationBase &&
+ ReadProcessMemory( pinfo->hProcess, minfo.AllocationBase,
+ &dos_header, sizeof(dos_header), &read ))
+ {
+ if (dos_header.e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ if (ReadProcessMemory( pinfo->hProcess, (char*)minfo.AllocationBase +
+ dos_header.e_lfanew, &nt_header,
+ sizeof(nt_header), &read ))
+ {
+ if (nt_header.Signature == IMAGE_NT_SIGNATURE &&
+ (nt_header.FileHeader.Characteristics &
+ (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
+ == IMAGE_FILE_EXECUTABLE_IMAGE)
+ {
+ *gui = (nt_header.OptionalHeader.Subsystem
+ == IMAGE_SUBSYSTEM_WINDOWS_GUI);
+ if (nt_header.OptionalHeader.Subsystem ==
+ IMAGE_SUBSYSTEM_WINDOWS_CUI || *gui)
+ {
+ if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
+ {
+ // Microsoft ignores precision on %p.
+ DEBUGSTR( 1, L" 32-bit %s (base = %.8X)",
+ (*gui) ? L"GUI" : L"console",
+ (DWORD)(DWORD_PTR)minfo.AllocationBase );
+ return 32;
+ }
+#ifdef _WIN64
+ if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
+ {
+ DEBUGSTR( 1, L" 64-bit %s (base = %p)",
+ (*gui) ? L"GUI" : L"console", minfo.AllocationBase );
+ return 64;
+ }
+#elif defined(W32ON64)
+ if (nt_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
+ {
+ DEBUGSTR( 1, L" 64-bit %s",
+ (*gui) ? L"GUI" : L"console" );
+ return 64;
+ }
+#endif
+ DEBUGSTR( 1, L" Ignoring unsupported machine (0x%X)",
+ nt_header.FileHeader.Machine );
+ }
+ else
+ {
+ DEBUGSTR( 1, L" Ignoring unsupported subsystem (%u)",
+ nt_header.OptionalHeader.Subsystem );
+ }
+ return 0;
+ }
+ }
+ }
+ }
+#ifdef _WIN32
+ // If a 32-bit process manages to load a 64-bit one, we may miss the base
+ // address. If the pointer overflows, assume 64-bit and abort.
+ if (ptr > ptr + minfo.RegionSize)
+ {
+#ifdef W32ON64
+ DEBUGSTR( 1, L" Pointer overflow: assuming 64-bit console" );
+ return 64;
+#else
+ DEBUGSTR( 1, L" Ignoring apparent 64-bit process" );
+ return 0;
+#endif
+ }
+#endif
+ }
+
+ DEBUGSTR( 1, L" Ignoring non-Windows process" );
+ return 0;
+}
diff --git a/ansicon/readme.txt b/ansicon/readme.txt
new file mode 100755
index 0000000..78f00eb
--- /dev/null
+++ b/ansicon/readme.txt
@@ -0,0 +1,439 @@
+
+ ANSICON
+
+ Copyright 2005-2012 Jason Hood
+
+ Version 1.53. Freeware
+
+
+ ===========
+ Description
+ ===========
+
+ ANSICON provides ANSI escape sequences for Windows console programs. It
+ provides much the same functionality as `ANSI.SYS' does for MS-DOS.
+
+
+ ============
+ Requirements
+ ============
+
+ Windows 2000 Professional and later (it won't work with NT or 9X).
+
+
+ ============
+ Installation
+ ============
+
+ Add x86 (if your OS is 32-bit) or x64 (if 64-bit) to your PATH, or copy
+ the relevant files to a directory already on the PATH. Alternatively,
+ use option `-i' (or `-I') to install it permanently, by adding an entry
+ to CMD.EXE's AutoRun registry value (current user or local machine,
+ respectively). Uninstall simply involves closing any programs that are
+ currently using it, running with `-u' (and/or `-U') to remove the Auto-
+ Run entry/ies, then removing the directory from PATH or deleting the
+ files. No other changes are made.
+
+ ---------
+ Upgrading
+ ---------
+
+ Delete ANSI.dll, it has been replaced with ANSI32.dll.
+ Delete ANSI-LLA.dll, it has been replaced with ANSI-LLW.dll.
+ Uninstall a pre-1.50 version and reinstall with this version.
+
+
+ =====
+ Usage
+ =====
+
+ Options (case sensitive):
+
+ -l Log to %temp%\ansicon.log.
+
+ -p Enable the parent process (i.e. the command shell used to
+ run ANSICON) to recognise escapes.
+
+ -m Set the current (and default) attribute to grey on black
+ ("monochrome"), or the attribute following the `m' (please
+ use `COLOR /?' for attribute values).
+
+ -e Echo the command line - a space or tab after the `e' is
+ ignored, the remainder is displayed verbatim.
+
+ -E As above, but no newline is added.
+
+ -t Display ("type") each file (or standard input if none or the
+ name is "-") as though they are a single file.
+
+ -T Display "==> FILE NAME <==", a blank line (or an error
+ message), the file and another blank line.
+
+ Running ANSICON with no arguments will start a new instance of the com-
+ mand processor (the program defined by the `ComSpec' environment var-
+ iable, typically `CMD.EXE'), or display standard input if it is redir-
+ ected. Any argument will be treated as a program and its arguments.
+
+ Eg: `ansicon -m30 -t file.ans' will display `file.ans' using black on
+ cyan as the default color.
+
+ The attribute may start with "-" to permanently reverse the foreground
+ and background colors (but not when using `-p'). Eg: `ansicon -m-f0 -t
+ file.log' will use reversed black on white as the default (i.e. white on
+ black, with foreground sequences changing the background).
+
+ If you experience trouble with certain programs, the log may help in
+ finding the cause; it can be found at "%TEMP%\ansicon.log". A number
+ should follow the `l':
+
+ 0 No logging
+ 1 Log process start and end
+ 2 Above, plus log modules used by the process
+ 3 Above, plus log functions that are hooked
+ 4 Log console output (add to any of the above)
+ 8 Append to the existing file (add to any of the above)
+ 16 Log all imported modules (add to any of the above)
+
+ The log option will not work with `-p'; set the environment variable
+ ANSICON_LOG instead. The variable is only read once when a new process
+ is started; changing it won't affect running processes. If you identify
+ a module that causes problems (one known is "nvd3d9wrap.dll") add it to
+ the ANSICON_EXC environment variable (see ANSICON_API below, but the
+ extension is required).
+
+ Once installed, the ANSICON environment variable will be created. This
+ variable is of the form "WxH (wxh)", where W & H are the width and
+ height of the buffer and w & h are the width and height of the window.
+ The variable is updated whenever a program reads it directly (i.e. as
+ an individual request, not as part of the entire environment block).
+ For example, "set an" will not update it, but "echo %ansicon%" will.
+ Also created is ANSICON_VER, which contains the version without the
+ point (1.50 becomes "150"). This variable does not exist as part of the
+ environment block ("set an" will not show it).
+
+ If installed, GUI programs will not be hooked. Either start the program
+ directly with `ansicon', or add it to the ANSICON_GUI variable (see
+ ANSICON_API below).
+
+ Using `ansicon' after install will always start with the default attrib-
+ utes, restoring the originals on exit; all other programs will use the
+ current attributes. The shift state is always reset for a new process.
+
+ The Windows API WriteFile and WriteConsoleA functions will set the num-
+ ber of characters written, not the number of bytes. When using a multi-
+ byte character set, this results in a smaller number (since multiple
+ bytes are used to represent a single character). Some programs recog-
+ nise this as a reduced write and will inadvertently repeat previous
+ characters. If you discover such a program, use the ANSICON_API envir-
+ onment variable to record it and override the API, returning the origin-
+ al byte count. Ruby is an example of such a program (at least, up till
+ 1.9.2p0), so use "set ANSICON_API=ruby" to avoid the repitition. The
+ full syntax of the variable is:
+
+ ANSICON_API=[!]program;program;program...
+
+ PROGRAM is the name of the program, with no path and extension. The
+ leading exclamation inverts the usage, meaning the API will always be
+ overridden, unless the program is in the list. The variable can be made
+ permanent by going to System Properties, selecting the Advanced tab and
+ clicking Environment Variables (using XP; Vista/7 may be different).
+
+
+ ====================
+ Sequences Recognised
+ ====================
+
+ The following escape sequences are recognised.
+
+ \e]0;titleBEL Set (xterm) window's title (and icon)
+ \e[21t Report (xterm) window's title
+ \e[s Save Cursor
+ \e[u Restore Cursor
+ \e[#G CHA Cursor Character Absolute
+ \e[#E CNL Cursor Next Line
+ \e[#F CPL Cursor Preceding Line
+ \e[#D CUB Cursor Left
+ \e[#B CUD Cursor Down
+ \e[#C CUF Cursor Right
+ \e[#;#H CUP Cursor Position
+ \e[#A CUU Cursor Up
+ \e[#P DCH Delete Character
+ \e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor)
+ \e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor)
+ \e[#M DL Delete Line
+ \e[#n DSR Device Status Report
+ \e[#X ECH Erase Character
+ \e[#J ED Erase In Page
+ \e[#K EL Erase In Line
+ \e[#` HPA Character Position Absolute
+ \e[#j HPB Character Position Backward
+ \e[#a HPR Character Position Forward
+ \e[#;#f HVP Character And Line Position
+ \e[#@ ICH Insert Character
+ \e[#L IL Insert Line
+ SI LS0 Locking-shift Zero (see below)
+ SO LS1 Locking-shift One
+ \e[#;#;#m SGR Select Graphic Rendition
+ \e[#d VPA Line Position Absolute
+ \e[#k VPB Line Position Backward
+ \e[#e VPR Line Position Forward
+
+ `\e' represents the escape character (ASCII 27); `#' represents a
+ decimal number (optional, in most cases defaulting to 1); BEL, SO and
+ SI are ASCII 7, 14 and 15. Regarding SGR: bold will set the foreground
+ intensity; underline and blink will set the background intensity;
+ conceal uses background as foreground.
+
+ I make a distinction between "\e[m" and "\e[0;...m". Both will restore
+ the original foreground/background colors (and so "0" should be the
+ first parameter); the former will also restore the original bold and
+ underline attributes, whilst the latter will explicitly reset them. The
+ environment variable ANSICON_DEF can be used to change the default col-
+ ors (same value as `-m'; setting the variable does not change the cur-
+ rent colors).
+
+
+ =================
+ Sequences Ignored
+ =================
+
+ The following escape sequences are explicitly ignored.
+
+ \e(? Designate G0 character set (`?' is anything).
+ \e)? Designate G1 character set (`?' is anything).
+ \e[?... Private sequence
+ \e[>... Private sequence
+
+ The G0 character set is always ASCII; the G1 character set is always
+ the DEC Special Graphics Character Set.
+
+
+ ==================================
+ DEC Special Graphics Character Set
+ ==================================
+
+ This is my interpretation of the set, as shown by
+ http://vt100.net/docs/vt220-rm/table2-4.html.
+
+
+ Char Unicode Code Point & Name
+ ---- -------------------------
+ _ U+0020 Space (blank)
+ ` U+2666 Black Diamond Suit
+ a U+2592 Medium Shade
+ b U+2409 Symbol For Horizontal Tabulation
+ c U+240C Symbol For Form Feed
+ d U+240D Symbol For Carriage Return
+ e U+240A Symbol For Line Feed
+ f U+00B0 Degree Sign
+ g U+00B1 Plus-Minus Sign
+ h U+2424 Symbol For Newline
+ i U+240B Symbol For Vertical Tabulation
+ j U+2518 Box Drawings Light Up And Left
+ k U+2510 Box Drawings Light Down And Left
+ l U+250C Box Drawings Light Down And Right
+ m U+2514 Box Drawings Light Up And Right
+ n U+253C Box Drawings Light Vertical And Horizontal
+ o U+00AF Macron (SCAN 1)
+ p U+25AC Black Rectangle (SCAN 3)
+ q U+2500 Box Drawings Light Horizontal (SCAN 5)
+ r U+005F Low Line (SCAN 7)
+ s U+005F Low Line (SCAN 9)
+ t U+251C Box Drawings Light Vertical And Right
+ u U+2524 Box Drawings Light Vertical And Left
+ v U+2534 Box Drawings Light Up And Horizontal
+ w U+252C Box Drawings Light Down And Horizontal
+ x U+2502 Box Drawings Light Vertical
+ y U+2264 Less-Than Or Equal To
+ z U+2265 Greater-Than Or Equal To
+ { U+03C0 Greek Small Letter Pi
+ | U+2260 Not Equal To
+ } U+00A3 Pound Sign
+ ~ U+00B7 Middle Dot
+
+
+ G1.txt is a Unicode file to view the glyphs "externally". G1.bat is a
+ batch file (using `x86\ansicon') to show the glyphs in the console. The
+ characters will appear as they should using Lucida (other than the Sym-
+ bols), but code page will influence them when using a raster font (but
+ of particular interest, 437 and 850 both show the Box Drawings).
+
+
+ ===========
+ Limitations
+ ===========
+
+ The entire console buffer is used, not just the visible window.
+
+ Building rubyinstaller on Win7 crashes (XP is fine).
+
+
+ ===============
+ Version History
+ ===============
+
+ Legend: + added, - bug-fixed, * changed.
+
+ 1.53 - 12 June, 2012:
+ - fix for multiple simultaneous process creation (e.g. "cl /MP ...").
+
+ 1.52 - 2 June, 2012:
+ + 32-bit processes can inject into 64-bit processes;
+ + implemented \e[39m & \e[49m (set default foreground/background color);
+ + added \e[#X, \e[#`, \e[#a, \e[#d, \e[#e, \[e#j and \e[#k;
+ * changed sequence descriptions to those in ECMA-48, ordered by acronym.
+
+ 1.51 - 24 February, 2012:
+ - fixed installing into a piped/redirected CMD.EXE;
+ - fixed 32-bit process trying to identify a 64-bit process;
+ - ignore version within core API DLL names (now Win8 works);
+ + hook _lwrite & _hwrite (now Silverfrost FTN95 v6.20 works).
+
+ 1.50 - 14 December, 2011:
+ - -u does not imply -p;
+ - return the program's exit code;
+ - -p by itself will not restore original color;
+ - output error messages to stderr;
+ * logging is always available, with various levels; include the pid;
+ * don't automatically hook GUI programs, use `ansicon' or ANSICON_GUI;
+ * always place first in AutoRun; don't run if already installed;
+ + global reverse video capability;
+ + added ANSICON_VER to provide version/install test;
+ + added ANSICON_EXC to exclude selected modules;
+ + added ANSICON_DEF to explicitly set the default SGM.
+
+ 1.40 - 1 March, 2011:
+ - hook GetProcAddress (now PowerShell works);
+ + add SO/SI, using the DEC Special Graphics Character Set for G1;
+ + add DECTCEM to show/hide the cursor.
+
+ 1.32 - 22 December, 2010:
+ - fixed crash due to NULL lpNumberOfBytesWritten/lpNumberOfCharsWritten;
+ - -p will test the parent process for validity;
+ * hook into GUI processes;
+ + recognise DSR and xterm window title sequences;
+ - fixed MinGW32 binaries (LLW was wrong).
+
+ 1.31 - 19 November, 2010:
+ - fixed multibyte support (no extra junk with UTF-8 files);
+ * provide workaround for API byte/character differences;
+ * fixed potential problem if install path uses Unicode.
+
+ 1.30 - 7 September, 2010:
+ + x64 version.
+
+ 1.25 - 22 July, 2010:
+ - hook LoadLibraryEx (now CScript works);
+ - fixed -i when AutoRun existed, but was empty;
+ + support for Windows 7;
+ + -I (and -U) use HKEY_LOCAL_MACHINE.
+
+ 1.24 - 7 January, 2010:
+ - fix -t and -e when ANSICON was already running;
+ + read standard input if redirected with no arguments, if -t has no
+ files, or if the name is "-" (which also serves as a workaround for
+ programs that don't get hooked, such as CScript).
+
+ 1.23 - 11 November, 2009:
+ - restore hooked functions when unloading;
+ - reverse the "bold" and "underline" settings;
+ * conceal characters by making foreground color same as background.
+
+ 1.22 - 5 October, 2009:
+ - hook LoadLibrary to inject into applications started via association.
+
+ 1.21 - 23 September, 2009:
+ + -i (and -u) option to add (remove) entry to AutoRun value.
+
+ 1.20 - 21 June, 2009:
+ * use another injection method;
+ + create ANSICON environment variable;
+ + -e (and -E) option to echo the command line (without newline);
+ + -t (and -T) option to type (display) files (with file name).
+
+ 1.15 - 17 May, 2009:
+ - fix output corruption for long (over 8192 characters) ANSI strings.
+
+ 1.14 - 3 April, 2009:
+ - fix the test for an empty import section (eg. XCOPY now works).
+
+ 1.13 - 21 & 27 March, 2009:
+ * use a new injection method (to work with DEP);
+ * use Unicode.
+
+ 1.12 - 9 March, 2009:
+ - fix processing child programs (generate a relocatable DLL).
+
+ 1.11 - 28 February, 2009:
+ - fix processing child programs (only use for console executables).
+
+ 1.10 - 22 February, 2009:
+ - fix output corruption (buffer overflow in MyConsoleWriteW);
+ - recognise current screen attributes as current ANSI atrributes;
+ - ignore Ctrl+C and Ctrl+Break;
+ + process child programs.
+
+ 1.01 - 12 March, 2006:
+ * \e[m will restore original color, not set grey on black;
+ + -m option to set default (and initial) color;
+ - restore original color on exit;
+ - disable escape processing when console has disabled processed output;
+ + \e[5m (blink) is the same as \e[4m (underline);
+ - do not conceal control characters (0 to 31).
+
+ 1.00 - 23 October, 2005:
+ + initial release.
+
+
+ ===============
+ Acknowledgments
+ ===============
+
+ Jean-Louis Morel, for his Perl package Win32::Console::ANSI. It
+ provided the basis of `ANSI.dll'.
+
+ Sergey Oblomov (hoopoepg), for Console Manager. It provided the basis
+ of `ansicon.exe'.
+
+ Anton Bassov's article "Process-wide API spying - an ultimate hack" in
+ "The Code Project".
+
+ Richard Quadling - his persistence in finding bugs has made ANSICON
+ what it is today.
+
+ Dmitry Menshikov, Marko Bozikovic and Philippe Villiers, for their
+ assistance in making the 64-bit version a reality.
+
+ Luis Lavena and the Ruby people for additional improvements.
+
+ Leigh Hebblethwaite for documentation tweaks.
+
+
+ =======
+ Contact
+ =======
+
+ mailto:jadoxa@yahoo.com.au
+ http://ansicon.adoxa.cjb.net/
+ https://github.com/adoxa/ansicon
+
+ Jason Hood
+ 11 Buckle Street
+ North Rockhampton
+ Qld 4701
+ Australia
+
+
+ ============
+ Distribution
+ ============
+
+ The original zipfile can be freely distributed, by any means. However,
+ I would like to be informed if it is placed on a CD-ROM (other than an
+ archive compilation; permission is granted, I'd just like to know).
+ Modified versions may be distributed, provided it is indicated as such
+ in the version text and a source diff is included.
+
+
+ ==========================
+ Jason Hood, 12 June, 2012.
diff --git a/ansicon/util.c b/ansicon/util.c
new file mode 100755
index 0000000..461b7b6
--- /dev/null
+++ b/ansicon/util.c
@@ -0,0 +1,128 @@
+/*
+ util.c - Utility functions.
+*/
+
+#include "ansicon.h"
+#include "version.h"
+
+
+TCHAR prog_path[MAX_PATH];
+LPTSTR prog;
+int log_level;
+char tempfile[MAX_PATH];
+DWORD pid;
+
+
+// Get just the name of the program: "C:\path\program.exe" -> "program".
+// Returns a pointer within program; it is modified to remove the extension.
+LPTSTR get_program_name( LPTSTR program )
+{
+ LPTSTR name, ext;
+
+ if (program == NULL)
+ {
+ GetModuleFileName( NULL, prog_path, lenof(prog_path) );
+ program = prog_path;
+ }
+ name = wcsrchr( program, '\\' );
+ if (name != NULL)
+ ++name;
+ else
+ name = program;
+ ext = wcsrchr( name, '.' );
+ if (ext != NULL && ext != name)
+ *ext = '\0';
+
+ return name;
+}
+
+
+void DEBUGSTR( int level, LPTSTR szFormat, ... )
+{
+ TCHAR szBuffer[1024], szEscape[1024];
+ va_list pArgList;
+ HANDLE mutex;
+ DWORD wait;
+ FILE* file;
+
+ if ((log_level & 3) < level && !(level & 4 & log_level))
+ return;
+
+ if (*tempfile == '\0')
+ {
+ _snprintf( tempfile, MAX_PATH, "%s\\ansicon.log", getenv( "TEMP" ) );
+ pid = GetCurrentProcessId();
+ }
+ if (szFormat == NULL)
+ {
+ file = fopen( tempfile, "wt" );
+ if (file != NULL)
+ {
+ SYSTEMTIME now;
+ GetLocalTime( &now );
+ fprintf( file, "ANSICON v" PVERSA " log (%d) started "
+ "%d-%.2d-%.2d %d:%.2d:%.2d\n",
+ log_level,
+ now.wYear, now.wMonth, now.wDay,
+ now.wHour, now.wMinute, now.wSecond );
+ fclose( file );
+ }
+ return;
+ }
+
+ va_start( pArgList, szFormat );
+ _vsnwprintf( szBuffer, lenof(szBuffer), szFormat, pArgList );
+ va_end( pArgList );
+
+ szFormat = szBuffer;
+ if (*szFormat == '\33')
+ {
+ BOOL first = TRUE;
+ LPTSTR pos = szEscape;
+ while (*++szFormat != '\0' && pos < szEscape + lenof(szEscape) - 4)
+ {
+ if (*szFormat < 32)
+ {
+ *pos++ = '\\';
+ switch (*szFormat)
+ {
+ case '\a': *pos++ = 'a'; break;
+ case '\b': *pos++ = 'b'; break;
+ case '\t': *pos++ = 't'; break;
+ case '\r': *pos++ = 'r'; break;
+ case '\n': *pos++ = 'n'; break;
+ case 27 : *pos++ = 'e'; break;
+ default:
+ pos += _snwprintf( pos, 32, L"%.*o",
+ (szFormat[1] >= '0' && szFormat[1] <= '7') ? 3 : 1,
+ *szFormat );
+ }
+ }
+ else
+ {
+ if (*szFormat == '"')
+ {
+ if (first)
+ first = FALSE;
+ else if (szFormat[1] != '\0')
+ *pos++ = '\\';
+ }
+ *pos++ = *szFormat;
+ }
+ }
+ *pos = '\0';
+ szFormat = szEscape;
+ }
+
+ mutex = CreateMutex( NULL, FALSE, L"ANSICON_debug_file" );
+ wait = WaitForSingleObject( mutex, 500 );
+ file = fopen( tempfile, "at" ); // _fmode might be binary
+ if (file != NULL)
+ {
+ fwprintf( file, L"%s (%lu): %s\n", prog, pid, szFormat );
+ fclose( file );
+ }
+ if (wait == WAIT_OBJECT_0)
+ ReleaseMutex( mutex );
+ CloseHandle( mutex );
+}
diff --git a/ansicon/version.h b/ansicon/version.h
new file mode 100755
index 0000000..ff76004
--- /dev/null
+++ b/ansicon/version.h
@@ -0,0 +1,9 @@
+/*
+ version.h - Version defines.
+*/
+
+#define PVERS L"1.53" // wide string
+#define PVERSA "1.53" // ANSI string (windres 2.16.91 didn't like L)
+#define PVERE L"153" // wide environment string
+#define PVEREA "153" // ANSI environment string
+#define PVERB 1,5,3,0 // binary (resource)
diff --git a/ansicon/wow64.h b/ansicon/wow64.h
new file mode 100755
index 0000000..33e0444
--- /dev/null
+++ b/ansicon/wow64.h
@@ -0,0 +1,88 @@
+/*
+ wow64.h - Definitions for Wow64.
+
+ Mingw64/TDM does not include these Wow64 definitions.
+*/
+
+#ifndef WOW64_H
+#define WOW64_H
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define WOW64_CONTEXT_i386 0x00010000
+
+#define WOW64_CONTEXT_CONTROL (WOW64_CONTEXT_i386 | 0x00000001L)
+#define WOW64_CONTEXT_INTEGER (WOW64_CONTEXT_i386 | 0x00000002L)
+#define WOW64_CONTEXT_SEGMENTS (WOW64_CONTEXT_i386 | 0x00000004L)
+#define WOW64_CONTEXT_FLOATING_POINT (WOW64_CONTEXT_i386 | 0x00000008L)
+#define WOW64_CONTEXT_DEBUG_REGISTERS (WOW64_CONTEXT_i386 | 0x00000010L)
+#define WOW64_CONTEXT_EXTENDED_REGISTERS (WOW64_CONTEXT_i386 | 0x00000020L)
+
+#define WOW64_CONTEXT_FULL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS)
+
+#define WOW64_CONTEXT_ALL (WOW64_CONTEXT_CONTROL | WOW64_CONTEXT_INTEGER | WOW64_CONTEXT_SEGMENTS | \
+ WOW64_CONTEXT_FLOATING_POINT | WOW64_CONTEXT_DEBUG_REGISTERS | \
+ WOW64_CONTEXT_EXTENDED_REGISTERS)
+
+#define WOW64_SIZE_OF_80387_REGISTERS 80
+
+#define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512
+
+typedef struct _WOW64_FLOATING_SAVE_AREA {
+ DWORD ControlWord;
+ DWORD StatusWord;
+ DWORD TagWord;
+ DWORD ErrorOffset;
+ DWORD ErrorSelector;
+ DWORD DataOffset;
+ DWORD DataSelector;
+ BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS];
+ DWORD Cr0NpxState;
+} WOW64_FLOATING_SAVE_AREA;
+
+typedef WOW64_FLOATING_SAVE_AREA *PWOW64_FLOATING_SAVE_AREA;
+
+typedef struct _WOW64_CONTEXT {
+
+ DWORD ContextFlags;
+
+ DWORD Dr0;
+ DWORD Dr1;
+ DWORD Dr2;
+ DWORD Dr3;
+ DWORD Dr6;
+ DWORD Dr7;
+
+ WOW64_FLOATING_SAVE_AREA FloatSave;
+
+ DWORD SegGs;
+ DWORD SegFs;
+ DWORD SegEs;
+ DWORD SegDs;
+
+ DWORD Edi;
+ DWORD Esi;
+ DWORD Ebx;
+ DWORD Edx;
+ DWORD Ecx;
+ DWORD Eax;
+
+ DWORD Ebp;
+ DWORD Eip;
+ DWORD SegCs;
+ DWORD EFlags;
+ DWORD Esp;
+ DWORD SegSs;
+
+ BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION];
+
+} WOW64_CONTEXT;
+
+typedef WOW64_CONTEXT *PWOW64_CONTEXT;
+
+
+typedef BOOL (WINAPI *TWow64GetThreadContext)( HANDLE hThread, PWOW64_CONTEXT lpContext );
+typedef BOOL (WINAPI *TWow64SetThreadContext)( HANDLE hThread, CONST WOW64_CONTEXT *lpContext );
+
+#endif
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..5d47358
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,7 @@
+sdbd (0.0.1-1) unstable; urgency=low
+
+ * Initial upload
+ * Git: 165.213.180.234:slp/pkgs/s/sdbd
+ * Tag: sdbd_0.0.1-1
+
+ -- Joogwan Kim <joogwan.kim@samsung.com> Sat, 26 Sep 2011 15:00:56 +0900
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7813681
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5 \ No newline at end of file
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..aa3ba16
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,10 @@
+Source: sdbd
+Section: net
+Priority: extra
+Maintainer: Kangho Kim <kh5325.kim@samsung.com>, Yoonki Park <yoonki.park@samsung.com>, Ho Namkoong <ho.namkoong@samsung.com>
+Build-Depends: debhelper (>= 5), libc6-dev
+Standards-Version: 0.0.1
+
+Package: sdbd
+Architecture: any
+Description: SDB daemon
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..6ebe31f
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,44 @@
+#!/usr/bin/make -f
+
+UNAME := $(shell uname -sm)
+ifneq (,$(findstring 86,$(UNAME)))
+ HOST_ARCH := x86
+endif
+
+configure: configure-stamp
+
+configure-stamp:
+ dh_testdir
+ touch configure-stamp
+
+build: build-stamp
+
+build-stamp: configure-stamp
+ dh_testdir
+ $(MAKE)
+ touch build-stamp
+
+install: build
+ dh_testdir
+ dh_testroot
+ $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install
+
+binary: build install
+ dh_testdir
+ dh_testroot
+ifeq ($(HOST_ARCH),x86)
+ dh_install --sourcedir=debian/tmp
+else
+ dh_install --sourcedir=debian/tmp -XS06sdbd
+endif
+ dh_strip
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+clean:
+ dh_testdir
+ rm -f build-stamp configure-stamp
+ $(MAKE) clean
+ dh_clean
+
diff --git a/debian/sdbd.install b/debian/sdbd.install
new file mode 100644
index 0000000..5ac7294
--- /dev/null
+++ b/debian/sdbd.install
@@ -0,0 +1,3 @@
+usr/sbin/sdbd
+etc/init.d/sdbd
+etc/rc.d/rc3.d/S06sdbd
diff --git a/package/build.linux b/package/build.linux
new file mode 100755
index 0000000..f7745eb
--- /dev/null
+++ b/package/build.linux
@@ -0,0 +1,76 @@
+#!/bin/bash
+# clean
+clean()
+{
+ echo "=========================================CLEAN============================================"
+ make clean
+ rm -rf ${SRCDIR}/*.zip
+ rm -rf ${SRCDIR}/*.tar.gz
+}
+
+# build
+build()
+{
+ pkgname_and_platform_list=`awk 'BEGIN{RS="\n\n"; FS="\n"} /Package:/{for(i=1;i<NF;i++){if($i ~ /^OS:/){print $1,$i}}}' ${SRCDIR}/package/pkginfo.manifest | tr ' ' '@'`
+ for pkgname_and_platform in ${pkgname_and_platform_list}
+ do
+ echo "=========================================BUILD============================================"
+ pkgname_and_platform=`echo $pkgname_and_platform | tr '@' ' '`
+ package_name=`echo ${pkgname_and_platform} | cut -f1 -d " " | cut -f2 -d ":"`
+ platform=`echo ${pkgname_and_platform} | cut -f2 -d " " | cut -f2 -d ":"`
+
+ if [ "x${TARGET_OS}" = "x${platform}" ]
+ then
+ if [ "x${package_name}" = "xsdb" ]
+ then
+ echo "build sdb............"
+ make TARGET_HOST=true
+
+ make_result=$?
+ if [ $make_result = 2 ]
+ then
+ echo "libncurses-dev was not installed."
+ echo "Please install \"libncurses-dev\" for build."
+ echo "ex) sudo apt-get install libncurses-dev"
+ exit $make_result
+ elif [ ! $make_result = 0 ]
+ then
+ echo "Unknown error occurred."
+ echo "Build result value : $make_result"
+ exit $make_result
+ fi
+ fi
+ else
+ echo ""
+ fi
+ done
+}
+
+# install
+install()
+{
+ pkgname_and_platform_list=`awk 'BEGIN{RS="\n\n"; FS="\n"} /Package:/{for(i=1;i<NF;i++){if($i ~ /^OS:/){print $1,$i}}}' ${SRCDIR}/package/pkginfo.manifest | tr ' ' '@'`
+ for pkgname_and_platform in ${pkgname_and_platform_list}
+ do
+ echo "=========================================INSTALL============================================"
+ pkgname_and_platform=`echo $pkgname_and_platform | tr '@' ' '`
+ package_name=`echo ${pkgname_and_platform} | cut -f1 -d " " | cut -f2 -d ":"`
+ platform=`echo ${pkgname_and_platform} | cut -f2 -d " " | cut -f2 -d ":"`
+
+ if [ "x${TARGET_OS}" = "x${platform}" ]
+ then
+
+ INSTALL_DIR=${SRCDIR}/package/${package_name}.package.${platform}
+
+ if [ "x${package_name}" = "xsdb" ]
+ then
+ mkdir -p ${INSTALL_DIR}/data/tools
+ cp ${SRCDIR}/bin/* ${INSTALL_DIR}/data/tools/
+ fi
+ fi
+ done
+}
+
+[ "$1" = "clean" ] && clean
+[ "$1" = "build" ] && build
+[ "$1" = "install" ] && install
diff --git a/package/build.macos-64 b/package/build.macos-64
new file mode 100755
index 0000000..45c1a99
--- /dev/null
+++ b/package/build.macos-64
@@ -0,0 +1,39 @@
+#!/bin/bash
+# clean
+clean()
+{
+ echo "=========================================CLEAN============================================"
+ make clean
+ rm -rf ${SRCDIR}/*.zip
+ rm -rf ${SRCDIR}/*.tar.gz
+}
+
+# build
+build()
+{
+ echo "=========================================BUILD============================================"
+ echo "build sdb............"
+ make TARGET_HOST=true
+ make_result=$?
+
+ if [ ! $make_result = 0 ]
+ then
+ echo "Unknown error occurred."
+ echo "Build result value : $make_result"
+ exit $make_result
+ fi
+}
+
+# install
+install()
+{
+ echo "=========================================INSTALL============================================"
+ INSTALL_DIR=${SRCDIR}/package/sdb.package.macos-64
+ mkdir -p ${INSTALL_DIR}/data/tools
+ cp -R ${SRCDIR}/bin/* ${INSTALL_DIR}/data/tools/
+}
+
+[ "$1" = "clean" ] && clean
+[ "$1" = "build" ] && build
+[ "$1" = "install" ] && install
+exit 0
diff --git a/package/build.windows b/package/build.windows
new file mode 100644
index 0000000..f4fd11b
--- /dev/null
+++ b/package/build.windows
@@ -0,0 +1,57 @@
+#!/bin/bash
+# clean
+clean()
+{
+ echo "=========================================CLEAN============================================"
+ make clean
+ rm -rf ${SRCDIR}/*.zip
+ rm -rf ${SRCDIR}/*.tar.gz
+}
+
+# build
+build()
+{
+ echo "=========================================BUILD============================================"
+
+ # gets a source dir which uses the platform-dependent path
+ WIN_SRC_DIR=` echo ${SRCDIR} | sed -e "s/^\/\([^/]\)/\1:/" -e 's/\//\\\/g' `
+ cd ${SRCDIR}
+ echo "Create .dll and .a"
+ cmd /c "call C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x86 WIN7 no_oacr & cd /d ${WIN_SRC_DIR} & call SdbWinApi.bat"
+
+ echo "Make sdb..."
+ make -f Makefile.win
+
+ echo "Make ancicon..."
+ cd "ansicon"
+ if [ "${TARGET_OS}" = "windows-32" ]
+ then
+ make ansicon32
+ else
+ make ansicon32
+ fi
+ cd ..
+}
+
+# install
+install()
+{
+ INSTALL_DIR=${SRCDIR}/package/sdb.package.${TARGET_OS}
+
+ mkdir -p ${INSTALL_DIR}/data/tools
+ cp ${SRCDIR}/bin/* ${INSTALL_DIR}/data/tools/
+ if [ "${TARGET_OS}" = "windows-32" ]
+ then
+ cp ${SRCDIR}/ansicon/x86/ansicon.exe ${INSTALL_DIR}/data/tools/
+ cp ${SRCDIR}/ansicon/x86/ANSI32.dll ${INSTALL_DIR}/data/tools/
+ else
+ cp ${SRCDIR}/ansicon/x86/ansicon.exe ${INSTALL_DIR}/data/tools/
+ cp ${SRCDIR}/ansicon/x86/ANSI32.dll ${INSTALL_DIR}/data/tools/
+ fi
+ cp ${SRCDIR}/package/sdb-run.bat ${INSTALL_DIR}/data/tools/
+}
+
+[ "$1" = "clean" ] && clean
+[ "$1" = "build" ] && build
+[ "$1" = "install" ] && install
+exit 0 \ No newline at end of file
diff --git a/package/changelog b/package/changelog
new file mode 100644
index 0000000..cb8eff0
--- /dev/null
+++ b/package/changelog
@@ -0,0 +1,42 @@
+* 1.0.24
+- fixed vms path for emulator name
+== yoonki.park <yoonki.park@samsung.com> 2012-12-22
+* 1.0.23
+- comment out goto fail when setting 2 configuration
+== yoonki.park <yoonki.park@samsung.com> 2012-12-22
+* 1.0.22
+- remove stdout if command -serial
+== yoonki.park <yoonki.park@samsung.com> 2012-12-22
+* 1.0.21
+- fixed dlog bug
+== ho.namkoong <ho.namkoong@samsung.com> 2012-12-18
+* 1.0.20
+- fixed install command
+== yoonki.park <yoonki.park@samsung.com> 2012-12-18 00:10
+* 1.0.19
+- add connect/disconnect command again
+== yoonki.park <yoonki.park@samsung.com> 2012-12-17 22:30
+* 1.0.18
+- fixed build error for windows
+== yoonki.park <yoonki.park@samsung.com> 2012-12-17 21:30
+* 1.0.17
+- add connect/disconnect command
+== yoonki.park <yoonki.park@samsung.com> 2012-12-17 17:30
+* 1.0.16
+- fixed build error in macos
+== yoonki.park <yoonki.park@samsung.com> 2012-11-27 17:30
+* 1.0.15
+- support install/uninstall command
+== yoonki.park <yoonki.park@samsung.com> 2012-11-27 17:30
+* 1.0.14
+- remove ssh package from sdb
+== yoonki.park <yoonki.park@samsung.com> 2012-11-06 17:30
+* 1.0.13
+- remove some feature from remove branch
+== yoonki.park <yoonki.park@samsung.com> 2012-11-06 16:30
+* 1.0.12
+- merge branch 'develop' into release
+== yoonki.park <yoonki.park@samsung.com> 2012-11-06 16:11
+* 1.0.11
+- uploaded sdb package
+== yoonki.park <yoonki.park@samsung.com> 2012-11-05 14:17
diff --git a/package/pkginfo.manifest b/package/pkginfo.manifest
new file mode 100644
index 0000000..b2a7572
--- /dev/null
+++ b/package/pkginfo.manifest
@@ -0,0 +1,23 @@
+Version:1.0.24
+Source:sdb
+Maintainer:Kangho Kim <kh5325.kim@samsung.com>, Yoonki Park<yoonki.park@samsung.com>, Hyunsik Noh<hyunsik.noh@samsung.com>, Gun Kim<gune.kim@samsung.com>, Ho Namkoong<ho.namkoong@samsung.com>, Taeyoung Son<taeyoung2.son@samsung.com>
+
+Package:sdb
+OS:ubuntu-32
+Build-host-os:ubuntu-32
+Description:Smart Development Bridge for device management
+
+Package:sdb
+OS:windows-32,windows-64
+Build-host-os:windows-32
+Description:Smart Development Bridge for device management
+
+Package:sdb
+OS:ubuntu-64
+Build-host-os:ubuntu-64
+Description:Smart Development Bridge for device management
+
+Package:sdb
+OS:macos-64
+Build-host-os:macos-64
+Description:Smart Development Bridge for device management
diff --git a/package/sdb-run.bat b/package/sdb-run.bat
new file mode 100755
index 0000000..7dc6f60
--- /dev/null
+++ b/package/sdb-run.bat
@@ -0,0 +1,25 @@
+@echo off
+set SCRIPT=%0
+
+:: delims is a TAB followed by a space
+set KEY=TIZEN_SDK_INSTALLED_PATH
+
+REM find sdk path
+set rkey="HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
+set rval="Local AppData"
+FOR /f "tokens=3*" %%a IN ('reg query %rkey% /v %rval%') DO (
+ set sdk_conf_path=%%b
+)
+
+REM find cli path
+FOR /f "tokens=1,2 delims==" %%i IN ('type "%sdk_conf_path%\tizen-sdk-data\tizensdkpath"') DO IF %%i==%KEY% (set SDK_PATH=%%j)
+set TOOLS_HOME=%SDK_PATH%\tools
+
+if "%1" == "-s" (
+title sdb - %2
+) else (
+title sdb
+)
+
+set EXEC=%TOOLS_HOME%\ansicon.exe %TOOLS_HOME%\sdb.exe %*
+%EXEC%
diff --git a/package/sdb.install.darwin b/package/sdb.install.darwin
new file mode 100755
index 0000000..76df931
--- /dev/null
+++ b/package/sdb.install.darwin
@@ -0,0 +1,18 @@
+#!/bin/bash -x
+
+## Set sdb's environment path
+s=`cat ${HOME}/.bashrc | grep -n "## Tizen SDK configuration" | cut -f1 -d":"`
+
+if [ "x${s}" = "x" ] ; then
+ cat >> ${HOME}/.bashrc << END
+## Tizen SDK configuration
+# This is generated by Tizen SDK. Please do not modify by yourself.
+# Set sdb environment path
+export PATH=\$PATH:${INSTALLED_PATH}/tools
+## End Tizen SDK configuration
+END
+fi
+
+source ${HOME}/.bashrc
+
+exit 0
diff --git a/package/sdb.install.linux b/package/sdb.install.linux
new file mode 100755
index 0000000..fc59231
--- /dev/null
+++ b/package/sdb.install.linux
@@ -0,0 +1,35 @@
+#!/bin/bash -x
+
+## Set sdb's environment path
+s=`cat ${HOME}/.bashrc | grep -n "## Tizen SDK configuration" | cut -f1 -d":"`
+
+if [ "x${s}" = "x" ] ; then
+ cat >> ${HOME}/.bashrc << END
+## Tizen SDK configuration
+# This is generated by Tizen SDK. Please do not modify by yourself.
+# Set sdb environment path
+export PATH=\$PATH:${INSTALLED_PATH}/tools
+## End Tizen SDK configuration
+END
+fi
+
+source ${HOME}/.bashrc
+
+# set udev rules file
+
+TMP_FILE=99-samsung-device.rules
+echo "# Add a udev rules when you want to develop a Tizen application with your device." >> $TMP_FILE
+echo "# Use this format to add each vendor to the file:" >> $TMP_FILE
+echo "# SUBSYSTEM==\"usb\", ATTR{idVendor}==\"04e8\", ATTRS{idProduct}==\"6864\", MODE=\"0666\", GROUP=\"plugdev\"" >> $TMP_FILE
+echo "# In the example, the vendor ID is for Samsung manufacture. The mode specifies read/write permissions, and group defines which Unix group owns the device node." >> $TMP_FILE
+echo "#" >> $TMP_FILE
+echo "# Contact : Kangho Kim <kh5325.kim@samsung.com>, Yoonki Park<yoonki.park@samsung.com>, Ho Namkoong <ho.namkoong@samsung.com>" >> $TMP_FILE
+echo "# See also udev(7) for an overview of rule syntax." >> $TMP_FILE
+echo "" >> $TMP_FILE
+echo "# Samsung" >> $TMP_FILE
+echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"04e8\", ATTRS{idProduct}==\"6864\", MODE=\"0666\", GROUP=\"plugdev\"" >> $TMP_FILE
+echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"04e8\", ATTRS{idProduct}==\"6863\", MODE=\"0666\", GROUP=\"plugdev\"" >> $TMP_FILE
+chmod +x $TMP_FILE
+gksudo mv ${TMP_FILE} /etc/udev/rules.d/
+
+exit 0
diff --git a/package/sdb.remove.darwin b/package/sdb.remove.darwin
new file mode 100755
index 0000000..7d0ea0f
--- /dev/null
+++ b/package/sdb.remove.darwin
@@ -0,0 +1,22 @@
+#!/bin/sh
+SDB_PATH=tools/sdb
+${INSTALLED_PATH}/${SDB_PATH} kill-server
+rm -rf ${INSTALLED_PATH}/${SDB_PATH}
+## remove sdb environment path
+s=`cat ${HOME}/.bashrc | grep -n "## Tizen SDK configuration" | cut -f1 -d":"`
+
+if [ "x${s}" = "x" ] ; then
+ exit 1
+fi
+
+if [ ${s} -ge 0 ] ; then
+ e=`cat ${HOME}/.bashrc | grep -n "## End Tizen SDK configuration" | cut -f1 -d":"`
+ if [ $e -ge $s ] ; then
+ cp ${HOME}/.bashrc ${HOME}/.bashrc.tizen
+ sed "${s},${e}d" ${HOME}/.bashrc > ${HOME}/.bashrc.swap
+ mv ${HOME}/.bashrc.swap ${HOME}/.bashrc
+ source ${HOME}/.bashrc
+ fi
+fi
+
+exit 0
diff --git a/package/sdb.remove.linux b/package/sdb.remove.linux
new file mode 100755
index 0000000..5ddd1cf
--- /dev/null
+++ b/package/sdb.remove.linux
@@ -0,0 +1,25 @@
+#!/bin/sh
+SDB_PATH=tools/sdb
+${INSTALLED_PATH}/${SDB_PATH} kill-server
+rm -rf ${INSTALLED_PATH}/${SDB_PATH}
+## remove sdb environment path
+s=`cat ${HOME}/.bashrc | grep -n "## Tizen SDK configuration" | cut -f1 -d":"`
+
+if [ "x${s}" = "x" ] ; then
+ exit 1
+fi
+
+if [ ${s} -ge 0 ] ; then
+ e=`cat ${HOME}/.bashrc | grep -n "## End Tizen SDK configuration" | cut -f1 -d":"`
+ if [ $e -ge $s ] ; then
+ cp ${HOME}/.bashrc ${HOME}/.bashrc.tizen
+ sed "${s},${e}d" ${HOME}/.bashrc > ${HOME}/.bashrc.swap
+ mv ${HOME}/.bashrc.swap ${HOME}/.bashrc
+ source ${HOME}/.bashrc
+ fi
+fi
+
+## remove udev rule file
+gksudo rm -rf /etc/udev/rules.d/99-samsung-device.rules
+
+exit 0
diff --git a/package/sdb.remove.windows b/package/sdb.remove.windows
new file mode 100644
index 0000000..88ae1bc
--- /dev/null
+++ b/package/sdb.remove.windows
@@ -0,0 +1,10 @@
+@echo off
+
+set execute_path=tools
+%INSTALLED_PATH%\%execute_path%\sdb.exe kill-server
+del %INSTALLED_PATH%\%execute_path%\sdb.exe
+del %INSTALLED_PATH%\%execute_path%\SdbWinApi.dll
+del %INSTALLED_PATH%\%execute_path%\SdbWinUsbApi.dll
+del %INSTALLED_PATH%\%execute_path%\ansicon.exe
+del %INSTALLED_PATH%\%execute_path%\ANSI32.dll
+exit 0
diff --git a/package/usb-connection-for-ssh.install.linux b/package/usb-connection-for-ssh.install.linux
new file mode 100755
index 0000000..b314d9d
--- /dev/null
+++ b/package/usb-connection-for-ssh.install.linux
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+samsung_udev="99-samsung-device.rules"
+rules_dir=${INSTALLED_PATH}/tools/ssh
+rules_path=${rules_dir}/${samsung_udev}
+
+sed "s|TIZEN_SDK_SSH_PATH|${rules_dir}|g" ${rules_path} > ${rules_path}.mod
+mv ${rules_path}.mod ${rules_path}
diff --git a/script/S06sdbd b/script/S06sdbd
new file mode 100755
index 0000000..6a05d55
--- /dev/null
+++ b/script/S06sdbd
@@ -0,0 +1,2 @@
+#!/bin/sh
+/etc/init.d/sdbd start
diff --git a/script/sdbd b/script/sdbd
new file mode 100755
index 0000000..85a868c
--- /dev/null
+++ b/script/sdbd
@@ -0,0 +1,39 @@
+#! /bin/sh
+
+. /lib/lsb/init-functions
+
+export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
+
+case "$1" in
+ start)
+ log_daemon_msg "Starting SDB Daemon..." "sdbd"
+ if start-stop-daemon --start --quiet --background --make-pidfile --pidfile /var/run/sdbd.pid --exec /usr/sbin/sdbd; then
+ log_end_msg 0
+ else
+ log_end_msg 1
+ fi
+ ;;
+ stop)
+ log_daemon_msg "Stopping SDB Daemon..." "sdbd"
+ if start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/sdbd.pid; then
+ log_end_msg 0
+ else
+ log_end_msg 1
+ fi
+ ;;
+ restart)
+ log_daemon_msg "Restarting SDB Daemon..." "sdbd"
+ start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile /var/run/sdbd.pid
+ sleep 1
+ if start-stop-daemon --start --quiet --background --make-pidfile --pidfile /var/run/sdbd.pid --exec /usr/sbin/sdbd; then
+ log_end_msg 0
+ else
+ log_end_msg 1
+ fi
+ ;;
+ *)
+ log_action_msg "Usage: /etc/init.d/sdbd {start|stop|restart}"
+ exit 1
+esac
+
+exit 0
diff --git a/src/Android.mk b/src/Android.mk
new file mode 100644
index 0000000..7faca9b
--- /dev/null
+++ b/src/Android.mk
@@ -0,0 +1,154 @@
+# 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),freebsd)
+ USB_SRCS := usb_libusb.c
+ EXTRA_SRCS := get_my_path_freebsd.c
+ LOCAL_LDLIBS += -lpthread -lusb
+endif
+
+ifeq ($(HOST_OS),windows)
+ USB_SRCS := usb_windows.c
+ EXTRA_SRCS := get_my_path_windows.c
+ EXTRA_STATIC_LIBS := AdbWinApi
+ ifneq ($(strip $(USE_CYGWIN)),)
+ # Pure cygwin case
+ LOCAL_LDLIBS += -lpthread
+ LOCAL_C_INCLUDES += /usr/include/w32api/ddk
+ endif
+ ifneq ($(strip $(USE_MINGW)),)
+ # MinGW under Linux case
+ LOCAL_LDLIBS += -lws2_32
+ USE_SYSDEPS_WIN32 := 1
+ LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk
+ endif
+ LOCAL_C_INCLUDES += development/host/windows/usb/api/
+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) \
+ utils.c \
+ usb_vendors.c
+
+
+ifneq ($(USE_SYSDEPS_WIN32),)
+ LOCAL_SRC_FILES += sysdeps_win32.c
+else
+ LOCAL_SRC_FILES += fdevent.c
+endif
+
+LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+LOCAL_MODULE := adb
+
+LOCAL_STATIC_LIBRARIES := libzipfile libunz $(EXTRA_STATIC_LIBS)
+ifeq ($(USE_SYSDEPS_WIN32),)
+ LOCAL_STATIC_LIBRARIES += libcutils
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
+
+ifeq ($(HOST_OS),windows)
+$(LOCAL_INSTALLED_MODULE): \
+ $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
+ $(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
+endif
+
+
+# adbd device daemon
+# =========================================================
+
+# build adbd in all non-simulator builds
+BUILD_ADBD := false
+ifneq ($(TARGET_SIMULATOR),true)
+ BUILD_ADBD := true
+endif
+
+# build adbd for the Linux simulator build
+# so we can use it to test the adb USB gadget driver on x86
+#ifeq ($(HOST_OS),linux)
+# BUILD_ADBD := true
+#endif
+
+
+ifeq ($(BUILD_ADBD),true)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ adb.c \
+ fdevent.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 \
+ utils.c
+
+LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
+
+# TODO: This should probably be board specific, whether or not the kernel has
+# the gadget driver; rather than relying on the architecture type.
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_CFLAGS += -DANDROID_GADGET=1
+endif
+
+LOCAL_MODULE := adbd
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_STATIC_LIBRARIES := libcutils
+ LOCAL_LDLIBS += -lpthread
+ include $(BUILD_HOST_EXECUTABLE)
+else
+ LOCAL_STATIC_LIBRARIES := libcutils libc
+ include $(BUILD_EXECUTABLE)
+endif
+
+endif
diff --git a/src/MODULE_LICENSE_APACHE2 b/src/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/MODULE_LICENSE_APACHE2
diff --git a/src/NOTICE b/src/NOTICE
new file mode 100644
index 0000000..9ffcc08
--- /dev/null
+++ b/src/NOTICE
@@ -0,0 +1,191 @@
+
+ Copyright (c) 2006-2009, The Android Open Source Project
+ Copyright 2006, Brian Swetland <swetland@frotz.net>
+
+ 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
+
diff --git a/src/OVERVIEW.TXT b/src/OVERVIEW.TXT
new file mode 100644
index 0000000..0d9784a
--- /dev/null
+++ b/src/OVERVIEW.TXT
@@ -0,0 +1,139 @@
+Implementation notes regarding SDB.
+
+I. General Overview:
+
+The Android Debug Bridge (SDB) is used to:
+
+- keep track of all Android devices and emulators instances
+ connected to or running on a given host developer machine
+
+- implement various control commands (e.g. "sdb shell", "sdb pull", etc..)
+ for the benefit of clients (command-line users, or helper programs like
+ DDMS). These commands are what is called a 'service' in SDB.
+
+As a whole, everything works through the following components:
+
+ 1. The SDB server
+
+ This is a background process that runs on the host machine. Its purpose
+ if to sense the USB ports to know when devices are attached/removed,
+ as well as when emulator instances start/stop.
+
+ It thus maintains a list of "connected devices" and assigns a 'state'
+ to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on
+ this below).
+
+ The SDB server is really one giant multiplexing loop whose purpose is
+ to orchestrate the exchange of data (packets, really) between clients,
+ services and devices.
+
+
+ 2. The SDB daemon (sdbd)
+
+ The 'sdbd' program runs as a background process within an Android device
+ or emulated system. Its purpose is to connect to the SDB server
+ (through USB for devices, through TCP for emulators) and provide a
+ few services for clients that run on the host.
+
+ The SDB server considers that a device is ONLINE when it has successfully
+ connected to the sdbd program within it. Otherwise, the device is OFFLINE,
+ meaning that the SDB server detected a new device/emulator, but could not
+ connect to the sdbd daemon.
+
+ the BOOTLOADER and RECOVERY states correspond to alternate states of
+ devices when they are in the bootloader or recovery mode.
+
+ 3. The SDB command-line client
+
+ The 'sdb' command-line program is used to run sdb commands from a shell
+ or a script. It first tries to locate the SDB server on the host machine,
+ and will start one automatically if none is found.
+
+ then, the client sends its service requests to the SDB server. It doesn't
+ need to know.
+
+ Currently, a single 'sdb' binary is used for both the server and client.
+ this makes distribution and starting the server easier.
+
+
+ 4. Services
+
+ There are essentially two kinds of services that a client can talk to.
+
+ Host Services:
+ these services run within the SDB Server and thus do not need to
+ communicate with a device at all. A typical example is "sdb devices"
+ which is used to return the list of currently known devices and their
+ state. They are a few couple other services though.
+
+ Local Services:
+ these services either run within the sdbd daemon, or are started by
+ it on the device. The SDB server is used to multiplex streams
+ between the client and the service running in sdbd. In this case
+ its role is to initiate the connection, then of being a pass-through
+ for the data.
+
+
+II. Protocol details:
+
+ 1. Client <-> Server protocol:
+
+ This details the protocol used between SDB clients and the SDB
+ server itself. The SDB server listens on TCP:localhost:5037.
+
+ A client sends a request using the following format:
+
+ 1. A 4-byte hexadecimal string giving the length of the payload
+ 2. Followed by the payload itself.
+
+ For example, to query the SDB server for its internal version number,
+ the client will do the following:
+
+ 1. Connect to tcp:localhost:5037
+ 2. Send the string "000Chost:version" to the corresponding socket
+
+ The 'host:' prefix is used to indicate that the request is addressed
+ to the server itself (we will talk about other kinds of requests later).
+ The content length is encoded in ASCII for easier debugging.
+
+ The server should answer a request with one of the following:
+
+ 1. For success, the 4-byte "OKAY" string
+
+ 2. For failure, the 4-byte "FAIL" string, followed by a
+ 4-byte hex length, followed by a string giving the reason
+ for failure.
+
+ 3. As a special exception, for 'host:version', a 4-byte
+ hex string corresponding to the server's internal version number
+
+ Note that the connection is still alive after an OKAY, which allows the
+ client to make other requests. But in certain cases, an OKAY will even
+ change the state of the connection.
+
+ For example, the case of the 'host:transport:<serialnumber>' request,
+ where '<serialnumber>' is used to identify a given device/emulator; after
+ the "OKAY" answer, all further requests made by the client will go
+ directly to the corresponding sdbd daemon.
+
+ The file SERVICES.TXT lists all services currently implemented by SDB.
+
+
+ 2. Transports:
+
+ An SDB transport models a connection between the SDB server and one device
+ or emulator. There are currently two kinds of transports:
+
+ - USB transports, for physical devices through USB
+
+ - Local transports, for emulators running on the host, connected to
+ the server through TCP
+
+ In theory, it should be possible to write a local transport that proxies
+ a connection between an SDB server and a device/emulator connected to/
+ running on another machine. This hasn't been done yet though.
+
+ Each transport can carry one or more multiplexed streams between clients
+ and the device/emulator they point to. The SDB server must handle
+ unexpected transport disconnections (e.g. when a device is physically
+ unplugged) properly.
diff --git a/src/SERVICES.TXT b/src/SERVICES.TXT
new file mode 100644
index 0000000..3bfbe37
--- /dev/null
+++ b/src/SERVICES.TXT
@@ -0,0 +1,236 @@
+This file tries to document all requests a client can make
+to the SDB server of an sdbd daemon. See the OVERVIEW.TXT document
+to understand what's going on here.
+
+HOST SERVICES:
+
+host:version
+ Ask the SDB server for its internal version number.
+
+ As a special exception, the server will respond with a 4-byte
+ hex string corresponding to its internal version number, without
+ any OKAY or FAIL.
+
+host:kill
+ Ask the SDB server to quit immediately. This is used when the
+ SDB client detects that an obsolete server is running after an
+ upgrade.
+
+host:devices
+ Ask to return the list of available Android devices and their
+ state. After the OKAY, this is followed by a 4-byte hex len,
+ and a string that will be dumped as-is by the client, then
+ the connection is closed
+
+host:track-devices
+ This is a variant of host:devices which doesn't close the
+ connection. Instead, a new device list description is sent
+ each time a device is added/removed or the state of a given
+ device changes (hex4 + content). This allows tools like DDMS
+ to track the state of connected devices in real-time without
+ polling the server repeatedly.
+
+host:emulator:<port>
+ This is a special query that is sent to the SDB server when a
+ new emulator starts up. <port> is a decimal number corresponding
+ to the emulator's SDB control port, i.e. the TCP port that the
+ emulator will forward automatically to the sdbd daemon running
+ in the emulator system.
+
+ This mechanism allows the SDB server to know when new emulator
+ instances start.
+
+host:transport:<serial-number>
+ Ask to switch the connection to the device/emulator identified by
+ <serial-number>. After the OKAY response, every client request will
+ be sent directly to the sdbd daemon running on the device.
+ (Used to implement the -s option)
+
+host:transport-usb
+ Ask to switch the connection to one device connected through USB
+ to the host machine. This will fail if there are more than one such
+ devices. (Used to implement the -d convenience option)
+
+host:transport-local
+ Ask to switch the connection to one emulator connected through TCP.
+ This will fail if there is more than one such emulator instance
+ running. (Used to implement the -e convenience option)
+
+host:transport-any
+ Another host:transport variant. Ask to switch the connection to
+ either the device or emulator connect to/running on the host.
+ Will fail if there is more than one such device/emulator available.
+ (Used when neither -s, -d or -e are provided)
+
+host-serial:<serial-number>:<request>
+ This is a special form of query, where the 'host-serial:<serial-number>:'
+ prefix can be used to indicate that the client is asking the SDB server
+ for information related to a specific device. <request> can be in one
+ of the format described below.
+
+host-usb:<request>
+ A variant of host-serial used to target the single USB device connected
+ to the host. This will fail if there is none or more than one.
+
+host-local:<request>
+ A variant of host-serial used to target the single emulator instance
+ running on the host. This will fail if there is none or more than one.
+
+host:<request>
+ When asking for information related to a device, 'host:' can also be
+ interpreted as 'any single device or emulator connected to/running on
+ the host'.
+
+<host-prefix>:get-product
+ XXX
+
+<host-prefix>:get-serialno
+ Returns the serial number of the corresponding device/emulator.
+ Note that emulator serial numbers are of the form "emulator-5554"
+
+<host-prefix>:get-state
+ Returns the state of a given device as a string.
+
+<host-prefix>:forward:<local>;<remote>
+ Asks the SDB server to forward local connections from <local>
+ to the <remote> address on a given device.
+
+ There, <host-prefix> can be one of the
+ host-serial/host-usb/host-local/host prefixes as described previously
+ and indicates which device/emulator to target.
+
+ the format of <local> is one of:
+
+ tcp:<port> -> TCP connection on localhost:<port>
+ local:<path> -> Unix local domain socket on <path>
+
+ the format of <remote> is one of:
+
+ tcp:<port> -> TCP localhost:<port> on device
+ local:<path> -> Unix local domain socket on device
+ jdwp:<pid> -> JDWP thread on VM process <pid>
+
+ or even any one of the local services described below.
+
+
+
+LOCAL SERVICES:
+
+All the queries below assumed that you already switched the transport
+to a real device, or that you have used a query prefix as described
+above.
+
+shell:command arg1 arg2 ...
+ Run 'command arg1 arg2 ...' in a shell on the device, and return
+ its output and error streams. Note that arguments must be separated
+ by spaces. If an argument contains a space, it must be quoted with
+ double-quotes. Arguments cannot contain double quotes or things
+ will go very wrong.
+
+ Note that this is the non-interactive version of "sdb shell"
+
+shell:
+ Start an interactive shell session on the device. Redirect
+ stdin/stdout/stderr as appropriate. Note that the SDB server uses
+ this to implement "sdb shell", but will also cook the input before
+ sending it to the device (see interactive_shell() in commandline.c)
+
+remount:
+ Ask sdbd to remount the device's filesystem in read-write mode,
+ instead of read-only. This is usually necessary before performing
+ an "sdb sync" or "sdb push" request.
+
+ This request may not succeed on certain builds which do not allow
+ that.
+
+dev:<path>
+ Opens a device file and connects the client directly to it for
+ read/write purposes. Useful for debugging, but may require special
+ privileges and thus may not run on all devices. <path> is a full
+ path from the root of the filesystem.
+
+tcp:<port>
+ Tries to connect to tcp port <port> on localhost.
+
+tcp:<port>:<server-name>
+ Tries to connect to tcp port <port> on machine <server-name> from
+ the device. This can be useful to debug some networking/proxy
+ issues that can only be revealed on the device itself.
+
+local:<path>
+ Tries to connect to a Unix domain socket <path> on the device
+
+localreserved:<path>
+localabstract:<path>
+localfilesystem:<path>
+ Variants of local:<path> that are used to access other Android
+ socket namespaces.
+
+log:<name>
+ Opens one of the system logs (/dev/log/<name>) and allows the client
+ to read them directly. Used to implement 'sdb logcat'. The stream
+ will be read-only for the client.
+
+framebuffer:
+ This service is used to send snapshots of the framebuffer to a client.
+ It requires sufficient privileges but works as follow:
+
+ After the OKAY, the service sends 16-byte binary structure
+ containing the following fields (little-endian format):
+
+ depth: uint32_t: framebuffer depth
+ size: uint32_t: framebuffer size in bytes
+ width: uint32_t: framebuffer width in pixels
+ height: uint32_t: framebuffer height in pixels
+
+ With the current implementation, depth is always 16, and
+ size is always width*height*2
+
+ Then, each time the client wants a snapshot, it should send
+ one byte through the channel, which will trigger the service
+ to send it 'size' bytes of framebuffer data.
+
+ If the sdbd daemon doesn't have sufficient privileges to open
+ the framebuffer device, the connection is simply closed immediately.
+
+dns:<server-name>
+ This service is an exception because it only runs within the SDB server.
+ It is used to implement USB networking, i.e. to provide a network connection
+ to the device through the host machine (note: this is the exact opposite of
+ network tethering).
+
+ It is used to perform a gethostbyname(<address>) on the host and return
+ the corresponding IP address as a 4-byte string.
+
+recover:<size>
+ This service is used to upload a recovery image to the device. <size>
+ must be a number corresponding to the size of the file. The service works
+ by:
+
+ - creating a file named /tmp/update
+ - reading 'size' bytes from the client and writing them to /tmp/update
+ - when everything is read successfully, create a file named /tmp/update.start
+
+ This service can only work when the device is in recovery mode. Otherwise,
+ the /tmp directory doesn't exist and the connection will be closed immediately.
+
+jdwp:<pid>
+ Connects to the JDWP thread running in the VM of process <pid>.
+
+track-jdwp
+ This is used to send the list of JDWP pids periodically to the client.
+ The format of the returned data is the following:
+
+ <hex4>: the length of all content as a 4-char hexadecimal string
+ <content>: a series of ASCII lines of the following format:
+ <pid> "\n"
+
+ This service is used by DDMS to know which debuggable processes are running
+ on the device/emulator.
+
+ Note that there is no single-shot service to retrieve the list only once.
+
+sync:
+ This starts the file synchronisation service, used to implement "sdb push"
+ and "sdb pull". Since this service is pretty complex, it will be detailed
+ in a companion document named SYNC.TXT
diff --git a/src/TizenConfig.h b/src/TizenConfig.h
new file mode 100644
index 0000000..81d36f3
--- /dev/null
+++ b/src/TizenConfig.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 have <sys/sendfile.h>
+ */
+#ifdef __CYGWIN__
+# define HAVE_SYS_SENDFILE_H 1
+#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 the open_memstream() function exists on the system.
+ */
+/* #define HAVE_OPEN_MEMSTREAM 1 */
+
+/*
+ * Define if the BSD funopen() function exists on the system.
+ */
+/* #define HAVE_FUNOPEN 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
+#endif
+
+/*
+ * Define if writev() exists.
+ */
+/* #define HAVE_WRITEV */
+
+/*
+ * Define if <stdint.h> exists.
+ */
+/* #define HAVE_STDINT_H */
+
+/*
+ * Define if <stdbool.h> exists.
+ */
+#define HAVE_STDBOOL_H
+
+/*
+ * Define if <sched.h> exists.
+ */
+/* #define HAVE_SCHED_H */
+
+/*
+ * Define if pread() exists
+ */
+/* #define HAVE_PREAD 1 */
+
+/*
+ * Define if we have st_mtim in struct stat
+ */
+/* #define HAVE_STAT_ST_MTIM 1 */
+
+/*
+ * Define if printf() supports %zd for size_t arguments
+ */
+/* #define HAVE_PRINTF_ZD 1 */
+
+#endif /*_ANDROID_CONFIG_H*/
diff --git a/src/android_reboot.c b/src/android_reboot.c
new file mode 100644
index 0000000..238a3d6
--- /dev/null
+++ b/src/android_reboot.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <unistd.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "android_reboot.h"
+
+/* Check to see if /proc/mounts contains any writeable filesystems
+ * backed by a block device.
+ * Return true if none found, else return false.
+ */
+static int remount_ro_done(void)
+{
+ FILE *f;
+ char mount_dev[256];
+ char mount_dir[256];
+ char mount_type[256];
+ char mount_opts[256];
+ int mount_freq;
+ int mount_passno;
+ int match;
+ int found_rw_fs = 0;
+
+ f = fopen("/proc/mounts", "r");
+ if (! f) {
+ /* If we can't read /proc/mounts, just give up */
+ return 1;
+ }
+
+ do {
+ match = fscanf(f, "%255s %255s %255s %255s %d %d\n",
+ mount_dev, mount_dir, mount_type,
+ mount_opts, &mount_freq, &mount_passno);
+ mount_dev[255] = 0;
+ mount_dir[255] = 0;
+ mount_type[255] = 0;
+ mount_opts[255] = 0;
+ if ((match == 6) && !strncmp(mount_dev, "/dev/block", 10) && strstr(mount_opts, "rw")) {
+ found_rw_fs = 1;
+ break;
+ }
+ } while (match != EOF);
+
+ fclose(f);
+
+ return !found_rw_fs;
+}
+
+/* Remounting filesystems read-only is difficult when there are files
+ * opened for writing or pending deletes on the filesystem. There is
+ * no way to force the remount with the mount(2) syscall. The magic sysrq
+ * 'u' command does an emergency remount read-only on all writable filesystems
+ * that have a block device (i.e. not tmpfs filesystems) by calling
+ * emergency_remount(), which knows how to force the remount to read-only.
+ * Unfortunately, that is asynchronous, and just schedules the work and
+ * returns. The best way to determine if it is done is to read /proc/mounts
+ * repeatedly until there are no more writable filesystems mounted on
+ * block devices.
+ */
+static void remount_ro(void)
+{
+ int fd, cnt = 0;
+ int len = 0;
+ /* Trigger the remount of the filesystems as read-only,
+ * which also marks them clean.
+ */
+ fd = open("/proc/sysrq-trigger", O_WRONLY);
+ if (fd < 0) {
+ return;
+ }
+ len = write(fd, "u", 1);
+ close(fd);
+
+ /* Now poll /proc/mounts till it's done */
+ while (!remount_ro_done() && (cnt < 50)) {
+ usleep(100000);
+ cnt++;
+ }
+
+ return;
+}
+
+
+int android_reboot(int cmd, int flags, char *arg)
+{
+ int ret;
+
+ if (!(flags & ANDROID_RB_FLAG_NO_SYNC))
+ sync();
+
+ if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO))
+ remount_ro();
+
+ switch (cmd) {
+ case ANDROID_RB_RESTART:
+ ret = reboot(RB_AUTOBOOT);
+ break;
+
+ case ANDROID_RB_POWEROFF:
+ ret = reboot(RB_POWER_OFF);
+ break;
+#if 0 /* tizen specific */
+ case ANDROID_RB_RESTART2:
+ ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_RESTART2, arg);
+ break;
+#endif
+ default:
+ ret = -1;
+ }
+
+ return ret;
+}
+
diff --git a/src/android_reboot.h b/src/android_reboot.h
new file mode 100644
index 0000000..5fcb61c
--- /dev/null
+++ b/src/android_reboot.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_ANDROID_REBOOT_H__
+#define __CUTILS_ANDROID_REBOOT_H__
+
+__BEGIN_DECLS
+
+/* Commands */
+#define ANDROID_RB_RESTART 0xDEAD0001
+#define ANDROID_RB_POWEROFF 0xDEAD0002
+#define ANDROID_RB_RESTART2 0xDEAD0003
+
+/* Flags */
+#define ANDROID_RB_FLAG_NO_SYNC 0x1
+#define ANDROID_RB_FLAG_NO_REMOUNT_RO 0x2
+
+int android_reboot(int cmd, int flags, char *arg);
+
+__END_DECLS
+
+#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/src/clear_vars.mk b/src/clear_vars.mk
new file mode 100644
index 0000000..ee28f21
--- /dev/null
+++ b/src/clear_vars.mk
@@ -0,0 +1,107 @@
+###########################################################
+## Clear out values of all variables used by rule templates.
+###########################################################
+
+LOCAL_MODULE:=
+LOCAL_MODULE_PATH:=
+LOCAL_MODULE_STEM:=
+LOCAL_DONT_CHECK_MODULE:=
+LOCAL_CHECKED_MODULE:=
+LOCAL_BUILT_MODULE:=
+LOCAL_BUILT_MODULE_STEM:=
+OVERRIDE_BUILT_MODULE_PATH:=
+LOCAL_INSTALLED_MODULE:=
+LOCAL_UNINSTALLABLE_MODULE:=
+LOCAL_INTERMEDIATE_TARGETS:=
+LOCAL_UNSTRIPPED_PATH:=
+LOCAL_MODULE_CLASS:=
+LOCAL_MODULE_SUFFIX:=
+LOCAL_PACKAGE_NAME:=
+LOCAL_OVERRIDES_PACKAGES:=
+LOCAL_EXPORT_PACKAGE_RESOURCES:=
+LOCAL_MANIFEST_PACKAGE_NAME:=
+LOCAL_REQUIRED_MODULES:=
+LOCAL_ACP_UNAVAILABLE:=
+LOCAL_MODULE_TAGS:=
+LOCAL_SRC_FILES:=
+LOCAL_PREBUILT_OBJ_FILES:=
+LOCAL_STATIC_JAVA_LIBRARIES:=
+LOCAL_STATIC_LIBRARIES:=
+LOCAL_WHOLE_STATIC_LIBRARIES:=
+LOCAL_SHARED_LIBRARIES:=
+LOCAL_IS_HOST_MODULE:=
+LOCAL_CC:=
+LOCAL_CXX:=
+LOCAL_CPP_EXTENSION:=
+LOCAL_NO_DEFAULT_COMPILER_FLAGS:=
+LOCAL_NO_FDO_SUPPORT :=
+LOCAL_ARM_MODE:=
+LOCAL_YACCFLAGS:=
+LOCAL_ASFLAGS:=
+LOCAL_CFLAGS:=
+LOCAL_CPPFLAGS:=
+LOCAL_C_INCLUDES:=
+LOCAL_LDFLAGS:=
+LOCAL_LDLIBS:=
+LOCAL_AAPT_FLAGS:=
+LOCAL_SYSTEM_SHARED_LIBRARIES:=none
+LOCAL_PREBUILT_LIBS:=
+LOCAL_PREBUILT_EXECUTABLES:=
+LOCAL_PREBUILT_JAVA_LIBRARIES:=
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=
+LOCAL_PREBUILT_STRIP_COMMENTS:=
+LOCAL_INTERMEDIATE_SOURCES:=
+LOCAL_INTERMEDIATE_SOURCE_DIR:=
+LOCAL_JAVACFLAGS:=
+LOCAL_JAVA_LIBRARIES:=
+LOCAL_NO_STANDARD_LIBRARIES:=
+LOCAL_CLASSPATH:=
+LOCAL_DROIDDOC_USE_STANDARD_DOCLET:=
+LOCAL_DROIDDOC_SOURCE_PATH:=
+LOCAL_DROIDDOC_TEMPLATE_DIR:=
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=
+LOCAL_DROIDDOC_ASSET_DIR:=
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=
+LOCAL_DROIDDOC_OPTIONS:=
+LOCAL_DROIDDOC_HTML_DIR:=
+LOCAL_ASSET_FILES:=
+LOCAL_ASSET_DIR:=
+LOCAL_RESOURCE_DIR:=
+LOCAL_JAVA_RESOURCE_DIRS:=
+LOCAL_JAVA_RESOURCE_FILES:=
+LOCAL_GENERATED_SOURCES:=
+LOCAL_COPY_HEADERS_TO:=
+LOCAL_COPY_HEADERS:=
+LOCAL_FORCE_STATIC_EXECUTABLE:=
+LOCAL_ADDITIONAL_DEPENDENCIES:=
+LOCAL_PRELINK_MODULE:=
+LOCAL_COMPRESS_MODULE_SYMBOLS:=
+LOCAL_STRIP_MODULE:=
+LOCAL_POST_PROCESS_COMMAND:=true
+LOCAL_JNI_SHARED_LIBRARIES:=
+LOCAL_JAR_MANIFEST:=
+LOCAL_INSTRUMENTATION_FOR:=
+LOCAL_MANIFEST_INSTRUMENTATION_FOR:=
+LOCAL_AIDL_INCLUDES:=
+LOCAL_JARJAR_RULES:=
+LOCAL_ADDITIONAL_JAVA_DIR:=
+LOCAL_ALLOW_UNDEFINED_SYMBOLS:=
+LOCAL_DX_FLAGS:=
+LOCAL_CERTIFICATE:=
+LOCAL_SDK_VERSION:=
+LOCAL_NDK_VERSION:=
+LOCAL_NO_EMMA_INSTRUMENT:=
+LOCAL_NO_EMMA_COMPILE:=
+LOCAL_PROGUARD_ENABLED:= # '',optonly,full,custom
+LOCAL_PROGUARD_FLAGS:=
+LOCAL_PROGUARD_FLAG_FILES:=
+LOCAL_EMMA_COVERAGE_FILTER:=
+LOCAL_MANIFEST_FILE:=
+LOCAL_BUILD_HOST_DEX:=
+LOCAL_DEX_PREOPT:=
+
+# Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to
+# iterate over thousands of entries every time.
+# Leave the current makefile to make sure we don't break anything
+# that expects to be able to find the name of the current makefile.
+MAKEFILE_LIST := $(lastword $(MAKEFILE_LIST))
diff --git a/src/commandline.c b/src/commandline.c
new file mode 100755
index 0000000..1e4ec1e
--- /dev/null
+++ b/src/commandline.c
@@ -0,0 +1,1676 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <unistd.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "sysdeps.h"
+
+#ifdef HAVE_TERMIO_H
+#include <termios.h>
+#endif
+
+#define TRACE_TAG TRACE_SDB
+#include "sdb.h"
+#include "sdb_client.h"
+#include "file_sync_service.h"
+
+static int do_cmd(transport_type ttype, char* serial, char *cmd, ...);
+
+void get_my_path(char *s, size_t maxLen);
+int find_sync_dirs(const char *srcarg,
+ char **android_srcdir_out, char **data_srcdir_out);
+int install_app(transport_type transport, char* serial, int argc, char** argv);
+int uninstall_app_sdb(const char *app_id);
+int install_app_sdb(const char *srcpath);
+int uninstall_app(transport_type transport, char* serial, int argc, char** argv);
+int sdb_command2(const char* cmd);
+void version_sdbd(transport_type ttype, char* serial);
+
+static const char *gProductOutPath = NULL;
+
+static char *product_file(const char *extra)
+{
+ int n;
+ char *x;
+
+ if (gProductOutPath == NULL) {
+ fprintf(stderr, "sdb: Product directory not specified; "
+ "use -p or define ANDROID_PRODUCT_OUT\n");
+ exit(1);
+ }
+
+ n = strlen(gProductOutPath) + strlen(extra) + 2;
+ x = malloc(n);
+ if (x == 0) {
+ fprintf(stderr, "sdb: Out of memory (product_file())\n");
+ exit(1);
+ }
+
+ snprintf(x, (size_t)n, "%s" OS_PATH_SEPARATOR_STR "%s", gProductOutPath, extra);
+ return x;
+}
+
+void version(FILE * out) {
+ fprintf(out, "Smart Development Bridge version %d.%d.%d\n",
+ SDB_VERSION_MAJOR, SDB_VERSION_MINOR, SDB_SERVER_VERSION);
+}
+
+void help()
+{
+ version(stderr);
+
+ fprintf(stderr,
+ "\n"
+ " Usage : sdb [option] <command> [parameters]\n"
+ "\n"
+ " options:\n"
+ " -d - directs command to the only connected USB device\n"
+ " returns an error if more than one USB device is present.\n"
+ " -e - directs command to the only running emulator.\n"
+ " returns an error if more than one emulator is running.\n"
+ " -s <serial number> - directs command to the USB device or emulator with\n"
+ " the given serial number.\n"
+ " devices - list all connected devices\n"
+ " connect <host>[:<port>] - connect to a device via TCP/IP\n"
+ " Port 26101 is used by default if no port number is specified.\n"
+ " disconnect [<host>[:<port>]] - disconnect from a TCP/IP device.\n"
+ " Port 26101 is used by default if no port number is specified.\n"
+ " Using this command with no additional arguments\n"
+ " will disconnect from all connected TCP/IP devices.\n"
+ "\n"
+ " commands:\n"
+ " sdb push <local> <remote> [--with-utf8]\n"
+ " - copy file/dir to device\n"
+ " (--with-utf8 means to create the remote file with utf-8 character encoding)\n"
+ " sdb pull <remote> [<local>] - copy file/dir from device\n"
+ " sdb shell - run remote shell interactively\n"
+ " sdb shell <command> - run remote shell \n"
+ " sdb dlog [ <filter-spec> ] - view device log\n"
+ " sdb install <path_to_tpk> - push tpk package file and install it\n"
+ " sdb uninstall <appid> - uninstall this app from the device\n"
+ " sdb forward <local> <remote> - forward socket connections\n"
+
+ " forward spec is : \n"
+ " tcp:<port>\n"
+ " sdb help - show this help message\n"
+ " sdb version - show version num\n"
+ "\n"
+ " sdb start-server - ensure that there is a server running\n"
+ " sdb kill-server - kill the server if it is running\n"
+ " sdb get-state - prints: offline | bootloader | device\n"
+ " sdb get-serialno - prints: <serial-number>\n"
+ " sdb status-window - continuously print device status for a specified device\n"
+// " sdb usb - restarts the sdbd daemon listing on USB\n"
+// " sdb tcpip - restarts the sdbd daemon listing on TCP"
+ "\n"
+ );
+}
+
+int usage()
+{
+ help();
+ return 1;
+}
+
+#ifdef HAVE_TERMIO_H
+static struct termios tio_save;
+
+static void stdin_raw_init(int fd)
+{
+ struct termios tio;
+
+ if(tcgetattr(fd, &tio)) return;
+ if(tcgetattr(fd, &tio_save)) return;
+
+ tio.c_lflag = 0; /* disable CANON, ECHO*, etc */
+
+ /* no timeout but request at least one character per read */
+ tio.c_cc[VTIME] = 0;
+ tio.c_cc[VMIN] = 1;
+
+ tcsetattr(fd, TCSANOW, &tio);
+ tcflush(fd, TCIFLUSH);
+}
+
+static void stdin_raw_restore(int fd)
+{
+ tcsetattr(fd, TCSANOW, &tio_save);
+ tcflush(fd, TCIFLUSH);
+}
+#endif
+
+static void read_and_dump(int fd)
+{
+ char buf[4096];
+ int len;
+
+ while(fd >= 0) {
+ D("read_and_dump(): pre sdb_read(fd=%d)\n", fd);
+ len = sdb_read(fd, buf, 4096);
+ D("read_and_dump(): post sdb_read(fd=%d): len=%d\n", fd, len);
+ if(len == 0) {
+ break;
+ }
+
+ if(len < 0) {
+ if(errno == EINTR) continue;
+ break;
+ }
+ fwrite(buf, 1, len, stdout);
+ fflush(stdout);
+ }
+}
+
+static void copy_to_file(int inFd, int outFd) {
+ const size_t BUFSIZE = 32 * 1024;
+ char* buf = (char*) malloc(BUFSIZE);
+ int len;
+ long total = 0;
+
+ D("copy_to_file(%d -> %d)\n", inFd, outFd);
+ for (;;) {
+ len = sdb_read(inFd, buf, BUFSIZE);
+ if (len == 0) {
+ D("copy_to_file() : read 0 bytes; exiting\n");
+ break;
+ }
+ if (len < 0) {
+ if (errno == EINTR) {
+ D("copy_to_file() : EINTR, retrying\n");
+ continue;
+ }
+ D("copy_to_file() : error %d\n", errno);
+ break;
+ }
+ sdb_write(outFd, buf, len);
+ total += len;
+ }
+ D("copy_to_file() finished after %lu bytes\n", total);
+ free(buf);
+}
+
+static void *stdin_read_thread(void *x)
+{
+ int fd, fdi;
+ unsigned char buf[1024];
+ int r, n;
+ int state = 0;
+
+ int *fds = (int*) x;
+ fd = fds[0];
+ fdi = fds[1];
+ free(fds);
+
+ for(;;) {
+ /* fdi is really the client's stdin, so use read, not sdb_read here */
+ D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi);
+ r = unix_read(fdi, buf, 1024);
+ D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi);
+ if(r == 0) break;
+ if(r < 0) {
+ if(errno == EINTR) continue;
+ break;
+ }
+ for(n = 0; n < r; n++){
+ switch(buf[n]) {
+ case '\n':
+ state = 1;
+ break;
+ case '\r':
+ state = 1;
+ break;
+ case '~':
+ if(state == 1) state++;
+ break;
+ case '.':
+ if(state == 2) {
+ fprintf(stderr,"\n* disconnect *\n");
+#ifdef HAVE_TERMIO_H
+ stdin_raw_restore(fdi);
+#endif
+ exit(0);
+ }
+ default:
+ state = 0;
+ }
+ }
+ r = sdb_write(fd, buf, r);
+ if(r <= 0) {
+ break;
+ }
+ }
+ return 0;
+}
+
+int interactive_shell(void)
+{
+ sdb_thread_t thr;
+ int fdi, fd;
+ int *fds;
+
+ fd = sdb_connect("shell:");
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+ fdi = 0; //dup(0);
+
+ fds = malloc(sizeof(int) * 2);
+ fds[0] = fd;
+ fds[1] = fdi;
+
+#ifdef HAVE_TERMIO_H
+ stdin_raw_init(fdi);
+#endif
+ sdb_thread_create(&thr, stdin_read_thread, fds);
+ read_and_dump(fd);
+#ifdef HAVE_TERMIO_H
+ stdin_raw_restore(fdi);
+#endif
+ return 0;
+}
+
+
+static void format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial)
+{
+ if (serial) {
+ snprintf(buffer, buflen, "host-serial:%s:%s", serial, command);
+ } else {
+ const char* prefix = "host";
+ if (ttype == kTransportUsb)
+ prefix = "host-usb";
+ else if (ttype == kTransportLocal)
+ prefix = "host-local";
+
+ snprintf(buffer, buflen, "%s:%s", prefix, command);
+ }
+}
+#if 0 /* tizen specific */
+int sdb_download_buffer(const char *service, const void* data, int sz,
+ unsigned progress)
+{
+ char buf[4096];
+ unsigned total;
+ int fd;
+ const unsigned char *ptr;
+
+ sprintf(buf,"%s:%d", service, sz);
+ fd = sdb_connect(buf);
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return -1;
+ }
+
+ int opt = CHUNK_SIZE;
+ opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+ total = sz;
+ ptr = data;
+
+ if(progress) {
+ char *x = strrchr(service, ':');
+ if(x) service = x + 1;
+ }
+
+ while(sz > 0) {
+ unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
+ if(writex(fd, ptr, xfer)) {
+ sdb_status(fd);
+ fprintf(stderr,"* failed to write data '%s' *\n", sdb_error());
+ return -1;
+ }
+ sz -= xfer;
+ ptr += xfer;
+ if(progress) {
+ printf("sending: '%s' %4d%% \r", service, (int)(100LL - ((100LL * sz) / (total))));
+ fflush(stdout);
+ }
+ }
+ if(progress) {
+ printf("\n");
+ }
+
+ if(readx(fd, buf, 4)){
+ fprintf(stderr,"* error reading response *\n");
+ sdb_close(fd);
+ return -1;
+ }
+ if(memcmp(buf, "OKAY", 4)) {
+ buf[4] = 0;
+ fprintf(stderr,"* error response '%s' *\n", buf);
+ sdb_close(fd);
+ return -1;
+ }
+
+ sdb_close(fd);
+ return 0;
+}
+
+int sdb_download(const char *service, const char *fn, unsigned progress)
+{
+ void *data;
+ unsigned sz;
+
+ data = load_file(fn, &sz);
+ if(data == 0) {
+ fprintf(stderr,"* cannot read '%s' *\n", service);
+ return -1;
+ }
+
+ int status = sdb_download_buffer(service, data, sz, progress);
+ free(data);
+ return status;
+}
+#endif
+static void status_window(transport_type ttype, const char* serial)
+{
+ char command[4096];
+ char *state = 0;
+ char *laststate = 0;
+
+ /* silence stderr */
+#ifdef _WIN32
+ /* XXX: TODO */
+#else
+ int fd;
+ fd = unix_open("/dev/null", O_WRONLY);
+ dup2(fd, 2);
+ sdb_close(fd);
+#endif
+
+ format_host_command(command, sizeof command, "get-state", ttype, serial);
+
+ for(;;) {
+ sdb_sleep_ms(250);
+
+ if(state) {
+ free(state);
+ state = 0;
+ }
+
+ state = sdb_query(command);
+
+ if(state) {
+ if(laststate && !strcmp(state,laststate)){
+ continue;
+ } else {
+ if(laststate) free(laststate);
+ laststate = strdup(state);
+ }
+ }
+
+ printf("%c[2J%c[2H", 27, 27);
+ printf("Samsung Development Bridge\n");
+ printf("State: %s\n", state ? state : "offline");
+ fflush(stdout);
+ }
+}
+
+/** duplicate string and quote all \ " ( ) chars + space character. */
+static char *
+dupAndQuote(const char *s)
+{
+ const char *ts;
+ size_t alloc_len;
+ char *ret;
+ char *dest;
+
+ ts = s;
+
+ alloc_len = 0;
+
+ for( ;*ts != '\0'; ts++) {
+ alloc_len++;
+ if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
+ alloc_len++;
+ }
+ }
+
+ ret = (char *)malloc(alloc_len + 1);
+
+ ts = s;
+ dest = ret;
+
+ for ( ;*ts != '\0'; ts++) {
+ if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') {
+ *dest++ = '\\';
+ }
+
+ *dest++ = *ts;
+ }
+
+ *dest++ = '\0';
+
+ return ret;
+}
+
+/**
+ * Run ppp in "notty" mode against a resource listed as the first parameter
+ * eg:
+ *
+ * ppp dev:/dev/omap_csmi_tty0 <ppp options>
+ *
+ */
+int ppp(int argc, char **argv)
+{
+#ifdef HAVE_WIN32_PROC
+ fprintf(stderr, "error: sdb %s not implemented on Win32\n", argv[0]);
+ return -1;
+#else
+ char *sdb_service_name;
+ pid_t pid;
+ int fd;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: sdb %s <sdb service name> [ppp opts]\n",
+ argv[0]);
+
+ return 1;
+ }
+
+ sdb_service_name = argv[1];
+
+ fd = sdb_connect(sdb_service_name);
+
+ if(fd < 0) {
+ fprintf(stderr,"Error: Could not open sdb service: %s. Error: %s\n",
+ sdb_service_name, sdb_error());
+ return 1;
+ }
+
+ pid = fork();
+
+ if (pid < 0) {
+ perror("from fork()");
+ return 1;
+ } else if (pid == 0) {
+ int err;
+ int i;
+ const char **ppp_args;
+
+ // copy args
+ ppp_args = (const char **) alloca(sizeof(char *) * argc + 1);
+ ppp_args[0] = "pppd";
+ for (i = 2 ; i < argc ; i++) {
+ //argv[2] and beyond become ppp_args[1] and beyond
+ ppp_args[i - 1] = argv[i];
+ }
+ ppp_args[i-1] = NULL;
+
+ // child side
+
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ sdb_close(STDERR_FILENO);
+ sdb_close(fd);
+
+ err = execvp("pppd", (char * const *)ppp_args);
+
+ if (err < 0) {
+ perror("execing pppd");
+ }
+ exit(-1);
+ } else {
+ // parent side
+
+ sdb_close(fd);
+ return 0;
+ }
+#endif /* !HAVE_WIN32_PROC */
+}
+
+static int send_shellcommand(transport_type transport, char* serial, char* buf)
+{
+ int fd, ret;
+
+ for(;;) {
+ fd = sdb_connect(buf);
+ if(fd >= 0)
+ break;
+ fprintf(stderr,"- waiting for device -\n");
+ sdb_sleep_ms(1000);
+ do_cmd(transport, serial, "wait-for-device", 0);
+ }
+
+ read_and_dump(fd);
+ ret = sdb_close(fd);
+ if (ret)
+ perror("close");
+
+ return ret;
+}
+
+static int logcat(transport_type transport, char* serial, int argc, char **argv)
+{
+ char buf[4096];
+
+ snprintf(buf, sizeof(buf),
+ "shell:dlogutil");
+
+/*
+ if (!strcmp(argv[0],"longcat")) {
+ strncat(buf, " -v long", sizeof(buf)-1);
+ }
+*/
+
+ argc -= 1;
+ argv += 1;
+ while(argc-- > 0) {
+ char *quoted;
+
+ quoted = dupAndQuote (*argv++);
+
+ strncat(buf, " ", sizeof(buf)-1);
+ strncat(buf, quoted, sizeof(buf)-1);
+ free(quoted);
+ }
+
+ send_shellcommand(transport, serial, buf);
+ return 0;
+}
+
+static int mkdirs(char *path)
+{
+ int ret;
+ char *x = path + 1;
+
+ for(;;) {
+ x = sdb_dirstart(x);
+ if(x == 0) return 0;
+ *x = 0;
+ ret = sdb_mkdir(path, 0775);
+ *x = OS_PATH_SEPARATOR;
+ if((ret < 0) && (errno != EEXIST)) {
+ return ret;
+ }
+ x++;
+ }
+ return 0;
+}
+
+static int backup(int argc, char** argv) {
+ char buf[4096];
+ char default_name[32];
+ const char* filename = strcpy(default_name, "./backup.ab");
+ int fd, outFd;
+ int i, j;
+
+ /* find, extract, and use any -f argument */
+ for (i = 1; i < argc; i++) {
+ if (!strcmp("-f", argv[i])) {
+ if (i == argc-1) {
+ fprintf(stderr, "sdb: -f passed with no filename\n");
+ return usage();
+ }
+ filename = argv[i+1];
+ for (j = i+2; j <= argc; ) {
+ argv[i++] = argv[j++];
+ }
+ argc -= 2;
+ argv[argc] = NULL;
+ }
+ }
+
+ /* bare "sdb backup" or "sdb backup -f filename" are not valid invocations */
+ if (argc < 2) return usage();
+
+ sdb_unlink(filename);
+ mkdirs((char *)filename);
+ outFd = sdb_creat(filename, 0640);
+ if (outFd < 0) {
+ fprintf(stderr, "sdb: unable to open file %s\n", filename);
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "backup");
+ for (argc--, argv++; argc; argc--, argv++) {
+ strncat(buf, ":", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, argv[0], sizeof(buf) - strlen(buf) - 1);
+ }
+
+ D("backup. filename=%s buf=%s\n", filename, buf);
+ fd = sdb_connect(buf);
+ if (fd < 0) {
+ fprintf(stderr, "sdb: unable to connect for backup\n");
+ sdb_close(outFd);
+ return -1;
+ }
+
+ printf("Now unlock your device and confirm the backup operation.\n");
+ copy_to_file(fd, outFd);
+
+ sdb_close(fd);
+ sdb_close(outFd);
+ return 0;
+}
+
+static int restore(int argc, char** argv) {
+ const char* filename;
+ int fd, tarFd;
+
+ if (argc != 2) return usage();
+
+ filename = argv[1];
+ tarFd = sdb_open(filename, O_RDONLY);
+ if (tarFd < 0) {
+ fprintf(stderr, "sdb: unable to open file %s\n", filename);
+ return -1;
+ }
+
+ fd = sdb_connect("restore:");
+ if (fd < 0) {
+ fprintf(stderr, "sdb: unable to connect for backup\n");
+ sdb_close(tarFd);
+ return -1;
+ }
+
+ printf("Now unlock your device and confirm the restore operation.\n");
+ copy_to_file(tarFd, fd);
+
+ sdb_close(fd);
+ sdb_close(tarFd);
+ return 0;
+}
+
+#define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make"
+static int top_works(const char *top)
+{
+ if (top != NULL && sdb_is_absolute_host_path(top)) {
+ char path_buf[PATH_MAX];
+ snprintf(path_buf, sizeof(path_buf),
+ "%s" OS_PATH_SEPARATOR_STR SENTINEL_FILE, top);
+ return access(path_buf, F_OK) == 0;
+ }
+ return 0;
+}
+
+static char *find_top_from(const char *indir, char path_buf[PATH_MAX])
+{
+ strcpy(path_buf, indir);
+ while (1) {
+ if (top_works(path_buf)) {
+ return path_buf;
+ }
+ char *s = sdb_dirstop(path_buf);
+ if (s != NULL) {
+ *s = '\0';
+ } else {
+ path_buf[0] = '\0';
+ return NULL;
+ }
+ }
+}
+
+static char *find_top(char path_buf[PATH_MAX])
+{
+ char *top = getenv("ANDROID_BUILD_TOP");
+ if (top != NULL && top[0] != '\0') {
+ if (!top_works(top)) {
+ fprintf(stderr, "sdb: bad ANDROID_BUILD_TOP value \"%s\"\n", top);
+ return NULL;
+ }
+ } else {
+ top = getenv("TOP");
+ if (top != NULL && top[0] != '\0') {
+ if (!top_works(top)) {
+ fprintf(stderr, "sdb: bad TOP value \"%s\"\n", top);
+ return NULL;
+ }
+ } else {
+ top = NULL;
+ }
+ }
+
+ if (top != NULL) {
+ /* The environment pointed to a top directory that works.
+ */
+ strcpy(path_buf, top);
+ return path_buf;
+ }
+
+ /* The environment didn't help. Walk up the tree from the CWD
+ * to see if we can find the top.
+ */
+ char dir[PATH_MAX];
+ top = find_top_from(getcwd(dir, sizeof(dir)), path_buf);
+ if (top == NULL) {
+ /* If the CWD isn't under a good-looking top, see if the
+ * executable is.
+ */
+ get_my_path(dir, PATH_MAX);
+ top = find_top_from(dir, path_buf);
+ }
+ return top;
+}
+
+/* <hint> may be:
+ * - A simple product name
+ * e.g., "sooner"
+TODO: debug? sooner-debug, sooner:debug?
+ * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir
+ * e.g., "out/target/product/sooner"
+ * - An absolute path to the PRODUCT_OUT dir
+ * e.g., "/src/device/out/target/product/sooner"
+ *
+ * Given <hint>, try to construct an absolute path to the
+ * ANDROID_PRODUCT_OUT dir.
+ */
+static const char *find_product_out_path(const char *hint)
+{
+ static char path_buf[PATH_MAX];
+
+ if (hint == NULL || hint[0] == '\0') {
+ return NULL;
+ }
+
+ /* If it's already absolute, don't bother doing any work.
+ */
+ if (sdb_is_absolute_host_path(hint)) {
+ strcpy(path_buf, hint);
+ return path_buf;
+ }
+
+ /* If there are any slashes in it, assume it's a relative path;
+ * make it absolute.
+ */
+ if (sdb_dirstart(hint) != NULL) {
+ if (getcwd(path_buf, sizeof(path_buf)) == NULL) {
+ fprintf(stderr, "sdb: Couldn't get CWD: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) {
+ fprintf(stderr, "sdb: Couldn't assemble path\n");
+ return NULL;
+ }
+ strcat(path_buf, OS_PATH_SEPARATOR_STR);
+ strcat(path_buf, hint);
+ return path_buf;
+ }
+
+ /* It's a string without any slashes. Try to do something with it.
+ *
+ * Try to find the root of the build tree, and build a PRODUCT_OUT
+ * path from there.
+ */
+ char top_buf[PATH_MAX];
+ const char *top = find_top(top_buf);
+ if (top == NULL) {
+ fprintf(stderr, "sdb: Couldn't find top of build tree\n");
+ return NULL;
+ }
+//TODO: if we have a way to indicate debug, look in out/debug/target/...
+ snprintf(path_buf, sizeof(path_buf),
+ "%s" OS_PATH_SEPARATOR_STR
+ "out" OS_PATH_SEPARATOR_STR
+ "target" OS_PATH_SEPARATOR_STR
+ "product" OS_PATH_SEPARATOR_STR
+ "%s", top_buf, hint);
+ if (access(path_buf, F_OK) < 0) {
+ fprintf(stderr, "sdb: Couldn't find a product dir "
+ "based on \"-p %s\"; \"%s\" doesn't exist\n", hint, path_buf);
+ return NULL;
+ }
+ return path_buf;
+}
+
+int sdb_commandline(int argc, char **argv)
+{
+ char buf[4096];
+ int no_daemon = 0;
+ int is_daemon = 0;
+ int is_server = 0;
+ int persist = 0;
+ int r;
+ int quote;
+ transport_type ttype = kTransportAny;
+ char* serial = NULL;
+ char* server_port_str = NULL;
+
+ /* If defined, this should be an absolute path to
+ * the directory containing all of the various system images
+ * for a particular product. If not defined, and the sdb
+ * command requires this information, then the user must
+ * specify the path using "-p".
+ */
+ gProductOutPath = getenv("ANDROID_PRODUCT_OUT");
+ if (gProductOutPath == NULL || gProductOutPath[0] == '\0') {
+ gProductOutPath = NULL;
+ }
+ // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
+
+ serial = getenv("ANDROID_SERIAL");
+
+ /* Validate and assign the server port */
+ server_port_str = getenv("ANDROID_SDB_SERVER_PORT");
+ int server_port = DEFAULT_SDB_PORT;
+ if (server_port_str && strlen(server_port_str) > 0) {
+ server_port = (int) strtol(server_port_str, NULL, 0);
+ if (server_port <= 0) {
+ fprintf(stderr,
+ "sdb: Env var ANDROID_SDB_SERVER_PORT must be a positive number. Got \"%s\"\n",
+ server_port_str);
+ return usage();
+ }
+ }
+
+ /* modifiers and flags */
+ while(argc > 0) {
+ if(!strcmp(argv[0],"server")) {
+ is_server = 1;
+ } else if(!strcmp(argv[0],"nodaemon")) {
+ no_daemon = 1;
+ } else if (!strcmp(argv[0], "fork-server")) {
+ /* this is a special flag used only when the SDB client launches the SDB Server */
+ is_daemon = 1;
+ } else if(!strcmp(argv[0],"persist")) {
+ persist = 1;
+ } else if(!strncmp(argv[0], "-p", 2)) {
+ const char *product = NULL;
+ if (argv[0][2] == '\0') {
+ if (argc < 2) return usage();
+ product = argv[1];
+ argc--;
+ argv++;
+ } else {
+ product = argv[1] + 2;
+ }
+ gProductOutPath = find_product_out_path(product);
+ if (gProductOutPath == NULL) {
+ fprintf(stderr, "sdb: could not resolve \"-p %s\"\n",
+ product);
+ return usage();
+ }
+ } else if (argv[0][0]=='-' && argv[0][1]=='s') {
+ if (isdigit(argv[0][2])) {
+ serial = argv[0] + 2;
+ } else {
+ if(argc < 2 || argv[0][2] != '\0') return usage();
+ serial = argv[1];
+ argc--;
+ argv++;
+ }
+ } else if (!strcmp(argv[0],"-d")) {
+ ttype = kTransportUsb;
+ } else if (!strcmp(argv[0],"-e")) {
+ ttype = kTransportLocal;
+ } else {
+ /* out of recognized modifiers and flags */
+ break;
+ }
+ argc--;
+ argv++;
+ }
+
+
+// sdb_set_transport(ttype, serial);
+// sdb_set_tcp_specifics(server_port);
+
+ if (is_server) {
+ if (no_daemon || is_daemon) {
+ r = sdb_main(is_daemon, server_port);
+ } else {
+ r = launch_server(server_port);
+ }
+ if(r) {
+ fprintf(stderr,"* could not start server *\n");
+ }
+ return r;
+ }
+
+top:
+ if(argc == 0) {
+ return usage();
+ }
+
+ // first, get the uniq device from the partial serial with prefix matching
+ if (serial) {
+ char *tmp;
+ snprintf(buf, sizeof(buf), "host:serial-match:%s", serial);
+ tmp = sdb_query(buf);
+ if (tmp) {
+ //printf("connect to device: %s\n", tmp);
+ serial = strdup(tmp);
+ sdb_set_transport(ttype, serial);
+ sdb_set_tcp_specifics(server_port);
+ } else {
+ return 1;
+ }
+ } else {
+ sdb_set_transport(ttype, serial);
+ sdb_set_tcp_specifics(server_port);
+ }
+ /* sdb_connect() commands */
+
+ if(!strcmp(argv[0], "devices")) {
+ char *tmp;
+ snprintf(buf, sizeof buf, "host:%s", argv[0]);
+ tmp = sdb_query(buf);
+ if(tmp) {
+ printf("List of devices attached \n");
+ printf("%s", tmp);
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if(!strcmp(argv[0], "connect")) {
+ char *tmp;
+ if (argc != 2) {
+ fprintf(stderr, "Usage: sdb connect <host>[:<port>]\n");
+ return 1;
+ }
+ snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
+ tmp = sdb_query(buf);
+ if(tmp) {
+ printf("%s\n", tmp);
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if(!strcmp(argv[0], "disconnect")) {
+ char *tmp;
+ if (argc > 2) {
+ fprintf(stderr, "Usage: sdb disconnect [<host>[:<port>]]\n");
+ return 1;
+ }
+ if (argc == 2) {
+ snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]);
+ } else {
+ snprintf(buf, sizeof buf, "host:disconnect:");
+ }
+ tmp = sdb_query(buf);
+ if(tmp) {
+ printf("%s\n", tmp);
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ if (!strcmp(argv[0], "emu")) {
+ return sdb_send_emulator_command(argc, argv);
+ }
+
+ if(!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
+ int r;
+ int fd;
+
+ char h = (argv[0][0] == 'h');
+
+ if (h) {
+ printf("\x1b[41;33m");
+ fflush(stdout);
+ }
+
+ if(argc < 2) {
+ D("starting interactive shell\n");
+ r = interactive_shell();
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
+ }
+ return r;
+ }
+
+ snprintf(buf, sizeof buf, "shell:%s", argv[1]);
+ argc -= 2;
+ argv += 2;
+ while(argc-- > 0) {
+ strcat(buf, " ");
+
+ /* quote empty strings and strings with spaces */
+ quote = (**argv == 0 || strchr(*argv, ' '));
+ if (quote)
+ strcat(buf, "\"");
+ strcat(buf, *argv++);
+ if (quote)
+ strcat(buf, "\"");
+ }
+
+ for(;;) {
+ D("interactive shell loop. buff=%s\n", buf);
+ fd = sdb_connect(buf);
+ if(fd >= 0) {
+ D("about to read_and_dump(fd=%d)\n", fd);
+ read_and_dump(fd);
+ D("read_and_dump() done.\n");
+ sdb_close(fd);
+ r = 0;
+ } else {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ r = -1;
+ }
+
+ if(persist) {
+ fprintf(stderr,"\n- waiting for device -\n");
+ sdb_sleep_ms(1000);
+ do_cmd(ttype, serial, "wait-for-device", 0);
+ } else {
+ if (h) {
+ printf("\x1b[0m");
+ fflush(stdout);
+ }
+ D("interactive shell loop. return r=%d\n", r);
+ return r;
+ }
+ }
+ }
+
+ if(!strcmp(argv[0], "kill-server")) {
+ int fd;
+ fd = _sdb_connect("host:kill");
+ if(fd == -1) {
+ fprintf(stderr,"* server not running *\n");
+ return 1;
+ }
+ return 0;
+ }
+#if 0 /* tizen specific */
+ if(!strcmp(argv[0], "sideload")) {
+ if(argc != 2) return usage();
+ if(sdb_download("sideload", argv[1], 1)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+#endif
+ if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot")
+ || !strcmp(argv[0], "reboot-bootloader")
+ || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb")
+ || !strcmp(argv[0], "root")) {
+ char command[100];
+ if (!strcmp(argv[0], "reboot-bootloader"))
+ snprintf(command, sizeof(command), "reboot:bootloader");
+ else if (argc > 1)
+ snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]);
+ else
+ snprintf(command, sizeof(command), "%s:", argv[0]);
+ int fd = sdb_connect(command);
+ if(fd >= 0) {
+ read_and_dump(fd);
+ sdb_close(fd);
+ return 0;
+ }
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+
+ if(!strcmp(argv[0], "bugreport")) {
+ if (argc != 1) return usage();
+ do_cmd(ttype, serial, "shell", "bugreport", 0);
+ return 0;
+ }
+
+ /* sdb_command() wrapper commands */
+
+ if(!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
+ char* service = argv[0];
+ if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) {
+ if (ttype == kTransportUsb) {
+ service = "wait-for-usb";
+ } else if (ttype == kTransportLocal) {
+ service = "wait-for-local";
+ } else {
+ service = "wait-for-any";
+ }
+ }
+
+ format_host_command(buf, sizeof buf, service, ttype, serial);
+
+ if (sdb_command(buf)) {
+ D("failure: %s *\n",sdb_error());
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+
+ /* Allow a command to be run after wait-for-device,
+ * e.g. 'sdb wait-for-device shell'.
+ */
+ if(argc > 1) {
+ argc--;
+ argv++;
+ goto top;
+ }
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "forward")) {
+ if(argc != 3) return usage();
+ if (serial) {
+ snprintf(buf, sizeof buf, "host-serial:%s:forward:%s;%s",serial, argv[1], argv[2]);
+ } else if (ttype == kTransportUsb) {
+ snprintf(buf, sizeof buf, "host-usb:forward:%s;%s", argv[1], argv[2]);
+ } else if (ttype == kTransportLocal) {
+ snprintf(buf, sizeof buf, "host-local:forward:%s;%s", argv[1], argv[2]);
+ } else {
+ snprintf(buf, sizeof buf, "host:forward:%s;%s", argv[1], argv[2]);
+ }
+ if(sdb_command(buf)) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+ return 0;
+ }
+
+ /* do_sync_*() commands */
+
+ if(!strcmp(argv[0], "ls")) {
+ if(argc != 2) return usage();
+ return do_sync_ls(argv[1]);
+ }
+
+ if(!strcmp(argv[0], "push")) {
+ int i=0;
+ int utf8=0;
+
+ if(argc < 3) return usage();
+ if (argv[argc-1][0] == '-') {
+ if (!strcmp(argv[argc-1], "--with-utf8")) {
+ utf8 = 1;
+ argc = argc - 1;
+ } else {
+ return usage();
+ }
+ }
+ for (i=1; i<argc-1; i++) {
+ do_sync_push(argv[i], argv[argc-1], 0 /* no verify APK */, utf8);
+ }
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "install")) {
+ if(argc != 2) return usage();
+
+ return install_app_sdb(argv[1]);
+ }
+
+ if(!strcmp(argv[0], "uninstall")) {
+ if(argc != 2) return usage();
+
+ return uninstall_app_sdb(argv[1]);
+ }
+
+ if(!strcmp(argv[0], "pull")) {
+ if (argc == 2) {
+ return do_sync_pull(argv[1], ".");
+ } else if (argc == 3) {
+ return do_sync_pull(argv[1], argv[2]);
+ } else {
+ return usage();
+ }
+ }
+
+ if(!strcmp(argv[0], "install")) {
+ if (argc < 2) return usage();
+ return install_app(ttype, serial, argc, argv);
+ }
+
+ if(!strcmp(argv[0], "uninstall")) {
+ if (argc < 2) return usage();
+ return uninstall_app(ttype, serial, argc, argv);
+ }
+
+ if(!strcmp(argv[0], "sync")) {
+ char *srcarg, *android_srcpath, *data_srcpath;
+ int listonly = 0;
+
+ int ret;
+ if(argc < 2) {
+ /* No local path was specified. */
+ srcarg = NULL;
+ } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) {
+ listonly = 1;
+ if (argc == 3) {
+ srcarg = argv[2];
+ } else {
+ srcarg = NULL;
+ }
+ } else if(argc == 2) {
+ /* A local path or "android"/"data" arg was specified. */
+ srcarg = argv[1];
+ } else {
+ return usage();
+ }
+ ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath);
+ if(ret != 0) return usage();
+
+ if(android_srcpath != NULL)
+ ret = do_sync_sync(android_srcpath, "/system", listonly);
+ if(ret == 0 && data_srcpath != NULL)
+ ret = do_sync_sync(data_srcpath, "/data", listonly);
+
+ free(android_srcpath);
+ free(data_srcpath);
+ return ret;
+ }
+
+ /* passthrough commands */
+
+ if(!strcmp(argv[0],"get-state") ||
+ !strcmp(argv[0],"get-serialno"))
+ {
+ char *tmp;
+
+ format_host_command(buf, sizeof buf, argv[0], ttype, serial);
+ tmp = sdb_query(buf);
+ if(tmp) {
+ printf("%s\n", tmp);
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ /* other commands */
+
+ if(!strcmp(argv[0],"status-window")) {
+ status_window(ttype, serial);
+ return 0;
+ }
+
+ if(!strcmp(argv[0],"dlog")) {
+ return logcat(ttype, serial, argc, argv);
+ }
+
+ if(!strcmp(argv[0],"ppp")) {
+ return ppp(argc, argv);
+ }
+
+ if (!strcmp(argv[0], "start-server")) {
+ return sdb_connect("host:start-server");
+ }
+
+ if (!strcmp(argv[0], "backup")) {
+ return backup(argc, argv);
+ }
+
+ if (!strcmp(argv[0], "restore")) {
+ return restore(argc, argv);
+ }
+
+ if (!strcmp(argv[0], "jdwp")) {
+ int fd = sdb_connect("jdwp");
+ if (fd >= 0) {
+ read_and_dump(fd);
+ sdb_close(fd);
+ return 0;
+ } else {
+ fprintf(stderr, "error: %s\n", sdb_error());
+ return -1;
+ }
+ }
+
+ /* "sdb /?" is a common idiom under Windows */
+ if(!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+ help();
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "version")) {
+ if (ttype == kTransportUsb || ttype == kTransportLocal) {
+ version_sdbd(ttype, serial);
+ } else {
+ version(stdout);
+ }
+ return 0;
+ }
+
+ usage();
+ return 1;
+}
+
+static int do_cmd(transport_type ttype, char* serial, char *cmd, ...)
+{
+ char *argv[16];
+ int argc;
+ va_list ap;
+
+ va_start(ap, cmd);
+ argc = 0;
+
+ if (serial) {
+ argv[argc++] = "-s";
+ argv[argc++] = serial;
+ } else if (ttype == kTransportUsb) {
+ argv[argc++] = "-d";
+ } else if (ttype == kTransportLocal) {
+ argv[argc++] = "-e";
+ }
+
+ argv[argc++] = cmd;
+ while((argv[argc] = va_arg(ap, char*)) != 0) {
+ argc++;
+ }
+ va_end(ap);
+
+#if 0
+ int n;
+ fprintf(stderr,"argc = %d\n",argc);
+ for(n = 0; n < argc; n++) {
+ fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]);
+ }
+#endif
+
+ return sdb_commandline(argc, argv);
+}
+
+int find_sync_dirs(const char *srcarg,
+ char **android_srcdir_out, char **data_srcdir_out)
+{
+ char *android_srcdir, *data_srcdir;
+
+ if(srcarg == NULL) {
+ android_srcdir = product_file("system");
+ data_srcdir = product_file("data");
+ } else {
+ /* srcarg may be "data", "system" or NULL.
+ * if srcarg is NULL, then both data and system are synced
+ */
+ if(strcmp(srcarg, "system") == 0) {
+ android_srcdir = product_file("system");
+ data_srcdir = NULL;
+ } else if(strcmp(srcarg, "data") == 0) {
+ android_srcdir = NULL;
+ data_srcdir = product_file("data");
+ } else {
+ /* It's not "system" or "data".
+ */
+ return 1;
+ }
+ }
+
+ if(android_srcdir_out != NULL)
+ *android_srcdir_out = android_srcdir;
+ else
+ free(android_srcdir);
+
+ if(data_srcdir_out != NULL)
+ *data_srcdir_out = data_srcdir;
+ else
+ free(data_srcdir);
+
+ return 0;
+}
+
+static int pm_command(transport_type transport, char* serial,
+ int argc, char** argv)
+{
+ char buf[4096];
+
+ snprintf(buf, sizeof(buf), "shell:pm");
+
+ while(argc-- > 0) {
+ char *quoted;
+
+ quoted = dupAndQuote(*argv++);
+
+ strncat(buf, " ", sizeof(buf)-1);
+ strncat(buf, quoted, sizeof(buf)-1);
+ free(quoted);
+ }
+
+ send_shellcommand(transport, serial, buf);
+ return 0;
+}
+
+int sdb_command2(const char* cmd) {
+ int result = sdb_connect(cmd);
+
+ if(result < 0) {
+ return result;
+ }
+
+ D("about to read_and_dump(fd=%d)\n", result);
+ read_and_dump(result);
+ D("read_and_dump() done.\n");
+ sdb_close(result);
+
+ return 0;
+}
+
+int install_app_sdb(const char *srcpath) {
+ D("Install start\n");
+ const char * APP_DEST = "/opt/apps/PKGS/%s";
+ const char* filename = sdb_dirstop(srcpath);
+ char destination[PATH_MAX];
+
+ if (filename) {
+ filename++;
+ snprintf(destination, sizeof destination, APP_DEST, filename);
+ } else {
+ snprintf(destination, sizeof destination, APP_DEST, srcpath);
+ }
+
+ D("Push file: %s to %s\n", srcpath, destination);
+ int result = do_sync_push(srcpath, destination, 0, 0);
+
+ if(result < 0) {
+ fprintf(stderr, "error: %s\n", sdb_error());
+ return -1;
+ }
+
+ const char* SHELL_INSTALL_CMD ="shell:pkgcmd -i -t tpk -p %s -q";
+ char full_cmd[PATH_MAX];
+ snprintf(full_cmd, sizeof full_cmd, SHELL_INSTALL_CMD, destination);
+ D("Install command: %s\n", full_cmd);
+ result = sdb_command2(full_cmd);
+
+ if(result < 0) {
+ fprintf(stderr, "error: %s\n", sdb_error());
+ return result;
+ }
+
+ const char* SHELL_REMOVE_CMD = "shell:rm %s";
+ snprintf(full_cmd, sizeof full_cmd, SHELL_REMOVE_CMD, destination);
+ D("Remove file command: %s\n", full_cmd);
+ result = sdb_command2(full_cmd);
+
+ if(result < 0) {
+ fprintf(stderr, "error: %s\n", sdb_error());
+ return result;
+ }
+
+ return 0;
+}
+
+int uninstall_app_sdb(const char *appid) {
+ const char* SHELL_UNINSTALL_CMD ="shell:pkgcmd -u -t tpk -n %s -q";
+ char full_cmd[PATH_MAX];
+ int result = 0;
+ snprintf(full_cmd, sizeof full_cmd, SHELL_UNINSTALL_CMD, appid);
+ result = sdb_command2(full_cmd);
+
+ if(result < 0) {
+ fprintf(stderr, "error: %s\n", sdb_error());
+ return result;
+ }
+
+ return 0;
+}
+
+int uninstall_app(transport_type transport, char* serial, int argc, char** argv)
+{
+ /* if the user choose the -k option, we refuse to do it until devices are
+ out with the option to uninstall the remaining data somehow (sdb/ui) */
+ if (argc == 3 && strcmp(argv[1], "-k") == 0)
+ {
+ printf(
+ "The -k option uninstalls the application while retaining the data/cache.\n"
+ "At the moment, there is no way to remove the remaining data.\n"
+ "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+ "If you truly wish to continue, execute 'sdb shell pm uninstall -k %s'\n", argv[2]);
+ return -1;
+ }
+
+ /* 'sdb uninstall' takes the same arguments as 'pm uninstall' on device */
+ return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(transport_type transport, char* serial, char* filename)
+{
+ char buf[4096];
+ char* quoted;
+
+ snprintf(buf, sizeof(buf), "shell:rm ");
+ quoted = dupAndQuote(filename);
+ strncat(buf, quoted, sizeof(buf)-1);
+ free(quoted);
+
+ send_shellcommand(transport, serial, buf);
+ return 0;
+}
+
+const char* get_basename(const char* filename)
+{
+ const char* basename = sdb_dirstop(filename);
+ if (basename) {
+ basename++;
+ return basename;
+ } else {
+ return filename;
+ }
+}
+
+static int check_file(const char* filename)
+{
+ struct stat st;
+
+ if (filename == NULL) {
+ return 0;
+ }
+
+ if (stat(filename, &st) != 0) {
+ fprintf(stderr, "can't find '%s' to install\n", filename);
+ return 1;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ fprintf(stderr, "can't install '%s' because it's not a file\n", filename);
+ return 1;
+ }
+
+ return 0;
+}
+
+int install_app(transport_type transport, char* serial, int argc, char** argv)
+{
+ static const char *const DATA_DEST = "/data/local/tmp/%s";
+ static const char *const SD_DEST = "/sdcard/tmp/%s";
+ const char* where = DATA_DEST;
+ char apk_dest[PATH_MAX];
+ char verification_dest[PATH_MAX];
+ char* apk_file;
+ char* verification_file = NULL;
+ int file_arg = -1;
+ int err;
+ int i;
+ int verify_apk = 1;
+
+ for (i = 1; i < argc; i++) {
+ if (*argv[i] != '-') {
+ file_arg = i;
+ break;
+ } else if (!strcmp(argv[i], "-i")) {
+ // Skip the installer package name.
+ i++;
+ } else if (!strcmp(argv[i], "-s")) {
+ where = SD_DEST;
+ } else if (!strcmp(argv[i], "--algo")) {
+ verify_apk = 0;
+ i++;
+ } else if (!strcmp(argv[i], "--iv")) {
+ verify_apk = 0;
+ i++;
+ } else if (!strcmp(argv[i], "--key")) {
+ verify_apk = 0;
+ i++;
+ }
+ }
+
+ if (file_arg < 0) {
+ fprintf(stderr, "can't find filename in arguments\n");
+ return 1;
+ } else if (file_arg + 2 < argc) {
+ fprintf(stderr, "too many files specified; only takes APK file and verifier file\n");
+ return 1;
+ }
+
+ apk_file = argv[file_arg];
+
+ if (file_arg != argc - 1) {
+ verification_file = argv[file_arg + 1];
+ }
+
+ if (check_file(apk_file) || check_file(verification_file)) {
+ return 1;
+ }
+
+ snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
+ if (verification_file != NULL) {
+ snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file));
+
+ if (!strcmp(apk_dest, verification_dest)) {
+ fprintf(stderr, "APK and verification file can't have the same name\n");
+ return 1;
+ }
+ }
+
+ err = do_sync_push(apk_file, apk_dest, verify_apk, 0);
+ if (err) {
+ goto cleanup_apk;
+ } else {
+ argv[file_arg] = apk_dest; /* destination name, not source location */
+ }
+
+ if (verification_file != NULL) {
+ err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */, 0);
+ if (err) {
+ goto cleanup_apk;
+ } else {
+ argv[file_arg + 1] = verification_dest; /* destination name, not source location */
+ }
+ }
+
+ pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+ if (verification_file != NULL) {
+ delete_file(transport, serial, verification_dest);
+ }
+
+ delete_file(transport, serial, apk_dest);
+
+ return err;
+}
+
+void version_sdbd(transport_type ttype, char* serial) {
+ char* VERSION_QUERY ="shell:rpm -qa | grep sdbd";
+ send_shellcommand(ttype, serial, VERSION_QUERY);
+}
diff --git a/src/console.c b/src/console.c
new file mode 100644
index 0000000..2926b5c
--- /dev/null
+++ b/src/console.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sysdeps.h"
+#include "sdb.h"
+#include "sdb_client.h"
+#include <stdio.h>
+
+static int connect_to_console(void)
+{
+ int fd, port;
+
+ port = sdb_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 sdb_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++) {
+ sdb_write( fd, argv[nn], strlen(argv[nn]) );
+ sdb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
+ }
+ sdb_write( fd, QUIT, sizeof(QUIT)-1 );
+ sdb_close(fd);
+
+ return 0;
+}
diff --git a/src/fdevent.c b/src/fdevent.c
new file mode 100644
index 0000000..4171acb
--- /dev/null
+++ b/src/fdevent.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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/ioctl.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <fcntl.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#include "fdevent.h"
+#include "transport.h"
+#include "sysdeps.h"
+
+
+/* !!! Do not enable DEBUG for the sdb that will run as the server:
+** both stdout and stderr are used to communicate between the client
+** and server. Any extra output will cause failures.
+*/
+#define DEBUG 0 /* non-0 will break sdb server */
+
+// This socket is used when a subproc shell service exists.
+// It wakes up the fdevent_loop() and cause the correct handling
+// of the shell's pseudo-tty master. I.e. force close it.
+int SHELL_EXIT_NOTIFY_FD = -1;
+
+static void fatal(const char *fn, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:", fn);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ abort();
+}
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+#define D(...) \
+ do { \
+ sdb_mutex_lock(&D_lock); \
+ int save_errno = errno; \
+ fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \
+ errno = save_errno; \
+ fprintf(stderr, __VA_ARGS__); \
+ sdb_mutex_unlock(&D_lock); \
+ errno = save_errno; \
+ } while(0)
+static void dump_fde(fdevent *fde, const char *info)
+{
+ sdb_mutex_lock(&D_lock);
+ fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+ fde->state & FDE_READ ? 'R' : ' ',
+ fde->state & FDE_WRITE ? 'W' : ' ',
+ fde->state & FDE_ERROR ? 'E' : ' ',
+ info);
+ sdb_mutex_unlock(&D_lock);
+}
+#else
+#define D(...) ((void)0)
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+#define FDE_CREATED 0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
+
+static fdevent list_pending = {
+ .next = &list_pending,
+ .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+#ifdef CRAPTASTIC
+//HAVE_EPOLL
+
+#include <sys/epoll.h>
+
+static int epoll_fd = -1;
+
+static void fdevent_init()
+{
+ /* XXX: what's a good size for the passed in hint? */
+ epoll_fd = epoll_create(256);
+
+ if(epoll_fd < 0) {
+ perror("epoll_create() failed");
+ exit(1);
+ }
+
+ /* mark for close-on-exec */
+ fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+#if 0
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+#endif
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ struct epoll_event ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+ /* technically we only need to delete if we
+ ** were actively monitoring events, but let's
+ ** be aggressive and do it anyway, just in case
+ ** something's out of sync
+ */
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ struct epoll_event ev;
+ int active;
+
+ active = (fde->state & FDE_EVENTMASK) != 0;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = 0;
+ ev.data.ptr = fde;
+
+ if(events & FDE_READ) ev.events |= EPOLLIN;
+ if(events & FDE_WRITE) ev.events |= EPOLLOUT;
+ if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(active) {
+ /* we're already active. if we're changing to *no*
+ ** events being monitored, we need to delete, otherwise
+ ** we need to just modify
+ */
+ if(ev.events) {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ } else {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ }
+ } else {
+ /* we're not active. if we're watching events, we need
+ ** to add, otherwise we can just do nothing
+ */
+ if(ev.events) {
+ if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
+ perror("epoll_ctl() failed\n");
+ exit(1);
+ }
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ struct epoll_event events[256];
+ fdevent *fde;
+ int i, n;
+
+ n = epoll_wait(epoll_fd, events, 256, -1);
+
+ if(n < 0) {
+ if(errno == EINTR) return;
+ perror("epoll_wait");
+ exit(1);
+ }
+
+ for(i = 0; i < n; i++) {
+ struct epoll_event *ev = events + i;
+ fde = ev->data.ptr;
+
+ if(ev->events & EPOLLIN) {
+ fde->events |= FDE_READ;
+ }
+ if(ev->events & EPOLLOUT) {
+ fde->events |= FDE_WRITE;
+ }
+ if(ev->events & (EPOLLERR | EPOLLHUP)) {
+ fde->events |= FDE_ERROR;
+ }
+ if(fde->events) {
+ if(fde->state & FDE_PENDING) continue;
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue(fde);
+ }
+ }
+}
+
+#else /* USE_SELECT */
+
+#ifdef HAVE_WINSOCK
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+static fd_set read_fds;
+static fd_set write_fds;
+static fd_set error_fds;
+
+static int select_n = 0;
+
+static void fdevent_init(void)
+{
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ FD_ZERO(&error_fds);
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ if(fde->fd >= select_n) {
+ select_n = fde->fd + 1;
+ }
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ int i, n;
+
+ FD_CLR(fde->fd, &read_fds);
+ FD_CLR(fde->fd, &write_fds);
+ FD_CLR(fde->fd, &error_fds);
+
+ for(n = 0, i = 0; i < select_n; i++) {
+ if(fd_table[i] != 0) n = i;
+ }
+ select_n = n + 1;
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ if(events & FDE_READ) {
+ FD_SET(fde->fd, &read_fds);
+ } else {
+ FD_CLR(fde->fd, &read_fds);
+ }
+ if(events & FDE_WRITE) {
+ FD_SET(fde->fd, &write_fds);
+ } else {
+ FD_CLR(fde->fd, &write_fds);
+ }
+ if(events & FDE_ERROR) {
+ FD_SET(fde->fd, &error_fds);
+ } else {
+ FD_CLR(fde->fd, &error_fds);
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+}
+
+/* Looks at fd_table[] for bad FDs and sets bit in fds.
+** Returns the number of bad FDs.
+*/
+static int fdevent_fd_check(fd_set *fds)
+{
+ int i, n = 0;
+ fdevent *fde;
+
+ for(i = 0; i < select_n; i++) {
+ fde = fd_table[i];
+ if(fde == 0) continue;
+ if(fcntl(i, F_GETFL, NULL) < 0) {
+ FD_SET(i, fds);
+ n++;
+ // fde->state |= FDE_DONT_CLOSE;
+
+ }
+ }
+ return n;
+}
+
+#if !DEBUG
+static inline void dump_all_fds(const char *extra_msg) {}
+#else
+static void dump_all_fds(const char *extra_msg)
+{
+int i;
+ fdevent *fde;
+ // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
+ char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
+ size_t max_chars = FD_SETSIZE * 6 + 1;
+ int printed_out;
+#define SAFE_SPRINTF(...) \
+ do { \
+ printed_out = snprintf(pb, max_chars, __VA_ARGS__); \
+ if (printed_out <= 0) { \
+ D("... snprintf failed.\n"); \
+ return; \
+ } \
+ if (max_chars < (unsigned int)printed_out) { \
+ D("... snprintf out of space.\n"); \
+ return; \
+ } \
+ pb += printed_out; \
+ max_chars -= printed_out; \
+ } while(0)
+
+ for(i = 0; i < select_n; i++) {
+ fde = fd_table[i];
+ SAFE_SPRINTF("%d", i);
+ if(fde == 0) {
+ SAFE_SPRINTF("? ");
+ continue;
+ }
+ if(fcntl(i, F_GETFL, NULL) < 0) {
+ SAFE_SPRINTF("b");
+ }
+ SAFE_SPRINTF(" ");
+ }
+ D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
+}
+#endif
+
+static int fdevent_process()
+{
+ int i, n;
+ fdevent *fde;
+ unsigned events;
+ fd_set rfd, wfd, efd;
+
+ memcpy(&rfd, &read_fds, sizeof(fd_set));
+ memcpy(&wfd, &write_fds, sizeof(fd_set));
+ memcpy(&efd, &error_fds, sizeof(fd_set));
+
+ dump_all_fds("pre select()");
+
+ n = select(select_n, &rfd, &wfd, &efd, NULL);
+ int saved_errno = errno;
+ D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
+
+ dump_all_fds("post select()");
+
+ if(n < 0) {
+ switch(saved_errno) {
+ case EINTR: return -1;
+ case EBADF:
+ // Can't trust the FD sets after an error.
+ FD_ZERO(&wfd);
+ FD_ZERO(&efd);
+ FD_ZERO(&rfd);
+ break;
+ default:
+ D("Unexpected select() error=%d\n", saved_errno);
+ return 0;
+ }
+ }
+ if(n <= 0) {
+ // We fake a read, as the rest of the code assumes
+ // that errors will be detected at that point.
+ n = fdevent_fd_check(&rfd);
+ }
+
+ for(i = 0; (i < select_n) && (n > 0); i++) {
+ events = 0;
+ if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
+ if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
+ if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
+
+ if(events) {
+ fde = fd_table[i];
+ if(fde == 0)
+ FATAL("missing fde for fd %d\n", i);
+
+ fde->events |= events;
+
+ D("got events fde->fd=%d events=%04x, state=%04x\n",
+ fde->fd, fde->events, fde->state);
+ if(fde->state & FDE_PENDING) continue;
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue(fde);
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+static void fdevent_register(fdevent *fde)
+{
+ if(fde->fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(fde->fd >= fd_table_max) {
+ int oldmax = fd_table_max;
+ if(fde->fd > 32000) {
+ FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+ }
+ if(fd_table_max == 0) {
+ fdevent_init();
+ fd_table_max = 256;
+ }
+ while(fd_table_max <= fde->fd) {
+ fd_table_max *= 2;
+ }
+ fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
+ if(fd_table == 0) {
+ FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+ }
+ memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+ }
+
+ fd_table[fde->fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fde->fd] != fde) {
+ FATAL("fd_table out of sync [%d]\n", fde->fd);
+ }
+
+ fd_table[fde->fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ sdb_close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+static void fdevent_call_fdfunc(fdevent* fde)
+{
+ unsigned events = fde->events;
+ fde->events = 0;
+ if(!(fde->state & FDE_PENDING)) return;
+ fde->state &= (~FDE_PENDING);
+ dump_fde(fde, "callback");
+ fde->func(fde->fd, events, fde->arg);
+}
+
+static void fdevent_subproc_event_func(int fd, unsigned ev, void *userdata)
+{
+
+ D("subproc handling on fd=%d ev=%04x\n", fd, ev);
+
+ // Hook oneself back into the fde's suitable for select() on read.
+ if((fd < 0) || (fd >= fd_table_max)) {
+ FATAL("fd %d out of range for fd_table \n", fd);
+ }
+ fdevent *fde = fd_table[fd];
+ fdevent_add(fde, FDE_READ);
+
+ if(ev & FDE_READ){
+ int subproc_fd;
+
+ if(readx(fd, &subproc_fd, sizeof(subproc_fd))) {
+ FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
+ }
+ if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
+ D("subproc_fd %d out of range 0, fd_table_max=%d\n",
+ subproc_fd, fd_table_max);
+ return;
+ }
+ fdevent *subproc_fde = fd_table[subproc_fd];
+ if(!subproc_fde) {
+ D("subproc_fd %d cleared from fd_table\n", subproc_fd);
+ return;
+ }
+ if(subproc_fde->fd != subproc_fd) {
+ // Already reallocated?
+ D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
+ return;
+ }
+
+ subproc_fde->force_eof = 1;
+
+ int rcount = 0;
+ ioctl(subproc_fd, FIONREAD, &rcount);
+ D("subproc with fd=%d has rcount=%d err=%d\n",
+ subproc_fd, rcount, errno);
+
+ if(rcount) {
+ // If there is data left, it will show up in the select().
+ // This works because there is no other thread reading that
+ // data when in this fd_func().
+ return;
+ }
+
+ D("subproc_fde.state=%04x\n", subproc_fde->state);
+ subproc_fde->events |= FDE_READ;
+ if(subproc_fde->state & FDE_PENDING) {
+ return;
+ }
+ subproc_fde->state |= FDE_PENDING;
+ fdevent_call_fdfunc(subproc_fde);
+ }
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+ fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+ if(fde == 0) return 0;
+ fdevent_install(fde, fd, func, arg);
+ fde->state |= FDE_CREATED;
+ return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+ if(fde == 0) return;
+ if(!(fde->state & FDE_CREATED)) {
+ FATAL("fde %p not created by fdevent_create()\n", fde);
+ }
+ fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+ memset(fde, 0, sizeof(fdevent));
+ fde->state = FDE_ACTIVE;
+ fde->fd = fd;
+ fde->force_eof = 0;
+ fde->func = func;
+ fde->arg = arg;
+
+#ifndef HAVE_WINSOCK
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+ fdevent_register(fde);
+ dump_fde(fde, "connect");
+ fdevent_connect(fde);
+ fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+ if(fde->state & FDE_PENDING) {
+ fdevent_plist_remove(fde);
+ }
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_disconnect(fde);
+ dump_fde(fde, "disconnect");
+ fdevent_unregister(fde);
+ }
+
+ fde->state = 0;
+ fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+ events &= FDE_EVENTMASK;
+
+ if((fde->state & FDE_EVENTMASK) == events) return;
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ dump_fde(fde, "update");
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(fde->state & FDE_PENDING) {
+ /* if we're pending, make sure
+ ** we don't signal an event that
+ ** is no longer wanted.
+ */
+ fde->events &= (~events);
+ if(fde->events == 0) {
+ fdevent_plist_remove(fde);
+ fde->state &= (~FDE_PENDING);
+ }
+ }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_subproc_setup()
+{
+ int s[2];
+
+ if(sdb_socketpair(s)) {
+ FATAL("cannot create shell-exit socket-pair\n");
+ }
+ SHELL_EXIT_NOTIFY_FD = s[0];
+ fdevent *fde;
+ fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+ if(!fde)
+ FATAL("cannot create fdevent for shell-exit handler\n");
+ fdevent_add(fde, FDE_READ);
+}
+
+void fdevent_loop()
+{
+ fdevent *fde;
+ fdevent_subproc_setup();
+
+ for(;;) {
+ D("--- ---- waiting for events\n");
+
+ if (fdevent_process() < 0) {
+ return;
+ }
+
+ while((fde = fdevent_plist_dequeue())) {
+ fdevent_call_fdfunc(fde);
+ }
+ }
+}
diff --git a/src/fdevent.h b/src/fdevent.h
new file mode 100644
index 0000000..a6db9ea
--- /dev/null
+++ b/src/fdevent.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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
+
+#include <stdint.h> /* for int64_t */
+
+/* events that may be observed */
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_TIMEOUT 0x0008
+
+/* 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
+ * Note: use FD_TIMER as 'fd' to create a fd-less object
+ * (used to implement timers).
+*/
+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);
+
+void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms);
+
+/* loop forever, handling events.
+*/
+void fdevent_loop();
+
+struct fdevent
+{
+ fdevent *next;
+ fdevent *prev;
+
+ int fd;
+ int force_eof;
+
+ unsigned short state;
+ unsigned short events;
+
+ fd_func func;
+ void *arg;
+};
+
+
+#endif
diff --git a/src/file_sync_client.c b/src/file_sync_client.c
new file mode 100755
index 0000000..2c5016c
--- /dev/null
+++ b/src/file_sync_client.c
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/types.h>
+// tizen specific #include <zipfile/zipfile.h>
+
+#include "sysdeps.h"
+#include "sdb.h"
+#include "sdb_client.h"
+#include "file_sync_service.h"
+
+
+static unsigned total_bytes;
+static long long start_time;
+extern const char* get_basename(const char* filename);
+
+static long long NOW()
+{
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ return ((long long) tv.tv_usec) +
+ 1000000LL * ((long long) tv.tv_sec);
+}
+
+static void BEGIN()
+{
+ total_bytes = 0;
+ start_time = NOW();
+}
+
+static void END(const char* filename)
+{
+ long long t = NOW() - start_time;
+ if(total_bytes == 0) return;
+
+ if (t == 0) /* prevent division by 0 :-) */
+ t = 1000000;
+
+ fprintf(stderr,"%-30s %lld KB/s (%lld bytes in %lld.%03llds)\n",
+ filename,
+ ((((long long) total_bytes) * 1000000LL) / t) / 1024LL,
+ (long long) total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+}
+
+
+void sync_quit(int fd)
+{
+ syncmsg msg;
+
+ msg.req.id = ID_QUIT;
+ msg.req.namelen = 0;
+
+ writex(fd, &msg.req, sizeof(msg.req));
+}
+
+typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
+
+int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
+{
+ syncmsg msg;
+ char buf[257];
+ int len;
+
+ len = strlen(path);
+ if(len > 1024) goto fail;
+
+ msg.req.id = ID_LIST;
+ msg.req.namelen = htoll(len);
+
+ if(writex(fd, &msg.req, sizeof(msg.req)) ||
+ writex(fd, path, len)) {
+ goto fail;
+ }
+
+ for(;;) {
+ if(readx(fd, &msg.dent, sizeof(msg.dent))) break;
+ if(msg.dent.id == ID_DONE) return 0;
+ if(msg.dent.id != ID_DENT) break;
+
+ len = ltohl(msg.dent.namelen);
+ if(len > 256) break;
+
+ if(readx(fd, buf, len)) break;
+ buf[len] = 0;
+
+ func(ltohl(msg.dent.mode),
+ ltohl(msg.dent.size),
+ ltohl(msg.dent.time),
+ buf, cookie);
+ }
+
+fail:
+ sdb_close(fd);
+ return -1;
+}
+
+typedef struct syncsendbuf syncsendbuf;
+
+struct syncsendbuf {
+ unsigned id;
+ unsigned size;
+ char data[SYNC_DATA_MAX];
+};
+
+static syncsendbuf send_buffer;
+
+int sync_readtime(int fd, const char *path, unsigned *timestamp)
+{
+ syncmsg msg;
+ int len = strlen(path);
+
+ msg.req.id = ID_STAT;
+ msg.req.namelen = htoll(len);
+
+ if(writex(fd, &msg.req, sizeof(msg.req)) ||
+ writex(fd, path, len)) {
+ return -1;
+ }
+
+ if(readx(fd, &msg.stat, sizeof(msg.stat))) {
+ return -1;
+ }
+
+ if(msg.stat.id != ID_STAT) {
+ return -1;
+ }
+
+ *timestamp = ltohl(msg.stat.time);
+ return 0;
+}
+
+static int sync_start_readtime(int fd, const char *path)
+{
+ syncmsg msg;
+ int len = strlen(path);
+
+ msg.req.id = ID_STAT;
+ msg.req.namelen = htoll(len);
+
+ if(writex(fd, &msg.req, sizeof(msg.req)) ||
+ writex(fd, path, len)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sync_finish_readtime(int fd, unsigned int *timestamp,
+ unsigned int *mode, unsigned int *size)
+{
+ syncmsg msg;
+
+ if(readx(fd, &msg.stat, sizeof(msg.stat)))
+ return -1;
+
+ if(msg.stat.id != ID_STAT)
+ return -1;
+
+ *timestamp = ltohl(msg.stat.time);
+ *mode = ltohl(msg.stat.mode);
+ *size = ltohl(msg.stat.size);
+
+ return 0;
+}
+
+int sync_readmode(int fd, const char *path, unsigned *mode)
+{
+ syncmsg msg;
+ int len = strlen(path);
+
+ msg.req.id = ID_STAT;
+ msg.req.namelen = htoll(len);
+
+ if(writex(fd, &msg.req, sizeof(msg.req)) ||
+ writex(fd, path, len)) {
+ return -1;
+ }
+
+ if(readx(fd, &msg.stat, sizeof(msg.stat))) {
+ return -1;
+ }
+
+ if(msg.stat.id != ID_STAT) {
+ return -1;
+ }
+
+ *mode = ltohl(msg.stat.mode);
+ return 0;
+}
+
+static int write_data_file(int fd, const char *path, syncsendbuf *sbuf)
+{
+ int lfd, err = 0;
+
+ lfd = sdb_open(path, O_RDONLY);
+ if(lfd < 0) {
+ fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ sbuf->id = ID_DATA;
+ for(;;) {
+ int ret;
+
+ ret = sdb_read(lfd, sbuf->data, SYNC_DATA_MAX);
+ if(!ret)
+ break;
+
+ if(ret < 0) {
+ if(errno == EINTR)
+ continue;
+ fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
+ break;
+ }
+
+ sbuf->size = htoll(ret);
+ if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){
+ err = -1;
+ break;
+ }
+ total_bytes += ret;
+ }
+
+ sdb_close(lfd);
+ return err;
+}
+
+static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf)
+{
+ int err = 0;
+ int total = 0;
+
+ sbuf->id = ID_DATA;
+ while (total < size) {
+ int count = size - total;
+ if (count > SYNC_DATA_MAX) {
+ count = SYNC_DATA_MAX;
+ }
+
+ memcpy(sbuf->data, &file_buffer[total], count);
+ sbuf->size = htoll(count);
+ if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){
+ err = -1;
+ break;
+ }
+ total += count;
+ total_bytes += count;
+ }
+
+ return err;
+}
+
+#ifdef HAVE_SYMLINKS
+static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
+{
+ int len, ret;
+
+ len = readlink(path, sbuf->data, SYNC_DATA_MAX-1);
+ if(len < 0) {
+ fprintf(stderr, "error reading link '%s': %s\n", path, strerror(errno));
+ return -1;
+ }
+ sbuf->data[len] = '\0';
+
+ sbuf->size = htoll(len + 1);
+ sbuf->id = ID_DATA;
+
+ ret = writex(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
+ if(ret)
+ return -1;
+
+ total_bytes += len + 1;
+
+ return 0;
+}
+#endif
+
+static int sync_send(int fd, const char *lpath, const char *rpath,
+ unsigned mtime, mode_t mode, int verifyApk)
+{
+ syncmsg msg;
+ int len, r;
+ syncsendbuf *sbuf = &send_buffer;
+ char* file_buffer = NULL;
+ int size = 0;
+ char tmp[64];
+
+ len = strlen(rpath);
+ if(len > 1024) goto fail;
+
+ snprintf(tmp, sizeof(tmp), ",%d", mode);
+ r = strlen(tmp);
+#if 0 /* tizen specific */
+ if (verifyApk) {
+ int lfd;
+ zipfile_t zip;
+ zipentry_t entry;
+ int amt;
+
+ // if we are transferring an APK file, then sanity check to make sure
+ // we have a real zip file that contains an AndroidManifest.xml
+ // this requires that we read the entire file into memory.
+ lfd = sdb_open(lpath, O_RDONLY);
+ if(lfd < 0) {
+ fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+ return -1;
+ }
+
+ size = sdb_lseek(lfd, 0, SEEK_END);
+ if (size == -1 || -1 == sdb_lseek(lfd, 0, SEEK_SET)) {
+ fprintf(stderr, "error seeking in file '%s'\n", lpath);
+ sdb_close(lfd);
+ return 1;
+ }
+
+ file_buffer = (char *)malloc(size);
+ if (file_buffer == NULL) {
+ fprintf(stderr, "could not allocate buffer for '%s'\n",
+ lpath);
+ sdb_close(lfd);
+ return 1;
+ }
+ amt = sdb_read(lfd, file_buffer, size);
+ if (amt != size) {
+ fprintf(stderr, "error reading from file: '%s'\n", lpath);
+ sdb_close(lfd);
+ free(file_buffer);
+ return 1;
+ }
+
+ sdb_close(lfd);
+
+ zip = init_zipfile(file_buffer, size);
+ if (zip == NULL) {
+ fprintf(stderr, "file '%s' is not a valid zip file\n",
+ lpath);
+ free(file_buffer);
+ return 1;
+ }
+
+ entry = lookup_zipentry(zip, "AndroidManifest.xml");
+ release_zipfile(zip);
+ if (entry == NULL) {
+ fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n",
+ lpath);
+ free(file_buffer);
+ return 1;
+ }
+ }
+#endif
+ msg.req.id = ID_SEND;
+ msg.req.namelen = htoll(len + r);
+
+ if(writex(fd, &msg.req, sizeof(msg.req)) ||
+ writex(fd, rpath, len) || writex(fd, tmp, r)) {
+ free(file_buffer);
+ goto fail;
+ }
+
+ if (file_buffer) {
+ write_data_buffer(fd, file_buffer, size, sbuf);
+ free(file_buffer);
+ } else if (S_ISREG(mode))
+ write_data_file(fd, lpath, sbuf);
+#ifdef HAVE_SYMLINKS
+ else if (S_ISLNK(mode))
+ write_data_link(fd, lpath, sbuf);
+#endif
+ else
+ goto fail;
+
+ msg.data.id = ID_DONE;
+ msg.data.size = htoll(mtime);
+ if(writex(fd, &msg.data, sizeof(msg.data)))
+ goto fail;
+
+ if(readx(fd, &msg.status, sizeof(msg.status)))
+ return -1;
+
+ if(msg.status.id != ID_OKAY) {
+ if(msg.status.id == ID_FAIL) {
+ len = ltohl(msg.status.msglen);
+ if(len > 256) len = 256;
+ if(readx(fd, sbuf->data, len)) {
+ return -1;
+ }
+ sbuf->data[len] = 0;
+ } else
+ strcpy(sbuf->data, "unknown reason");
+
+ fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
+ return -1;
+ }
+
+ return 0;
+
+fail:
+ fprintf(stderr,"protocol failure\n");
+ sdb_close(fd);
+ return -1;
+}
+
+static int mkdirs(char *name)
+{
+ int ret;
+ char *x = name + 1;
+
+ for(;;) {
+ x = sdb_dirstart(x);
+ if(x == 0) return 0;
+ *x = 0;
+ ret = sdb_mkdir(name, 0775);
+ *x = OS_PATH_SEPARATOR;
+ if((ret < 0) && (errno != EEXIST)) {
+ return ret;
+ }
+ x++;
+ }
+ return 0;
+}
+
+int sync_recv(int fd, const char *rpath, const char *lpath)
+{
+ syncmsg msg;
+ int len;
+ int lfd = -1;
+ char *buffer = send_buffer.data;
+ unsigned id;
+
+ len = strlen(rpath);
+ if(len > 1024) return -1;
+
+ msg.req.id = ID_RECV;
+ msg.req.namelen = htoll(len);
+ if(writex(fd, &msg.req, sizeof(msg.req)) ||
+ writex(fd, rpath, len)) {
+ return -1;
+ }
+
+ if(readx(fd, &msg.data, sizeof(msg.data))) {
+ return -1;
+ }
+ id = msg.data.id;
+
+ if((id == ID_DATA) || (id == ID_DONE)) {
+ sdb_unlink(lpath);
+ mkdirs((char *)lpath);
+ lfd = sdb_creat(lpath, 0644);
+ if(lfd < 0) {
+ fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
+ return -1;
+ }
+ goto handle_data;
+ } else {
+ goto remote_error;
+ }
+
+ for(;;) {
+ if(readx(fd, &msg.data, sizeof(msg.data))) {
+ return -1;
+ }
+ id = msg.data.id;
+
+ handle_data:
+ len = ltohl(msg.data.size);
+ if(id == ID_DONE) break;
+ if(id != ID_DATA) goto remote_error;
+ if(len > SYNC_DATA_MAX) {
+ fprintf(stderr,"data overrun\n");
+ sdb_close(lfd);
+ return -1;
+ }
+
+ if(readx(fd, buffer, len)) {
+ sdb_close(lfd);
+ return -1;
+ }
+
+ if(writex(lfd, buffer, len)) {
+ fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
+ sdb_close(lfd);
+ return -1;
+ }
+
+ total_bytes += len;
+ }
+
+ sdb_close(lfd);
+ return 0;
+
+remote_error:
+ sdb_close(lfd);
+ sdb_unlink(lpath);
+
+ if(id == ID_FAIL) {
+ len = ltohl(msg.data.size);
+ if(len > 256) len = 256;
+ if(readx(fd, buffer, len)) {
+ return -1;
+ }
+ buffer[len] = 0;
+ } else {
+ memcpy(buffer, &id, 4);
+ buffer[4] = 0;
+// strcpy(buffer,"unknown reason");
+ }
+ fprintf(stderr,"failed to copy '%s' to '%s': %s\n", rpath, lpath, buffer);
+ return 0;
+}
+
+
+
+/* --- */
+
+
+static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
+ const char *name, void *cookie)
+{
+ printf("%08x %08x %08x %s\n", mode, size, time, name);
+}
+
+int do_sync_ls(const char *path)
+{
+ int fd = sdb_connect("sync:");
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+
+ if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
+ return 1;
+ } else {
+ sync_quit(fd);
+ return 0;
+ }
+}
+
+typedef struct copyinfo copyinfo;
+
+struct copyinfo
+{
+ copyinfo *next;
+ const char *src;
+ const char *dst;
+ unsigned int time;
+ unsigned int mode;
+ unsigned int size;
+ int flag;
+ //char data[0];
+};
+
+copyinfo *mkcopyinfo(const char *spath, const char *dpath,
+ const char *name, int isdir)
+{
+ int slen = strlen(spath);
+ int dlen = strlen(dpath);
+ int nlen = strlen(name);
+ int ssize = slen + nlen + 2;
+ int dsize = dlen + nlen + 2;
+
+ copyinfo *ci = malloc(sizeof(copyinfo) + ssize + dsize);
+ if(ci == 0) {
+ fprintf(stderr,"out of memory\n");
+ abort();
+ }
+
+ ci->next = 0;
+ ci->time = 0;
+ ci->mode = 0;
+ ci->size = 0;
+ ci->flag = 0;
+ ci->src = (const char*)(ci + 1);
+ ci->dst = ci->src + ssize;
+ snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
+ snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
+
+// fprintf(stderr,"mkcopyinfo('%s','%s')\n", ci->src, ci->dst);
+ return ci;
+}
+
+
+static int local_build_list(copyinfo **filelist,
+ const char *lpath, const char *rpath)
+{
+ DIR *d;
+ struct dirent *de;
+ struct stat st;
+ copyinfo *dirlist = 0;
+ copyinfo *ci, *next;
+
+// fprintf(stderr,"local_build_list('%s','%s')\n", lpath, rpath);
+
+ d = opendir(lpath);
+ if(d == 0) {
+ fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
+ return -1;
+ }
+
+ while((de = readdir(d))) {
+ char stat_path[PATH_MAX];
+ char *name = de->d_name;
+
+ if(name[0] == '.') {
+ if(name[1] == 0) continue;
+ if((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ /*
+ * We could use d_type if HAVE_DIRENT_D_TYPE is defined, but reiserfs
+ * always returns DT_UNKNOWN, so we just use stat() for all cases.
+ */
+ if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path))
+ continue;
+ strcpy(stat_path, lpath);
+ strcat(stat_path, de->d_name);
+ stat(stat_path, &st);
+
+ if (S_ISDIR(st.st_mode)) {
+ ci = mkcopyinfo(lpath, rpath, name, 1);
+ ci->next = dirlist;
+ dirlist = ci;
+ } else {
+ ci = mkcopyinfo(lpath, rpath, name, 0);
+ if(lstat(ci->src, &st)) {
+ fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
+ closedir(d);
+ if (ci != NULL) {
+ free(ci);
+ }
+ return -1;
+ }
+ if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+ fprintf(stderr, "skipping special file '%s'\n", ci->src);
+ free(ci);
+ } else {
+ ci->time = st.st_mtime;
+ ci->mode = st.st_mode;
+ ci->size = st.st_size;
+ ci->next = *filelist;
+ *filelist = ci;
+ }
+ }
+ }
+
+ closedir(d);
+
+ for(ci = dirlist; ci != 0; ci = next) {
+ next = ci->next;
+ local_build_list(filelist, ci->src, ci->dst);
+ free(ci);
+ }
+
+ return 0;
+}
+
+
+static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
+{
+ copyinfo *filelist = 0;
+ copyinfo *ci, *next;
+ int pushed = 0;
+ int skipped = 0;
+
+ if((lpath[0] == 0) || (rpath[0] == 0)) return -1;
+ if(lpath[strlen(lpath) - 1] != '/') {
+ int tmplen = strlen(lpath)+2;
+ char *tmp = malloc(tmplen);
+ if(tmp == 0) return -1;
+ snprintf(tmp, tmplen, "%s/",lpath);
+ lpath = tmp;
+ }
+ if(rpath[strlen(rpath) - 1] != '/') {
+ int tmplen = strlen(rpath)+2;
+ char *tmp = malloc(tmplen);
+ if(tmp == 0) return -1;
+ snprintf(tmp, tmplen, "%s/",rpath);
+ rpath = tmp;
+ }
+
+ if(local_build_list(&filelist, lpath, rpath)) {
+ return -1;
+ }
+
+ if(checktimestamps){
+ for(ci = filelist; ci != 0; ci = ci->next) {
+ if(sync_start_readtime(fd, ci->dst)) {
+ return 1;
+ }
+ }
+ for(ci = filelist; ci != 0; ci = ci->next) {
+ unsigned int timestamp, mode, size;
+ if(sync_finish_readtime(fd, &timestamp, &mode, &size))
+ return 1;
+ if(size == ci->size) {
+ /* for links, we cannot update the atime/mtime */
+ if((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+ (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+ ci->flag = 1;
+ }
+ }
+ }
+ for(ci = filelist; ci != 0; ci = next) {
+ next = ci->next;
+ if(ci->flag == 0) {
+ fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
+ if(!listonly &&
+ sync_send(fd, ci->src, ci->dst, ci->time, ci->mode, 0 /* no verify APK */)){
+ return 1;
+ }
+ pushed++;
+ } else {
+ skipped++;
+ }
+ free(ci);
+ }
+
+ fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
+ pushed, (pushed == 1) ? "" : "s",
+ skipped, (skipped == 1) ? "" : "s");
+
+ return 0;
+}
+
+
+int do_sync_push(const char *lpath, const char *rpath, int verifyApk, int isUtf8)
+{
+ struct stat st;
+ unsigned mode;
+ int fd;
+ char *tmp = NULL;
+ char *utf8 = NULL;
+ int ret = 0;
+
+ fd = sdb_connect("sync:");
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+
+ if(stat(lpath, &st)) {
+ fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
+ sync_quit(fd);
+ return 1;
+ }
+
+ if(S_ISDIR(st.st_mode)) {
+ BEGIN();
+ if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
+ return 1;
+ } else {
+ END(get_basename(lpath));
+ sync_quit(fd);
+ }
+ } else {
+ if(sync_readmode(fd, rpath, &mode)) {
+ return 1;
+ }
+
+ if((mode != 0) && S_ISDIR(mode)) {
+ /* if we're copying a local file to a remote directory,
+ ** we *really* want to copy to remotedir + "/" + localfilename
+ */
+ const char *name = sdb_dirstop(lpath);
+ if(name == 0) {
+ name = lpath;
+ } else {
+ name++;
+ }
+ int tmplen = strlen(name) + strlen(rpath) + 2;
+ tmp = malloc(strlen(name) + strlen(rpath) + 2);
+ if(tmp == 0) return 1;
+ snprintf(tmp, tmplen, "%s/%s", rpath, name);
+ if (isUtf8 != 0) { //ansi to utf8
+ utf8 = ansi_to_utf8(tmp);
+ rpath = utf8;
+ } else {
+ rpath = tmp;
+ }
+ } // FIXME
+ /* else {
+ fprintf(stderr, "error: file exists : %s\n", rpath);
+ return 1;
+ }*/
+
+ BEGIN();
+ if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) {
+ ret = 1;
+ goto cleanup;
+ } else {
+ END(get_basename(lpath));
+ sync_quit(fd);
+ ret = 0;
+ goto cleanup;
+ }
+ }
+ return 0;
+cleanup:
+ if (tmp != NULL) {
+ free(tmp);
+ }
+ if (utf8 != NULL) {
+ free(utf8);
+ }
+ return ret;
+}
+
+
+typedef struct {
+ copyinfo **filelist;
+ copyinfo **dirlist;
+ const char *rpath;
+ const char *lpath;
+} sync_ls_build_list_cb_args;
+
+void
+sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
+ const char *name, void *cookie)
+{
+ sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
+ copyinfo *ci;
+
+ if (S_ISDIR(mode)) {
+ copyinfo **dirlist = args->dirlist;
+
+ /* Don't try recursing down "." or ".." */
+ if (name[0] == '.') {
+ if (name[1] == '\0') return;
+ if ((name[1] == '.') && (name[2] == '\0')) return;
+ }
+
+ ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
+ ci->next = *dirlist;
+ *dirlist = ci;
+ } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+ copyinfo **filelist = args->filelist;
+
+ ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
+ ci->time = time;
+ ci->mode = mode;
+ ci->size = size;
+ ci->next = *filelist;
+ *filelist = ci;
+ } else {
+ fprintf(stderr, "skipping special file '%s'\n", name);
+ }
+}
+
+static int remote_build_list(int syncfd, copyinfo **filelist,
+ const char *rpath, const char *lpath)
+{
+ copyinfo *dirlist = NULL;
+ sync_ls_build_list_cb_args args;
+
+ args.filelist = filelist;
+ args.dirlist = &dirlist;
+ args.rpath = rpath;
+ args.lpath = lpath;
+
+ /* Put the files/dirs in rpath on the lists. */
+ if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
+ return 1;
+ }
+
+ /* Recurse into each directory we found. */
+ while (dirlist != NULL) {
+ copyinfo *next = dirlist->next;
+ if (remote_build_list(syncfd, filelist, dirlist->src, dirlist->dst)) {
+ return 1;
+ }
+ free(dirlist);
+ dirlist = next;
+ }
+
+ return 0;
+}
+
+static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
+ int checktimestamps)
+{
+ copyinfo *filelist = 0;
+ copyinfo *ci, *next;
+ int pulled = 0;
+ int skipped = 0;
+
+ /* Make sure that both directory paths end in a slash. */
+ if (rpath[0] == 0 || lpath[0] == 0) return -1;
+ if (rpath[strlen(rpath) - 1] != '/') {
+ int tmplen = strlen(rpath) + 2;
+ char *tmp = malloc(tmplen);
+ if (tmp == 0) return -1;
+ snprintf(tmp, tmplen, "%s/", rpath);
+ rpath = tmp;
+ }
+ if (lpath[strlen(lpath) - 1] != '/') {
+ int tmplen = strlen(lpath) + 2;
+ char *tmp = malloc(tmplen);
+ if (tmp == 0) return -1;
+ snprintf(tmp, tmplen, "%s/", lpath);
+ lpath = tmp;
+ }
+
+ fprintf(stderr, "pull: building file list...\n");
+ /* Recursively build the list of files to copy. */
+ if (remote_build_list(fd, &filelist, rpath, lpath)) {
+ return -1;
+ }
+
+#if 0
+ if (checktimestamps) {
+ for (ci = filelist; ci != 0; ci = ci->next) {
+ if (sync_start_readtime(fd, ci->dst)) {
+ return 1;
+ }
+ }
+ for (ci = filelist; ci != 0; ci = ci->next) {
+ unsigned int timestamp, mode, size;
+ if (sync_finish_readtime(fd, &timestamp, &mode, &size))
+ return 1;
+ if (size == ci->size) {
+ /* for links, we cannot update the atime/mtime */
+ if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
+ (S_ISLNK(ci->mode & mode) && timestamp >= ci->time))
+ ci->flag = 1;
+ }
+ }
+ }
+#endif
+ for (ci = filelist; ci != 0; ci = next) {
+ next = ci->next;
+ if (ci->flag == 0) {
+ fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
+ if (sync_recv(fd, ci->src, ci->dst)) {
+ return 1;
+ }
+ pulled++;
+ } else {
+ skipped++;
+ }
+ free(ci);
+ }
+
+ fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
+ pulled, (pulled == 1) ? "" : "s",
+ skipped, (skipped == 1) ? "" : "s");
+
+ return 0;
+}
+
+int do_sync_pull(const char *rpath, const char *lpath)
+{
+ unsigned mode;
+ struct stat st;
+
+ int fd;
+
+ fd = sdb_connect("sync:");
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+
+ if(sync_readmode(fd, rpath, &mode)) {
+ return 1;
+ }
+ if(mode == 0) {
+ fprintf(stderr,"remote object '%s' does not exist\n", rpath);
+ return 1;
+ }
+
+ if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+ if(stat(lpath, &st) == 0) {
+ if(S_ISDIR(st.st_mode)) {
+ /* if we're copying a remote file to a local directory,
+ ** we *really* want to copy to localdir + "/" + remotefilename
+ */
+ const char *name = sdb_dirstop(rpath);
+ if(name == 0) {
+ name = rpath;
+ } else {
+ name++;
+ }
+ int tmplen = strlen(name) + strlen(lpath) + 2;
+ char *tmp = malloc(tmplen);
+ if(tmp == 0) return 1;
+ snprintf(tmp, tmplen, "%s/%s", lpath, name);
+ lpath = tmp;
+ }
+ }
+ BEGIN();
+ if(sync_recv(fd, rpath, lpath)) {
+ return 1;
+ } else {
+ END(get_basename(rpath));
+ sync_quit(fd);
+ return 0;
+ }
+ } else if(S_ISDIR(mode)) {
+ BEGIN();
+ if (copy_remote_dir_local(fd, rpath, lpath, 0)) {
+ return 1;
+ } else {
+ END(get_basename(rpath));
+ sync_quit(fd);
+ return 0;
+ }
+ } else {
+ fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
+ return 1;
+ }
+}
+
+int do_sync_sync(const char *lpath, const char *rpath, int listonly)
+{
+ fprintf(stderr,"syncing %s...\n",rpath);
+
+ int fd = sdb_connect("sync:");
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", sdb_error());
+ return 1;
+ }
+
+ BEGIN();
+ if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){
+ return 1;
+ } else {
+ END(get_basename(lpath));
+ sync_quit(fd);
+ return 0;
+ }
+}
diff --git a/src/file_sync_service.c b/src/file_sync_service.c
new file mode 100644
index 0000000..c0e342d
--- /dev/null
+++ b/src/file_sync_service.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sdb.h"
+#include "file_sync_service.h"
+
+/* The typical default value for the umask is S_IWGRP | S_IWOTH (octal 022).
+ * Before use the DIR_PERMISSION, the process umask value should be set 0 using umask().
+ */
+#define DIR_PERMISSION 0777
+
+static int mkdirs(char *name)
+{
+ int ret;
+ char *x = name + 1;
+
+ if(name[0] != '/') return -1;
+
+ for(;;) {
+ x = sdb_dirstart(x);
+ if(x == 0) return 0;
+ *x = 0;
+ /* tizen specific */
+ ret = sdb_mkdir(name, DIR_PERMISSION);
+
+ 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;
+
+ /* follw link */
+ if(stat(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 = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ if(fd < 0 && errno == ENOENT) {
+ mkdirs(path);
+ fd = sdb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ }
+ if(fd < 0 && errno == EEXIST) {
+ fd = sdb_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)) {
+ int saved_errno = errno;
+ sdb_close(fd);
+ sdb_unlink(path);
+ fd = -1;
+ errno = saved_errno;
+ if(fail_errno(s)) return -1;
+ }
+ }
+
+ if(fd >= 0) {
+ struct utimbuf u;
+ sdb_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)
+ sdb_close(fd);
+ sdb_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
+ // extracts file permission from stat.mode. (ex 100644 & 0777 = 644);
+ mode &= 0777; // combination of (S_IRWXU | S_IRWXG | S_IRWXO)
+ }
+ if(!tmp || errno) {
+ mode = 0644; // set default permission value in most of unix system.
+ is_link = 0;
+ }
+
+ // sdb does not allow to check that file exists or not. After deleting old file and creating new file again unconditionally.
+ sdb_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.
+ * ex) 0644 file will be created copied 0666 file.
+ * the following 2 lines should be commented if sdb process has been set to umask 0.
+ */
+
+ //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 = sdb_open(path, O_RDONLY);
+ if(fd < 0) {
+ if(fail_errno(s)) return -1;
+ return 0;
+ }
+
+ msg.data.id = ID_DATA;
+ for(;;) {
+ r = sdb_read(fd, buffer, SYNC_DATA_MAX);
+ if(r <= 0) {
+ if(r == 0) break;
+ if(errno == EINTR) continue;
+ r = fail_errno(s);
+ sdb_close(fd);
+ return r;
+ }
+ msg.data.size = htoll(r);
+ if(writex(s, &msg.data, sizeof(msg.data)) ||
+ writex(s, buffer, r)) {
+ sdb_close(fd);
+ return -1;
+ }
+ }
+
+ sdb_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");
+ sdb_close(fd);
+}
diff --git a/src/file_sync_service.h b/src/file_sync_service.h
new file mode 100644
index 0000000..84f06bb
--- /dev/null
+++ b/src/file_sync_service.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 HAVE_BIG_ENDIAN
+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 isUtf8);
+int do_sync_sync(const char *lpath, const char *rpath, int listonly);
+int do_sync_pull(const char *rpath, const char *lpath);
+
+#define SYNC_DATA_MAX (64*1024)
+
+#endif
diff --git a/src/framebuffer_service.c b/src/framebuffer_service.c
new file mode 100644
index 0000000..0aac145
--- /dev/null
+++ b/src/framebuffer_service.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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/types.h>
+#include <sys/wait.h>
+
+#include "fdevent.h"
+#include "sdb.h"
+
+#include <linux/fb.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+/* TODO:
+** - sync with vsync to avoid tearing
+*/
+/* This version number defines the format of the fbinfo struct.
+ It must match versioning in ddms where this data is consumed. */
+#define DDMS_RAWIMAGE_VERSION 1
+struct fbinfo {
+ unsigned int version;
+ unsigned int bpp;
+ unsigned int size;
+ unsigned int width;
+ unsigned int height;
+ unsigned int red_offset;
+ unsigned int red_length;
+ unsigned int blue_offset;
+ unsigned int blue_length;
+ unsigned int green_offset;
+ unsigned int green_length;
+ unsigned int alpha_offset;
+ unsigned int alpha_length;
+} __attribute__((packed));
+
+void framebuffer_service(int fd, void *cookie)
+{
+ struct fbinfo fbinfo;
+ unsigned int i;
+ char buf[640];
+ int fd_screencap;
+ int w, h, f;
+ int fds[2];
+ pid_t pid = 0;
+
+ if (pipe(fds) < 0) goto done;
+
+ pid = fork();
+ if (pid < 0) goto done;
+
+ if (pid == 0) {
+ dup2(fds[1], STDOUT_FILENO);
+ close(fds[0]);
+ close(fds[1]);
+ const char* command = "screencap";
+ const char *args[2] = {command, NULL};
+ execvp(command, (char**)args);
+ exit(1);
+ }
+
+ fd_screencap = fds[0];
+
+ /* read w, h & format */
+ if(readx(fd_screencap, &w, 4)) goto done;
+ if(readx(fd_screencap, &h, 4)) goto done;
+ if(readx(fd_screencap, &f, 4)) goto done;
+
+ fbinfo.version = DDMS_RAWIMAGE_VERSION;
+ /* see hardware/hardware.h */
+ switch (f) {
+ case 1: /* RGBA_8888 */
+ fbinfo.bpp = 32;
+ fbinfo.size = w * h * 4;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 0;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 16;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 8;
+ break;
+ case 2: /* RGBX_8888 */
+ fbinfo.bpp = 32;
+ fbinfo.size = w * h * 4;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 0;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 16;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 0;
+ break;
+ case 3: /* RGB_888 */
+ fbinfo.bpp = 24;
+ fbinfo.size = w * h * 3;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 0;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 16;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 0;
+ break;
+ case 4: /* RGB_565 */
+ fbinfo.bpp = 16;
+ fbinfo.size = w * h * 2;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 11;
+ fbinfo.red_length = 5;
+ fbinfo.green_offset = 5;
+ fbinfo.green_length = 6;
+ fbinfo.blue_offset = 0;
+ fbinfo.blue_length = 5;
+ fbinfo.alpha_offset = 0;
+ fbinfo.alpha_length = 0;
+ break;
+ case 5: /* BGRA_8888 */
+ fbinfo.bpp = 32;
+ fbinfo.size = w * h * 4;
+ fbinfo.width = w;
+ fbinfo.height = h;
+ fbinfo.red_offset = 16;
+ fbinfo.red_length = 8;
+ fbinfo.green_offset = 8;
+ fbinfo.green_length = 8;
+ fbinfo.blue_offset = 0;
+ fbinfo.blue_length = 8;
+ fbinfo.alpha_offset = 24;
+ fbinfo.alpha_length = 8;
+ break;
+ default:
+ goto done;
+ }
+
+ /* write header */
+ if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;
+
+ /* write data */
+ for(i = 0; i < fbinfo.size; i += sizeof(buf)) {
+ if(readx(fd_screencap, buf, sizeof(buf))) goto done;
+ if(writex(fd, buf, sizeof(buf))) goto done;
+ }
+ if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done;
+ if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done;
+
+done:
+ TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
+
+ close(fds[0]);
+ close(fds[1]);
+ close(fd);
+}
diff --git a/src/get_my_path_darwin.c b/src/get_my_path_darwin.c
new file mode 100644
index 0000000..880ccfd
--- /dev/null
+++ b/src/get_my_path_darwin.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+void get_my_path(char *s, size_t maxLen)
+{
+ ProcessSerialNumber psn;
+ GetCurrentProcess(&psn);
+ CFDictionaryRef dict;
+ dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+ CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
+ CFSTR("CFBundleExecutable"));
+ CFStringGetCString(value, s, maxLen, kCFStringEncodingUTF8);
+}
+
diff --git a/src/get_my_path_freebsd.c b/src/get_my_path_freebsd.c
new file mode 100644
index 0000000..628a2b0
--- /dev/null
+++ b/src/get_my_path_freebsd.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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, size_t maxLen)
+{
+ char proc[64];
+
+ snprintf(proc, sizeof(proc), "/proc/%d/file", getpid());
+
+ int err = readlink(proc, exe, maxLen - 1);
+
+ exe[err > 0 ? err : 0] = '\0';
+}
+
diff --git a/src/get_my_path_linux.c b/src/get_my_path_linux.c
new file mode 100644
index 0000000..08ba668
--- /dev/null
+++ b/src/get_my_path_linux.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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, size_t maxLen)
+{
+ char proc[64];
+ snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
+ int err = readlink(proc, exe, maxLen - 1);
+ if(err > 0) {
+ exe[err] = '\0';
+ } else {
+ exe[0] = '\0';
+ }
+}
+
diff --git a/src/get_my_path_windows.c b/src/get_my_path_windows.c
new file mode 100644
index 0000000..630e4d1
--- /dev/null
+++ b/src/get_my_path_windows.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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, size_t maxLen)
+{
+ char *r;
+
+ /* XXX: should be GetModuleFileNameA */
+ if (GetModuleFileName(NULL, exe, maxLen) > 0) {
+ r = strrchr(exe, '\\');
+ if (r != NULL)
+ *r = '\0';
+ } else {
+ exe[0] = '\0';
+ }
+}
+
diff --git a/src/jdwp_service.c b/src/jdwp_service.c
new file mode 100644
index 0000000..1b8b1dc
--- /dev/null
+++ b/src/jdwp_service.c
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+/* implement the "debug-ports" and "track-debug-ports" device services */
+#include "sysdeps.h"
+#define TRACE_TAG TRACE_JDWP
+#include "sdb.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* here's how these things work.
+
+ when sdbd 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 sdbd to detect dead
+ processes).
+
+ sdbd 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 "sdb 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,
+ sdbd 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:
+ ____________________________________
+ | |
+ | SDB Server (host) |
+ | |
+ Debugger <---> LocalSocket <----> RemoteSocket |
+ | ^^ |
+ |___________________________||_______|
+ ||
+ Transport ||
+ (TCP for emulator - USB for device) ||
+ ||
+ ___________________________||_______
+ | || |
+ | SDBD (device) || |
+ | VV |
+ JDWP <======> LocalSocket <----> RemoteSocket |
+ | |
+ |____________________________________|
+
+ due to the way sdb 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 !SDB_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) {
+ sdb_shutdown(proc->socket);
+ sdb_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++) {
+ sdb_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)];
+ int flags;
+
+ 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;
+
+ flags = fcntl(proc->socket,F_GETFL,0);
+
+ if (flags == -1) {
+ D("failed to get cntl flags for socket %d: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+
+ }
+
+ if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
+ D("failed to remove O_NONBLOCK flag for socket %d: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+ }
+
+ for (;;) {
+ ret = sendmsg(proc->socket, &msg, 0);
+ if (ret >= 0) {
+ sdb_close(fd);
+ 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 (fcntl(proc->socket, F_SETFL, flags) == -1) {
+ D("failed to set O_NONBLOCK flag for socket %d: %s\n",
+ proc->pid, strerror(errno));
+ goto CloseProcess;
+ }
+
+ 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 (sdb_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) );
+ sdb_close(s);
+ return -1;
+ }
+
+ if ( listen(s, 4) < 0 ) {
+ D("listen failed in jdwp control socket: %d: %s\n",
+ errno, strerror(errno));
+ sdb_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" );
+ sdb_close(s);
+ return -1;
+ }
+
+ /* only wait for incoming connections */
+ fdevent_add(control->fde, FDE_READ);
+ close_on_exec(s);
+
+ 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 = sdb_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 /* !SDB_HOST */
diff --git a/src/mutex_list.h b/src/mutex_list.h
new file mode 100644
index 0000000..3e55e9f
--- /dev/null
+++ b/src/mutex_list.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+/* the list of mutexes used by sdb */
+/* #ifndef __MUTEX_LIST_H
+ * Do not use an include-guard. This file is included once to declare the locks
+ * and once in win32 to actually do the runtime initialization.
+ */
+#ifndef SDB_MUTEX
+#error SDB_MUTEX not defined when including this file
+#endif
+SDB_MUTEX(dns_lock)
+SDB_MUTEX(socket_list_lock)
+SDB_MUTEX(transport_lock)
+#if SDB_HOST
+SDB_MUTEX(local_transports_lock)
+#endif
+SDB_MUTEX(usb_lock)
+
+// Sadly logging to /data/sdb/sdb-... is not thread safe.
+// After modifying sdb.h::D() to count invocations:
+// DEBUG(jpa):0:Handling main()
+// DEBUG(jpa):1:[ usb_init - starting thread ]
+// (Oopsies, no :2:, and matching message is also gone.)
+// DEBUG(jpa):3:[ usb_thread - opening device ]
+// DEBUG(jpa):4:jdwp control socket started (10)
+SDB_MUTEX(D_lock)
+
+#undef SDB_MUTEX
diff --git a/src/properties.c b/src/properties.c
new file mode 100644
index 0000000..078afc9
--- /dev/null
+++ b/src/properties.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include "sockets.h"
+#include <errno.h>
+#include <assert.h>
+
+#include "properties.h"
+//#include "loghack.h"
+#include "sysdeps.h"
+#define TRACE_TAG TRACE_PROPERTIES
+#include "sdb.h"
+
+#define HAVE_TIZEN_PROPERTY
+
+#ifdef HAVE_TIZEN_PROPERTY
+
+#define HAVE_PTHREADS
+#include <stdio.h>
+#include "threads.h"
+
+static mutex_t env_lock = MUTEX_INITIALIZER;
+
+#define TIZEN_PROPERTY_FILE "/tmp/.sdb.conf" /* tizen specific*/
+#define PROPERTY_SEPARATOR "="
+
+struct config_node {
+ char *key;
+ char value[PROPERTY_VALUE_MAX];
+} sdbd_config[] = {
+ { "service.sdb.tcp.port", "0" },
+ { NULL, "" }
+};
+
+void property_save();
+int read_line(const int fd, char* ptr, const unsigned int maxlen);
+
+static void property_init(void)
+{
+ int fd;
+ int i = 0;
+ char buffer[PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX+1];
+ char *tok = NULL;
+
+ fd = unix_open(TIZEN_PROPERTY_FILE, O_RDONLY);
+ if (fd < 0)
+ return;
+ for(;;) {
+ if(read_line(fd, buffer, PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX+1) < 0)
+ break;
+ tok = strtok(buffer, PROPERTY_SEPARATOR);
+ for (i = 0; sdbd_config[i].key; i++) {
+ if (!strcmp(tok, sdbd_config[i].key)) {
+ tok = strtok(NULL, PROPERTY_SEPARATOR);
+ strncpy(sdbd_config[i].value, tok, PROPERTY_VALUE_MAX);
+ D("property init key=%s, value=%s\n", sdbd_config[i].key, tok);
+ }
+ }
+
+ }
+ sdb_close(fd);
+ D("called property_init\n");
+}
+
+void property_save()
+{
+ int fd;
+ int i = 0;
+ char buffer[PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX+1];
+
+ mutex_lock(&env_lock);
+ if (access(TIZEN_PROPERTY_FILE, F_OK) == 0) // if exist
+ sdb_unlink(TIZEN_PROPERTY_FILE);
+
+ fd = unix_open(TIZEN_PROPERTY_FILE, O_WRONLY | O_CREAT | O_APPEND, 0640);
+ if (fd <0 )
+ return;
+
+ for (i = 0; sdbd_config[i].key; i++) {
+ sprintf(buffer,"%s%s%s\n", sdbd_config[i].key, PROPERTY_SEPARATOR, sdbd_config[i].value);
+ sdb_write(fd, buffer, strlen(buffer));
+ }
+ sdb_close(fd);
+ mutex_unlock(&env_lock);
+}
+
+int property_set(const char *key, const char *value)
+{
+ int i = 0;
+
+ mutex_lock(&env_lock);
+ for (i = 0; sdbd_config[i].key; i++) {
+ if (!strcmp(key,sdbd_config[i].key)) {
+ strncpy(sdbd_config[i].value, value, PROPERTY_VALUE_MAX);
+ D("property set key=%s, value=%s\n", key, value);
+ break;
+ }
+ }
+ mutex_unlock(&env_lock);
+ property_save();
+ return -1;
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ int len = 0;
+ int i = 0;
+
+ property_init();
+ mutex_lock(&env_lock);
+ for (i = 0; sdbd_config[i].key; i++) {
+ if (!strcmp(key,sdbd_config[i].key)) {
+ len = strlen(sdbd_config[i].value);
+ memcpy(value, sdbd_config[i].value, len + 1);
+ D("property get key=%s, value=%s\n", key, value);
+ mutex_unlock(&env_lock);
+ return len;
+ }
+ }
+
+ if(default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ D("by default, property get key=%s, value=%s\n", key, value);
+ }
+ mutex_unlock(&env_lock);
+ return len;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ return 0;
+}
+
+int read_line(const int fd, char* ptr, const unsigned int maxlen)
+{
+ unsigned int n = 0;
+ char c[2];
+ int rc;
+
+ while(n != maxlen) {
+ if((rc = sdb_read(fd, c, 1)) != 1)
+ return -1; // eof or read err
+
+ if(*c == '\n') {
+ ptr[n] = 0;
+ return n;
+ }
+ ptr[n++] = *c;
+ }
+ return -1; // no space
+}
+
+#elif defined(HAVE_LIBC_SYSTEM_PROPERTIES)
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+//#include <sys/_system_properties.h>
+
+int property_set(const char *key, const char *value)
+{
+ return __system_property_set(key, value);
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ int len;
+
+ len = __system_property_get(key, value);
+ if(len > 0) {
+ return len;
+ }
+
+ if(default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ }
+ return len;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+ const prop_info *pi;
+ unsigned n;
+
+ for(n = 0; (pi = __system_property_find_nth(n)); n++) {
+ __system_property_read(pi, name, value);
+ propfn(name, value, cookie);
+ }
+ return 0;
+}
+
+#elif defined(HAVE_SYSTEM_PROPERTY_SERVER)
+
+/*
+ * The Linux simulator provides a "system property server" that uses IPC
+ * to set/get/list properties. The file descriptor is shared by all
+ * threads in the process, so we use a mutex to ensure that requests
+ * from multiple threads don't get interleaved.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <pthread.h>
+
+static pthread_once_t gInitOnce = PTHREAD_ONCE_INIT;
+static pthread_mutex_t gPropertyFdLock = PTHREAD_MUTEX_INITIALIZER;
+static int gPropFd = -1;
+
+/*
+ * Connect to the properties server.
+ *
+ * Returns the socket descriptor on success.
+ */
+static int connectToServer(const char* fileName)
+{
+ int sock = -1;
+ int cc;
+
+ struct sockaddr_un addr;
+
+ sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ D("UNIX domain socket create failed (errno=%d)\n", errno);
+ return -1;
+ }
+
+ /* connect to socket; fails if file doesn't exist */
+ strcpy(addr.sun_path, fileName); // max 108 bytes
+ addr.sun_family = AF_UNIX;
+ cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr));
+ if (cc < 0) {
+ // ENOENT means socket file doesn't exist
+ // ECONNREFUSED means socket exists but nobody is listening
+ D("AF_UNIX connect failed for '%s': %s\n",
+ fileName, strerror(errno));
+ sdb_close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+/*
+ * Perform one-time initialization.
+ */
+static void init(void)
+{
+ assert(gPropFd == -1);
+
+ gPropFd = connectToServer(SYSTEM_PROPERTY_PIPE_NAME);
+ if (gPropFd < 0) {
+ D("not connected to system property server\n");
+ } else {
+ D("Connected to system property server\n");
+ }
+}
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ char sendBuf[1+PROPERTY_KEY_MAX];
+ char recvBuf[1+PROPERTY_VALUE_MAX];
+ int len = -1;
+
+ D("PROPERTY GET [%s]\n", key);
+
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0) {
+ /* this mimics the behavior of the device implementation */
+ if (default_value != NULL) {
+ strcpy(value, default_value);
+ len = strlen(value);
+ }
+ return len;
+ }
+
+ if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+
+ memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
+
+ sendBuf[0] = (char) kSystemPropertyGet;
+ strcpy(sendBuf+1, key);
+
+ pthread_mutex_lock(&gPropertyFdLock);
+ if (sdb_write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ if (sdb_read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ pthread_mutex_unlock(&gPropertyFdLock);
+
+ /* first byte is 0 if value not defined, 1 if found */
+ if (recvBuf[0] == 0) {
+ if (default_value != NULL) {
+ strcpy(value, default_value);
+ len = strlen(value);
+ } else {
+ /*
+ * If the value isn't defined, hand back an empty string and
+ * a zero length, rather than a failure. This seems wrong,
+ * since you can't tell the difference between "undefined" and
+ * "defined but empty", but it's what the device does.
+ */
+ value[0] = '\0';
+ len = 0;
+ }
+ } else if (recvBuf[0] == 1) {
+ strcpy(value, recvBuf+1);
+ len = strlen(value);
+ } else {
+ D("Got strange response to property_get request (%d)\n",
+ recvBuf[0]);
+ assert(0);
+ return -1;
+ }
+ D("PROP [found=%d def='%s'] (%d) [%s]: [%s]\n",
+ recvBuf[0], default_value, len, key, value);
+
+ return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+ char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
+ char recvBuf[1];
+ int result = -1;
+
+ D("PROPERTY SET [%s]: [%s]\n", key, value);
+
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0)
+ return -1;
+
+ if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
+ if (strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+ memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
+
+ sendBuf[0] = (char) kSystemPropertySet;
+ strcpy(sendBuf+1, key);
+ strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
+
+ pthread_mutex_lock(&gPropertyFdLock);
+ if (sdb_write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ if (sdb_read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
+ pthread_mutex_unlock(&gPropertyFdLock);
+ return -1;
+ }
+ pthread_mutex_unlock(&gPropertyFdLock);
+
+ if (recvBuf[0] != 1)
+ return -1;
+ return 0;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ D("PROPERTY LIST\n");
+ pthread_once(&gInitOnce, init);
+ if (gPropFd < 0)
+ return -1;
+
+ return 0;
+}
+
+#else
+
+/* SUPER-cheesy place-holder implementation for Win32 */
+#define HAVE_PTHREADS /*tizen specific */
+#include <stdio.h>
+#include "threads.h"
+
+static mutex_t env_lock = MUTEX_INITIALIZER;
+
+int property_get(const char *key, char *value, const char *default_value)
+{
+ char ename[PROPERTY_KEY_MAX + 6];
+ char *p;
+ int len;
+
+ len = strlen(key);
+ if(len >= PROPERTY_KEY_MAX) return -1;
+ memcpy(ename, "PROP_", 5);
+ memcpy(ename + 5, key, len + 1);
+
+ mutex_lock(&env_lock);
+
+ p = getenv(ename);
+ if(p == 0) p = "";
+ len = strlen(p);
+ if(len >= PROPERTY_VALUE_MAX) {
+ len = PROPERTY_VALUE_MAX - 1;
+ }
+
+ if((len == 0) && default_value) {
+ len = strlen(default_value);
+ memcpy(value, default_value, len + 1);
+ } else {
+ memcpy(value, p, len);
+ value[len] = 0;
+ }
+
+ mutex_unlock(&env_lock);
+ D("get [key=%s value='%s:%s']\n", key, ename, value);
+ return len;
+}
+
+
+int property_set(const char *key, const char *value)
+{
+ char ename[PROPERTY_KEY_MAX + 6];
+ char *p;
+ int len;
+ int r;
+
+ if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
+
+ len = strlen(key);
+ if(len >= PROPERTY_KEY_MAX) return -1;
+ memcpy(ename, "PROP_", 5);
+ memcpy(ename + 5, key, len + 1);
+
+ mutex_lock(&env_lock);
+#ifdef HAVE_MS_C_RUNTIME
+ {
+ char temp[256];
+ snprintf( temp, sizeof(temp), "%s=%s", ename, value);
+ putenv(temp);
+ r = 0;
+ }
+#else
+ r = setenv(ename, value, 1);
+#endif
+ mutex_unlock(&env_lock);
+ D("set [key=%s value='%s%s']\n", key, ename, value);
+ return r;
+}
+
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie),
+ void *cookie)
+{
+ return 0;
+}
+
+#endif
diff --git a/src/properties.h b/src/properties.h
new file mode 100644
index 0000000..08f9d18
--- /dev/null
+++ b/src/properties.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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/sdb-sysporp"
+
+enum {
+ kSystemPropertyUnknown = 0,
+ kSystemPropertyGet,
+ kSystemPropertySet,
+ kSystemPropertyList
+};
+#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/protocol.txt b/src/protocol.txt
new file mode 100644
index 0000000..e1e84d0
--- /dev/null
+++ b/src/protocol.txt
@@ -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 sdb 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>
+
diff --git a/src/qemu_pipe.h b/src/qemu_pipe.h
new file mode 100644
index 0000000..5d6e87e
--- /dev/null
+++ b/src/qemu_pipe.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_INCLUDE_HARDWARE_QEMU_PIPE_H
+#define ANDROID_INCLUDE_HARDWARE_QEMU_PIPE_H
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h> /* for pthread_once() */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef D
+# define D(...) do{}while(0)
+#endif
+
+/* Try to open a new Qemu fast-pipe. This function returns a file descriptor
+ * that can be used to communicate with a named service managed by the
+ * emulator.
+ *
+ * This file descriptor can be used as a standard pipe/socket descriptor.
+ *
+ * 'pipeName' is the name of the emulator service you want to connect to.
+ * E.g. 'opengles' or 'camera'.
+ *
+ * On success, return a valid file descriptor
+ * Returns -1 on error, and errno gives the error code, e.g.:
+ *
+ * EINVAL -> unknown/unsupported pipeName
+ * ENOSYS -> fast pipes not available in this system.
+ *
+ * ENOSYS should never happen, except if you're trying to run within a
+ * misconfigured emulator.
+ *
+ * You should be able to open several pipes to the same pipe service,
+ * except for a few special cases (e.g. GSM modem), where EBUSY will be
+ * returned if more than one client tries to connect to it.
+ */
+static __inline__ int
+qemu_pipe_open(const char* pipeName)
+{
+ char buff[256];
+ int buffLen;
+ int fd, ret;
+
+ if (pipeName == NULL || pipeName[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ snprintf(buff, sizeof buff, "pipe:%s", pipeName);
+
+ fd = open("/dev/qemu_pipe", O_RDWR);
+ if (fd < 0) {
+ D("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__, strerror(errno));
+ //errno = ENOSYS;
+ return -1;
+ }
+
+ buffLen = strlen(buff);
+
+ ret = TEMP_FAILURE_RETRY(write(fd, buff, buffLen+1));
+ if (ret != buffLen+1) {
+ D("%s: Could not connect to %s pipe service: %s", __FUNCTION__, pipeName, strerror(errno));
+ if (ret == 0) {
+ errno = ECONNRESET;
+ } else if (ret > 0) {
+ errno = EINVAL;
+ }
+ return -1;
+ }
+
+ return fd;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/src/remount_service.c b/src/remount_service.c
new file mode 100644
index 0000000..61d439b
--- /dev/null
+++ b/src/remount_service.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_SDB
+#include "sdb.h"
+
+
+static int system_ro = 1;
+
+/* Returns the device used to mount a directory in /proc/mounts */
+static char *find_mount(const char *dir)
+{
+ int fd;
+ int res;
+ int size;
+ char *token = NULL;
+ const char delims[] = "\n";
+ char buf[4096];
+
+ fd = unix_open("/proc/mounts", O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ buf[sizeof(buf) - 1] = '\0';
+ size = sdb_read(fd, buf, sizeof(buf) - 1);
+ sdb_close(fd);
+
+ token = strtok(buf, delims);
+
+ while (token) {
+ char mount_dev[256];
+ char mount_dir[256];
+ int mount_freq;
+ int mount_passno;
+
+ res = sscanf(token, "%255s %255s %*s %*s %d %d\n",
+ mount_dev, mount_dir, &mount_freq, &mount_passno);
+ mount_dev[255] = 0;
+ mount_dir[255] = 0;
+ if (res == 4 && (strcmp(dir, mount_dir) == 0))
+ return strdup(mount_dev);
+
+ token = strtok(NULL, delims);
+ }
+ return NULL;
+}
+
+/* Init mounts /system as read only, remount to enable writes. */
+static int remount_system()
+{
+ char *dev;
+
+ if (system_ro == 0) {
+ return 0;
+ }
+
+ dev = find_mount("/system");
+
+ if (!dev)
+ return -1;
+
+ system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL);
+
+ free(dev);
+
+ 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);
+ }
+
+ sdb_close(fd);
+}
+
diff --git a/src/sdb.c b/src/sdb.c
new file mode 100755
index 0000000..a92ab5b
--- /dev/null
+++ b/src/sdb.c
@@ -0,0 +1,1477 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 TRACE_TAG TRACE_SDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include "sysdeps.h"
+#include "sdb.h"
+
+#if !SDB_HOST
+//#include <private/android_filesystem_config.h> eric
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#define SDB_PIDPATH "/var/run/sdbd.pid"
+#else
+#include "usb_vendors.h"
+#endif
+
+#if SDB_TRACE
+SDB_MUTEX_DEFINE( D_lock );
+#endif
+
+int HOST = 0;
+
+/*
+void handle_sig_term(int sig) {
+#ifdef SDB_PIDPATH
+ if (access(SDB_PIDPATH, F_OK) == 0)
+ sdb_unlink(SDB_PIDPATH);
+#endif
+ if (access("/dev/samsung_sdb", F_OK) == 0) {
+ exit(0);
+ } else {
+ // do nothing on a emulator
+ }
+}*/
+
+static const char *sdb_device_banner = "device";
+
+void fatal(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 fatal_errno(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ fprintf(stderr, "error: %s: ", strerror(errno));
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(-1);
+}
+
+int sdb_trace_mask;
+
+/* read a comma/space/colum/semi-column separated list of tags
+ * from the SDB_TRACE environment variable and build the trace
+ * mask from it. note that '1' and 'all' are special cases to
+ * enable all tracing
+ */
+void sdb_trace_init(void)
+{
+ const char* p = getenv("SDB_TRACE");
+ const char* q;
+
+ static const struct {
+ const char* tag;
+ int flag;
+ } tags[] = {
+ { "1", 0 },
+ { "all", 0 },
+ { "sdb", TRACE_SDB },
+ { "sockets", TRACE_SOCKETS },
+ { "packets", TRACE_PACKETS },
+ { "rwx", TRACE_RWX },
+ { "usb", TRACE_USB },
+ { "sync", TRACE_SYNC },
+ { "sysdeps", TRACE_SYSDEPS },
+ { "transport", TRACE_TRANSPORT },
+ { "jdwp", TRACE_JDWP },
+ { "services", TRACE_SERVICES },
+ { "properties", TRACE_PROPERTIES },
+ { NULL, 0 }
+ };
+
+ if (p == NULL)
+ return;
+
+ /* use a comma/column/semi-colum/space separated list */
+ while (*p) {
+ int len, tagn;
+
+ q = strpbrk(p, " ,:;");
+ if (q == NULL) {
+ q = p + strlen(p);
+ }
+ len = q - p;
+
+ for (tagn = 0; tags[tagn].tag != NULL; tagn++)
+ {
+ int taglen = strlen(tags[tagn].tag);
+
+ if (len == taglen && !memcmp(tags[tagn].tag, p, len) )
+ {
+ int flag = tags[tagn].flag;
+ if (flag == 0) {
+ sdb_trace_mask = ~0;
+ return;
+ }
+ sdb_trace_mask |= (1 << flag);
+ break;
+ }
+ }
+ p = q;
+ if (*p)
+ p++;
+ }
+}
+
+#if !SDB_HOST
+/*
+ * Implements SDB tracing inside the emulator.
+ */
+
+#include <stdarg.h>
+
+/*
+ * Redefine open and write for qemu_pipe.h that contains inlined references
+ * to those routines. We will redifine them back after qemu_pipe.h inclusion.
+ */
+
+#undef open
+#undef write
+#define open sdb_open
+#define write sdb_write
+#include "qemu_pipe.h"
+#undef open
+#undef write
+#define open ___xxx_open
+#define write ___xxx_write
+
+/* A handle to sdb-debug qemud service in the emulator. */
+int sdb_debug_qemu = -1;
+
+/* Initializes connection with the sdb-debug qemud service in the emulator. */
+#if 0 /* doen't support in Tizen */
+static int sdb_qemu_trace_init(void)
+{
+ char con_name[32];
+
+ if (sdb_debug_qemu >= 0) {
+ return 0;
+ }
+
+ /* sdb debugging QEMUD service connection request. */
+ snprintf(con_name, sizeof(con_name), "qemud:sdb-debug");
+ sdb_debug_qemu = qemu_pipe_open(con_name);
+ return (sdb_debug_qemu >= 0) ? 0 : -1;
+}
+
+void sdb_qemu_trace(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ char msg[1024];
+
+ if (sdb_debug_qemu >= 0) {
+ vsnprintf(msg, sizeof(msg), fmt, args);
+ sdb_write(sdb_debug_qemu, msg, strlen(msg));
+ }
+}
+#endif
+#endif /* !SDB_HOST */
+
+apacket *get_apacket(void)
+{
+ apacket *p = malloc(sizeof(apacket));
+ if(p == 0) fatal("failed to allocate an apacket");
+ memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+ return p;
+}
+
+void put_apacket(apacket *p)
+{
+ free(p);
+}
+
+void handle_online(void)
+{
+ D("sdb: online\n");
+}
+
+void handle_offline(atransport *t)
+{
+ D("sdb: offline\n");
+ //Close the associated usb
+ run_transport_disconnects(t);
+}
+
+#if TRACE_PACKETS
+#define DUMPMAX 32
+void print_packet(const char *label, apacket *p)
+{
+ char *tag;
+ char *x;
+ unsigned count;
+
+ switch(p->msg.command){
+ case A_SYNC: tag = "SYNC"; break;
+ case A_CNXN: tag = "CNXN" ; break;
+ case A_OPEN: tag = "OPEN"; break;
+ case A_OKAY: tag = "OKAY"; break;
+ case A_CLSE: tag = "CLSE"; break;
+ case A_WRTE: tag = "WRTE"; break;
+ default: tag = "????"; break;
+ }
+
+ fprintf(stderr, "%s: %s %08x %08x %04x \"",
+ label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
+ count = p->msg.data_length;
+ x = (char*) p->data;
+ if(count > DUMPMAX) {
+ count = DUMPMAX;
+ tag = "\n";
+ } else {
+ tag = "\"\n";
+ }
+ while(count-- > 0){
+ if((*x >= ' ') && (*x < 127)) {
+ fputc(*x, stderr);
+ } else {
+ fputc('.', stderr);
+ }
+ x++;
+ }
+ fprintf(stderr, tag);
+}
+#endif
+
+static void send_ready(unsigned local, unsigned remote, atransport *t)
+{
+ D("Calling send_ready \n");
+ apacket *p = get_apacket();
+ p->msg.command = A_OKAY;
+ p->msg.arg0 = local;
+ p->msg.arg1 = remote;
+ send_packet(p, t);
+}
+
+static void send_close(unsigned local, unsigned remote, atransport *t)
+{
+ D("Calling send_close \n");
+ apacket *p = get_apacket();
+ p->msg.command = A_CLSE;
+ p->msg.arg0 = local;
+ p->msg.arg1 = remote;
+ send_packet(p, t);
+}
+
+static void send_connect(atransport *t)
+{
+ D("Calling send_connect \n");
+ apacket *cp = get_apacket();
+ cp->msg.command = A_CNXN;
+ cp->msg.arg0 = A_VERSION;
+ cp->msg.arg1 = MAX_PAYLOAD;
+ snprintf((char*) cp->data, sizeof cp->data, "%s::",
+ HOST ? "host" : sdb_device_banner);
+ cp->msg.data_length = strlen((char*) cp->data) + 1;
+ send_packet(cp, t);
+#if SDB_HOST
+ /* XXX why sleep here? */
+ // allow the device some time to respond to the connect message
+ sdb_sleep_ms(1000);
+#endif
+}
+
+static char *connection_state_name(atransport *t)
+{
+ if (t == NULL) {
+ return "unknown";
+ }
+
+ switch(t->connection_state) {
+ case CS_BOOTLOADER:
+ return "bootloader";
+ case CS_DEVICE:
+ return "device";
+ case CS_OFFLINE:
+ return "offline";
+ default:
+ return "unknown";
+ }
+}
+
+void parse_banner(char *banner, atransport *t)
+{
+ char *type, *product, *end;
+
+ D("parse_banner: %s\n", banner);
+ type = banner;
+ product = strchr(type, ':');
+ if(product) {
+ *product++ = 0;
+ } else {
+ product = "";
+ }
+
+ /* remove trailing ':' */
+ end = strchr(product, ':');
+ if(end) *end = 0;
+
+ /* save product name in device structure */
+ if (t->product == NULL) {
+ t->product = strdup(product);
+ } else if (strcmp(product, t->product) != 0) {
+ free(t->product);
+ t->product = strdup(product);
+ }
+
+ if(!strcmp(type, "bootloader")){
+ D("setting connection_state to CS_BOOTLOADER\n");
+ t->connection_state = CS_BOOTLOADER;
+ update_transports();
+ return;
+ }
+
+ if(!strcmp(type, "device")) {
+ D("setting connection_state to CS_DEVICE\n");
+ t->connection_state = CS_DEVICE;
+ update_transports();
+ return;
+ }
+
+ if(!strcmp(type, "recovery")) {
+ D("setting connection_state to CS_RECOVERY\n");
+ t->connection_state = CS_RECOVERY;
+ update_transports();
+ return;
+ }
+
+ if(!strcmp(type, "sideload")) {
+ D("setting connection_state to CS_SIDELOAD\n");
+ t->connection_state = CS_SIDELOAD;
+ update_transports();
+ return;
+ }
+
+ t->connection_state = CS_HOST;
+}
+
+void handle_packet(apacket *p, atransport *t)
+{
+ asocket *s;
+
+ D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
+ ((char*) (&(p->msg.command)))[1],
+ ((char*) (&(p->msg.command)))[2],
+ ((char*) (&(p->msg.command)))[3]);
+
+ print_packet("recv", p);
+
+ switch(p->msg.command){
+ case A_SYNC:
+ if(p->msg.arg0){
+ send_packet(p, t);
+ if(HOST) send_connect(t);
+ } else {
+ t->connection_state = CS_OFFLINE;
+ handle_offline(t);
+ send_packet(p, t);
+ }
+ return;
+
+ case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
+ /* XXX verify version, etc */
+ if(t->connection_state != CS_OFFLINE) {
+ t->connection_state = CS_OFFLINE;
+ handle_offline(t);
+ }
+ parse_banner((char*) p->data, t);
+ handle_online();
+ if(!HOST) send_connect(t);
+ break;
+
+ case A_OPEN: /* OPEN(local-id, 0, "destination") */
+ if(t->connection_state != CS_OFFLINE) {
+ char *name = (char*) p->data;
+ name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
+ s = create_local_service_socket(name);
+ if(s == 0) {
+ send_close(0, p->msg.arg0, t);
+ } else {
+ s->peer = create_remote_socket(p->msg.arg0, t);
+ s->peer->peer = s;
+ send_ready(s->id, s->peer->id, t);
+ s->ready(s);
+ }
+ }
+ break;
+
+ case A_OKAY: /* READY(local-id, remote-id, "") */
+ if(t->connection_state != CS_OFFLINE) {
+ if((s = find_local_socket(p->msg.arg1))) {
+ if(s->peer == 0) {
+ s->peer = create_remote_socket(p->msg.arg0, t);
+ s->peer->peer = s;
+ }
+ s->ready(s);
+ }
+ }
+ break;
+
+ case A_CLSE: /* CLOSE(local-id, remote-id, "") */
+ if(t->connection_state != CS_OFFLINE) {
+ if((s = find_local_socket(p->msg.arg1))) {
+ s->close(s);
+ }
+ }
+ break;
+
+ case A_WRTE:
+ if(t->connection_state != CS_OFFLINE) {
+ if((s = find_local_socket(p->msg.arg1))) {
+ unsigned rid = p->msg.arg0;
+ p->len = p->msg.data_length;
+
+ if(s->enqueue(s, p) == 0) {
+ D("Enqueue the socket\n");
+ send_ready(s->id, rid, t);
+ }
+ return;
+ }
+ }
+ break;
+
+ default:
+ printf("handle_packet: what is %08x?!\n", p->msg.command);
+ }
+
+ put_apacket(p);
+}
+
+alistener listener_list = {
+ .next = &listener_list,
+ .prev = &listener_list,
+};
+
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l)
+{
+ asocket *s;
+
+ if(ev & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int fd;
+
+ alen = sizeof(addr);
+ fd = sdb_socket_accept(_fd, &addr, &alen);
+ if(fd < 0) return;
+
+ sdb_socket_setbufsize(fd, CHUNK_SIZE);
+
+ s = create_local_socket(fd);
+ if(s) {
+ connect_to_smartsocket(s);
+ return;
+ }
+
+ sdb_close(fd);
+ }
+}
+
+static void listener_event_func(int _fd, unsigned ev, void *_l)
+{
+ alistener *l = _l;
+ asocket *s;
+
+ if(ev & FDE_READ) {
+ struct sockaddr addr;
+ socklen_t alen;
+ int fd;
+
+ alen = sizeof(addr);
+ fd = sdb_socket_accept(_fd, &addr, &alen);
+ if(fd < 0) return;
+
+ s = create_local_socket(fd);
+ if(s) {
+ s->transport = l->transport;
+ connect_to_remote(s, l->connect_to);
+ return;
+ }
+
+ sdb_close(fd);
+ }
+}
+
+static void free_listener(alistener* l)
+{
+ if (l->next) {
+ l->next->prev = l->prev;
+ l->prev->next = l->next;
+ l->next = l->prev = l;
+ }
+
+ // closes the corresponding fd
+ fdevent_remove(&l->fde);
+
+ if (l->local_name)
+ free((char*)l->local_name);
+
+ if (l->connect_to)
+ free((char*)l->connect_to);
+
+ if (l->transport) {
+ remove_transport_disconnect(l->transport, &l->disconnect);
+ }
+ free(l);
+}
+
+static void listener_disconnect(void* _l, atransport* t)
+{
+ alistener* l = _l;
+
+ free_listener(l);
+}
+
+int local_name_to_fd(const char *name)
+{
+ int port;
+
+ if(!strncmp("tcp:", name, 4)){
+ int ret;
+ port = atoi(name + 4);
+ ret = socket_loopback_server(port, SOCK_STREAM);
+ return ret;
+ }
+#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */
+ // It's non-sensical to support the "reserved" space on the sdb host side
+ if(!strncmp(name, "local:", 6)) {
+ return socket_local_server(name + 6,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if(!strncmp(name, "localabstract:", 14)) {
+ return socket_local_server(name + 14,
+ ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
+ } else if(!strncmp(name, "localfilesystem:", 16)) {
+ return socket_local_server(name + 16,
+ ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM);
+ }
+
+#endif
+ printf("unknown local portname '%s'\n", name);
+ return -1;
+}
+
+static int remove_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+ alistener *l;
+
+ for (l = listener_list.next; l != &listener_list; l = l->next) {
+ if (!strcmp(local_name, l->local_name) &&
+ !strcmp(connect_to, l->connect_to) &&
+ l->transport && l->transport == transport) {
+
+ listener_disconnect(l, transport);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int install_listener(const char *local_name, const char *connect_to, atransport* transport)
+{
+ alistener *l;
+
+ //printf("install_listener('%s','%s')\n", local_name, connect_to);
+
+ for(l = listener_list.next; l != &listener_list; l = l->next){
+ if(strcmp(local_name, l->local_name) == 0) {
+ char *cto;
+
+ /* can't repurpose a smartsocket */
+ if(l->connect_to[0] == '*') {
+ return -1;
+ }
+
+ cto = strdup(connect_to);
+ if(cto == 0) {
+ return -1;
+ }
+
+ //printf("rebinding '%s' to '%s'\n", local_name, connect_to);
+ free((void*) l->connect_to);
+ l->connect_to = cto;
+ if (l->transport != transport) {
+ remove_transport_disconnect(l->transport, &l->disconnect);
+ l->transport = transport;
+ add_transport_disconnect(l->transport, &l->disconnect);
+ }
+ return 0;
+ }
+ }
+
+ if((l = calloc(1, sizeof(alistener))) == 0) goto nomem;
+ if((l->local_name = strdup(local_name)) == 0) goto nomem;
+ if((l->connect_to = strdup(connect_to)) == 0) goto nomem;
+
+
+ l->fd = local_name_to_fd(local_name);
+ if(l->fd < 0) {
+ free((void*) l->local_name);
+ free((void*) l->connect_to);
+ free(l);
+ printf("cannot bind '%s'\n", local_name);
+ return -2;
+ }
+
+ close_on_exec(l->fd);
+ if(!strcmp(l->connect_to, "*smartsocket*")) {
+ fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);
+ } else {
+ fdevent_install(&l->fde, l->fd, listener_event_func, l);
+ }
+ fdevent_set(&l->fde, FDE_READ);
+
+ l->next = &listener_list;
+ l->prev = listener_list.prev;
+ l->next->prev = l;
+ l->prev->next = l;
+ l->transport = transport;
+
+ if (transport) {
+ l->disconnect.opaque = l;
+ l->disconnect.func = listener_disconnect;
+ add_transport_disconnect(transport, &l->disconnect);
+ }
+ return 0;
+
+nomem:
+ fatal("cannot allocate listener");
+ return 0;
+}
+
+#ifdef HAVE_WIN32_PROC
+static BOOL WINAPI ctrlc_handler(DWORD type)
+{
+ exit(STATUS_CONTROL_C_EXIT);
+ return TRUE;
+}
+#endif
+
+static void sdb_cleanup(void)
+{
+ usb_cleanup();
+}
+
+void start_logging(void)
+{
+#ifdef HAVE_WIN32_PROC
+ char temp[ MAX_PATH ];
+ FILE* fnul;
+ FILE* flog;
+
+ GetTempPath( sizeof(temp) - 8, temp );
+ strcat( temp, "sdb.log" );
+
+ /* Win32 specific redirections */
+ fnul = fopen( "NUL", "rt" );
+ if (fnul != NULL)
+ stdin[0] = fnul[0];
+
+ flog = fopen( temp, "at" );
+ if (flog == NULL)
+ flog = fnul;
+
+ setvbuf( flog, NULL, _IONBF, 0 );
+
+ stdout[0] = flog[0];
+ stderr[0] = flog[0];
+ fprintf(stderr,"--- sdb starting (pid %d) ---\n", getpid());
+#else
+ int fd;
+
+ fd = unix_open("/dev/null", O_RDONLY);
+ dup2(fd, 0);
+ sdb_close(fd);
+
+ fd = unix_open("/tmp/sdb.log", O_WRONLY | O_CREAT | O_APPEND, 0640);
+ if(fd < 0) {
+ fd = unix_open("/dev/null", O_WRONLY);
+ }
+ dup2(fd, 1);
+ dup2(fd, 2);
+ sdb_close(fd);
+ fprintf(stderr,"--- sdb starting (pid %d) ---\n", getpid());
+#endif
+}
+
+#if !SDB_HOST
+void start_device_log(void)
+{
+ int fd;
+ char path[PATH_MAX];
+ struct tm now;
+ time_t t;
+// char value[PROPERTY_VALUE_MAX];
+ const char* p = getenv("SDB_TRACE");
+ // read the trace mask from persistent property persist.sdb.trace_mask
+ // give up if the property is not set or cannot be parsed
+#if 0 /* tizen specific */
+ property_get("persist.sdb.trace_mask", value, "");
+ if (sscanf(value, "%x", &sdb_trace_mask) != 1)
+ return;
+#endif
+
+ if (p == NULL) {
+ return;
+ }
+ tzset();
+ time(&t);
+ localtime_r(&t, &now);
+ strftime(path, sizeof(path),
+ "/tmp/sdbd-%Y-%m-%d-%H-%M-%S.txt",
+ &now);
+ fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+ if (fd < 0)
+ return;
+
+ // redirect stdout and stderr to the log file
+ dup2(fd, 1);
+ dup2(fd, 2);
+ fprintf(stderr,"--- sdbd starting (pid %d) ---\n", getpid());
+ sdb_close(fd);
+
+ fd = unix_open("/dev/null", O_RDONLY);
+ dup2(fd, 0);
+ sdb_close(fd);
+}
+
+int daemonize(void) {
+
+ // set file creation mask to 0
+ umask(0);
+
+ switch (fork()) {
+ case -1:
+ return -1;
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+#ifdef SDB_PIDPATH
+ FILE *f = fopen(SDB_PIDPATH, "w");
+
+ if (f != NULL) {
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+ }
+#endif
+ if (setsid() == -1)
+ return -1;
+
+ if (chdir("/") < 0)
+ D("sdbd: unable to change working directory to /\n");
+
+ return 0;
+}
+#endif
+
+#if SDB_HOST
+int launch_server(int server_port)
+{
+#ifdef HAVE_WIN32_PROC
+ /* we need to start the server in the background */
+ /* we create a PIPE that will be used to wait for the server's "OK" */
+ /* message since the pipe handles must be inheritable, we use a */
+ /* security attribute */
+ HANDLE pipe_read, pipe_write;
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+ char program_path[ MAX_PATH ];
+ int ret;
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ /* create pipe, and ensure its read handle isn't inheritable */
+ ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
+ if (!ret) {
+ fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+ return -1;
+ }
+
+ SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+
+ ZeroMemory( &startup, sizeof(startup) );
+ startup.cb = sizeof(startup);
+ startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
+ startup.hStdOutput = pipe_write;
+ startup.hStdError = GetStdHandle( STD_ERROR_HANDLE );
+ startup.dwFlags = STARTF_USESTDHANDLES;
+
+ ZeroMemory( &pinfo, sizeof(pinfo) );
+
+ /* get path of current program */
+ GetModuleFileName( NULL, program_path, sizeof(program_path) );
+
+ ret = CreateProcess(
+ program_path, /* program path */
+ "sdb fork-server server",
+ /* the fork-server argument will set the
+ debug = 2 in the child */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ TRUE, /* yes, inherit some handles */
+ DETACHED_PROCESS, /* the new process doesn't have a console */
+ NULL, /* use parent's environment block */
+ NULL, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo );
+
+ CloseHandle( pipe_write );
+
+ if (!ret) {
+ fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+ CloseHandle( pipe_read );
+ return -1;
+ }
+
+ CloseHandle( pinfo.hProcess );
+ CloseHandle( pinfo.hThread );
+
+ /* wait for the "OK\n" message */
+ {
+ char temp[3];
+ DWORD count;
+
+ ret = ReadFile( pipe_read, temp, 3, &count, NULL );
+ CloseHandle( pipe_read );
+ if ( !ret ) {
+ fprintf(stderr, "could not read ok from SDB Server, error = %ld\n", GetLastError() );
+ return -1;
+ }
+ if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+ fprintf(stderr, "SDB server didn't ACK\n" );
+ return -1;
+ }
+ }
+#elif defined(HAVE_FORKEXEC)
+ char path[PATH_MAX];
+ int fd[2];
+
+ // set up a pipe so the child can tell us when it is ready.
+ // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child.
+ if (pipe(fd)) {
+ fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
+ return -1;
+ }
+ get_my_path(path, PATH_MAX);
+ pid_t pid = fork();
+ if(pid < 0) return -1;
+
+ if (pid == 0) {
+ // child side of the fork
+
+ // redirect stderr to the pipe
+ // we use stderr instead of stdout due to stdout's buffering behavior.
+ sdb_close(fd[0]);
+ dup2(fd[1], STDERR_FILENO);
+ sdb_close(fd[1]);
+
+ // child process
+ int result = execl(path, "sdb", "fork-server", "server", NULL);
+ // this should not return
+ fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+ } else {
+ // parent side of the fork
+
+ char temp[3];
+
+ temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
+ // wait for the "OK\n" message
+ sdb_close(fd[1]);
+ int ret = sdb_read(fd[0], temp, 3);
+ int saved_errno = errno;
+ sdb_close(fd[0]);
+ if (ret < 0) {
+ fprintf(stderr, "could not read ok from SDB Server, errno = %d\n", saved_errno);
+ return -1;
+ }
+ if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
+ fprintf(stderr, "SDB server didn't ACK\n" );
+ return -1;
+ }
+
+ setsid();
+ }
+#else
+#error "cannot implement background server start on this platform"
+#endif
+ return 0;
+}
+#endif
+
+/* Constructs a local name of form tcp:port.
+ * target_str points to the target string, it's content will be overwritten.
+ * target_size is the capacity of the target string.
+ * server_port is the port number to use for the local name.
+ */
+void build_local_name(char* target_str, size_t target_size, int server_port)
+{
+ snprintf(target_str, target_size, "tcp:%d", server_port);
+}
+
+#if 0
+#if !SDB_HOST
+static int should_drop_privileges() {
+#ifndef ALLOW_SDBD_ROOT
+ return 1;
+#else /* ALLOW_SDBD_ROOT */
+ int secure = 0;
+ char value[PROPERTY_VALUE_MAX];
+
+ /* run sdbd in secure mode if ro.secure is set and
+ ** we are not in the emulator
+ */
+ property_get("ro.kernel.qemu", value, "");
+ if (strcmp(value, "1") != 0) {
+ property_get("ro.secure", value, "1");
+ if (strcmp(value, "1") == 0) {
+ // don't run as root if ro.secure is set...
+ secure = 1;
+
+ // ... except we allow running as root in userdebug builds if the
+ // service.sdb.root property has been set by the "sdb root" command
+ property_get("ro.debuggable", value, "");
+ if (strcmp(value, "1") == 0) {
+ property_get("service.sdb.root", value, "");
+ if (strcmp(value, "1") == 0) {
+ secure = 0;
+ }
+ }
+ }
+ }
+ return secure;
+#endif /* ALLOW_SDBD_ROOT */
+}
+#endif /* !SDB_HOST */
+#endif
+
+int sdb_main(int is_daemon, int server_port)
+{
+#if !SDB_HOST
+ int port;
+ char value[PROPERTY_VALUE_MAX];
+
+ umask(000);
+#endif
+
+ atexit(sdb_cleanup);
+#ifdef HAVE_WIN32_PROC
+ SetConsoleCtrlHandler( ctrlc_handler, TRUE );
+#elif defined(HAVE_FORKEXEC)
+ // No SIGCHLD. Let the service subproc handle its children.
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ init_transport_registration();
+
+
+#if SDB_HOST
+ HOST = 1;
+ usb_vendors_init();
+ usb_init();
+ local_init(DEFAULT_SDB_LOCAL_TRANSPORT_PORT);
+
+ char local_name[30];
+ build_local_name(local_name, sizeof(local_name), server_port);
+ if(install_listener(local_name, "*smartsocket*", NULL)) {
+ exit(1);
+ }
+#else
+#if 0 /* tizen specific */
+ /* don't listen on a port (default 5037) if running in secure mode */
+ /* don't run as root if we are running in secure mode */
+ if (should_drop_privileges()) {
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
+ exit(1);
+ }
+
+ /* add extra groups:
+ ** AID_SDB to access the USB driver
+ ** AID_LOG to read system logs (sdb logcat)
+ ** AID_INPUT to diagnose input issues (getevent)
+ ** AID_INET to diagnose network issues (netcfg, ping)
+ ** AID_GRAPHICS to access the frame buffer
+ ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+ ** AID_SDCARD_R to allow reading from the SD card
+ ** AID_SDCARD_RW to allow writing to the SD card
+ ** AID_MOUNT to allow unmounting the SD card before rebooting
+ ** AID_NET_BW_STATS to read out qtaguid statistics
+ */
+ gid_t groups[] = { AID_SDB, AID_LOG, AID_INPUT, AID_INET, AID_GRAPHICS,
+ AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
+ AID_MOUNT, AID_NET_BW_STATS };
+ if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
+ exit(1);
+ }
+
+ /* then switch user and group to "shell" */
+ if (setgid(AID_SHELL) != 0) {
+ exit(1);
+ }
+ if (setuid(AID_SHELL) != 0) {
+ exit(1);
+ }
+
+ /* set CAP_SYS_BOOT capability, so "sdb reboot" will succeed */
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+ cap.effective = cap.permitted = (1 << CAP_SYS_BOOT);
+ cap.inheritable = 0;
+ capset(&header, &cap);
+
+ D("Local port disabled\n");
+ } else {
+#endif
+ char local_name[30];
+ build_local_name(local_name, sizeof(local_name), server_port);
+ if(install_listener(local_name, "*smartsocket*", NULL)) {
+ exit(1);
+ }
+#if 0 /* tizen specific */
+ }
+#endif
+ /* for the device, start the usb transport if the
+ ** android usb device exists and the "service.sdb.tcp.port" and
+ ** "persist.sdb.tcp.port" properties are not set.
+ ** Otherwise start the network transport.
+ */
+ property_get("service.sdb.tcp.port", value, "");
+#if 0 /* tizen specific */
+ if (!value[0])
+ property_get("persist.sdb.tcp.port", value, "");
+#endif
+ if (sscanf(value, "%d", &port) == 1 && port > 0) {
+ // listen on TCP port specified by service.sdb.tcp.port property
+ local_init(port);
+ } else if (access("/dev/samsung_sdb", F_OK) == 0) {
+ // listen on USB
+ usb_init();
+ } else {
+ // listen on default port
+ local_init(DEFAULT_SDB_LOCAL_TRANSPORT_PORT);
+ }
+
+#if 0 /* tizen specific */
+ D("sdb_main(): pre init_jdwp()\n");
+ init_jdwp();
+ D("sdb_main(): post init_jdwp()\n");
+#endif
+#endif
+
+ if (is_daemon)
+ {
+ // inform our parent that we are up and running.
+#ifdef HAVE_WIN32_PROC
+ DWORD count;
+ WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
+#elif defined(HAVE_FORKEXEC)
+ fprintf(stderr, "OK\n");
+#endif
+ start_logging();
+ }
+ D("Event loop starting\n");
+
+ fdevent_loop();
+
+ usb_cleanup();
+
+ return 0;
+}
+
+#if SDB_HOST
+void connect_device(char* host, char* buffer, int buffer_size)
+{
+ int port, fd;
+ char* portstr = strchr(host, ':');
+ char hostbuf[100];
+ char serial[100];
+
+ strncpy(hostbuf, host, sizeof(hostbuf) - 1);
+ if (portstr) {
+ if (portstr - host >= sizeof(hostbuf)) {
+ snprintf(buffer, buffer_size, "bad host name %s", host);
+ return;
+ }
+ // zero terminate the host at the point we found the colon
+ hostbuf[portstr - host] = 0;
+ if (sscanf(portstr + 1, "%d", &port) == 0) {
+ snprintf(buffer, buffer_size, "bad port number %s", portstr);
+ return;
+ }
+ } else {
+ port = DEFAULT_SDB_LOCAL_TRANSPORT_PORT;
+ }
+
+ snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port);
+ if (find_transport(serial)) {
+ snprintf(buffer, buffer_size, "already connected to %s", serial);
+ return;
+ }
+
+ fd = socket_network_client(hostbuf, port, SOCK_STREAM);
+ if (fd < 0) {
+ snprintf(buffer, buffer_size, "unable to connect to %s", host);
+ return;
+ }
+
+ D("client: connected on remote on fd %d\n", fd);
+ close_on_exec(fd);
+ disable_tcp_nagle(fd);
+ register_socket_transport(fd, serial, port, 0, NULL);
+ snprintf(buffer, buffer_size, "connected to %s", serial);
+}
+
+void connect_emulator(char* port_spec, char* buffer, int buffer_size)
+{
+ char* port_separator = strchr(port_spec, ',');
+ if (!port_separator) {
+ snprintf(buffer, buffer_size,
+ "unable to parse '%s' as <console port>,<sdb port>",
+ port_spec);
+ return;
+ }
+
+ // Zero-terminate console port and make port_separator point to 2nd port.
+ *port_separator++ = 0;
+ int console_port = strtol(port_spec, NULL, 0);
+ int sdb_port = strtol(port_separator, NULL, 0);
+ if (!(console_port > 0 && sdb_port > 0)) {
+ *(port_separator - 1) = ',';
+ snprintf(buffer, buffer_size,
+ "Invalid port numbers: Expected positive numbers, got '%s'",
+ port_spec);
+ return;
+ }
+
+ /* Check if the emulator is already known.
+ * Note: There's a small but harmless race condition here: An emulator not
+ * present just yet could be registered by another invocation right
+ * after doing this check here. However, local_connect protects
+ * against double-registration too. From here, a better error message
+ * can be produced. In the case of the race condition, the very specific
+ * error message won't be shown, but the data doesn't get corrupted. */
+ atransport* known_emulator = find_emulator_transport_by_sdb_port(sdb_port);
+ if (known_emulator != NULL) {
+ snprintf(buffer, buffer_size,
+ "Emulator on port %d already registered.", sdb_port);
+ return;
+ }
+
+ /* Check if more emulators can be registered. Similar unproblematic
+ * race condition as above. */
+ int candidate_slot = get_available_local_transport_index();
+ if (candidate_slot < 0) {
+ snprintf(buffer, buffer_size, "Cannot accept more emulators.");
+ return;
+ }
+
+ /* Preconditions met, try to connect to the emulator. */
+ if (!local_connect_arbitrary_ports(console_port, sdb_port, NULL)) {
+ snprintf(buffer, buffer_size,
+ "Connected to emulator on ports %d,%d", console_port, sdb_port);
+ } else {
+ snprintf(buffer, buffer_size,
+ "Could not connect to emulator on ports %d,%d",
+ console_port, sdb_port);
+ }
+}
+#endif
+
+int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
+{
+ atransport *transport = NULL;
+ char buf[4096];
+
+ if(!strcmp(service, "kill")) {
+ fprintf(stderr,"sdb server killed by remote request\n");
+ fflush(stdout);
+ sdb_write(reply_fd, "OKAY", 4);
+ usb_cleanup();
+ exit(0);
+ }
+
+#if SDB_HOST
+ // "transport:" is used for switching transport with a specified serial number
+ // "transport-usb:" is used for switching transport to the only USB transport
+ // "transport-local:" is used for switching transport to the only local transport
+ // "transport-any:" is used for switching transport to the only transport
+ if (!strncmp(service, "transport", strlen("transport"))) {
+ char* error_string = "unknown failure";
+ transport_type type = kTransportAny;
+
+ if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
+ type = kTransportUsb;
+ } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
+ type = kTransportLocal;
+ } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
+ type = kTransportAny;
+ } else if (!strncmp(service, "transport:", strlen("transport:"))) {
+ service += strlen("transport:");
+ serial = service;
+ }
+
+ transport = acquire_one_transport(CS_ANY, type, serial, &error_string);
+
+ if (transport) {
+ s->transport = transport;
+ sdb_write(reply_fd, "OKAY", 4);
+ } else {
+ sendfailmsg(reply_fd, error_string);
+ }
+ return 1;
+ }
+
+ // find a device which prefix match the partial serial num
+ // for example, there are two devices
+ // fjldsads9uq2oeu
+ // fjkuiaoejlfamck
+ // to make user's life more easier, it can specify a device with
+ // it's uniq prefix, in the above example, that means
+ // fjl <==> fjldsads9uq2oeu
+ // fjk <==> fjkuiaoejlfamck
+ if (!strncmp(service, "serial-match:", 13)) {
+ char *tmp = service + 13;
+ char *serial = NULL;
+ int ret = -1;
+ D("Try to find device for: %s\n", tmp);
+ ret = find_transports(&serial, tmp);
+ if (ret <= 0) {
+ D("No device found\n");
+ snprintf(buf, sizeof(buf), "FAIL%04x%s", (unsigned)strlen(serial), serial);
+ } else if (ret == 1) {
+ D("found device: %s\n", serial);
+ snprintf(buf, sizeof(buf), "OKAY%04x%s", (unsigned)strlen(serial), serial);
+ } else {
+ D("found more than one devices matched: %d\n", ret);
+ snprintf(buf, sizeof(buf), "FAIL%04x%s", (unsigned)strlen(serial), serial);
+ }
+
+ writex(reply_fd, buf, strlen(buf));
+ free(serial);
+ return 0;
+ }
+ // return a list of all connected devices
+ if (!strcmp(service, "devices")) {
+ char buffer[4096];
+ memset(buf, 0, sizeof(buf));
+ memset(buffer, 0, sizeof(buffer));
+ D("Getting device list \n");
+ list_transports(buffer, sizeof(buffer));
+ snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
+ D("Wrote device list \n");
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ // add a new TCP transport, device or emulator
+ if (!strncmp(service, "connect:", 8)) {
+ char buffer[4096];
+ char* host = service + 8;
+ if (!strncmp(host, "emu:", 4)) {
+ connect_emulator(host + 4, buffer, sizeof(buffer));
+ } else {
+ connect_device(host, buffer, sizeof(buffer));
+ }
+ // Send response for emulator and device
+ snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ // remove TCP transport
+ if (!strncmp(service, "disconnect:", 11)) {
+ char buffer[4096];
+ memset(buffer, 0, sizeof(buffer));
+ char* serial = service + 11;
+ if (serial[0] == 0) {
+ // disconnect from all TCP devices
+ unregister_all_tcp_transports();
+ } else {
+ char hostbuf[100];
+ // assume port 26101 if no port is specified
+ if (!strchr(serial, ':')) {
+ snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:26101", serial);
+ serial = hostbuf;
+ }
+ atransport *t = find_transport(serial);
+
+ if (t) {
+ unregister_transport(t);
+ } else {
+ snprintf(buffer, sizeof(buffer), "No such device %s", serial);
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ // returns our value for SDB_SERVER_VERSION
+ if (!strcmp(service, "version")) {
+ char version[12];
+ snprintf(version, sizeof version, "%04x", SDB_SERVER_VERSION);
+ snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+
+ if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
+ char *out = "unknown";
+ transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+ if (transport && transport->serial) {
+ out = transport->serial;
+ }
+ snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+ // indicates a new emulator instance has started
+ if (!strncmp(service,"emulator:",9)) { /* tizen specific */
+ char *tmp = strtok(service+9, DEVICEMAP_SEPARATOR);
+ int port = 0;
+
+ if (tmp == NULL) {
+ port = atoi(service+9);
+ } else {
+ port = atoi(tmp);
+ tmp = strtok(NULL, DEVICEMAP_SEPARATOR);
+ if (tmp != NULL) {
+ local_connect(port, tmp);
+ }
+ }
+ local_connect(port, NULL);
+ return 0;
+ }
+#endif // SDB_HOST
+
+ if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) {
+ char *local, *remote, *err;
+ int r;
+ atransport *transport;
+
+ int createForward = strncmp(service,"kill",4);
+
+ local = service + (createForward ? 8 : 12);
+ remote = strchr(local,';');
+ if(remote == 0) {
+ sendfailmsg(reply_fd, "malformed forward spec");
+ return 0;
+ }
+
+ *remote++ = 0;
+ if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){
+ sendfailmsg(reply_fd, "malformed forward spec");
+ return 0;
+ }
+
+ transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
+ if (!transport) {
+ sendfailmsg(reply_fd, err);
+ return 0;
+ }
+
+ if (createForward) {
+ r = install_listener(local, remote, transport);
+ } else {
+ r = remove_listener(local, remote, transport);
+ }
+ if(r == 0) {
+ /* 1st OKAY is connect, 2nd OKAY is status */
+ writex(reply_fd, "OKAYOKAY", 8);
+ return 0;
+ }
+
+ if (createForward) {
+ sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket");
+ } else {
+ sendfailmsg(reply_fd, "cannot remove listener");
+ }
+ return 0;
+ }
+
+ if(!strncmp(service,"get-state",strlen("get-state"))) {
+ transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
+ char *state = connection_state_name(transport);
+ snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
+ writex(reply_fd, buf, strlen(buf));
+ return 0;
+ }
+ return -1;
+}
+
+#if !SDB_HOST
+int recovery_mode = 0;
+#endif
+
+int main(int argc, char **argv)
+{
+ sdb_trace_init(); /* tizen specific */
+#if SDB_HOST
+ sdb_sysdeps_init();
+ sdb_trace_init();
+ return sdb_commandline(argc - 1, argv + 1);
+#else
+ /* If sdbd runs inside the emulator this will enable sdb tracing via
+ * sdb-debug qemud service in the emulator. */
+#if 0 /* tizen specific */
+ sdb_qemu_trace_init();
+ if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
+ sdb_device_banner = "recovery";
+ recovery_mode = 1;
+ }
+#endif
+#if !SDB_HOST
+ if (daemonize() < 0)
+ fatal("daemonize() failed: %.200s", strerror(errno));
+#endif
+
+ start_device_log();
+ D("Handling main()\n");
+
+ //sdbd will never die on emulator!
+ //signal(SIGTERM, handle_sig_term); /* tizen specific */
+ return sdb_main(0, DEFAULT_SDB_PORT);
+#endif
+}
diff --git a/src/sdb.h b/src/sdb.h
new file mode 100755
index 0000000..4eef2af
--- /dev/null
+++ b/src/sdb.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __SDB_H
+#define __SDB_H
+
+#include <limits.h>
+
+#include "transport.h" /* readx(), writex() */
+
+#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 // SDB protocol version
+
+#define SDB_VERSION_MAJOR 2 // Used for help/version information
+#define SDB_VERSION_MINOR 0 // Used for help/version information
+
+#define SDB_SERVER_VERSION 2 // Increment this when we want to force users to start a new sdb 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;
+
+ /* flag: set when the socket's peer has closed
+ ** but packets are still queued for delivery
+ */
+ int closing;
+
+ /* flag: quit adbd when both ends close the
+ ** local service socket
+ */
+ int exit_on_close;
+
+ /* 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 SDB 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;
+ int sdb_port; // Use for emulators (local transport)
+ char *device_name; // for connection explorer
+
+ /* 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, size_t maxLen);
+int launch_server(int server_port);
+int sdb_main(int is_daemon, int server_port);
+
+
+/* transports are ref-counted
+** get_device_transport does an acquire on your behalf before returning
+*/
+void init_transport_registration(void);
+int find_transports(char **serial, const char *prefix);
+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 */
+#if SDB_HOST
+int get_available_local_transport_index();
+#endif
+int init_socket_transport(atransport *t, int s, int port, int local);
+void init_usb_transport(atransport *t, usb_handle *usb, int state);
+
+/* 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, int local, const char *device_name);
+
+/* these should only be used for the "sdb disconnect" command */
+void unregister_transport(atransport *t);
+void unregister_all_tcp_transports();
+
+void register_usb_transport(usb_handle *h, const char *serial, unsigned writeable);
+
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb);
+
+atransport *find_transport(const char *serial);
+#if SDB_HOST
+atransport* find_emulator_transport_by_sdb_port(int sdb_port);
+#endif
+
+int service_to_fd(const char *name);
+#if SDB_HOST
+asocket *host_service_to_socket(const char* name, const char *serial);
+#endif
+
+#if !SDB_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 !SDB_HOST
+typedef enum {
+ BACKUP,
+ RESTORE
+} BackupOperation;
+int backup_service(BackupOperation operation, char* args);
+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);
+
+/* define SDB_TRACE to 1 to enable tracing support, or 0 to disable it */
+
+#define SDB_TRACE 1
+
+/* IMPORTANT: if you change the following list, don't
+ * forget to update the corresponding 'tags' table in
+ * the sdb_trace_init() function implemented in sdb.c
+ */
+typedef enum {
+ TRACE_SDB = 0,
+ TRACE_SOCKETS,
+ TRACE_PACKETS,
+ TRACE_TRANSPORT,
+ TRACE_RWX,
+ TRACE_USB,
+ TRACE_SYNC,
+ TRACE_SYSDEPS,
+ TRACE_JDWP,
+ TRACE_SERVICES,
+ TRACE_PROPERTIES
+} SdbTrace;
+
+#if SDB_TRACE
+
+#if !SDB_HOST
+/*
+ * When running inside the emulator, guest's sdbd can connect to 'sdb-debug'
+ * qemud service that can display sdb trace messages (on condition that emulator
+ * has been started with '-debug sdb' option).
+ */
+
+/* Delivers a trace message to the emulator via QEMU pipe. */
+void sdb_qemu_trace(const char* fmt, ...);
+/* Macro to use to send SDB trace messages to the emulator. */
+#define DQ(...) sdb_qemu_trace(__VA_ARGS__)
+#else
+#define DQ(...) ((void)0)
+#endif /* !SDB_HOST */
+
+ extern int sdb_trace_mask;
+ extern unsigned char sdb_trace_output_count;
+ void sdb_trace_init(void);
+
+# define SDB_TRACING ((sdb_trace_mask & (1 << TRACE_TAG)) != 0)
+
+ /* you must define TRACE_TAG before using this macro */
+# define D(...) \
+ do { \
+ if (SDB_TRACING) { \
+ int save_errno = errno; \
+ sdb_mutex_lock(&D_lock); \
+ fprintf(stderr, "%s::%s():", \
+ __FILE__, __FUNCTION__); \
+ errno = save_errno; \
+ fprintf(stderr, __VA_ARGS__ ); \
+ fflush(stderr); \
+ sdb_mutex_unlock(&D_lock); \
+ errno = save_errno; \
+ } \
+ } while (0)
+# define DR(...) \
+ do { \
+ if (SDB_TRACING) { \
+ int save_errno = errno; \
+ sdb_mutex_lock(&D_lock); \
+ errno = save_errno; \
+ fprintf(stderr, __VA_ARGS__ ); \
+ fflush(stderr); \
+ sdb_mutex_unlock(&D_lock); \
+ errno = save_errno; \
+ } \
+ } while (0)
+#else
+# define D(...) ((void)0)
+# define DR(...) ((void)0)
+# define SDB_TRACING 0
+#endif
+
+
+#if !TRACE_PACKETS
+#define print_packet(tag,p) do {} while (0)
+#endif
+
+#if SDB_HOST_ON_TARGET
+/* sdb and sdbd are coexisting on the target, so use 26099 for sdb
+ * to avoid conflicting with sdbd's usage of 26098
+ */
+# define DEFAULT_SDB_PORT 26099 /* tizen specific */
+#else
+# define DEFAULT_SDB_PORT 26099 /* tizen specific */
+#endif
+
+#define DEFAULT_SDB_LOCAL_TRANSPORT_PORT 26101 /* tizen specific */
+
+#define SDB_CLASS 0xff
+#define SDB_SUBCLASS 0x20 //0x42 /* tizen specific */
+#define SDB_PROTOCOL 0x02 //0x01 /* tizen specific */
+
+
+void local_init(int port);
+int local_connect(int port, const char *device_name);
+int local_connect_arbitrary_ports(int console_port, int sdb_port, const char *device_name);
+
+/* 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 */
+#if SDB_HOST
+int is_sdb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
+#endif
+
+unsigned host_to_le32(unsigned n);
+int sdb_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_NOPERM 5 /* Insufficient permissions to communicate with the device */
+#define CS_SIDELOAD 6
+
+extern int HOST;
+extern int SHELL_EXIT_NOTIFY_FD;
+
+#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);
+
+#if SDB_HOST /* tizen-specific */
+#define DEVICEMAP_SEPARATOR ":"
+#define DEVICENAME_MAX 256
+#define VMS_PATH OS_PATH_SEPARATOR_STR "vms" OS_PATH_SEPARATOR_STR // should include sysdeps.h above
+#define DEFAULT_DEVICENAME "<unknown>"
+void register_device_name(const char *device_type, const char *device_name, int port);
+int get_devicename_from_shdmem(int port, char *device_name);
+int read_line(const int fd, char* ptr, const size_t maxlen);
+#endif
+#endif
diff --git a/src/sdb_client.c b/src/sdb_client.c
new file mode 100644
index 0000000..08766cd
--- /dev/null
+++ b/src/sdb_client.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <limits.h>
+#include <stdarg.h>
+// tizen specific #include <zipfile/zipfile.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_SDB
+#include "sdb_client.h"
+
+static transport_type __sdb_transport = kTransportAny;
+static const char* __sdb_serial = NULL;
+
+static int __sdb_server_port = DEFAULT_SDB_PORT;
+
+void sdb_set_transport(transport_type type, const char* serial)
+{
+ __sdb_transport = type;
+ __sdb_serial = serial;
+}
+
+void sdb_set_tcp_specifics(int server_port)
+{
+ __sdb_server_port = server_port;
+}
+
+int sdb_get_emulator_console_port(void)
+{
+ const char* serial = __sdb_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 = sdb_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 __sdb_error[256] = { 0 };
+
+const char *sdb_error(void)
+{
+ return __sdb_error;
+}
+
+static int switch_socket_transport(int fd)
+{
+ char service[64];
+ char tmp[5];
+ int len;
+
+ if (__sdb_serial)
+ snprintf(service, sizeof service, "host:transport:%s", __sdb_serial);
+ else {
+ char* transport_type = "???";
+
+ switch (__sdb_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(__sdb_error, "write failure during connection");
+ sdb_close(fd);
+ return -1;
+ }
+ D("Switch transport in progress\n");
+
+ if(sdb_status(fd)) {
+ sdb_close(fd);
+ D("Switch transport failed\n");
+ return -1;
+ }
+ D("Switch transport success\n");
+ return 0;
+}
+
+int sdb_status(int fd)
+{
+ unsigned char buf[5];
+ unsigned len;
+
+ if(readx(fd, buf, 4)) {
+ strcpy(__sdb_error, "protocol fault (no status)");
+ return -1;
+ }
+
+ if(!memcmp(buf, "OKAY", 4)) {
+ return 0;
+ }
+
+ if(memcmp(buf, "FAIL", 4)) {
+ sprintf(__sdb_error,
+ "protocol fault (status %02x %02x %02x %02x?!)",
+ buf[0], buf[1], buf[2], buf[3]);
+ return -1;
+ }
+
+ if(readx(fd, buf, 4)) {
+ strcpy(__sdb_error, "protocol fault (status len)");
+ return -1;
+ }
+ buf[4] = 0;
+ len = strtoul((char*)buf, 0, 16);
+ if(len > 255) len = 255;
+ if(readx(fd, __sdb_error, len)) {
+ strcpy(__sdb_error, "protocol fault (status read)");
+ return -1;
+ }
+ __sdb_error[len] = 0;
+ return -1;
+}
+
+int _sdb_connect(const char *service)
+{
+ char tmp[5];
+ int len;
+ int fd;
+
+ D("_sdb_connect: %s\n", service);
+ len = strlen(service);
+ if((len < 1) || (len > 1024)) {
+ strcpy(__sdb_error, "service name too long");
+ return -1;
+ }
+ snprintf(tmp, sizeof tmp, "%04x", len);
+
+ fd = socket_loopback_client(__sdb_server_port, SOCK_STREAM);
+ if(fd < 0) {
+ strcpy(__sdb_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(__sdb_error, "write failure during connection");
+ sdb_close(fd);
+ return -1;
+ }
+
+ if(sdb_status(fd)) {
+ sdb_close(fd);
+ return -1;
+ }
+
+ D("_sdb_connect: return fd %d\n", fd);
+ return fd;
+}
+
+int sdb_connect(const char *service)
+{
+ // first query the sdb server's version
+ int fd = _sdb_connect("host:version");
+
+ D("sdb_connect: service %s\n", service);
+ if(fd == -2) {
+ fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
+ __sdb_server_port);
+ start_server:
+ if(launch_server(__sdb_server_port)) {
+ 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 */
+ sdb_sleep_ms(3000);
+ // fall through to _sdb_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 = SDB_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;
+ sdb_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 sdb that does not support the version command
+ if (strcmp(__sdb_error, "unknown host service") != 0)
+ return fd;
+ }
+
+ if(version != SDB_SERVER_VERSION) {
+ printf("sdb server is out of date. killing...\n");
+ fd = _sdb_connect("host:kill");
+ sdb_close(fd);
+
+ /* XXX can we better detect its death? */
+ sdb_sleep_ms(2000);
+ goto start_server;
+ }
+ }
+
+ // if the command is start-server, we are done.
+ if (!strcmp(service, "host:start-server"))
+ return 0;
+
+ fd = _sdb_connect(service);
+ if(fd == -2) {
+ fprintf(stderr,"** daemon still not running");
+ }
+ D("sdb_connect: return fd %d\n", fd);
+
+ return fd;
+error:
+ sdb_close(fd);
+ return -1;
+}
+
+
+int sdb_command(const char *service)
+{
+ int fd = sdb_connect(service);
+ if(fd < 0) {
+ return -1;
+ }
+
+ if(sdb_status(fd)) {
+ sdb_close(fd);
+ return -1;
+ }
+
+ return 0;
+}
+
+char *sdb_query(const char *service)
+{
+ char buf[5];
+ unsigned n;
+ char *tmp;
+
+ D("sdb_query: %s\n", service);
+ int fd = sdb_connect(service);
+ if(fd < 0) {
+ fprintf(stderr,"error: %s\n", __sdb_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;
+ sdb_close(fd);
+ return tmp;
+ }
+ free(tmp);
+
+oops:
+ sdb_close(fd);
+ return 0;
+}
diff --git a/src/sdb_client.h b/src/sdb_client.h
new file mode 100644
index 0000000..86e9ef1
--- /dev/null
+++ b/src/sdb_client.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 _SDB_CLIENT_H_
+#define _SDB_CLIENT_H_
+
+#include "sdb.h"
+
+/* connect to sdb, connect to the named service, and return
+** a valid fd for interacting with that service upon success
+** or a negative number on failure
+*/
+int sdb_connect(const char *service);
+int _sdb_connect(const char *service);
+
+/* connect to sdb, connect to the named service, return 0 if
+** the connection succeeded AND the service returned OKAY
+*/
+int sdb_command(const char *service);
+
+/* connect to sdb, connect to the named service, return
+** a malloc'd string of its response upon success or NULL
+** on failure.
+*/
+
+char *sdb_query(const char *service);
+
+/* Set the preferred transport to connect to.
+*/
+void sdb_set_transport(transport_type type, const char* serial);
+
+/* Set TCP specifics of the transport to use
+*/
+void sdb_set_tcp_specifics(int server_port);
+
+/* 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 sdb_set_transport() was alled previously...
+ */
+int sdb_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 sdb_send_emulator_command(int argc, char** argv);
+
+/* return verbose error string from last operation */
+const char *sdb_error(void);
+
+/* read a standard sdb status response (OKAY|FAIL) and
+** return 0 in the event of OKAY, -1 in the event of FAIL
+** or protocol error
+*/
+int sdb_status(int fd);
+
+#endif
diff --git a/src/sdbwinapi/BUILDME.TXT b/src/sdbwinapi/BUILDME.TXT
new file mode 100644
index 0000000..a8a2ca2
--- /dev/null
+++ b/src/sdbwinapi/BUILDME.TXT
@@ -0,0 +1,19 @@
+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.
+
+In order to build SdbWinApi.dll you will need to install Windows Driver Kit,
+which can be obtained from Microsoft. Assuming that WDK is installed, you
+need to set one of the WDK's build environments, "cd" back into this directory,
+and execute "build -cbeEIFZ" to clean and rebuild this project, or you can
+execute "build -befEIF" to do a minimal build.
diff --git a/src/sdbwinapi/MAKEFILE b/src/sdbwinapi/MAKEFILE
new file mode 100644
index 0000000..c38c73f
--- /dev/null
+++ b/src/sdbwinapi/MAKEFILE
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/src/sdbwinapi/MAKEFILE.org b/src/sdbwinapi/MAKEFILE.org
new file mode 100644
index 0000000..c38c73f
--- /dev/null
+++ b/src/sdbwinapi/MAKEFILE.org
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/src/sdbwinapi/Resource.h b/src/sdbwinapi/Resource.h
new file mode 100644
index 0000000..4a394fd
--- /dev/null
+++ b/src/sdbwinapi/Resource.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by SdbWinApi.rc
+//
+
+#define IDS_PROJNAME 100
+#define IDR_SDBWINAPI 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 201
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 201
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
diff --git a/src/sdbwinapi/SOURCES b/src/sdbwinapi/SOURCES
new file mode 100644
index 0000000..6b3e319
--- /dev/null
+++ b/src/sdbwinapi/SOURCES
@@ -0,0 +1,96 @@
+#
+# 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.
+#
+
+TARGETNAME = SdbWinApi
+TARGETPATH = obj
+TARGETTYPE = DYNLINK
+
+UMTYPE = windows
+DLLDEF = SdbWinApi.def
+
+# Use statically linked atl libraries:
+# - atls.lib for free build
+# - atlsd.lib for checked build
+USE_STATIC_ATL = 1
+# Use ATL v. 7.1
+ATL_VER = 71
+# Use STL v. 6.0
+USE_STL = 1
+STL_VER = 60
+# Use multithreaded libraries
+USE_LIBCMT = 1
+
+# Include directories
+INCLUDES = $(DDK_INC_PATH); \
+ $(SDK_INC_PATH); \
+ $(CRT_INC_PATH); \
+ $(SDK_INC_PATH)\crt; \
+ $(CRT_INC_PATH)\atl71; \
+ $(SDK_INC_PATH)\crt\stl60
+
+# Common target libraries
+TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib \
+ $(SDK_LIB_PATH)\Advapi32.lib \
+ $(SDK_LIB_PATH)\Kernel32.lib \
+ $(SDK_LIB_PATH)\User32.lib \
+ $(SDK_LIB_PATH)\oleaut32.lib \
+ $(SDK_LIB_PATH)\wbemuuid.lib \
+ $(SDK_LIB_PATH)\uuid.lib \
+ $(SDK_LIB_PATH)\setupapi.lib \
+ $(SDK_LIB_PATH)\usbd.lib
+
+!IF "$(DDKBUILDENV)" == "fre"
+# Libraries for release (free) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atls.lib
+!ELSE
+# Libraries for debug (checked) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atlsd.lib
+!ENDIF
+
+# Common C defines
+C_DEFINES= $(C_DEFINES) -DSDBWIN_EXPORTS -D_UNICODE \
+ -DUNICODE -DWIN32 -D_WINDOWS -D_USRDLL -D_WINDLL
+
+!IF "$(DDKBUILDENV)" == "fre"
+# C defines for release (free) builds
+C_DEFINES = $(C_DEFINES) -DNDEBUG
+!ELSE
+# C defines for debug (checked) builds
+C_DEFINES = $(C_DEFINES) -D_DEBUG
+!ENDIF
+
+# Turn on all warnings, and treat warnings as errors
+MSC_WARNING_LEVEL = /W4 /Wp64 /WX
+# Common C defines
+USER_C_FLAGS = $(USER_C_FLAGS) /FD /EHsc /wd4100 /wd4200 /wd4702 /nologo
+# Set precompiled header information
+PRECOMPILED_CXX = 1
+PRECOMPILED_INCLUDE = stdafx.h
+PRECOMPILED_SOURCEFILE = stdafx.cpp
+
+# Define source files for SdbWinApi.dll
+SOURCES = sdb_api.cpp \
+ sdb_endpoint_object.cpp \
+ sdb_legacy_endpoint_object.cpp \
+ sdb_helper_routines.cpp \
+ sdb_interface.cpp \
+ sdb_legacy_interface.cpp \
+ sdb_interface_enum.cpp \
+ sdb_io_completion.cpp \
+ sdb_legacy_io_completion.cpp \
+ sdb_object_handle.cpp \
+ SdbWinApi.cpp \
+ SdbWinApi.rc
diff --git a/src/sdbwinapi/SdbWinApi.cpp b/src/sdbwinapi/SdbWinApi.cpp
new file mode 100644
index 0000000..6892496
--- /dev/null
+++ b/src/sdbwinapi/SdbWinApi.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+// SdbWinApi.cpp : Implementation of DLL Exports.
+
+#include "stdafx.h"
+#include "sdb_api.h"
+#include "sdb_winusb_api.h"
+
+extern "C" {
+int _forceCRTManifest;
+int _forceMFCManifest;
+int _forceAtlDllManifest;
+};
+
+/// References InstantiateWinUsbInterface declared in sdb_api.cpp
+extern PFN_INSTSDBWINUSBINTERFACE InstantiateSdbWinUsbInterface;
+
+class CSdbWinApiModule : public CAtlDllModuleT< CSdbWinApiModule > {
+ public:
+ CSdbWinApiModule()
+ : CAtlDllModuleT< CSdbWinApiModule >(),
+ sdbwinusbapi_handle_(NULL),
+ is_initialized_(false) {
+ }
+
+ ~CSdbWinApiModule() {
+ // Unload SdbWinUsbApi.dll before we exit
+ if (NULL != sdbwinusbapi_handle_) {
+ FreeLibrary(sdbwinusbapi_handle_);
+ }
+ }
+
+ /** \brief Loads SdbWinUsbApi.dll and caches its InstantiateWinUsbInterface
+ export.
+
+ This method is called from DllMain on DLL_PROCESS_ATTACH event. In this
+ method we will check if WINUSB.DLL required by SdbWinUsbApi.dll is
+ installed, and if it is we will load SdbWinUsbApi.dll and cache address of
+ InstantiateWinUsbInterface routine exported from SdbWinUsbApi.dll
+ */
+ void AttachToSdbWinUsbApi() {
+ // We only need to run this only once.
+ if (is_initialized_) {
+ return;
+ }
+
+ // Just mark that we have ran initialization.
+ is_initialized_ = true;
+
+ // Before we can load SdbWinUsbApi.dll we must make sure that WINUSB.DLL
+ // has been installed. Build path to the file.
+ wchar_t path_to_winusb_dll[MAX_PATH+1];
+ if (!GetSystemDirectory(path_to_winusb_dll, MAX_PATH)) {
+ return;
+ }
+ wcscat(path_to_winusb_dll, L"\\WINUSB.DLL");
+
+ if (0xFFFFFFFF == GetFileAttributes(path_to_winusb_dll)) {
+ // WINUSB.DLL is not installed. We don't (in fact, can't) load
+ // SdbWinUsbApi.dll
+ return;
+ }
+
+ // WINUSB.DLL is installed. Lets load SdbWinUsbApi.dll and cache its
+ // InstantiateWinUsbInterface export.
+ // We require that SdbWinUsbApi.dll is located in the same folder
+ // where SdbWinApi.dll and sdb.exe are located, so by Windows
+ // conventions we can pass just module name, and not the full path.
+ sdbwinusbapi_handle_ = LoadLibrary(L"SdbWinUsbApi.dll");
+ if (NULL != sdbwinusbapi_handle_) {
+ InstantiateSdbWinUsbInterface = reinterpret_cast<PFN_INSTSDBWINUSBINTERFACE>
+ (GetProcAddress(sdbwinusbapi_handle_, "InstantiateSdbWinUsbInterface"));
+ }
+ }
+
+ protected:
+ /// Handle to the loaded SdbWinUsbApi.dll
+ HINSTANCE sdbwinusbapi_handle_;
+
+ /// Flags whether or not this module has been initialized.
+ bool is_initialized_;
+};
+
+CSdbWinApiModule _AtlModule;
+
+// DLL Entry Point
+extern "C" BOOL WINAPI DllMain(HINSTANCE instance,
+ DWORD reason,
+ LPVOID reserved) {
+ // Lets see if we need to initialize InstantiateWinUsbInterface
+ // variable. We do that only once, on condition that this DLL is
+ // being attached to the process and InstantiateWinUsbInterface
+ // address has not been calculated yet.
+ if (DLL_PROCESS_ATTACH == reason) {
+ _AtlModule.AttachToSdbWinUsbApi();
+ }
+ return _AtlModule.DllMain(reason, reserved);
+}
diff --git a/src/sdbwinapi/SdbWinApi.def b/src/sdbwinapi/SdbWinApi.def
new file mode 100644
index 0000000..e2dbec2
--- /dev/null
+++ b/src/sdbwinapi/SdbWinApi.def
@@ -0,0 +1,18 @@
+; SdbWinApi.def : Declares the module parameters.
+
+LIBRARY "SdbWinApi.DLL"
+
+EXPORTS
+SdbEnumInterfaces
+SdbNextInterface
+SdbCreateInterfaceByName
+SdbOpenDefaultBulkReadEndpoint
+SdbOpenDefaultBulkWriteEndpoint
+SdbCloseHandle
+SdbGetInterfaceName
+SdbWriteEndpointSync
+SdbReadEndpointSync
+SdbGetSerialNumber
+SdbGetUsbInterfaceDescriptor
+SdbGetUsbDeviceDescriptor
+SdbGetEndpointInformation
diff --git a/src/sdbwinapi/SdbWinApi.rc b/src/sdbwinapi/SdbWinApi.rc
new file mode 100644
index 0000000..c5b19a4
--- /dev/null
+++ b/src/sdbwinapi/SdbWinApi.rc
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+//Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,0,0
+ PRODUCTVERSION 2,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "Google, inc"
+ VALUE "FileDescription", "Android SDB API"
+ VALUE "FileVersion", "2.0.0.0"
+ VALUE "LegalCopyright", "Copyright (C) 2006 The Android Open Source Project"
+ VALUE "InternalName", "SdbWinApi.dll"
+ VALUE "OriginalFilename", "SdbWinApi.dll"
+ VALUE "ProductName", "Android SDK"
+ VALUE "ProductVersion", "2.0.0.0"
+ VALUE "OLESelfRegister", ""
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
+
+#endif // !_MAC
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_PROJNAME "SdbWinApi"
+END
+
+////////////////////////////////////////////////////////////////////////////
+
+
+#endif
+
+#ifndef APSTUDIO_INVOKED
+#endif // not APSTUDIO_INVOKED
diff --git a/src/sdbwinapi/sdb_api.cpp b/src/sdbwinapi/sdb_api.cpp
new file mode 100644
index 0000000..f2c80c5
--- /dev/null
+++ b/src/sdbwinapi/sdb_api.cpp
@@ -0,0 +1,537 @@
+/*
+ * 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.
+ */
+
+/** \file
+ This file consists of implementation of rotines that are exported
+ from this DLL.
+*/
+
+#include "stdafx.h"
+#include "sdb_api.h"
+#include "sdb_object_handle.h"
+#include "sdb_interface_enum.h"
+#include "sdb_interface.h"
+#include "sdb_legacy_interface.h"
+#include "sdb_endpoint_object.h"
+#include "sdb_io_completion.h"
+#include "sdb_helper_routines.h"
+#include "sdb_winusb_api.h"
+
+/** \brief Points to InstantiateWinUsbInterface exported from SdbWinUsbApi.dll.
+
+ This variable is initialized with the actual address in DllMain routine for
+ this DLL on DLL_PROCESS_ATTACH event.
+ @see PFN_INSTWINUSBINTERFACE for more information.
+*/
+PFN_INSTSDBWINUSBINTERFACE InstantiateSdbWinUsbInterface = NULL;
+
+SDBAPIHANDLE __cdecl SdbEnumInterfaces(GUID class_id,
+ bool exclude_not_present,
+ bool exclude_removed,
+ bool active_only) {
+ SdbInterfaceEnumObject* enum_obj = NULL;
+ SDBAPIHANDLE ret = NULL;
+
+ try {
+ // Instantiate and initialize enum object
+ enum_obj = new SdbInterfaceEnumObject();
+
+ if (enum_obj->InitializeEnum(class_id,
+ exclude_not_present,
+ exclude_removed,
+ active_only)) {
+ // After successful initialization we can create handle.
+ ret = enum_obj->CreateHandle();
+ }
+ } catch (...) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+
+ if (NULL != enum_obj)
+ enum_obj->Release();
+
+ return ret;
+}
+
+bool __cdecl SdbNextInterface(SDBAPIHANDLE sdb_handle,
+ SdbInterfaceInfo* info,
+ unsigned long* size) {
+ if (NULL == size) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Lookup SdbInterfaceEnumObject object for the handle
+ SdbInterfaceEnumObject* sdb_ienum_object =
+ LookupObject<SdbInterfaceEnumObject>(sdb_handle);
+ if (NULL == sdb_ienum_object)
+ return false;
+
+ // Everything is verified. Pass it down to the object
+ bool ret = sdb_ienum_object->Next(info, size);
+
+ sdb_ienum_object->Release();
+
+ return ret;
+}
+
+bool __cdecl SdbResetInterfaceEnum(SDBAPIHANDLE sdb_handle) {
+ // Lookup SdbInterfaceEnumObject object for the handle
+ SdbInterfaceEnumObject* sdb_ienum_object =
+ LookupObject<SdbInterfaceEnumObject>(sdb_handle);
+ if (NULL == sdb_ienum_object)
+ return false;
+
+ // Everything is verified. Pass it down to the object
+ bool ret = sdb_ienum_object->Reset();
+
+ sdb_ienum_object->Release();
+
+ return ret;
+}
+
+SDBAPIHANDLE __cdecl SdbCreateInterfaceByName(
+ const wchar_t* interface_name) {
+ SdbInterfaceObject* obj = NULL;
+ SDBAPIHANDLE ret = NULL;
+
+ try {
+ // Instantiate interface object, depending on the USB driver type.
+ if (IsLegacyInterface(interface_name)) {
+ // We have legacy USB driver underneath us.
+ obj = new SdbLegacyInterfaceObject(interface_name);
+ } else {
+ printf("IsLegacyInterface failed\n");
+ // We have WinUsb driver underneath us. Make sure that SdbWinUsbApi.dll
+ // is loaded and its InstantiateWinUsbInterface routine address has
+ // been cached.
+ if (NULL != InstantiateSdbWinUsbInterface) {
+ printf("NULL != InstantiateSdbWinUsbInterface\n");
+ obj = InstantiateSdbWinUsbInterface(interface_name);
+ if (NULL == obj) {
+ printf("NULL == obj\n");
+ return NULL;
+ }
+ } else {
+ printf("NULL != InstantiateWinUsbInterface\n");
+ return NULL;
+ }
+ }
+
+ // Create handle for it
+ ret = obj->CreateHandle();
+ } catch (...) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+
+ if (NULL != obj)
+ obj->Release();
+
+ return ret;
+}
+
+SDBAPIHANDLE __cdecl SdbCreateInterface(GUID class_id,
+ unsigned short vendor_id,
+ unsigned short product_id,
+ unsigned char interface_id) {
+ // Enumerate all active interfaces for the given class
+ SdbEnumInterfaceArray interfaces;
+
+ if (!EnumerateDeviceInterfaces(class_id,
+ DIGCF_DEVICEINTERFACE | DIGCF_PRESENT,
+ true,
+ true,
+ &interfaces)) {
+ return NULL;
+ }
+
+ if (interfaces.empty()) {
+ SetLastError(ERROR_DEVICE_NOT_AVAILABLE);
+ return NULL;
+ }
+
+ // Now iterate over active interfaces looking for the name match.
+ // The name is formatted as such:
+ // "\\\\?\\usb#vid_xxxx&pid_xxxx&mi_xx#123456789abcdef#{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
+ // where
+ // vid_xxxx is for the vendor id (xxxx are hex for the given vendor id),
+ // pid_xxxx is for the product id (xxxx are hex for the given product id)
+ // mi_xx is for the interface id (xx are hex for the given interface id)
+ // EnumerateDeviceInterfaces will guarantee that returned interface names
+ // will have our class id at the end of the name (those last XXXes in the
+ // format). So, we only need to match the beginning of the name
+ wchar_t match_name[64];
+ if (0xFF == interface_id) {
+ // No interface id for the name.
+ swprintf(match_name, L"\\\\?\\usb#vid_%04x&pid_%04x#",
+ vendor_id, product_id);
+ } else {
+ // With interface id for the name.
+ swprintf(match_name, L"\\\\?\\usb#vid_%04x&pid_%04x&mi_%02x#",
+ vendor_id, product_id, interface_id);
+ }
+ size_t match_len = wcslen(match_name);
+
+ for (SdbEnumInterfaceArray::iterator it = interfaces.begin();
+ it != interfaces.end(); it++) {
+ const SdbInstanceEnumEntry& next_interface = *it;
+ if (0 == _wcsnicmp(match_name,
+ next_interface.device_name().c_str(),
+ match_len)) {
+ // Found requested interface among active interfaces.
+ return SdbCreateInterfaceByName(next_interface.device_name().c_str());
+ }
+ }
+
+ SetLastError(ERROR_DEVICE_NOT_AVAILABLE);
+ return NULL;
+}
+
+bool __cdecl SdbGetInterfaceName(SDBAPIHANDLE sdb_interface,
+ void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch call to the found object
+ bool ret = sdb_object->GetInterfaceName(buffer, buffer_char_size, ansi);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetSerialNumber(SDBAPIHANDLE sdb_interface,
+ void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch call to the found object
+ bool ret = sdb_object->GetSerialNumber(buffer, buffer_char_size, ansi);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetUsbDeviceDescriptor(SDBAPIHANDLE sdb_interface,
+ USB_DEVICE_DESCRIPTOR* desc) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch close to the found object
+ bool ret = sdb_object->GetUsbDeviceDescriptor(desc);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetUsbConfigurationDescriptor(SDBAPIHANDLE sdb_interface,
+ USB_CONFIGURATION_DESCRIPTOR* desc) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch close to the found object
+ bool ret = sdb_object->GetUsbConfigurationDescriptor(desc);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetUsbInterfaceDescriptor(SDBAPIHANDLE sdb_interface,
+ USB_INTERFACE_DESCRIPTOR* desc) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch close to the found object
+ bool ret = sdb_object->GetUsbInterfaceDescriptor(desc);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetEndpointInformation(SDBAPIHANDLE sdb_interface,
+ UCHAR endpoint_index,
+ SdbEndpointInformation* info) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch close to the found object
+ bool ret = sdb_object->GetEndpointInformation(endpoint_index, info);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetDefaultBulkReadEndpointInformation(SDBAPIHANDLE sdb_interface,
+ SdbEndpointInformation* info) {
+ return SdbGetEndpointInformation(sdb_interface,
+ SDB_QUERY_BULK_READ_ENDPOINT_INDEX,
+ info);
+}
+
+bool __cdecl SdbGetDefaultBulkWriteEndpointInformation(SDBAPIHANDLE sdb_interface,
+ SdbEndpointInformation* info) {
+ return SdbGetEndpointInformation(sdb_interface,
+ SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX,
+ info);
+}
+
+SDBAPIHANDLE __cdecl SdbOpenEndpoint(SDBAPIHANDLE sdb_interface,
+ unsigned char endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) {
+ // Lookup interface object for the handle
+ SdbInterfaceObject* sdb_object =
+ LookupObject<SdbInterfaceObject>(sdb_interface);
+
+ if (NULL != sdb_object) {
+ // Dispatch close to the found object
+ SDBAPIHANDLE ret =
+ sdb_object->OpenEndpoint(endpoint_index, access_type, sharing_mode);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+}
+
+SDBAPIHANDLE __cdecl SdbOpenDefaultBulkReadEndpoint(SDBAPIHANDLE sdb_interface,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) {
+ return SdbOpenEndpoint(sdb_interface,
+ SDB_QUERY_BULK_READ_ENDPOINT_INDEX,
+ access_type,
+ sharing_mode);
+}
+
+SDBAPIHANDLE __cdecl SdbOpenDefaultBulkWriteEndpoint(SDBAPIHANDLE sdb_interface,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) {
+ return SdbOpenEndpoint(sdb_interface,
+ SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX,
+ access_type,
+ sharing_mode);
+}
+
+SDBAPIHANDLE __cdecl SdbGetEndpointInterface(SDBAPIHANDLE sdb_endpoint) {
+ // Lookup endpoint object for the handle
+ SdbEndpointObject* sdb_object =
+ LookupObject<SdbEndpointObject>(sdb_endpoint);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ SDBAPIHANDLE ret = sdb_object->GetParentInterfaceHandle();
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+}
+
+bool __cdecl SdbQueryInformationEndpoint(SDBAPIHANDLE sdb_endpoint,
+ SdbEndpointInformation* info) {
+ // Lookup endpoint object for the handle
+ SdbEndpointObject* sdb_object =
+ LookupObject<SdbEndpointObject>(sdb_endpoint);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ bool ret = sdb_object->GetEndpointInformation(info);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+SDBAPIHANDLE __cdecl SdbReadEndpointAsync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_read,
+ unsigned long* bytes_read,
+ unsigned long time_out,
+ HANDLE event_handle) {
+ // Lookup endpoint object for the handle
+ SdbEndpointObject* sdb_object =
+ LookupObject<SdbEndpointObject>(sdb_endpoint);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ SDBAPIHANDLE ret = sdb_object->AsyncRead(buffer,
+ bytes_to_read,
+ bytes_read,
+ event_handle,
+ time_out);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+}
+
+SDBAPIHANDLE __cdecl SdbWriteEndpointAsync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_write,
+ unsigned long* bytes_written,
+ unsigned long time_out,
+ HANDLE event_handle) {
+ // Lookup endpoint object for the handle
+ SdbEndpointObject* sdb_object =
+ LookupObject<SdbEndpointObject>(sdb_endpoint);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ SDBAPIHANDLE ret = sdb_object->AsyncWrite(buffer,
+ bytes_to_write,
+ bytes_written,
+ event_handle,
+ time_out);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbReadEndpointSync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_read,
+ unsigned long* bytes_read,
+ unsigned long time_out) {
+ // Lookup endpoint object for the handle
+ SdbEndpointObject* sdb_object =
+ LookupObject<SdbEndpointObject>(sdb_endpoint);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ bool ret =
+ sdb_object->SyncRead(buffer, bytes_to_read, bytes_read, time_out);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return NULL;
+ }
+}
+
+bool __cdecl SdbWriteEndpointSync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_write,
+ unsigned long* bytes_written,
+ unsigned long time_out) {
+ // Lookup endpoint object for the handle
+ SdbEndpointObject* sdb_object =
+ LookupObject<SdbEndpointObject>(sdb_endpoint);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ bool ret =
+ sdb_object->SyncWrite(buffer, bytes_to_write, bytes_written, time_out);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbGetOvelappedIoResult(SDBAPIHANDLE sdb_io_completion,
+ LPOVERLAPPED overlapped,
+ unsigned long* bytes_transferred,
+ bool wait) {
+ // Lookup endpoint object for the handle
+ SdbIOCompletion* sdb_object =
+ LookupObject<SdbIOCompletion>(sdb_io_completion);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ bool ret =
+ sdb_object->GetOvelappedIoResult(overlapped, bytes_transferred, wait);
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
+
+bool __cdecl SdbHasOvelappedIoComplated(SDBAPIHANDLE sdb_io_completion) {
+ // Lookup endpoint object for the handle
+ SdbIOCompletion* sdb_object =
+ LookupObject<SdbIOCompletion>(sdb_io_completion);
+
+ if (NULL != sdb_object) {
+ // Dispatch the call to the found object
+ bool ret =
+ sdb_object->IsCompleted();
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return true;
+ }
+}
+
+bool __cdecl SdbCloseHandle(SDBAPIHANDLE sdb_handle) {
+ // Lookup object for the handle
+ SdbObjectHandle* sdb_object = SdbObjectHandle::Lookup(sdb_handle);
+
+ if (NULL != sdb_object) {
+ // Dispatch close to the found object
+ bool ret = sdb_object->CloseHandle();
+ sdb_object->Release();
+ return ret;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+}
diff --git a/src/sdbwinapi/sdb_api.h b/src/sdbwinapi/sdb_api.h
new file mode 100644
index 0000000..2ae7aa6
--- /dev/null
+++ b/src/sdbwinapi/sdb_api.h
@@ -0,0 +1,622 @@
+/*
+ * 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_USB_API_SDBWINAPI_H__
+#define ANDROID_USB_API_SDBWINAPI_H__
+
+/** \file
+ This file consists of declarations of routines exported by the API as well
+ as types, structures, and constants definitions used in the API.
+*/
+
+// Enables compillation for "straight" C
+#ifdef __cplusplus
+ #define EXTERN_C extern "C"
+#else
+ #define EXTERN_C extern
+ typedef int bool;
+ #define true 1
+ #define false 0
+#endif
+
+/** \brief Enumerates SDB endpoint types.
+
+ This enum is taken from WDF_USB_PIPE_TYPE enum found in WDK.
+*/
+typedef enum _SdbEndpointType {
+ /// Unknown (invalid, or not initialized) endpoint type.
+ SdbEndpointTypeInvalid = 0,
+
+ /// Endpoint is device control pipe.
+ SdbEndpointTypeControl,
+
+ /// Endpoint is isochronous r/w pipe.
+ SdbEndpointTypeIsochronous,
+
+ /// Endpoint is a bulk r/w pipe.
+ SdbEndpointTypeBulk,
+
+ /// Endpoint is an interrupt r/w pipe.
+ SdbEndpointTypeInterrupt,
+} SdbEndpointType;
+
+/** \brief Endpoint desriptor.
+
+ This structure is based on WDF_USB_PIPE_INFORMATION structure found in WDK.
+*/
+typedef struct _SdbEndpointInformation {
+ /// Maximum packet size this endpoint is capable of.
+ unsigned long max_packet_size;
+
+ /// Maximum size of one transfer which should be sent to the host controller.
+ unsigned long max_transfer_size;
+
+ /// SDB endpoint type.
+ SdbEndpointType endpoint_type;
+
+ /// Raw endpoint address on the device as described by its descriptor.
+ unsigned char endpoint_address;
+
+ /// Polling interval.
+ unsigned char polling_interval;
+
+ /// Which alternate setting this structure is relevant for.
+ unsigned char setting_index;
+} SdbEndpointInformation;
+
+/// Shortcut to default write bulk endpoint in zero-based endpoint index API.
+#define SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX 0xFC
+
+/// Shortcut to default read bulk endpoint in zero-based endpoint index API.
+#define SDB_QUERY_BULK_READ_ENDPOINT_INDEX 0xFE
+
+// {F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}
+/// Our USB class id that driver uses to register our device.
+#define ANDROID_USB_CLASS_ID {0x9ca29f37, 0xdd62, 0x4aad, {0x82, 0x65, 0xcf, 0xf7, 0x88, 0xc8, 0xba, 0x89}};
+//{0xf72fe0d4, 0xcbcb, 0x407d, {0x88, 0x14, 0x9e, 0xd6, 0x73, 0xd0, 0xdd, 0x6b}};
+
+/// Defines vendor ID for HCT devices.
+#define DEVICE_VENDOR_ID 0x0BB4
+
+/// Defines product ID for the device with single interface.
+#define DEVICE_SINGLE_PRODUCT_ID 0x0C01
+
+/// Defines product ID for the Dream composite device.
+#define DEVICE_COMPOSITE_PRODUCT_ID 0x0C02
+
+/// Defines product ID for the Magic composite device.
+#define DEVICE_MAGIC_COMPOSITE_PRODUCT_ID 0x0C03
+
+/// Defines interface ID for the device.
+#define DEVICE_INTERFACE_ID 0x01
+
+/// Defines vendor ID for the device
+#define DEVICE_EMULATOR_VENDOR_ID 0x18D1
+
+/// Defines product ID for a SoftUSB device simulator that is used to test
+/// the driver in isolation from hardware.
+#define DEVICE_EMULATOR_PROD_ID 0xDDDD
+
+// The following ifdef block is the standard way of creating macros which make
+// exporting from a DLL simpler. All files within this DLL are compiled with
+// the SDBWIN_EXPORTS symbol defined on the command line. this symbol should
+// not be defined on any project that uses this DLL. This way any other project
+// whose source files include this file see SDBWIN_API functions as being
+// imported from a DLL, whereas this DLL sees symbols defined with this macro
+// as being exported.
+#ifdef SDBWIN_EXPORTS
+#define SDBWIN_API EXTERN_C __declspec(dllexport)
+#define SDBWIN_API_CLASS __declspec(dllexport)
+#else
+#define SDBWIN_API EXTERN_C __declspec(dllimport)
+#define SDBWIN_API_CLASS __declspec(dllimport)
+#endif
+
+/** \brief Handle to an API object.
+
+ To access USB interface and its components clients must first obtain a
+ handle to the required object. API Objects that are represented by a
+ handle are:
+ 1. Interface enumerator that provides access to a list of interfaces that
+ match certain criterias that were specified when interface enumerator
+ has been created. This handle is created in SdbEnumInterfaces routine.
+ 2. Interface that is the major object this API deals with. In Windows
+ model of the USB stack each USB device (that is physical device,
+ attached to a USB port) exposes one or more interfaces that become the
+ major entities through which that device gets accessed. Each of these
+ interfaces are represented as Windows Device Objects on the USB stack.
+ So, to this extent, at least as this API is concerned, terms "interface"
+ and "device" are interchangeable, since each interface is represented by
+ a device object on the Windows USB stack. This handle is created in
+ either SdbCreateInterface or SdbCreateInterfaceByName routines.
+ 3. Endpoint object (also called a pipe) represents an endpoint on interface
+ through which all I/O operations are performed. This handle is created in
+ one of these routines: SdbOpenEndpoint, SdbOpenDefaultBulkReadEndpoint,
+ or SdbOpenDefaultBulkWriteEndpoint.
+ 4. I/O completion object that tracks completion information of asynchronous
+ I/O performed on an endpoint. When an endpoint object gets opened through
+ this API it is opened for asynchronous (or overlapped) I/O. And each time
+ an asynchronous I/O is performed by this API an I/O completion object is
+ created to track the result of that I/O when it gets completed. Clients
+ of the API can then use a handle to I/O completion object to query for
+ the status and result of asynchronous I/O as well as wait for this I/O
+ completion. This handle is created in one of these routines:
+ SdbReadEndpointAsync, or SdbWriteEndpointAsync.
+ After object is no longer needed by the client, its handle must be closed
+ using SdbCloseHandle routine.
+*/
+typedef void* SDBAPIHANDLE;
+
+/** \brief Defines access type with which an I/O object (endpoint)
+ should be opened.
+*/
+typedef enum _SdbOpenAccessType {
+ /// Opens for read and write access.
+ SdbOpenAccessTypeReadWrite,
+
+ /// Opens for read only access.
+ SdbOpenAccessTypeRead,
+
+ /// Opens for write only access.
+ SdbOpenAccessTypeWrite,
+
+ /// Opens for querying information.
+ SdbOpenAccessTypeQueryInfo,
+} SdbOpenAccessType;
+
+/** \brief Defines sharing mode with which an I/O object (endpoint)
+ should be opened.
+*/
+typedef enum _SdbOpenSharingMode {
+ /// Shares read and write.
+ SdbOpenSharingModeReadWrite,
+
+ /// Shares only read.
+ SdbOpenSharingModeRead,
+
+ /// Shares only write.
+ SdbOpenSharingModeWrite,
+
+ /// Opens exclusive.
+ SdbOpenSharingModeExclusive,
+} SdbOpenSharingMode;
+
+/** \brief Provides information about an interface.
+*/
+typedef struct _SdbInterfaceInfo {
+ /// Inteface's class id (see SP_DEVICE_INTERFACE_DATA for details)
+ GUID class_id;
+
+ /// Interface flags (see SP_DEVICE_INTERFACE_DATA for details)
+ unsigned long flags;
+
+ /// Device name for the interface (see SP_DEVICE_INTERFACE_DETAIL_DATA
+ /// for details)
+ wchar_t device_name[1];
+} SdbInterfaceInfo;
+
+/** \brief Creates USB interface enumerator
+
+ This routine enumerates all USB interfaces that match provided class ID.
+ This routine uses SetupDiGetClassDevs SDK routine to enumerate devices that
+ match class ID and then SetupDiEnumDeviceInterfaces SDK routine is called
+ to enumerate interfaces on the devices.
+ @param[in] class_id Device class ID, assigned by the driver.
+ @param[in] exclude_not_present If true enumation will include only those
+ devices that are currently present.
+ @param[in] exclude_removed If true interfaces with SPINT_REMOVED flag set
+ will be not included in the enumeration.
+ @param[in] active_only If true only active interfaces (with flag
+ SPINT_ACTIVE set) will be included in the enumeration.
+ @return Handle to the enumerator object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbEnumInterfaces(GUID class_id,
+ bool exclude_not_present,
+ bool exclude_removed,
+ bool active_only);
+
+/** \brief Gets next interface information
+
+ @param[in] sdb_handle Handle to interface enumerator object obtained via
+ SdbEnumInterfaces call.
+ @param[out] info Upon successful completion will receive interface
+ information. Can be NULL. If it is NULL, upon return from this
+ routine size parameter will contain memory size required for the
+ next entry.
+ @param[in,out] size On the way in provides size of the memory buffer
+ addressed by info parameter. On the way out (only if buffer was not
+ big enough) will provide memory size required for the next entry.
+ @return true on success, false on error. If false is returned
+ GetLastError() provides extended error information.
+ ERROR_INSUFFICIENT_BUFFER indicates that buffer provided in info
+ parameter was not big enough and size parameter contains memory size
+ required for the next entry. ERROR_NO_MORE_ITEMS indicates that
+ enumeration is over and there are no more entries to return.
+*/
+SDBWIN_API bool __cdecl SdbNextInterface(SDBAPIHANDLE sdb_handle,
+ SdbInterfaceInfo* info,
+ unsigned long* size);
+
+/** \brief Resets enumerator so next call to SdbNextInterface will start
+ from the beginning.
+
+ @param[in] sdb_handle Handle to interface enumerator object obtained via
+ SdbEnumInterfaces call.
+ @return true on success, false on error. If false is returned GetLastError()
+ provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbResetInterfaceEnum(SDBAPIHANDLE sdb_handle);
+
+/** \brief Creates USB interface object
+
+ This routine creates an object that represents a USB interface.
+ @param[in] interface_name Name of the interface.
+ @return Handle to the interface object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbCreateInterfaceByName(const wchar_t* interface_name);
+
+/** \brief Creates USB interface object based on vendor, product and
+ interface IDs.
+
+ This routine creates and object that represents a USB interface on our
+ device. It uses SdbCreateInterfaceByName to actually do the create.
+ @param[in] class_id Device class ID, assigned by the driver.
+ @param[in] vendor_id Device vendor ID
+ @param[in] product_id Device product ID
+ @param[in] interface_id Device interface ID. This parameter is optional.
+ Value 0xFF indicates that interface should be addressed by vendor
+ and product IDs only.
+ @return Handle to the interface object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbCreateInterface(GUID class_id,
+ unsigned short vendor_id,
+ unsigned short product_id,
+ unsigned char interface_id);
+
+/** \brief Gets interface name.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] buffer Buffer for the name. Can be NULL in which case
+ buffer_char_size will contain number of characters required for
+ the name.
+ @param[in,out] buffer_char_size On the way in supplies size (in characters)
+ of the buffer. On the way out, if method failed and GetLastError
+ reports ERROR_INSUFFICIENT_BUFFER, will contain number of characters
+ required for the name.
+ @param[in] ansi If true the name will be returned as single character
+ string. Otherwise name will be returned as wide character string.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetInterfaceName(SDBAPIHANDLE sdb_interface,
+ void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi);
+
+/** \brief Gets serial number for interface's device.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] buffer Buffer for the serail number string. Can be NULL in which
+ case buffer_char_size will contain number of characters required for
+ the string.
+ @param[in,out] buffer_char_size On the way in supplies size (in characters)
+ of the buffer. On the way out, if method failed and GetLastError
+ reports ERROR_INSUFFICIENT_BUFFER, will contain number of characters
+ required for the name.
+ @param[in] ansi If true the name will be returned as single character
+ string. Otherwise name will be returned as wide character string.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetSerialNumber(SDBAPIHANDLE sdb_interface,
+ void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi);
+
+/** \brief Gets device descriptor for the USB device associated with
+ the given interface.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] desc Upon successful completion will have usb device
+ descriptor.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetUsbDeviceDescriptor(SDBAPIHANDLE sdb_interface,
+ USB_DEVICE_DESCRIPTOR* desc);
+
+/** \brief Gets descriptor for the selected USB device configuration.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] desc Upon successful completion will have usb device
+ configuration descriptor.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetUsbConfigurationDescriptor(
+ SDBAPIHANDLE sdb_interface,
+ USB_CONFIGURATION_DESCRIPTOR* desc);
+
+/** \brief Gets descriptor for the given interface.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] desc Upon successful completion will have usb device
+ configuration descriptor.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetUsbInterfaceDescriptor(SDBAPIHANDLE sdb_interface,
+ USB_INTERFACE_DESCRIPTOR* desc);
+
+/** \brief Gets information about an endpoint on the given interface.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide information
+ about bulk write and bulk read endpoints respectively.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetEndpointInformation(SDBAPIHANDLE sdb_interface,
+ unsigned char endpoint_index,
+ SdbEndpointInformation* info);
+
+/** \brief Gets information about default bulk read endpoint on the given
+ interface.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetDefaultBulkReadEndpointInformation(
+ SDBAPIHANDLE sdb_interface,
+ SdbEndpointInformation* info);
+
+/** \brief Gets information about default bulk write endpoint on the given
+ interface.
+
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbGetDefaultBulkWriteEndpointInformation(
+ SDBAPIHANDLE sdb_interface,
+ SdbEndpointInformation* info);
+
+/** \brief Opens an endpoint on the given interface.
+
+ Endpoints are always opened for overlapped I/O.
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide information
+ about bulk write and bulk read endpoints respectively.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbOpenEndpoint(SDBAPIHANDLE sdb_interface,
+ unsigned char endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode);
+
+/** \brief Opens default bulk read endpoint on the given interface.
+
+ Endpoints are always opened for overlapped I/O.
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbOpenDefaultBulkReadEndpoint(
+ SDBAPIHANDLE sdb_interface,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode);
+
+/** \brief Opens default bulk write endpoint on the given interface.
+
+ Endpoints are always opened for overlapped I/O.
+ @param[in] sdb_interface A handle to interface object created with
+ SdbCreateInterface call.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbOpenDefaultBulkWriteEndpoint(
+ SDBAPIHANDLE sdb_interface,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode);
+
+/** \brief Gets handle to interface object for the given endpoint
+
+ @param[in] sdb_endpoint A handle to opened endpoint object, obtained via one
+ of the SdbOpenXxxEndpoint calls.
+ @return Handle to the interface for this endpoint or NULL on failure. If NULL
+ is returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbGetEndpointInterface(SDBAPIHANDLE sdb_endpoint);
+
+/** \brief Gets information about the given endpoint.
+
+ @param[in] sdb_endpoint A handle to opened endpoint object, obtained via one
+ of the SdbOpenXxxEndpoint calls.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbQueryInformationEndpoint(SDBAPIHANDLE sdb_endpoint,
+ SdbEndpointInformation* info);
+
+/** \brief Asynchronously reads from the given endpoint.
+
+ @param[in] sdb_endpoint A handle to opened endpoint object, obtained via one
+ of the SdbOpenXxxEndpoint calls.
+ @param[out] buffer Pointer to the buffer that receives the data.
+ @param[in] bytes_to_read Number of bytes to be read.
+ @param[out] bytes_read Number of bytes read. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value for this parameter means that there is no
+ timeout for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbReadEndpointAsync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_read,
+ unsigned long* bytes_read,
+ unsigned long time_out,
+ HANDLE event_handle);
+
+/** \brief Asynchronously writes to the given endpoint.
+
+ @param[in] sdb_endpoint A handle to opened endpoint object, obtained via one
+ of the SdbOpenXxxEndpoint calls.
+ @param[in] buffer Pointer to the buffer containing the data to be written.
+ @param[in] bytes_to_write Number of bytes to be written.
+ @param[out] bytes_written Number of bytes written. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value for this parameter means that there is no
+ timeout for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API SDBAPIHANDLE __cdecl SdbWriteEndpointAsync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_write,
+ unsigned long* bytes_written,
+ unsigned long time_out,
+ HANDLE event_handle);
+
+/** \brief Synchronously reads from the given endpoint.
+
+ @param[in] sdb_endpoint A handle to opened endpoint object, obtained via one
+ of the SdbOpenXxxEndpoint calls.
+ @param[out] buffer Pointer to the buffer that receives the data.
+ @param[in] bytes_to_read Number of bytes to be read.
+ @param[out] bytes_read Number of bytes read. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value for this parameter means that there is no
+ timeout for this I/O.
+ @return true on success and false on failure. If false is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbReadEndpointSync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_read,
+ unsigned long* bytes_read,
+ unsigned long time_out);
+
+/** \brief Synchronously writes to the given endpoint.
+
+ @param[in] sdb_endpoint A handle to opened endpoint object, obtained via one
+ of the SdbOpenXxxEndpoint calls.
+ @param[in] buffer Pointer to the buffer containing the data to be written.
+ @param[in] bytes_to_write Number of bytes to be written.
+ @param[out] bytes_written Number of bytes written. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value for this parameter means that there is no
+ timeout for this I/O.
+ @return true on success and false on failure. If false is
+ returned GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbWriteEndpointSync(SDBAPIHANDLE sdb_endpoint,
+ void* buffer,
+ unsigned long bytes_to_write,
+ unsigned long* bytes_written,
+ unsigned long time_out);
+
+/** \brief Gets overlapped I/O result for async I/O performed on the
+ given endpoint.
+
+ @param[in] sdb_io_completion A handle to an I/O completion object returned
+ from SdbRead/WriteAsync routines.
+ @param[out] ovl_data Buffer for the copy of this object's OVERLAPPED
+ structure. Can be NULL.
+ @param[out] bytes_transferred Pointer to a variable that receives the
+ number of bytes that were actually transferred by a read or write
+ operation. See SDK doc on GetOvelappedResult for more information.
+ Unlike regular GetOvelappedResult call this parameter can be NULL.
+ @param[in] wait If this parameter is true, the method does not return
+ until the operation has been completed. If this parameter is false
+ and the operation is still pending, the method returns false and
+ the GetLastError function returns ERROR_IO_INCOMPLETE.
+ @return true if I/O has been completed or false on failure or if request
+ is not yet completed. If false is returned GetLastError() provides
+ extended error information. If GetLastError returns
+ ERROR_IO_INCOMPLETE it means that I/O is not yet completed.
+*/
+SDBWIN_API bool __cdecl SdbGetOvelappedIoResult(SDBAPIHANDLE sdb_io_completion,
+ LPOVERLAPPED overlapped,
+ unsigned long* bytes_transferred,
+ bool wait);
+
+/** \brief Checks if overlapped I/O has been completed.
+
+ @param[in] sdb_io_completion A handle to an I/O completion object returned
+ from SdbRead/WriteAsync routines.
+ @return true if I/O has been completed or false if it's still
+ incomplete. Regardless of the returned value, caller should
+ check GetLastError to validate that handle was OK.
+*/
+SDBWIN_API bool __cdecl SdbHasOvelappedIoComplated(SDBAPIHANDLE sdb_io_completion);
+
+/** \brief Closes handle previously opened with one of the API calls
+
+ @param[in] sdb_handle SDB handle previously opened with one of the API calls
+ @return true on success or false on failure. If false is returned
+ GetLastError() provides extended error information.
+*/
+SDBWIN_API bool __cdecl SdbCloseHandle(SDBAPIHANDLE sdb_handle);
+
+#endif // ANDROID_USB_API_SDBWINAPI_H__
diff --git a/src/sdbwinapi/sdb_api_instance.cpp b/src/sdbwinapi/sdb_api_instance.cpp
new file mode 100644
index 0000000..085f2c7
--- /dev/null
+++ b/src/sdbwinapi/sdb_api_instance.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbApiInstance that is a main
+ API object representing a device interface that is in the interest of
+ the API client. All device (interface) related operations go through this
+ class first.
+*/
+
+#include "stdafx.h"
+#include "sdb_api_instance.h"
+#include "sdb_helper_routines.h"
+
+/// Map that holds all instances of this object
+SdbApiInstanceMap sdb_app_instance_map;
+ULONG_PTR sdb_app_instance_id = 0;
+CComAutoCriticalSection sdb_app_instance_map_locker;
+
+SdbApiInstance::SdbApiInstance()
+ : ref_count_(1) {
+ // Generate inteface handle
+ sdb_app_instance_map_locker.Lock();
+ sdb_app_instance_id++;
+ sdb_app_instance_map_locker.Unlock();
+ instance_handle_ =
+ reinterpret_cast<SDBAPIINSTANCEHANDLE>(sdb_app_instance_id);
+}
+
+SdbApiInstance::~SdbApiInstance() {
+}
+
+void SdbApiInstance::LastReferenceReleased() {
+}
diff --git a/src/sdbwinapi/sdb_api_instance.h b/src/sdbwinapi/sdb_api_instance.h
new file mode 100644
index 0000000..50a5568
--- /dev/null
+++ b/src/sdbwinapi/sdb_api_instance.h
@@ -0,0 +1,113 @@
+/*
+ * 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_USB_API_SDB_API_INSTANCE_H__
+#define ANDROID_USB_API_SDB_API_INSTANCE_H__
+/** \file
+ This file consists of declaration of class SdbApiInstance that is a main
+ API object representing a device interface that is in the interest of
+ the API client. All device (interface) related operations go through this
+ class first.
+*/
+
+#include "sdb_api.h"
+#include "sdb_api_private_defines.h"
+
+/** Class SdbApiInstance is the main API interbal object representing a device
+ interface that is in the interest of the API client. All device (interface)
+ related operations go through this class first. So, before doing anything
+ meaningfull with the API a client must first create instance of the API
+ via CreateSdbApiInstance, select a device interface for that instance and
+ then do everything else.
+ Objects of this class are globally stored in the map that matches
+ SDBAPIINSTANCEHANDLE to the corresponded object.
+ This class is self-referenced with the following reference model:
+ 1. When object of this class is created and added to the map, its recount
+ is set to 1.
+ 2. Every time the client makes an API call that uses SDBAPIINSTANCEHANDLE
+ a corresponded SdbApiInstance object is looked up in the table and its
+ refcount is incremented. Upon return from the API call that incremented
+ the refcount refcount gets decremented.
+ 3. When the client closes SDBAPIINSTANCEHANDLE via DeleteSdbApiInstance call
+ corresponded object gets deleted from the map and its refcount is
+ decremented.
+ So, at the end, this object destroys itself when refcount drops to zero.
+*/
+class SdbApiInstance {
+ public:
+ /** \brief Constructs the object
+
+ @param handle[in] Instance handle associated with this object
+ */
+ SdbApiInstance();
+
+ private:
+ /// Destructs the object
+ ~SdbApiInstance();
+
+ /** \brief
+ This method is called when last reference to this object has been released
+
+ In this method object is uninitialized and deleted (that is "delete this"
+ is called).
+ */
+ void LastReferenceReleased();
+
+ public:
+ /// Gets name of the USB interface (device name) for this instance
+ const std::wstring& interface_name() const {
+ return interface_name_;
+ }
+
+ /// References the object and returns number of references
+ LONG AddRef() {
+ return InterlockedIncrement(&ref_count_);
+ }
+
+ /** \brief Dereferences the object and returns number of references
+
+ Object may be deleted in this method, so you cannot touch it after
+ this method returns, even if returned value is not zero, because object
+ can be deleted in another thread.
+ */
+ LONG Release() {
+ LONG ret = InterlockedDecrement(&ref_count_);
+ if (0 == ret)
+ LastReferenceReleased();
+
+ return ret;
+ }
+
+ /// Checks if instance has been initialized
+ bool IsInitialized() const {
+ return !interface_name_.empty();
+ }
+
+private:
+ /// Name of the USB interface (device name) for this instance
+ std::wstring interface_name_;
+
+ /// Instance handle for this object
+ SDBAPIINSTANCEHANDLE instance_handle_;
+
+ /// Reference counter for this instance
+ LONG ref_count_;
+};
+
+/// Defines map that matches SDBAPIINSTANCEHANDLE with SdbApiInstance object
+typedef std::map< SDBAPIINSTANCEHANDLE, SdbApiInstance* > SdbApiInstanceMap;
+
+#endif // ANDROID_USB_API_SDB_API_INSTANCE_H__
diff --git a/src/sdbwinapi/sdb_api_legacy.h b/src/sdbwinapi/sdb_api_legacy.h
new file mode 100644
index 0000000..552d951
--- /dev/null
+++ b/src/sdbwinapi/sdb_api_legacy.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_API_LEGACY_H_
+#define ANDROID_USB_API_SDB_API_LEGACY_H_
+/** \file
+ This file consists of declarations of constants and structures required
+ for supporting communications of this API with a legacy (custom) USB
+ driver.
+*/
+
+// Enables compillation for "straight" C
+#ifdef __cplusplus
+ #define EXTERN_C extern "C"
+#else
+ #define EXTERN_C extern
+ typedef int bool;
+ #define true 1
+ #define false 0
+#endif
+
+/// Name for the default bulk read pipe
+#define DEVICE_BULK_READ_PIPE_NAME L"BulkRead"
+
+/// Name for the default bulk write pipe
+#define DEVICE_BULK_WRITE_PIPE_NAME L"BulkWrite"
+
+/// Prefix for an index-based pipe name
+#define DEVICE_PIPE_NAME_PREFIX L"PIPE_"
+
+/** \name IOCTL codes for the driver
+*/
+///@{
+
+/// Control code for IOCTL that gets USB_DEVICE_DESCRIPTOR
+#define SDB_CTL_GET_USB_DEVICE_DESCRIPTOR 10
+
+/// Control code for IOCTL that gets USB_CONFIGURATION_DESCRIPTOR
+#define SDB_CTL_GET_USB_CONFIGURATION_DESCRIPTOR 11
+
+/// Control code for IOCTL that gets USB_INTERFACE_DESCRIPTOR
+#define SDB_CTL_GET_USB_INTERFACE_DESCRIPTOR 12
+
+/// Control code for IOCTL that gets endpoint information
+#define SDB_CTL_GET_ENDPOINT_INFORMATION 13
+
+/// Control code for bulk read IOCTL
+#define SDB_CTL_BULK_READ 14
+
+/// Control code for bulk write IOCTL
+#define SDB_CTL_BULK_WRITE 15
+
+/// Control code for IOCTL that gets device serial number
+#define SDB_CTL_GET_SERIAL_NUMBER 16
+
+/// IOCTL that gets USB_DEVICE_DESCRIPTOR
+#define SDB_IOCTL_GET_USB_DEVICE_DESCRIPTOR \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_GET_USB_DEVICE_DESCRIPTOR, \
+ METHOD_BUFFERED, \
+ FILE_READ_ACCESS)
+
+/// IOCTL that gets USB_CONFIGURATION_DESCRIPTOR
+#define SDB_IOCTL_GET_USB_CONFIGURATION_DESCRIPTOR \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_GET_USB_CONFIGURATION_DESCRIPTOR, \
+ METHOD_BUFFERED, \
+ FILE_READ_ACCESS)
+
+/// IOCTL that gets USB_INTERFACE_DESCRIPTOR
+#define SDB_IOCTL_GET_USB_INTERFACE_DESCRIPTOR \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_GET_USB_INTERFACE_DESCRIPTOR, \
+ METHOD_BUFFERED, \
+ FILE_READ_ACCESS)
+
+/// IOCTL that gets endpoint information
+#define SDB_IOCTL_GET_ENDPOINT_INFORMATION \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_GET_ENDPOINT_INFORMATION, \
+ METHOD_BUFFERED, \
+ FILE_READ_ACCESS)
+
+/// Bulk read IOCTL
+#define SDB_IOCTL_BULK_READ \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_BULK_READ, \
+ METHOD_OUT_DIRECT, \
+ FILE_READ_ACCESS)
+
+// For bulk write IOCTL we send request data in the form of SdbBulkTransfer
+// structure and output buffer is just ULONG that receives number of bytes
+// actually written. Since both of these are tiny we can use buffered I/O
+// for this IOCTL.
+/// Bulk write IOCTL
+#define SDB_IOCTL_BULK_WRITE \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_BULK_WRITE, \
+ METHOD_BUFFERED, \
+ FILE_WRITE_ACCESS)
+
+/// IOCTL that gets device serial number
+#define SDB_IOCTL_GET_SERIAL_NUMBER \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, \
+ SDB_CTL_GET_SERIAL_NUMBER, \
+ METHOD_BUFFERED, \
+ FILE_READ_ACCESS)
+
+///@}
+
+/** Structure SdbQueryEndpointInformation formats input for
+ SDB_IOCTL_GET_ENDPOINT_INFORMATION IOCTL request
+*/
+struct SdbQueryEndpointInformation {
+ /// Zero-based endpoint index for which information is queried.
+ /// See SDB_QUERY_BULK_xxx_ENDPOINT_INDEX for shortcuts.
+ UCHAR endpoint_index;
+};
+
+/** Structure SdbBulkTransfer formats parameters for SDB_CTL_BULK_READ and
+ SDB_CTL_BULK_WRITE IOCTL requests.
+*/
+struct SdbBulkTransfer {
+ /// Time in milliseconds to complete this request
+ ULONG time_out;
+
+ /// Size of the data to transfer. This parameter is used only for
+ /// SDB_CTL_BULK_WRITE request. For SDB_CTL_BULK_READ requests transfer
+ /// size is defined by the output buffer size.
+ ULONG transfer_size;
+
+ /// Initializes statically allocated structure
+ __forceinline SdbBulkTransfer() {
+ time_out = 0;
+ transfer_size = 0;
+ for_x64 = 0;
+ }
+
+ /// Provides access to protected write_buffer field
+ void* GetWriteBuffer() {
+ return write_buffer;
+ }
+
+ /// Provides access to protected write_buffer field
+ const void* GetWriteBuffer() const {
+ return write_buffer;
+ }
+
+ /// Sets write_buffer field.
+ void SetWriteBuffer(void* buffer) {
+ // For 32-bit we must zero out high 32 bit of the address, so 64-bit
+ // driver will see valid address when accessing 64-bit write_buffer.
+ for_x64 = 0;
+ write_buffer = buffer;
+ }
+
+protected:
+ /// Pointer to the actual buffer for SDB_CTL_BULK_WRITE request. This field
+ /// is not used in SDB_CTL_BULK_READ request. Note that in order to support
+ /// compatibility between 32-bit and 64-bit versions of both, driver and
+ /// application we must sizeof this field to the max pointer sizeof (which
+ /// is 64 bit in our case). The idea is that if IOCTL was issued by a 64-bit
+ /// process to a 64-bit driver, write_buffer will be valid 64-bit pointer to
+ /// the write buffer. Same is true for 32-bit app talking to 32-bit driver.
+ /// If, however, a 32-bit app is talking to 64-bit driver, then write_buffer
+ /// initialized by 32-bit app will contain 32-bit address, which will be
+ /// correctly picked up ("extended") by 64-bit driver. Since when setting
+ /// this field by a 32-bit app requires some extra work (see SetWriteBuffer)
+ /// we hide this field, making it accessible only throug the accessor
+ /// methods (Get/SetWriteBuffer).
+ union {
+ void* write_buffer;
+ __int64 for_x64;
+ };
+};
+
+#endif // ANDROID_USB_API_SDB_API_LEGACY_H_
diff --git a/src/sdbwinapi/sdb_api_private_defines.h b/src/sdbwinapi/sdb_api_private_defines.h
new file mode 100644
index 0000000..ce3f1c9
--- /dev/null
+++ b/src/sdbwinapi/sdb_api_private_defines.h
@@ -0,0 +1,114 @@
+/*
+ * 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_USB_SDB_API_PRIVATE_DEFINES_H__
+#define ANDROID_USB_SDB_API_PRIVATE_DEFINES_H__
+/** \file
+ This file consists of private definitions used inside the API
+*/
+
+#include "sdb_api.h"
+
+/** \brief Encapsulates an entry in the array of enumerated interfaces.
+*/
+class SdbInstanceEnumEntry {
+ public:
+ /** \brief Constructs an empty object.
+ */
+ SdbInstanceEnumEntry()
+ : flags_(0) {
+ ZeroMemory(&class_id_, sizeof(class_id_));
+ }
+
+ /** \brief Copy constructor
+ */
+ SdbInstanceEnumEntry(const SdbInstanceEnumEntry& proto) {
+ Set(proto.device_name().c_str(), proto.class_id(), proto.flags());
+ }
+
+ /** \brief Constructs the object with parameters.
+ */
+ SdbInstanceEnumEntry(const wchar_t* dev_name, GUID cls_id, DWORD flgs) {
+ Set(dev_name, cls_id, flgs);
+ }
+
+ /** \brief Destructs the object.
+ */
+ ~SdbInstanceEnumEntry() {
+ }
+
+ /// Operator =
+ SdbInstanceEnumEntry& operator=(const SdbInstanceEnumEntry& proto) {
+ Set(proto.device_name().c_str(), proto.class_id(), proto.flags());
+ return *this;
+ }
+
+ /// Initializes instance with parameters
+ void Set(const wchar_t* dev_name, GUID cls_id, DWORD flgs) {
+ device_name_ = dev_name;
+ class_id_ = cls_id;
+ flags_ = flgs;
+ }
+
+ /// Calculates memory size needed to save this entry into SdbInterfaceInfo
+ /// structure
+ ULONG GetFlatSize() const {
+ return static_cast<ULONG>(FIELD_OFFSET(SdbInterfaceInfo, device_name) +
+ (device_name_.length() + 1) * sizeof(wchar_t));
+ }
+
+ /** \brief Saves this entry into SdbInterfaceInfo structure.
+
+ @param[in] info Buffer to save this entry to. Must be big enough to fit it.
+ Use GetFlatSize() method to get buffer size needed for that.
+
+ */
+ void Save(SdbInterfaceInfo* info) const {
+ info->class_id = class_id();
+ info->flags = flags();
+ wcscpy(info->device_name, device_name().c_str());
+ }
+
+ /// Gets interface's device name
+ const std::wstring& device_name() const {
+ return device_name_;
+ }
+
+ /// Gets inteface's class id
+ GUID class_id() const {
+ return class_id_;
+ }
+
+ /// Gets interface flags
+ DWORD flags() const {
+ return flags_;
+ }
+
+ private:
+ /// Inteface's class id (see SP_DEVICE_INTERFACE_DATA)
+ GUID class_id_;
+
+ /// Interface's device name
+ std::wstring device_name_;
+
+ /// Interface flags (see SP_DEVICE_INTERFACE_DATA)
+ DWORD flags_;
+};
+
+/// Defines array of enumerated interface entries
+typedef std::vector< SdbInstanceEnumEntry > SdbEnumInterfaceArray;
+
+#endif // ANDROID_USB_SDB_API_PRIVATE_DEFINES_H__
diff --git a/src/sdbwinapi/sdb_endpoint_object.cpp b/src/sdbwinapi/sdb_endpoint_object.cpp
new file mode 100644
index 0000000..f099ab5
--- /dev/null
+++ b/src/sdbwinapi/sdb_endpoint_object.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbEndpointObject that
+ encapsulates a handle opened to an endpoint on our device.
+*/
+
+#include "stdafx.h"
+#include "sdb_endpoint_object.h"
+
+SdbEndpointObject::SdbEndpointObject(SdbInterfaceObject* parent_interf,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index)
+ : SdbObjectHandle(SdbObjectTypeEndpoint),
+ parent_interface_(parent_interf),
+ endpoint_id_(endpoint_id),
+ endpoint_index_(endpoint_index) {
+ if (NULL != parent_interface_)
+ parent_interface_->AddRef();
+}
+
+SdbEndpointObject::~SdbEndpointObject() {
+ if (NULL != parent_interface_)
+ parent_interface_->Release();
+}
+
+bool SdbEndpointObject::GetEndpointInformation(SdbEndpointInformation* info) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ return parent_interface()->GetEndpointInformation(endpoint_index(), info);
+}
+
+SDBAPIHANDLE SdbEndpointObject::AsyncRead(void* buffer,
+ ULONG bytes_to_read,
+ ULONG* bytes_read,
+ HANDLE event_handle,
+ ULONG time_out) {
+ return CommonAsyncReadWrite(true,
+ buffer,
+ bytes_to_read,
+ bytes_read,
+ event_handle,
+ time_out);
+}
+
+SDBAPIHANDLE SdbEndpointObject::AsyncWrite(void* buffer,
+ ULONG bytes_to_write,
+ ULONG* bytes_written,
+ HANDLE event_handle,
+ ULONG time_out) {
+ return CommonAsyncReadWrite(false,
+ buffer,
+ bytes_to_write,
+ bytes_written,
+ event_handle,
+ time_out);
+}
+
+bool SdbEndpointObject::SyncRead(void* buffer,
+ ULONG bytes_to_read,
+ ULONG* bytes_read,
+ ULONG time_out) {
+ return CommonSyncReadWrite(true,
+ buffer,
+ bytes_to_read,
+ bytes_read,
+ time_out);
+}
+
+bool SdbEndpointObject::SyncWrite(void* buffer,
+ ULONG bytes_to_write,
+ ULONG* bytes_written,
+ ULONG time_out) {
+ return CommonSyncReadWrite(false,
+ buffer,
+ bytes_to_write,
+ bytes_written,
+ time_out);
+}
diff --git a/src/sdbwinapi/sdb_endpoint_object.h b/src/sdbwinapi/sdb_endpoint_object.h
new file mode 100644
index 0000000..f670ebe
--- /dev/null
+++ b/src/sdbwinapi/sdb_endpoint_object.h
@@ -0,0 +1,222 @@
+/*
+ * 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_USB_API_SDB_ENDPOINT_OBJECT_H__
+#define ANDROID_USB_API_SDB_ENDPOINT_OBJECT_H__
+/** \file
+ This file consists of declaration of class SdbEndpointObject that
+ encapsulates a handle opened to an endpoint on our device.
+*/
+
+#include "sdb_interface.h"
+
+/** Class SdbEndpointObject encapsulates a handle opened to an endpoint on
+ our device.
+
+ This class implement functionality that is common for both, WinUsb and
+ legacy APIs.
+*/
+class SDBWIN_API_CLASS SdbEndpointObject : public SdbObjectHandle {
+ public:
+ /** \brief Constructs the object
+
+ @param[in] interface Parent interface for this object. Interface will be
+ referenced in this object's constructur and released in the
+ destructor.
+ @param[in] endpoint_id Endpoint ID (endpoint address) on the device.
+ @param[in] endpoint_index Zero-based endpoint index in the interface's
+ array of endpoints.
+ */
+ SdbEndpointObject(SdbInterfaceObject* parent_interf,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbEndpointObject();
+
+ //
+ // Abstract
+ //
+
+ protected:
+ /** \brief Common code for async read / write
+
+ @param[in] is_read Read or write selector.
+ @param[in,out] buffer Pointer to the buffer for read / write.
+ @param[in] bytes_to_transfer Number of bytes to be read / written.
+ @param[out] bytes_transferred Number of bytes read / written. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+ */
+ virtual SDBAPIHANDLE CommonAsyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ HANDLE event_handle,
+ ULONG time_out) = 0;
+
+ /** \brief Common code for sync read / write
+
+ @param[in] is_read Read or write selector.
+ @param[in,out] buffer Pointer to the buffer for read / write.
+ @param[in] bytes_to_transfer Number of bytes to be read / written.
+ @param[out] bytes_transferred Number of bytes read / written. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool CommonSyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ ULONG time_out) = 0;
+
+ //
+ // Operations
+ //
+
+ public:
+ /** \brief Gets information about this endpoint.
+
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetEndpointInformation(SdbEndpointInformation* info);
+
+ /** \brief Reads from opened I/O object asynchronously
+
+ @param[out] buffer Pointer to the buffer that receives the data.
+ @param[in] bytes_to_read Number of bytes to be read.
+ @param[out] bytes_read Number of bytes read. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+ */
+ virtual SDBAPIHANDLE AsyncRead(void* buffer,
+ ULONG bytes_to_read,
+ ULONG* bytes_read,
+ HANDLE event_handle,
+ ULONG time_out);
+
+ /** \brief Writes to opened I/O object asynchronously
+
+ @param[in] buffer Pointer to the buffer containing the data to be written.
+ @param[in] bytes_to_write Number of bytes to be written.
+ @param[out] bytes_written Number of bytes written. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+ */
+ virtual SDBAPIHANDLE AsyncWrite(void* buffer,
+ ULONG bytes_to_write,
+ ULONG* bytes_written,
+ HANDLE event_handle,
+ ULONG time_out);
+
+ /** \brief Reads from opened I/O object synchronously
+
+ @param[out] buffer Pointer to the buffer that receives the data.
+ @param[in] bytes_to_read Number of bytes to be read.
+ @param[out] bytes_read Number of bytes read. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return true on success and false on failure. If false is
+ returned GetLastError() provides extended error information.
+ */
+ virtual bool SyncRead(void* buffer,
+ ULONG bytes_to_read,
+ ULONG* bytes_read,
+ ULONG time_out);
+
+ /** \brief Writes to opened I/O object synchronously
+
+ @param[in] buffer Pointer to the buffer containing the data to be written.
+ @param[in] bytes_to_write Number of bytes to be written.
+ @param[out] bytes_written Number of bytes written. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return true on success and false on failure. If false is
+ returned GetLastError() provides extended error information.
+ */
+ virtual bool SyncWrite(void* buffer,
+ ULONG bytes_to_write,
+ ULONG* bytes_written,
+ ULONG time_out);
+
+ public:
+ /// This is a helper for extracting object from the SdbObjectHandleMap
+ static SdbObjectType Type() {
+ return SdbObjectTypeEndpoint;
+ }
+
+ /// Gets parent interface
+ SdbInterfaceObject* parent_interface() const {
+ return parent_interface_;
+ }
+ /// Gets this endpoint ID
+ UCHAR endpoint_id() const {
+ return endpoint_id_;
+ }
+
+ /// Gets this endpoint index on the interface
+ UCHAR endpoint_index() const {
+ return endpoint_index_;
+ }
+
+ /// Gets parent interface handle
+ SDBAPIHANDLE GetParentInterfaceHandle() const {
+ return (NULL != parent_interface()) ? parent_interface()->sdb_handle() :
+ NULL;
+ }
+
+ protected:
+ /// Parent interface
+ SdbInterfaceObject* parent_interface_;
+
+ /// This endpoint id
+ UCHAR endpoint_id_;
+
+ /// This endpoint index on the interface
+ UCHAR endpoint_index_;
+};
+
+#endif // ANDROID_USB_API_SDB_ENDPOINT_OBJECT_H__
diff --git a/src/sdbwinapi/sdb_helper_routines.cpp b/src/sdbwinapi/sdb_helper_routines.cpp
new file mode 100644
index 0000000..4dbff40
--- /dev/null
+++ b/src/sdbwinapi/sdb_helper_routines.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of helper routines used
+ in the API.
+*/
+
+#include "stdafx.h"
+#include "sdb_api.h"
+#include "sdb_api_legacy.h"
+#include "sdb_helper_routines.h"
+#include "sdb_interface_enum.h"
+
+bool GetSDKComplientParam(SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode,
+ ULONG* desired_access,
+ ULONG* desired_sharing) {
+ if (NULL != desired_access) {
+ switch (access_type) {
+ case SdbOpenAccessTypeReadWrite:
+ *desired_access = GENERIC_READ | GENERIC_WRITE;
+ break;
+
+ case SdbOpenAccessTypeRead:
+ *desired_access = GENERIC_READ;
+ break;
+
+ case SdbOpenAccessTypeWrite:
+ *desired_access = GENERIC_WRITE;
+ break;
+
+ case SdbOpenAccessTypeQueryInfo:
+ *desired_access = FILE_READ_ATTRIBUTES | FILE_READ_EA;
+ break;
+
+ default:
+ SetLastError(ERROR_INVALID_ACCESS);
+ return false;
+ }
+ }
+
+ if (NULL != desired_sharing) {
+ switch (sharing_mode) {
+ case SdbOpenSharingModeReadWrite:
+ *desired_sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ break;
+
+ case SdbOpenSharingModeRead:
+ *desired_sharing = FILE_SHARE_READ;
+ break;
+
+ case SdbOpenSharingModeWrite:
+ *desired_sharing = FILE_SHARE_WRITE;
+ break;
+
+ case SdbOpenSharingModeExclusive:
+ *desired_sharing = 0;
+ break;
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool EnumerateDeviceInterfaces(HDEVINFO hardware_dev_info,
+ GUID class_id,
+ bool exclude_removed,
+ bool active_only,
+ SdbEnumInterfaceArray* interfaces) {
+ SdbEnumInterfaceArray tmp;
+ bool ret = false;
+
+ // Enumerate interfaces on this device
+ for (ULONG index = 0; ; index++) {
+ SP_DEVICE_INTERFACE_DATA interface_data;
+ interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ // SetupDiEnumDeviceInterfaces() returns information about device
+ // interfaces exposed by one or more devices defined by our interface
+ // class. Each call returns information about one interface. The routine
+ // can be called repeatedly to get information about several interfaces
+ // exposed by one or more devices.
+ if (SetupDiEnumDeviceInterfaces(hardware_dev_info,
+ 0,
+ &class_id,
+ index,
+ &interface_data)) {
+ // Satisfy "exclude removed" and "active only" filters.
+ if ((!exclude_removed || (0 == (interface_data.Flags & SPINT_REMOVED))) &&
+ (!active_only || (interface_data.Flags & SPINT_ACTIVE))) {
+ std::wstring dev_name;
+
+ if (GetUsbDeviceName(hardware_dev_info, &interface_data, &dev_name)) {
+ try {
+ // Add new entry to the array
+ tmp.push_back(SdbInstanceEnumEntry(dev_name.c_str(),
+ interface_data.InterfaceClassGuid,
+ interface_data.Flags));
+ } catch (... ) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ break;
+ }
+ } else {
+ // Something went wrong in getting device name
+ break;
+ }
+ }
+ } else {
+ if (ERROR_NO_MORE_ITEMS == GetLastError()) {
+ // There are no more items in the list. Enum is completed.
+ ret = true;
+ break;
+ } else {
+ // Something went wrong in SDK enum
+ break;
+ }
+ }
+ }
+
+ // On success, swap temp array with the returning one
+ if (ret)
+ interfaces->swap(tmp);
+
+ return ret;
+}
+
+bool EnumerateDeviceInterfaces(GUID class_id,
+ ULONG flags,
+ bool exclude_removed,
+ bool active_only,
+ SdbEnumInterfaceArray* interfaces) {
+ // Open a handle to the plug and play dev node.
+ // SetupDiGetClassDevs() returns a device information set that
+ // contains info on all installed devices of a specified class.
+ HDEVINFO hardware_dev_info =
+ SetupDiGetClassDevs(&class_id, NULL, NULL, flags);
+
+ bool ret = false;
+
+ if (INVALID_HANDLE_VALUE != hardware_dev_info) {
+ // Do the enum
+ ret = EnumerateDeviceInterfaces(hardware_dev_info,
+ class_id,
+ exclude_removed,
+ active_only,
+ interfaces);
+
+ // Preserve last error accross hardware_dev_info destruction
+ ULONG error_to_report = ret ? NO_ERROR : GetLastError();
+
+ SetupDiDestroyDeviceInfoList(hardware_dev_info);
+
+ if (NO_ERROR != error_to_report)
+ SetLastError(error_to_report);
+ }
+
+ return ret;
+}
+
+bool GetUsbDeviceDetails(
+ HDEVINFO hardware_dev_info,
+ PSP_DEVICE_INTERFACE_DATA dev_info_data,
+ PSP_DEVICE_INTERFACE_DETAIL_DATA* dev_info_detail_data) {
+ ULONG required_len = 0;
+
+ // First query for the structure size. At this point we expect this call
+ // to fail with ERROR_INSUFFICIENT_BUFFER error code.
+ if (SetupDiGetDeviceInterfaceDetail(hardware_dev_info,
+ dev_info_data,
+ NULL,
+ 0,
+ &required_len,
+ NULL)) {
+ return false;
+ }
+
+ if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
+ return false;
+
+ // Allocate buffer for the structure
+ PSP_DEVICE_INTERFACE_DETAIL_DATA buffer =
+ reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(malloc(required_len));
+
+ if (NULL == buffer) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return false;
+ }
+
+ buffer->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ // Retrieve the information from Plug and Play.
+ if (SetupDiGetDeviceInterfaceDetail(hardware_dev_info,
+ dev_info_data,
+ buffer,
+ required_len,
+ &required_len,
+ NULL)) {
+ *dev_info_detail_data = buffer;
+ return true;
+ } else {
+ // Free the buffer if this call failed
+ free(buffer);
+
+ return false;
+ }
+}
+
+bool GetUsbDeviceName(HDEVINFO hardware_dev_info,
+ PSP_DEVICE_INTERFACE_DATA dev_info_data,
+ std::wstring* name) {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA func_class_dev_data = NULL;
+ if (!GetUsbDeviceDetails(hardware_dev_info,
+ dev_info_data,
+ &func_class_dev_data)) {
+ return false;
+ }
+
+ try {
+ *name = func_class_dev_data->DevicePath;
+ } catch (...) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+
+ free(func_class_dev_data);
+
+ return !name->empty();
+}
+
+bool IsLegacyInterface(const wchar_t* interface_name) {
+ // Open USB device for this intefface
+ HANDLE usb_device_handle = CreateFile(interface_name,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if (INVALID_HANDLE_VALUE == usb_device_handle) {
+ printf("INVALID_HANDLE_VALUE == usb_device_handle\n");
+ return NULL;
+ }
+ printf("INVALID_HANDLE_VALUE != usb_device_handle\n");
+ // Try to issue SDB_IOCTL_GET_USB_DEVICE_DESCRIPTOR IOCTL that is supported
+ // by the legacy driver, but is not implemented in the WinUsb driver.
+ DWORD ret_bytes = 0;
+ USB_DEVICE_DESCRIPTOR descriptor;
+ BOOL ret = DeviceIoControl(usb_device_handle,
+ SDB_IOCTL_GET_USB_DEVICE_DESCRIPTOR,
+ NULL, 0,
+ &descriptor,
+ sizeof(descriptor),
+ &ret_bytes,
+ NULL);
+ ::CloseHandle(usb_device_handle);
+
+ // If IOCTL succeeded we've got legacy driver underneath.
+ return ret ? true : false;
+}
diff --git a/src/sdbwinapi/sdb_helper_routines.h b/src/sdbwinapi/sdb_helper_routines.h
new file mode 100644
index 0000000..37580d3
--- /dev/null
+++ b/src/sdbwinapi/sdb_helper_routines.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_HELPER_ROUTINES_H__
+#define ANDROID_USB_API_SDB_HELPER_ROUTINES_H__
+/** \file
+ This file consists of declarations of helper routines used
+ in the API.
+*/
+
+#include "sdb_api_private_defines.h"
+
+/** \brief Converts access type and share mode from our enum into
+ SDK - complient values.
+
+ @param[in] access_type Enumerated access type
+ @param[in] sharing_mode Enumerated share mode
+ @param[out] desired_access Will receive SDK - complient desired access
+ flags. This parameter can be NULL.
+ @param[out] desired_sharing Will receive SDK - complient share mode.
+ This parameter can be NULL.
+ @return True on success, false on failure, in which case GetLastError()
+ provides extended information about the error that occurred.
+*/
+bool GetSDKComplientParam(SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode,
+ ULONG* desired_access,
+ ULONG* desired_sharing);
+
+/** \brief Given the hardware device information enumerates interfaces for
+ this device.
+
+ @param[in] hardware_dev_info A handle to hardware device information obtained
+ from PnP manager via SetupDiGetClassDevs()
+ @param[in] class_id Device class ID how it is specified by our USB driver.
+ @param[in] exclude_removed If true interfaces with SPINT_REMOVED flag set
+ will be not included in the enumeration.
+ @param[in] active_only If true only active interfaces (with flag
+ SPINT_ACTIVE set) will be included in the enumeration.
+ @param[out] interfaces Upon successfull completion will consist of array of
+ all interfaces found for this device (matching all filters).
+ @return True on success, false on failure, in which case GetLastError()
+ provides extended information about the error that occurred.
+*/
+bool EnumerateDeviceInterfaces(HDEVINFO hardware_dev_info,
+ GUID class_id,
+ bool exclude_removed,
+ bool active_only,
+ SdbEnumInterfaceArray* interfaces);
+
+/** \brief Enumerates all interfaces for our device class.
+
+ This routine uses SetupDiGetClassDevs to get our device info and calls
+ EnumerateDeviceInterfaces to perform the enumeration.
+ @param[in] class_id Device class ID how it is specified by our USB driver
+ @param[in] flags Flags to pass to SetupDiGetClassDevs to filter devices. See
+ SetupDiGetClassDevs() in SDK for more info on these flags.
+ @param[in] exclude_removed If true interfaces with SPINT_REMOVED flag set
+ will be not included in the enumeration.
+ @param[in] active_only If true only active interfaces (with flag
+ SPINT_ACTIVE set) will be included in the enumeration.
+ @param[out] interfaces Upon successfull completion will consist of array of
+ all interfaces found for this device (matching all filters).
+ @return True on success, false on failure, in which case GetLastError()
+ provides extended information about the error that occurred.
+*/
+bool EnumerateDeviceInterfaces(GUID class_id,
+ ULONG flags,
+ bool exclude_removed,
+ bool active_only,
+ SdbEnumInterfaceArray* interfaces);
+
+/** \brief Given the hardware device information and data gets data details.
+
+ Given the hardware_dev_info, representing a handle to the plug and
+ play information, and dev_info_data, representing a specific usb device,
+ gets detailed data about the device (interface).
+ @param[in] hardware_dev_info A handle to hardware device information obtained
+ from PnP manager via SetupDiGetClassDevs()
+ @param[in] dev_info_data Device information data obtained via call to
+ SetupDiEnumDeviceInterfaces()
+ @param[out] dev_info_detail_data Upon successfull completion will consist of
+ the detailed data about device interface. This routine always
+ allocates memory for the output structure so content of this pointer
+ doesn't matter and will be overwritten by this routine. The caller
+ of this method is responsible for freeing allocated data using free()
+ routine.
+ @return True on success, false on failure, in which case GetLastError()
+ provides extended information about the error that occurred.
+*/
+bool GetUsbDeviceDetails(HDEVINFO hardware_dev_info,
+ PSP_DEVICE_INTERFACE_DATA dev_info_data,
+ PSP_DEVICE_INTERFACE_DETAIL_DATA* dev_info_detail_data);
+
+/** \brief Given the hardware device information and data gets device name.
+
+ Given the hardware_dev_info, representing a handle to the plug and
+ play information, and dev_info_data, representing a specific usb device,
+ gets device name. This routine uses GetUsbDeviceDetails to extract device
+ name.
+ @param[in] hardware_dev_info A handle to hardware device information obtained
+ from PnP manager via SetupDiGetClassDevs()
+ @param[in] dev_info_data Device information data obtained via call to
+ SetupDiEnumDeviceInterfaces()
+ @param[out] name Upon successfull completion will have name for the device.
+ @return True on success, false on failure, in which case GetLastError()
+ provides extended information about the error that occurred.
+*/
+bool GetUsbDeviceName(HDEVINFO hardware_dev_info,
+ PSP_DEVICE_INTERFACE_DATA dev_info_data,
+ std::wstring* name);
+
+/** \brief Checks if given interface is available via custom USB driver.
+
+ In this routine we will query the given interface with an IOCTL that should
+ be supported by the custom driver. If IOCTL fails, we make a conclusion that
+ this interface is available via WinUsb, and not via custom USB driver.
+ @param[in] interface_name Name of the interface to check.
+ @return true if interface is available via custom USB driver, or false
+ otherwise.
+*/
+bool IsLegacyInterface(const wchar_t* interface_name);
+
+#endif // ANDROID_USB_API_SDB_HELPER_ROUTINES_H__
diff --git a/src/sdbwinapi/sdb_interface.cpp b/src/sdbwinapi/sdb_interface.cpp
new file mode 100644
index 0000000..08baa3c
--- /dev/null
+++ b/src/sdbwinapi/sdb_interface.cpp
@@ -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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbInterfaceObject that
+ encapsulates a generic interface on our USB device.
+*/
+
+#include "stdafx.h"
+#include "sdb_interface.h"
+
+SdbInterfaceObject::SdbInterfaceObject(const wchar_t* interf_name)
+ : SdbObjectHandle(SdbObjectTypeInterface),
+ interface_name_(interf_name) {
+ ATLASSERT(NULL != interf_name);
+}
+
+SdbInterfaceObject::~SdbInterfaceObject() {
+}
+
+bool SdbInterfaceObject::GetInterfaceName(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi) {
+ if (NULL == buffer_char_size) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Lets see if buffer is big enough
+ ULONG name_len = static_cast<ULONG>(interface_name_.length() + 1);
+ if ((NULL == buffer) || (*buffer_char_size < name_len)) {
+ *buffer_char_size = name_len;
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return false;
+ }
+
+ if (!ansi) {
+ // If user asked for wide char name just return it
+ wcscpy(reinterpret_cast<wchar_t*>(buffer), interface_name().c_str());
+ return true;
+ }
+
+ // We need to convert name from wide char to ansi string
+ int res = WideCharToMultiByte(CP_ACP,
+ 0,
+ interface_name().c_str(),
+ static_cast<int>(name_len),
+ reinterpret_cast<PSTR>(buffer),
+ static_cast<int>(*buffer_char_size),
+ NULL,
+ NULL);
+ return (res != 0);
+}
+
+bool SdbInterfaceObject::GetUsbDeviceDescriptor(USB_DEVICE_DESCRIPTOR* desc) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ if (NULL == desc) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ CopyMemory(desc, usb_device_descriptor(), sizeof(USB_DEVICE_DESCRIPTOR));
+
+ return true;
+}
+
+bool SdbInterfaceObject::GetUsbConfigurationDescriptor(
+ USB_CONFIGURATION_DESCRIPTOR* desc) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ if (NULL == desc) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ CopyMemory(desc, usb_config_descriptor(),
+ sizeof(USB_CONFIGURATION_DESCRIPTOR));
+
+ return true;
+}
+
+bool SdbInterfaceObject::GetUsbInterfaceDescriptor(
+ USB_INTERFACE_DESCRIPTOR* desc) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ if (NULL == desc) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ CopyMemory(desc, usb_interface_descriptor(), sizeof(USB_INTERFACE_DESCRIPTOR));
+
+ return true;
+}
diff --git a/src/sdbwinapi/sdb_interface.h b/src/sdbwinapi/sdb_interface.h
new file mode 100644
index 0000000..2937d4a
--- /dev/null
+++ b/src/sdbwinapi/sdb_interface.h
@@ -0,0 +1,203 @@
+/*
+ * 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_USB_API_SDB_INTERFACE_H__
+#define ANDROID_USB_API_SDB_INTERFACE_H__
+/** \file
+ This file consists of declaration of class SdbInterfaceObject that
+ encapsulates a generic interface on our USB device.
+*/
+
+#include "sdb_object_handle.h"
+
+// 'SdbInterfaceObject::interface_name_' : class 'std::basic_string<_E,_Tr,_A>'
+// needs to have dll-interface to be used by clients of class
+// 'SdbInterfaceObject' We're ok with that, since interface_name_ will not
+// be referenced by name from outside of this class.
+#pragma warning(disable: 4251)
+/** \brief Encapsulates an interface on our USB device.
+
+ This is an abstract class that implements functionality common for both,
+ legacy, and WinUsb based interfaces.
+*/
+class SDBWIN_API_CLASS SdbInterfaceObject : public SdbObjectHandle {
+ public:
+ /** \brief Constructs the object.
+
+ @param[in] interf_name Name of the interface
+ */
+ explicit SdbInterfaceObject(const wchar_t* interf_name);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbInterfaceObject();
+
+ //
+ // Abstract
+ //
+
+ public:
+ /** \brief Gets serial number for interface's device.
+
+ @param[out] buffer Buffer for the serail number string. Can be NULL in
+ which case buffer_char_size will contain number of characters
+ required for the string.
+ @param[in,out] buffer_char_size On the way in supplies size (in characters)
+ of the buffer. On the way out, if method failed and GetLastError
+ reports ERROR_INSUFFICIENT_BUFFER, will contain number of characters
+ required for the name.
+ @param[in] ansi If true the name will be returned as single character
+ string. Otherwise name will be returned as wide character string.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetSerialNumber(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi) = 0;
+
+
+ /** \brief Gets information about an endpoint on this interface.
+
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide infor about
+ (default?) bulk write and read endpoints respectively.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetEndpointInformation(UCHAR endpoint_index,
+ SdbEndpointInformation* info) = 0;
+
+ /** \brief Opens an endpoint on this interface.
+
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide infor about
+ (default?) bulk write and read endpoints respectively.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure.
+ If NULL is returned GetLastError() provides extended information
+ about the error that occurred.
+ */
+ virtual SDBAPIHANDLE OpenEndpoint(UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) = 0;
+
+ //
+ // Operations
+ //
+
+ public:
+ /** \brief Gets interface device name.
+
+ @param[out] buffer Buffer for the name. Can be NULL in which case
+ buffer_char_size will contain number of characters required to fit
+ the name.
+ @param[in,out] buffer_char_size On the way in supplies size (in characters)
+ of the buffer. On the way out if method failed and GetLastError
+ reports ERROR_INSUFFICIENT_BUFFER will contain number of characters
+ required to fit the name.
+ @param[in] ansi If true the name will be returned as single character
+ string. Otherwise name will be returned as wide character string.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetInterfaceName(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi);
+
+ /** \brief Gets device descriptor for the USB device associated with
+ this interface.
+
+ @param[out] desc Upon successful completion will have usb device
+ descriptor.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetUsbDeviceDescriptor(USB_DEVICE_DESCRIPTOR* desc);
+
+ /** \brief Gets descriptor for the selected USB device configuration.
+
+ @param[out] desc Upon successful completion will have usb device
+ configuration descriptor.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetUsbConfigurationDescriptor(
+ USB_CONFIGURATION_DESCRIPTOR* desc);
+
+ /** \brief Gets descriptor for this interface.
+
+ @param[out] desc Upon successful completion will have interface
+ descriptor.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetUsbInterfaceDescriptor(USB_INTERFACE_DESCRIPTOR* desc);
+
+ public:
+ /// Gets name of the USB interface (device name) for this object
+ const std::wstring& interface_name() const {
+ return interface_name_;
+ }
+
+ /// This is a helper for extracting object from the SdbObjectHandleMap
+ static SdbObjectType Type() {
+ return SdbObjectTypeInterface;
+ }
+
+ /// Gets cached usb device descriptor
+ const USB_DEVICE_DESCRIPTOR* usb_device_descriptor() const {
+ return &usb_device_descriptor_;
+ }
+
+ /// Gets cached usb configuration descriptor
+ const USB_CONFIGURATION_DESCRIPTOR* usb_config_descriptor() const {
+ return &usb_config_descriptor_;
+ }
+
+ /// Gets cached usb interface descriptor
+ const USB_INTERFACE_DESCRIPTOR* usb_interface_descriptor() const {
+ return &usb_interface_descriptor_;
+ }
+
+ protected:
+ /// Cached usb device descriptor
+ USB_DEVICE_DESCRIPTOR usb_device_descriptor_;
+
+ /// Cached usb configuration descriptor
+ USB_CONFIGURATION_DESCRIPTOR usb_config_descriptor_;
+
+ /// Cached usb interface descriptor
+ USB_INTERFACE_DESCRIPTOR usb_interface_descriptor_;
+
+ private:
+ /// Name of the USB interface (device name) for this object
+ std::wstring interface_name_;
+};
+#pragma warning(default: 4251)
+
+#endif // ANDROID_USB_API_SDB_INTERFACE_H__
diff --git a/src/sdbwinapi/sdb_interface_enum.cpp b/src/sdbwinapi/sdb_interface_enum.cpp
new file mode 100644
index 0000000..693ae20
--- /dev/null
+++ b/src/sdbwinapi/sdb_interface_enum.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+/** \file
+ This file consists of implementation of SdbInterfaceEnumObject class that
+ encapsulates enumerator of USB interfaces available through this API.
+*/
+
+#include "stdafx.h"
+#include "sdb_api.h"
+#include "sdb_interface_enum.h"
+#include "sdb_helper_routines.h"
+
+SdbInterfaceEnumObject::SdbInterfaceEnumObject()
+ : SdbObjectHandle(SdbObjectTypeInterfaceEnumerator) {
+ current_interface_ = interfaces_.begin();
+}
+
+SdbInterfaceEnumObject::~SdbInterfaceEnumObject() {
+}
+
+bool SdbInterfaceEnumObject::InitializeEnum(GUID class_id,
+ bool exclude_not_present,
+ bool exclude_removed,
+ bool active_only) {
+ // Calc flags for SetupDiGetClassDevs
+ DWORD flags = DIGCF_DEVICEINTERFACE;
+ if (exclude_not_present)
+ flags |= DIGCF_PRESENT;
+
+ // Do the enum
+ bool ret = EnumerateDeviceInterfaces(class_id,
+ flags,
+ exclude_removed,
+ active_only,
+ &interfaces_);
+
+ // If enum was successfull set current enum pointer
+ // to the beginning of the array
+ if (ret)
+ current_interface_ = interfaces_.begin();
+
+ return ret;
+}
+
+bool SdbInterfaceEnumObject::Next(SdbInterfaceInfo* info, ULONG* size) {
+ // Make sure that it's opened
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ ATLASSERT(NULL != size);
+ if (NULL == size) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Lets see if enum is over
+ if (interfaces_.end() == current_interface_) {
+ SetLastError(ERROR_NO_MORE_ITEMS);
+ return false;
+ }
+
+ SdbInstanceEnumEntry& entry = *current_interface_;
+
+ // Big enough?
+ if ((NULL == info) || (*size < entry.GetFlatSize())) {
+ *size = entry.GetFlatSize();
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return false;
+ }
+
+ // All checks passed
+ entry.Save(info);
+ current_interface_++;
+ return true;
+}
+
+bool SdbInterfaceEnumObject::Reset() {
+ // Make sure that it's opened
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ current_interface_ = interfaces_.begin();
+
+ return true;
+}
diff --git a/src/sdbwinapi/sdb_interface_enum.h b/src/sdbwinapi/sdb_interface_enum.h
new file mode 100644
index 0000000..4d2d634
--- /dev/null
+++ b/src/sdbwinapi/sdb_interface_enum.h
@@ -0,0 +1,98 @@
+/*
+ * 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_USB_API_SDB_INTERFACE_ENUM_H__
+#define ANDROID_USB_API_SDB_INTERFACE_ENUM_H__
+/** \file
+ This file consists of declaration of SdbInterfaceEnumObject class that
+ encapsulates enumerator of USB interfaces available through this API.
+*/
+
+#include "sdb_object_handle.h"
+
+/** \brief Enumerator of USB interfaces available through this API.
+*/
+class SdbInterfaceEnumObject : public SdbObjectHandle {
+ public:
+ /** \brief Constructs the object.
+ */
+ SdbInterfaceEnumObject();
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbInterfaceEnumObject();
+
+ public:
+ /** \brief Enumerates all interfaces for the given device class.
+
+ This routine uses SetupDiGetClassDevs to get our device info and calls
+ EnumerateDeviceInterfaces to perform the enumeration.
+ @param[in] class_id Device class ID that is specified by our USB driver
+ @param[in] exclude_not_present If set include only those devices that are
+ currently present.
+ @param[in] exclude_removed If true interfaces with SPINT_REMOVED flag set
+ will be not included in the enumeration.
+ @param[in] active_only If true only active interfaces (with flag
+ SPINT_ACTIVE set) will be included in the enumeration.
+ @return True on success, false on failure, in which case GetLastError()
+ provides extended information about the error that occurred.
+ */
+ bool InitializeEnum(GUID class_id,
+ bool exclude_not_present,
+ bool exclude_removed,
+ bool active_only);
+
+ /** \brief Gets next enumerated interface information
+ @param[out] info Upon successful completion will receive interface
+ information. Can be NULL. If it is NULL, upon return from this
+ method *size will have memory size required to fit this entry.
+ @param[in,out] size On the way in provides size of the memory buffer
+ addressed by info param. On the way out (only if buffer is not
+ big enough) will provide memory size required to fit this entry.
+ @return true on success, false on error. If false is returned
+ GetLastError() provides extended information about the error that
+ occurred. ERROR_INSUFFICIENT_BUFFER indicates that buffer provided
+ in info param was not big enough and *size specifies memory size
+ required to fit this entry. ERROR_NO_MORE_ITEMS indicates that
+ enumeration is over and there are no more entries to return.
+ */
+ bool Next(SdbInterfaceInfo* info, ULONG* size);
+
+ /** \brief Makes enumerator to start from the beginning.
+ @return true on success, false on error. If false is returned
+ GetLastError() provides extended information about the error that
+ occurred.
+ */
+ bool Reset();
+
+ // This is a helper for extracting object from the SdbObjectHandleMap
+ static SdbObjectType Type() {
+ return SdbObjectTypeInterfaceEnumerator;
+ }
+
+ protected:
+ /// Array of interfaces enumerated with this object
+ SdbEnumInterfaceArray interfaces_;
+
+ /// Current enumerator
+ SdbEnumInterfaceArray::iterator current_interface_;
+};
+
+#endif // ANDROID_USB_API_SDB_INTERFACE_ENUM_H__
diff --git a/src/sdbwinapi/sdb_io_completion.cpp b/src/sdbwinapi/sdb_io_completion.cpp
new file mode 100644
index 0000000..b8b62d4
--- /dev/null
+++ b/src/sdbwinapi/sdb_io_completion.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbIOCompletion that
+ encapsulates a generic wrapper around OVERLAPPED Win32 structure
+ returned from asynchronous I/O requests.
+*/
+
+#include "stdafx.h"
+#include "sdb_io_completion.h"
+
+SdbIOCompletion::SdbIOCompletion(SdbEndpointObject* parent_io_obj,
+ ULONG expected_trans_size,
+ HANDLE event_hndl)
+ : SdbObjectHandle(SdbObjectTypeIoCompletion),
+ expected_transfer_size_(expected_trans_size),
+ parent_io_object_(parent_io_obj) {
+ ATLASSERT(NULL != parent_io_obj);
+ parent_io_obj->AddRef();
+ ZeroMemory(&overlapped_, sizeof(overlapped_));
+ overlapped_.hEvent = event_hndl;
+}
+
+SdbIOCompletion::~SdbIOCompletion() {
+ parent_io_object_->Release();
+}
+
+bool SdbIOCompletion::IsCompleted() {
+ SetLastError(NO_ERROR);
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return true;
+ }
+
+ return HasOverlappedIoCompleted(overlapped()) ? true : false;
+}
diff --git a/src/sdbwinapi/sdb_io_completion.h b/src/sdbwinapi/sdb_io_completion.h
new file mode 100644
index 0000000..954789a
--- /dev/null
+++ b/src/sdbwinapi/sdb_io_completion.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_IO_COMPLETION_H__
+#define ANDROID_USB_API_SDB_IO_COMPLETION_H__
+/** \file
+ This file consists of declaration of class SdbIOCompletion that encapsulates
+ a generic wrapper around OVERLAPPED Win32 structure returned from
+ asynchronous I/O requests.
+*/
+
+#include "sdb_endpoint_object.h"
+
+/** \brief Encapsulates encapsulates a generic wrapper around OVERLAPPED Win32
+ structure returned from asynchronous I/O requests.
+
+ This is an abstract class that implements functionality common for I/O
+ performed via WinUsb as well as legacy driver APIs. A handle to this object
+ is returned to the caller of each successful asynchronous I/O request. Just
+ like all other handles this handle must be closed after it's no longer
+ needed.
+*/
+class SDBWIN_API_CLASS SdbIOCompletion : public SdbObjectHandle {
+ public:
+ /** \brief Constructs the object
+
+ @param[in] parent_io_obj Parent I/O object that created this instance.
+ Parent object will be referenced in this object's constructor and
+ released in the destructor.
+ @param[in] expected_trans_size Number of bytes expected to be transferred
+ with the I/O.
+ @param[in] event_hndl Event handle that should be signaled when I/O
+ completes. Can be NULL. If it's not NULL this handle will be
+ used to initialize OVERLAPPED structure for this object.
+ */
+ SdbIOCompletion(SdbEndpointObject* parent_io_obj,
+ ULONG expected_trans_size,
+ HANDLE event_hndl);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbIOCompletion();
+
+ //
+ // Abstract
+ //
+
+ public:
+ /** \brief Gets overlapped I/O result
+
+ @param[out] ovl_data Buffer for the copy of this object's OVERLAPPED
+ structure. Can be NULL.
+ @param[out] bytes_transferred Pointer to a variable that receives the
+ number of bytes that were actually transferred by a read or write
+ operation. See SDK doc on GetOvelappedResult for more information.
+ Unlike regular GetOvelappedResult call this parameter can be NULL.
+ @param[in] wait If this parameter is true, the method does not return
+ until the operation has been completed. If this parameter is false
+ and the operation is still pending, the method returns false and
+ the GetLastError function returns ERROR_IO_INCOMPLETE.
+ @return true if I/O has been completed or false on failure or if request
+ is not yet completed. If false is returned GetLastError() provides
+ extended error information. If GetLastError returns
+ ERROR_IO_INCOMPLETE it means that I/O is not yet completed.
+ */
+ virtual bool GetOvelappedIoResult(LPOVERLAPPED ovl_data,
+ ULONG* bytes_transferred,
+ bool wait) = 0;
+
+ //
+ // Operations
+ //
+
+ public:
+ /** \brief Checks if I/O that this object represents has completed.
+
+ @return true if I/O has been completed or false if it's still
+ incomplete. Regardless of the returned value, caller should
+ check GetLastError to validate that handle was OK.
+ */
+ virtual bool IsCompleted();
+
+ public:
+ /// Gets overlapped structure for this I/O
+ LPOVERLAPPED overlapped() {
+ return &overlapped_;
+ }
+
+ /// Gets parent object
+ SdbEndpointObject* parent_io_object() const {
+ return parent_io_object_;
+ }
+
+ /// Gets parent object handle
+ SDBAPIHANDLE GetParentObjectHandle() const {
+ return (NULL != parent_io_object()) ? parent_io_object()->sdb_handle() :
+ NULL;
+ }
+
+ // This is a helper for extracting object from the SdbObjectHandleMap
+ static SdbObjectType Type() {
+ return SdbObjectTypeIoCompletion;
+ }
+
+ protected:
+ /// Overlapped structure for this I/O
+ OVERLAPPED overlapped_;
+
+ /// Parent I/O object
+ SdbEndpointObject* parent_io_object_;
+
+ /// Expected number of bytes transferred in thi I/O
+ ULONG expected_transfer_size_;
+};
+
+#endif // ANDROID_USB_API_SDB_IO_COMPLETION_H__
diff --git a/src/sdbwinapi/sdb_legacy_endpoint_object.cpp b/src/sdbwinapi/sdb_legacy_endpoint_object.cpp
new file mode 100644
index 0000000..8f7d16a
--- /dev/null
+++ b/src/sdbwinapi/sdb_legacy_endpoint_object.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbLegacyEndpointObject that
+ encapsulates a handle opened to an endpoint on our device controlled by
+ a custom (legacy) USB driver.
+*/
+
+#include "stdafx.h"
+#include "sdb_api_legacy.h"
+#include "sdb_legacy_endpoint_object.h"
+#include "sdb_legacy_io_completion.h"
+#include "sdb_helper_routines.h"
+
+SdbLegacyEndpointObject::SdbLegacyEndpointObject(
+ SdbLegacyInterfaceObject* parent_interf,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index)
+ : SdbEndpointObject(parent_interf, endpoint_id, endpoint_index),
+ usb_handle_(INVALID_HANDLE_VALUE) {
+}
+
+SdbLegacyEndpointObject::~SdbLegacyEndpointObject() {
+ if (INVALID_HANDLE_VALUE != usb_handle_) {
+ ::CloseHandle(usb_handle_);
+ }
+}
+
+SDBAPIHANDLE SdbLegacyEndpointObject::CommonAsyncReadWrite(
+ bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ HANDLE event_handle,
+ ULONG time_out) {
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = 0;
+ }
+
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ bool is_ioctl_write = is_read ? false : (0 != time_out);
+
+ // Create completion i/o object
+ SdbLegacyIOCompletion* sdb_io_completion = NULL;
+
+ try {
+ sdb_io_completion = new SdbLegacyIOCompletion(this,
+ bytes_to_transfer,
+ event_handle,
+ is_ioctl_write);
+ } catch (... ) {
+ // We don't expect exceptions other than OOM thrown here.
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+
+ // Create a handle for it
+ SDBAPIHANDLE ret = sdb_io_completion->CreateHandle();
+ ULONG transferred = 0;
+ if (NULL != ret) {
+ BOOL res = TRUE;
+ if (0 == time_out) {
+ // Go the read / write file way
+ res = is_read ? ReadFile(usb_handle(),
+ buffer,
+ bytes_to_transfer,
+ &transferred,
+ sdb_io_completion->overlapped()) :
+ WriteFile(usb_handle(),
+ buffer,
+ bytes_to_transfer,
+ &transferred,
+ sdb_io_completion->overlapped());
+ } else {
+ // Go IOCTL way
+ SdbBulkTransfer transfer_param;
+ transfer_param.time_out = time_out;
+ transfer_param.transfer_size = is_read ? 0 : bytes_to_transfer;
+ transfer_param.SetWriteBuffer(is_read ? NULL : buffer);
+
+ res = DeviceIoControl(usb_handle(),
+ is_read ? SDB_IOCTL_BULK_READ : SDB_IOCTL_BULK_WRITE,
+ &transfer_param, sizeof(transfer_param),
+ is_read ? buffer : sdb_io_completion->transferred_bytes_ptr(),
+ is_read ? bytes_to_transfer : sizeof(ULONG),
+ &transferred,
+ sdb_io_completion->overlapped());
+ }
+
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = transferred;
+ }
+
+ ULONG error = GetLastError();
+ if (!res && (ERROR_IO_PENDING != error)) {
+ // I/O failed immediatelly. We need to close i/o completion object
+ // before we return NULL to the caller.
+ sdb_io_completion->CloseHandle();
+ ret = NULL;
+ SetLastError(error);
+ }
+ }
+
+ // Offseting 'new'
+ sdb_io_completion->Release();
+
+ return ret;
+}
+
+bool SdbLegacyEndpointObject::CommonSyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ ULONG time_out) {
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = 0;
+ }
+
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ bool is_ioctl_write = is_read ? false : (0 != time_out);
+
+ // This is synchronous I/O. Since we always open I/O items for
+ // overlapped I/O we're obligated to always provide OVERLAPPED
+ // structure to read / write routines. Prepare it now.
+ OVERLAPPED overlapped;
+ ZeroMemory(&overlapped, sizeof(overlapped));
+
+ BOOL ret = TRUE;
+ ULONG ioctl_write_transferred = 0;
+ if (0 == time_out) {
+ // Go the read / write file way
+ ret = is_read ?
+ ReadFile(usb_handle(), buffer, bytes_to_transfer, bytes_transferred, &overlapped) :
+ WriteFile(usb_handle(), buffer, bytes_to_transfer, bytes_transferred, &overlapped);
+ } else {
+ // Go IOCTL way
+ SdbBulkTransfer transfer_param;
+ transfer_param.time_out = time_out;
+ transfer_param.transfer_size = is_read ? 0 : bytes_to_transfer;
+ transfer_param.SetWriteBuffer(is_read ? NULL : buffer);
+
+ ULONG tmp;
+ ret = DeviceIoControl(usb_handle(),
+ is_read ? SDB_IOCTL_BULK_READ : SDB_IOCTL_BULK_WRITE,
+ &transfer_param, sizeof(transfer_param),
+ is_read ? buffer : &ioctl_write_transferred,
+ is_read ? bytes_to_transfer : sizeof(ULONG),
+ &tmp,
+ &overlapped);
+ }
+
+ // Lets see the result
+ if (!ret && (ERROR_IO_PENDING != GetLastError())) {
+ // I/O failed.
+ return false;
+ }
+
+ // Lets wait till I/O completes
+ ULONG transferred = 0;
+ ret = GetOverlappedResult(usb_handle(), &overlapped, &transferred, TRUE);
+ if (ret && (NULL != bytes_transferred)) {
+ *bytes_transferred = is_ioctl_write ? ioctl_write_transferred :
+ transferred;
+ }
+
+ return ret ? true : false;
+}
+
+SDBAPIHANDLE SdbLegacyEndpointObject::CreateHandle(
+ const wchar_t* item_path,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode share_mode) {
+ // Convert access / share parameters into CreateFile - compatible
+ ULONG desired_access;
+ ULONG desired_sharing;
+
+ if (!GetSDKComplientParam(access_type, share_mode,
+ &desired_access, &desired_sharing)) {
+ return NULL;
+ }
+
+ // Open USB handle
+ usb_handle_ = CreateFile(item_path,
+ desired_access,
+ share_mode,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, // Always overlapped!
+ NULL);
+ if (INVALID_HANDLE_VALUE == usb_handle_) {
+ return NULL;
+ }
+
+ // Create SDB handle
+ SDBAPIHANDLE ret = SdbObjectHandle::CreateHandle();
+
+ if (NULL == ret) {
+ // If creation of SDB handle failed we have to close USB handle too.
+ ULONG error = GetLastError();
+ ::CloseHandle(usb_handle());
+ usb_handle_ = INVALID_HANDLE_VALUE;
+ SetLastError(error);
+ }
+
+ return ret;
+}
+
+bool SdbLegacyEndpointObject::CloseHandle() {
+ if (INVALID_HANDLE_VALUE != usb_handle_) {
+ ::CloseHandle(usb_handle_);
+ usb_handle_ = INVALID_HANDLE_VALUE;
+ }
+
+ return SdbEndpointObject::CloseHandle();
+}
diff --git a/src/sdbwinapi/sdb_legacy_endpoint_object.h b/src/sdbwinapi/sdb_legacy_endpoint_object.h
new file mode 100644
index 0000000..5ae8290
--- /dev/null
+++ b/src/sdbwinapi/sdb_legacy_endpoint_object.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_LEGACY_ENDPOINT_OBJECT_H__
+#define ANDROID_USB_API_SDB_LEGACY_ENDPOINT_OBJECT_H__
+/** \file
+ This file consists of declaration of class SdbLegacyEndpointObject that
+ encapsulates a handle opened to an endpoint on our device controlled by
+ a custom (legacy) USB driver.
+*/
+
+#include "sdb_endpoint_object.h"
+#include "sdb_legacy_interface.h"
+
+/** Encapsulates a handle opened to an endpoint on our device controlled by
+ a custom (legacy) USB driver.
+*/
+class SdbLegacyEndpointObject : public SdbEndpointObject {
+ public:
+ /** \brief Constructs the object
+
+ @param[in] interface Parent legacy USB interface for this object.
+ @param[in] endpoint_id Endpoint ID (endpoint address) on the device.
+ @param[in] endpoint_index Zero-based endpoint index in the interface's
+ array of endpoints.
+ */
+ SdbLegacyEndpointObject(SdbLegacyInterfaceObject* parent_interf,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbLegacyEndpointObject();
+
+ //
+ // Abstract overrides
+ //
+
+ protected:
+ /** \brief Common code for async read / write
+
+ @param[in] is_read Read or write selector.
+ @param[in,out] buffer Pointer to the buffer for read / write.
+ @param[in] bytes_to_transfer Number of bytes to be read / written.
+ @param[out] bytes_transferred Number of bytes read / written. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+ */
+ virtual SDBAPIHANDLE CommonAsyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ HANDLE event_handle,
+ ULONG time_out);
+
+ /** \brief Common code for sync read / write
+
+ @param[in] is_read Read or write selector.
+ @param[in,out] buffer Pointer to the buffer for read / write.
+ @param[in] bytes_to_transfer Number of bytes to be read / written.
+ @param[out] bytes_transferred Number of bytes read / written. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool CommonSyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ ULONG time_out);
+
+ //
+ // Operations
+ //
+
+ public:
+ /** \brief Opens endpoint and creates a handle to this object
+
+ @param item_path[in] Path to the endpoint on our USB device.
+ @param access_type[in] Desired access type. In the current implementation
+ this parameter has no effect on the way item is opened. It's
+ always read / write access.
+ @param sharing_mode[in] Desired share mode. In the current implementation
+ this parameter has no effect on the way item is opened. It's
+ always shared for read / write.
+ @return A handle to this object on success or NULL on an error.
+ If NULL is returned GetLastError() provides extended error
+ information. ERROR_GEN_FAILURE is set if an attempt was
+ made to create already opened object.
+ */
+ virtual SDBAPIHANDLE CreateHandle(const wchar_t* item_path,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode share_mode);
+
+
+ /** \brief This method is called when handle to this object gets closed.
+
+ We override this method in order to close handle to the endpoint opened
+ in CreateHandle method of this class.
+ @return true on success or false if object is already closed. If
+ false is returned GetLastError() provides extended error
+ information.
+ */
+ virtual bool CloseHandle();
+
+ public:
+ /// Gets handle to the endpoint opened on our USB device.
+ HANDLE usb_handle() const {
+ return usb_handle_;
+ }
+
+ protected:
+ /// Handle to the endpoint opened on our USB device.
+ HANDLE usb_handle_;
+};
+
+#endif // ANDROID_USB_API_SDB_LEGACY_ENDPOINT_OBJECT_H__
diff --git a/src/sdbwinapi/sdb_legacy_interface.cpp b/src/sdbwinapi/sdb_legacy_interface.cpp
new file mode 100644
index 0000000..3011bf7
--- /dev/null
+++ b/src/sdbwinapi/sdb_legacy_interface.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbLegacyInterfaceObject
+ that encapsulates an interface on our USB device that is accessible
+*/
+
+#include "stdafx.h"
+#include "sdb_api_legacy.h"
+#include "sdb_legacy_interface.h"
+#include "sdb_legacy_endpoint_object.h"
+
+SdbLegacyInterfaceObject::SdbLegacyInterfaceObject(const wchar_t* interf_name)
+ : SdbInterfaceObject(interf_name),
+ def_read_endpoint_(0xFF),
+ read_endpoint_id_(0xFF),
+ def_write_endpoint_(0xFF),
+ write_endpoint_id_(0xFF) {
+}
+
+SdbLegacyInterfaceObject::~SdbLegacyInterfaceObject() {
+}
+
+SDBAPIHANDLE SdbLegacyInterfaceObject::CreateHandle() {
+ // Open USB device for this intefface
+ HANDLE usb_device_handle = CreateFile(interface_name().c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if (INVALID_HANDLE_VALUE == usb_device_handle) {
+ return NULL;
+ }
+
+ // Now, we ensured that our usb device / interface is up and running.
+ // Lets collect device, interface and pipe information
+ bool ok = true;
+ if (!CacheUsbDeviceDescriptor(usb_device_handle) ||
+ !CacheUsbConfigurationDescriptor(usb_device_handle) ||
+ !CacheUsbInterfaceDescriptor(usb_device_handle)) {
+ ok = false;
+ }
+
+ // Preserve error accross handle close
+ ULONG error = ok ? NO_ERROR : GetLastError();
+
+ ::CloseHandle(usb_device_handle);
+
+ if (NO_ERROR != error) {
+ SetLastError(error);
+ }
+
+ if (!ok) {
+ return false;
+ }
+
+ // Save indexes and IDs for bulk read / write endpoints. We will use them to
+ // convert SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX and
+ // SDB_QUERY_BULK_READ_ENDPOINT_INDEX into actual endpoint indexes and IDs.
+ for (UCHAR endpoint = 0; endpoint < usb_interface_descriptor_.bNumEndpoints;
+ endpoint++) {
+ // Get endpoint information
+ SdbEndpointInformation pipe_info;
+ if (!GetEndpointInformation(endpoint, &pipe_info)) {
+ return false;
+ }
+
+ if (SdbEndpointTypeBulk == pipe_info.endpoint_type) {
+ // This is a bulk endpoint. Cache its index and ID.
+ if (0 != (pipe_info.endpoint_address & USB_ENDPOINT_DIRECTION_MASK)) {
+ // Use this endpoint as default bulk read endpoint
+ ATLASSERT(0xFF == def_read_endpoint_);
+ def_read_endpoint_ = endpoint;
+ read_endpoint_id_ = pipe_info.endpoint_address;
+ } else {
+ // Use this endpoint as default bulk write endpoint
+ ATLASSERT(0xFF == def_write_endpoint_);
+ def_write_endpoint_ = endpoint;
+ write_endpoint_id_ = pipe_info.endpoint_address;
+ }
+ }
+ }
+
+ return SdbObjectHandle::CreateHandle();
+}
+
+bool SdbLegacyInterfaceObject::GetSerialNumber(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ // Open USB device for this intefface
+ HANDLE usb_device_handle = CreateFile(interface_name().c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if (INVALID_HANDLE_VALUE == usb_device_handle) {
+ return NULL;
+ }
+
+ WCHAR serial_number[512];
+
+ // Send IOCTL
+ DWORD ret_bytes = 0;
+ BOOL ret = DeviceIoControl(usb_device_handle,
+ SDB_IOCTL_GET_SERIAL_NUMBER,
+ NULL, 0,
+ serial_number, sizeof(serial_number),
+ &ret_bytes,
+ NULL);
+
+ // Preserve error accross CloseHandle
+ ULONG error = ret ? NO_ERROR : GetLastError();
+
+ ::CloseHandle(usb_device_handle);
+
+ if (NO_ERROR != error) {
+ SetLastError(error);
+ return false;
+ }
+
+ unsigned long str_len =
+ static_cast<unsigned long>(wcslen(serial_number) + 1);
+
+ if ((NULL == buffer) || (*buffer_char_size < str_len)) {
+ *buffer_char_size = str_len;
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return false;
+ }
+
+ if (!ansi) {
+ // If user asked for wide char name just return it
+ wcscpy(reinterpret_cast<wchar_t*>(buffer), serial_number);
+ return true;
+ }
+
+ // We need to convert name from wide char to ansi string
+ int res = WideCharToMultiByte(CP_ACP,
+ 0,
+ serial_number,
+ static_cast<int>(str_len),
+ reinterpret_cast<PSTR>(buffer),
+ static_cast<int>(*buffer_char_size),
+ NULL,
+ NULL);
+ return (res != 0);
+}
+
+bool SdbLegacyInterfaceObject::GetEndpointInformation(
+ UCHAR endpoint_index,
+ SdbEndpointInformation* info) {
+ // Open USB device for this intefface
+ HANDLE usb_device_handle = CreateFile(interface_name().c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+ if (INVALID_HANDLE_VALUE == usb_device_handle) {
+ return NULL;
+ }
+
+ // Init ICTL param
+ SdbQueryEndpointInformation param;
+ param.endpoint_index = endpoint_index;
+
+ // Send IOCTL
+ DWORD ret_bytes = 0;
+ BOOL ret = DeviceIoControl(usb_device_handle,
+ SDB_IOCTL_GET_ENDPOINT_INFORMATION,
+ &param, sizeof(param),
+ info, sizeof(SdbEndpointInformation),
+ &ret_bytes,
+ NULL);
+ ATLASSERT(!ret || (sizeof(SdbEndpointInformation) == ret_bytes));
+
+ // Preserve error accross CloseHandle
+ ULONG error = ret ? NO_ERROR : GetLastError();
+
+ ::CloseHandle(usb_device_handle);
+
+ if (NO_ERROR != error) {
+ SetLastError(error);
+ }
+
+ return ret ? true : false;
+}
+
+SDBAPIHANDLE SdbLegacyInterfaceObject::OpenEndpoint(
+ UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) {
+ // Convert index into name and ID.
+ std::wstring endpoint_name;
+ UCHAR endpoint_id;
+
+ try {
+ if ((SDB_QUERY_BULK_READ_ENDPOINT_INDEX == endpoint_index) ||
+ (def_read_endpoint_ == endpoint_index)) {
+ endpoint_name = DEVICE_BULK_READ_PIPE_NAME;
+ endpoint_id = read_endpoint_id_;
+ endpoint_index = def_read_endpoint_;
+ } else if ((SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX == endpoint_index) ||
+ (def_write_endpoint_ == endpoint_index)) {
+ endpoint_name = DEVICE_BULK_WRITE_PIPE_NAME;
+ endpoint_id = write_endpoint_id_;
+ endpoint_index = def_write_endpoint_;
+ } else {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ } catch (...) {
+ // We don't expect exceptions other than OOM thrown here.
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+
+ return OpenEndpoint(endpoint_name.c_str(), endpoint_id, endpoint_index,
+ access_type, sharing_mode);
+}
+
+SDBAPIHANDLE SdbLegacyInterfaceObject::OpenEndpoint(
+ const wchar_t* endpoint_name,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ SdbLegacyEndpointObject* sdb_endpoint = NULL;
+
+ try {
+ sdb_endpoint =
+ new SdbLegacyEndpointObject(this, endpoint_id, endpoint_index);
+ } catch (...) {
+ // We don't expect exceptions other than OOM thrown here.
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+
+ // Build full path to the object
+ std::wstring endpoint_path = interface_name();
+ endpoint_path += L"\\";
+ endpoint_path += endpoint_name;
+
+ SDBAPIHANDLE ret = sdb_endpoint->CreateHandle(endpoint_path.c_str(),
+ access_type,
+ sharing_mode);
+
+ sdb_endpoint->Release();
+
+ return ret;
+}
+
+bool SdbLegacyInterfaceObject::CacheUsbDeviceDescriptor(
+ HANDLE usb_device_handle) {
+ DWORD ret_bytes = 0;
+ BOOL ret = DeviceIoControl(usb_device_handle,
+ SDB_IOCTL_GET_USB_DEVICE_DESCRIPTOR,
+ NULL, 0,
+ &usb_device_descriptor_,
+ sizeof(usb_device_descriptor_),
+ &ret_bytes,
+ NULL);
+ ATLASSERT(!ret || (sizeof(USB_DEVICE_DESCRIPTOR) == ret_bytes));
+
+ return ret ? true : false;
+}
+
+bool SdbLegacyInterfaceObject::CacheUsbConfigurationDescriptor(
+ HANDLE usb_device_handle) {
+ DWORD ret_bytes = 0;
+ BOOL ret = DeviceIoControl(usb_device_handle,
+ SDB_IOCTL_GET_USB_CONFIGURATION_DESCRIPTOR,
+ NULL, 0,
+ &usb_config_descriptor_,
+ sizeof(usb_config_descriptor_),
+ &ret_bytes,
+ NULL);
+ ATLASSERT(!ret || (sizeof(USB_CONFIGURATION_DESCRIPTOR) == ret_bytes));
+
+ return ret ? true : false;
+}
+
+bool SdbLegacyInterfaceObject::CacheUsbInterfaceDescriptor(
+ HANDLE usb_device_handle) {
+ DWORD ret_bytes = 0;
+ BOOL ret = DeviceIoControl(usb_device_handle,
+ SDB_IOCTL_GET_USB_INTERFACE_DESCRIPTOR,
+ NULL, 0,
+ &usb_interface_descriptor_,
+ sizeof(usb_interface_descriptor_),
+ &ret_bytes,
+ NULL);
+ ATLASSERT(!ret || (sizeof(USB_INTERFACE_DESCRIPTOR) == ret_bytes));
+
+ return ret ? true : false;
+}
diff --git a/src/sdbwinapi/sdb_legacy_interface.h b/src/sdbwinapi/sdb_legacy_interface.h
new file mode 100644
index 0000000..6f88669
--- /dev/null
+++ b/src/sdbwinapi/sdb_legacy_interface.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_LEGACY_INTERFACE_H__
+#define ANDROID_USB_API_SDB_LEGACY_INTERFACE_H__
+/** \file
+ This file consists of declaration of class SdbLegacyInterfaceObject
+ that encapsulates an interface on our USB device that is accessible
+ via custom USB driver.
+*/
+
+#include "sdb_interface.h"
+
+/** \brief Encapsulates an interface on our USB device that is accessible
+ via custom USB driver.
+*/
+class SdbLegacyInterfaceObject : public SdbInterfaceObject {
+ public:
+ /** \brief Constructs the object.
+
+ @param[in] interf_name Name of the interface
+ */
+ explicit SdbLegacyInterfaceObject(const wchar_t* interf_name);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbLegacyInterfaceObject();
+
+ //
+ // Virtual overrides
+ //
+
+ public:
+ /** \brief Creates handle to this object.
+
+ In this call a handle for this object is generated and object is added
+ to the SdbObjectHandleMap. We override this method in order to initialize
+ access to the custom driver.
+ @return A handle to this object on success or NULL on an error.
+ If NULL is returned GetLastError() provides extended error
+ information. ERROR_GEN_FAILURE is set if an attempt was
+ made to create already opened object.
+ */
+ virtual SDBAPIHANDLE CreateHandle();
+
+ //
+ // Abstract overrides
+ //
+
+ public:
+ /** \brief Gets serial number for interface's device.
+
+ @param[out] buffer Buffer for the serail number string. Can be NULL in
+ which case buffer_char_size will contain number of characters
+ required for the string.
+ @param[in,out] buffer_char_size On the way in supplies size (in characters)
+ of the buffer. On the way out, if method failed and GetLastError
+ reports ERROR_INSUFFICIENT_BUFFER, will contain number of characters
+ required for the name.
+ @param[in] ansi If true the name will be returned as single character
+ string. Otherwise name will be returned as wide character string.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetSerialNumber(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi);
+
+ /** \brief Gets information about an endpoint on this interface.
+
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide infor about
+ (default?) bulk write and read endpoints respectively.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetEndpointInformation(UCHAR endpoint_index,
+ SdbEndpointInformation* info);
+
+ /** \brief Opens an endpoint on this interface.
+
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide infor about
+ (default?) bulk write and read endpoints respectively.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure.
+ If NULL is returned GetLastError() provides extended information
+ about the error that occurred.
+ */
+ virtual SDBAPIHANDLE OpenEndpoint(UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode);
+
+ //
+ // Internal operations
+ //
+
+ protected:
+ /** \brief Opens an endpoint on this interface.
+
+ @param[in] endpoint_name Endpoint file name.
+ @param[in] endpoint_id Endpoint (pipe) address on the device.
+ @param[in] endpoint_index Zero-based endpoint index.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure.
+ If NULL is returned GetLastError() provides extended information
+ about the error that occurred.
+ */
+ SDBAPIHANDLE OpenEndpoint(const wchar_t* endpoint_name,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode);
+
+ /** \brief Caches device descriptor for the USB device associated with
+ this interface.
+
+ This method is called from CreateHandle method to cache some interface
+ information.
+ @param[in] usb_device_handle Handle to USB device.
+ @return 'true' on success, 'false' on failure. If 'false' is returned
+ GetLastError() provides extended error information.
+ */
+ bool CacheUsbDeviceDescriptor(HANDLE usb_device_handle);
+
+ /** \brief Caches descriptor for the selected USB device configuration.
+
+ This method is called from CreateHandle method to cache some interface
+ information.
+ @param[in] usb_device_handle Handle to USB device.
+ @return 'true' on success, 'false' on failure. If 'false' is returned
+ GetLastError() provides extended error information.
+ */
+ bool CacheUsbConfigurationDescriptor(HANDLE usb_device_handle);
+
+ /** \brief Caches descriptor for this interface.
+
+ This method is called from CreateHandle method to cache some interface
+ information.
+ @param[in] usb_device_handle Handle to USB device.
+ @return 'true' on success, 'false' on failure. If 'false' is returned
+ GetLastError() provides extended error information.
+ */
+ bool CacheUsbInterfaceDescriptor(HANDLE usb_device_handle);
+
+ protected:
+ /// Index for the default bulk read endpoint
+ UCHAR def_read_endpoint_;
+
+ /// ID for the default bulk read endpoint
+ UCHAR read_endpoint_id_;
+
+ /// Index for the default bulk write endpoint
+ UCHAR def_write_endpoint_;
+
+ /// ID for the default bulk write endpoint
+ UCHAR write_endpoint_id_;
+};
+
+#endif // ANDROID_USB_API_SDB_LEGACY_INTERFACE_H__
diff --git a/src/sdbwinapi/sdb_legacy_io_completion.cpp b/src/sdbwinapi/sdb_legacy_io_completion.cpp
new file mode 100644
index 0000000..1cf1f8a
--- /dev/null
+++ b/src/sdbwinapi/sdb_legacy_io_completion.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbLegacyIOCompletion that
+ encapsulates a wrapper around OVERLAPPED Win32 structure returned from
+ asynchronous I/O requests issued via legacy USB API.
+*/
+
+#include "stdafx.h"
+#include "sdb_legacy_io_completion.h"
+
+SdbLegacyIOCompletion::SdbLegacyIOCompletion(
+ SdbLegacyEndpointObject* parent_io_obj,
+ ULONG expected_trans_size,
+ HANDLE event_hndl,
+ bool is_write_ctl)
+ : SdbIOCompletion(parent_io_obj, expected_trans_size, event_hndl),
+ transferred_bytes_(0),
+ is_write_ioctl_(is_write_ctl) {
+}
+
+SdbLegacyIOCompletion::~SdbLegacyIOCompletion() {
+}
+
+bool SdbLegacyIOCompletion::GetOvelappedIoResult(LPOVERLAPPED ovl_data,
+ ULONG* bytes_transferred,
+ bool wait) {
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = 0;
+ }
+
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ ULONG transfer;
+ bool ret = GetOverlappedResult(parent_legacy_io_object()->usb_handle(),
+ overlapped(),
+ &transfer,
+ wait) ? true :
+ false;
+
+ // TODO: This is bizzare but I've seen it happening
+ // that GetOverlappedResult with wait set to true returns "prematurely",
+ // with wrong transferred bytes value and GetLastError reporting
+ // ERROR_IO_PENDING. So, lets give it an up to a 20 ms loop!
+ ULONG error = GetLastError();
+
+ if (wait && ret && (0 == transfer) && (0 != expected_transfer_size_) &&
+ ((ERROR_IO_INCOMPLETE == error) || (ERROR_IO_PENDING == error))) {
+ for (int trying = 0; trying < 10; trying++) {
+ Sleep(2);
+ ret = GetOverlappedResult(parent_legacy_io_object()->usb_handle(),
+ overlapped(),
+ &transfer,
+ wait) ? true :
+ false;
+ error = GetLastError();
+ if (!ret || (0 != transfer) ||
+ ((ERROR_IO_INCOMPLETE != error) && (ERROR_IO_PENDING != error))) {
+ break;
+ }
+ }
+ }
+
+ if (NULL != ovl_data) {
+ CopyMemory(ovl_data, overlapped(), sizeof(OVERLAPPED));
+ }
+
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = is_write_ioctl() ? transferred_bytes_ : transfer;
+ }
+
+ return ret;
+}
diff --git a/src/sdbwinapi/sdb_legacy_io_completion.h b/src/sdbwinapi/sdb_legacy_io_completion.h
new file mode 100644
index 0000000..f51e5b8
--- /dev/null
+++ b/src/sdbwinapi/sdb_legacy_io_completion.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_LEGACY_IO_COMPLETION_H__
+#define ANDROID_USB_API_SDB_LEGACY_IO_COMPLETION_H__
+/** \file
+ This file consists of declaration of class SdbLegacyIOCompletion that
+ encapsulates a wrapper around OVERLAPPED Win32 structure returned from
+ asynchronous I/O requests issued via legacy USB API.
+*/
+
+#include "sdb_io_completion.h"
+#include "sdb_legacy_endpoint_object.h"
+
+/** \brief Encapsulates a wrapper around OVERLAPPED Win32 structure returned
+ from asynchronous I/O requests issued via legacy USB API.
+
+ A handle to this object is returned to the caller of each successful
+ asynchronous I/O request. Just like all other handles this handle
+ must be closed after it's no longer needed.
+*/
+class SdbLegacyIOCompletion : public SdbIOCompletion {
+ public:
+ /** \brief Constructs the object.
+
+ @param[in] parent_io_obj Parent legacy endpoint that created this
+ instance.
+ @param[in] expected_trans_size Number of bytes expected to be transferred
+ with the I/O.
+ @param[in] event_hndl Event handle that should be signaled when I/O
+ completes. Can be NULL. If it's not NULL this handle will be
+ used to initialize OVERLAPPED structure for this object.
+ @param[in] is_write_ctl Flag indicating whether or not this completion
+ object is created for SDB_IOCTL_BULK_WRITE I/O.
+ */
+ SdbLegacyIOCompletion(SdbLegacyEndpointObject* parent_io_obj,
+ ULONG expected_trans_size,
+ HANDLE event_hndl,
+ bool is_write_ctl);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbLegacyIOCompletion();
+
+ //
+ // Abstract overrides
+ //
+
+ public:
+ /** \brief Gets overlapped I/O result
+
+ This method uses GetOverlappedResult to get results of the overlapped I/O
+ operation.
+ @param[out] ovl_data Buffer for the copy of this object's OVERLAPPED
+ structure. Can be NULL.
+ @param[out] bytes_transferred Pointer to a variable that receives the
+ number of bytes that were actually transferred by a read or write
+ operation. See SDK doc on GetOvelappedResult for more information.
+ Unlike regular GetOvelappedResult call this parameter can be NULL.
+ @param[in] wait If this parameter is true, the method does not return
+ until the operation has been completed. If this parameter is false
+ and the operation is still pending, the method returns false and
+ the GetLastError function returns ERROR_IO_INCOMPLETE.
+ @return true if I/O has been completed or false on failure or if request
+ is not yet completed. If false is returned GetLastError() provides
+ extended error information. If GetLastError returns
+ ERROR_IO_INCOMPLETE it means that I/O is not yet completed.
+ */
+ virtual bool GetOvelappedIoResult(LPOVERLAPPED ovl_data,
+ ULONG* bytes_transferred,
+ bool wait);
+
+ public:
+ /// Gets parent legacy endpoint.
+ SdbLegacyEndpointObject* parent_legacy_io_object() const {
+ return reinterpret_cast<SdbLegacyEndpointObject*>(parent_io_object());
+ }
+
+ /// Gets write IOCTL flag.
+ bool is_write_ioctl() const {
+ return is_write_ioctl_;
+ }
+
+ /// Gets address for SDB_IOCTL_BULK_WRITE output buffer.
+ ULONG* transferred_bytes_ptr() {
+ ATLASSERT(is_write_ioctl());
+ return &transferred_bytes_;
+ }
+
+ protected:
+ /// Recepient for number of transferred bytes in write IOCTL.
+ ULONG transferred_bytes_;
+
+ /// Write IOCTL flag.
+ bool is_write_ioctl_;
+};
+
+#endif // ANDROID_USB_API_SDB_LEGACY_IO_COMPLETION_H__
diff --git a/src/sdbwinapi/sdb_object_handle.cpp b/src/sdbwinapi/sdb_object_handle.cpp
new file mode 100644
index 0000000..256a5be
--- /dev/null
+++ b/src/sdbwinapi/sdb_object_handle.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+/** \file
+ This file consists of implementation of a class SdbObjectHandle that
+ encapsulates an internal API object that is visible to the outside
+ of the API through a handle.
+*/
+
+#include "stdafx.h"
+#include "sdb_api.h"
+#include "sdb_object_handle.h"
+
+/// Global SDBAPIHANDLE -> SdbObjectHandle* map
+SdbObjectHandleMap the_map;
+
+/// Locker for the SdbObjectHandleMap instance
+CComAutoCriticalSection the_map_locker;
+
+/// Next sdb handle value generator
+ULONG_PTR next_sdb_handle_value = 0;
+
+SdbObjectHandle::SdbObjectHandle(SdbObjectType obj_type)
+ : sdb_handle_(NULL),
+ object_type_(obj_type),
+ ref_count_(1) {
+ ATLASSERT(obj_type < SdbObjectTypeMax);
+}
+
+SdbObjectHandle::~SdbObjectHandle() {
+ ATLASSERT(0 == ref_count_);
+ ATLASSERT(NULL == sdb_handle_);
+}
+
+LONG SdbObjectHandle::AddRef() {
+ ATLASSERT(ref_count_ > 0);
+ return InterlockedIncrement(&ref_count_);
+}
+
+LONG SdbObjectHandle::Release() {
+ ATLASSERT(ref_count_ > 0);
+ LONG ret = InterlockedDecrement(&ref_count_);
+ ATLASSERT(ret >= 0);
+ if (0 == ret) {
+ LastReferenceReleased();
+ delete this;
+ }
+ return ret;
+}
+
+SDBAPIHANDLE SdbObjectHandle::CreateHandle() {
+ SDBAPIHANDLE ret = NULL;
+
+ // We have to hold this lock while we're dealing with the handle
+ // and the table
+ the_map_locker.Lock();
+
+ ATLASSERT(!IsOpened());
+
+ if (!IsOpened()) {
+ try {
+ // Generate next handle value
+ next_sdb_handle_value++;
+ ret = reinterpret_cast<SDBAPIHANDLE>(next_sdb_handle_value);
+
+ // Add ourselves to the map
+ the_map[ret] = this;
+
+ // Save handle, addref and return
+ sdb_handle_ = ret;
+ AddRef();
+ } catch (...) {
+ ret = NULL;
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ } else {
+ // Signaling that this object is already opened
+ SetLastError(ERROR_GEN_FAILURE);
+ }
+
+ the_map_locker.Unlock();
+
+ return ret;
+}
+
+bool SdbObjectHandle::CloseHandle() {
+ bool ret = false;
+
+ // Addref just in case that last reference to this object is being
+ // held in the map
+ AddRef();
+
+ the_map_locker.Lock();
+
+ ATLASSERT(IsOpened());
+
+ if (IsOpened()) {
+ try {
+ // Look us up in the map.
+ SdbObjectHandleMap::iterator found = the_map.find(sdb_handle());
+ ATLASSERT((found != the_map.end()) && (this == found->second));
+
+ if ((found != the_map.end()) && (this == found->second)) {
+ // Remove ourselves from the map, close and release the object
+ the_map.erase(found);
+ sdb_handle_ = NULL;
+ Release();
+ ret = true;
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+ } catch (...) {
+ ret = false;
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+
+ the_map_locker.Unlock();
+
+ Release();
+
+ return ret;
+}
+
+bool SdbObjectHandle::IsObjectOfType(SdbObjectType obj_type) const {
+ return (obj_type == object_type());
+}
+
+void SdbObjectHandle::LastReferenceReleased() {
+ ATLASSERT(!IsOpened());
+}
+
+SdbObjectHandle* SdbObjectHandle::Lookup(SDBAPIHANDLE sdb_hndl) {
+ SdbObjectHandle* ret = NULL;
+
+ the_map_locker.Lock();
+
+ try {
+ // Look us up in the map.
+ SdbObjectHandleMap::iterator found = the_map.find(sdb_hndl);
+ if (found != the_map.end()) {
+ ret = found->second;
+ ret->AddRef();
+ }
+ } catch (...) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+
+ the_map_locker.Unlock();
+
+ return ret;
+}
diff --git a/src/sdbwinapi/sdb_object_handle.h b/src/sdbwinapi/sdb_object_handle.h
new file mode 100644
index 0000000..c46690d
--- /dev/null
+++ b/src/sdbwinapi/sdb_object_handle.h
@@ -0,0 +1,212 @@
+/*
+ * 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_USB_API_SDB_OBJECT_HANDLE_H__
+#define ANDROID_USB_API_SDB_OBJECT_HANDLE_H__
+/** \file
+ This file consists of declaration of a class SdbObjectHandle that
+ encapsulates an internal API object that is visible to the outside
+ of the API through a handle.
+*/
+
+#include "sdb_api.h"
+#include "sdb_api_private_defines.h"
+
+/** \brief Defines types of internal API objects
+*/
+enum SdbObjectType {
+ /// Object is SdbInterfaceEnumObject.
+ SdbObjectTypeInterfaceEnumerator,
+
+ /// Object is SdbInterfaceObject.
+ SdbObjectTypeInterface,
+
+ /// Object is SdbEndpointObject.
+ SdbObjectTypeEndpoint,
+
+ /// Object is SdbIOCompletion.
+ SdbObjectTypeIoCompletion,
+
+ SdbObjectTypeMax
+};
+
+/** \brief Encapsulates an internal API basic object that is visible to the
+ outside of the API through a handle.
+
+ In order to prevent crashes when API client tries to access an object through
+ an invalid or already closed handle, we keep track of all opened handles in
+ SdbObjectHandleMap that maps association between valid SDBAPIHANDLE and
+ an object that this handle represents. All objects that are exposed to the
+ outside of API via SDBAPIHANDLE are self-destructing referenced objects.
+ The reference model for these objects is as such:
+ 1. When CreateHandle() method is called on an object, a handle (SDBAPIHANDLE
+ that is) is assigned to it, a pair <handle, object> is added to the global
+ SdbObjectHandleMap instance, object is referenced and then handle is
+ returned to the API client.
+ 2. Every time API is called with a handle, a lookup is performed in
+ SdbObjectHandleMap to find an object that is associated with the handle.
+ If object is not found then ERROR_INVALID_HANDLE is immediatelly returned
+ (via SetLastError() call). If object is found then it is referenced and
+ API call is dispatched to appropriate method of the found object. Upon
+ return from this method, just before returning from the API call, object
+ is dereferenced back to match lookup reference.
+ 3. When object handle gets closed, assuming object is found in the map, that
+ <handle, object> pair is deleted from the map and object's refcount is
+ decremented to match refcount increment performed when object has been
+ added to the map.
+ 4. When object's refcount drops to zero, the object commits suicide by
+ calling "delete this".
+ All API objects that have handles that are sent back to API client must be
+ derived from this class.
+*/
+class SDBWIN_API_CLASS SdbObjectHandle {
+ public:
+ /** \brief Constructs the object
+
+ Refernce counter is set to 1 in the constructor.
+ @param[in] obj_type Object type from SdbObjectType enum
+ */
+ explicit SdbObjectHandle(SdbObjectType obj_type);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attempt occurs, compiler will error.
+ */
+ virtual ~SdbObjectHandle();
+
+ public:
+ /** \brief References the object.
+
+ @return Value of the reference counter after object is referenced in this
+ method.
+ */
+ virtual LONG AddRef();
+
+ /** \brief Releases the object.
+
+ If refcount drops to zero as the result of this release, the object is
+ destroyed in this method. As a general rule, objects must not be touched
+ after this method returns even if returned value is not zero.
+ @return Value of the reference counter after object is released in this
+ method.
+ */
+ virtual LONG Release();
+
+ /** \brief Creates handle to this object.
+
+ In this call a handle for this object is generated and object is added
+ to the SdbObjectHandleMap.
+ @return A handle to this object on success or NULL on an error.
+ If NULL is returned GetLastError() provides extended error
+ information. ERROR_GEN_FAILURE is set if an attempt was
+ made to create already opened object.
+ */
+ virtual SDBAPIHANDLE CreateHandle();
+
+ /** \brief This method is called when handle to this object gets closed.
+
+ In this call object is deleted from the SdbObjectHandleMap.
+ @return true on success or false if object is already closed. If
+ false is returned GetLastError() provides extended error
+ information.
+ */
+ virtual bool CloseHandle();
+
+ /** \brief Checks if this object is of the given type.
+
+ @param[in] obj_type One of the SdbObjectType types to check
+ @return true is this object type matches obj_type, or false otherwise.
+ */
+ virtual bool IsObjectOfType(SdbObjectType obj_type) const;
+
+ /** \brief Looks up SdbObjectHandle instance associated with the given handle
+ in the SdbObjectHandleMap.
+
+ This method increments reference counter for the returned found object.
+ @param[in] sdb_handle SDB handle to the object
+ @return API object associated with the handle or NULL if object is not
+ found. If NULL is returned GetLastError() provides extended error
+ information.
+ */
+ static SdbObjectHandle* Lookup(SDBAPIHANDLE sdb_handle);
+
+ protected:
+ /** \brief Called when last reference to this object is released.
+
+ Derived object should override this method to perform cleanup that is not
+ suitable for destructors.
+ */
+ virtual void LastReferenceReleased();
+
+ public:
+ /// Gets SDB handle associated with this object
+ SDBAPIHANDLE sdb_handle() const {
+ return sdb_handle_;
+ }
+
+ /// Gets type of this object
+ SdbObjectType object_type() const {
+ return object_type_;
+ }
+
+ /// Checks if object is still opened. Note that it is not guaranteed that
+ /// object remains opened when this method returns.
+ bool IsOpened() const {
+ return (NULL != sdb_handle());
+ }
+
+ protected:
+ /// API handle associated with this object
+ SDBAPIHANDLE sdb_handle_;
+
+ /// Type of this object
+ SdbObjectType object_type_;
+
+ /// This object's reference counter
+ LONG ref_count_;
+};
+
+/// Maps SDBAPIHANDLE to associated SdbObjectHandle object
+typedef std::map< SDBAPIHANDLE, SdbObjectHandle* > SdbObjectHandleMap;
+
+/** \brief Template routine that unifies extracting of objects of different
+ types from the SdbObjectHandleMap
+
+ @param[in] sdb_handle API handle for the object
+ @return Object associated with the handle or NULL on error. If NULL is
+ returned GetLastError() provides extended error information.
+*/
+template<class obj_class>
+obj_class* LookupObject(SDBAPIHANDLE sdb_handle) {
+ // Lookup object for the handle in the map
+ SdbObjectHandle* sdb_object = SdbObjectHandle::Lookup(sdb_handle);
+ if (NULL != sdb_object) {
+ // Make sure it's of the correct type
+ if (!sdb_object->IsObjectOfType(obj_class::Type())) {
+ sdb_object->Release();
+ sdb_object = NULL;
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+ } else {
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+ return (sdb_object != NULL) ? reinterpret_cast<obj_class*>(sdb_object) :
+ NULL;
+}
+
+#endif // ANDROID_USB_API_SDB_OBJECT_HANDLE_H__
diff --git a/src/sdbwinapi/sdb_winusb_api.h b/src/sdbwinapi/sdb_winusb_api.h
new file mode 100644
index 0000000..d3b2525
--- /dev/null
+++ b/src/sdbwinapi/sdb_winusb_api.h
@@ -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 ANDROID_USB_API_SDBWINUSBAPI_H__
+#define ANDROID_USB_API_SDBWINUSBAPI_H__
+
+/** \file
+ Contains declarations required to link SdbWinApi and SdbWinUsbApi DLLs.
+*/
+
+/** \brief Function prototype for InstantiateWinUsbInterface routine exported
+ from SdbWinUsbApi.dll
+
+ In order to provide backward compatibility with the systems that still run
+ legacy (custom) USB drivers, and have not installed WINUSB.DLL we need to
+ split functionality of our SDB API on Windows between two DLLs: SdbWinApi,
+ and SdbWinUsbApi. SdbWinApi is fully capable of working on top of the legacy
+ driver, but has no traces to WinUsb. SdbWinUsbApi is capable of working on
+ top of WinUsb API. We are forced to do this split, because we can have
+ dependency on WINUSB.DLL in the DLL that implements legacy API. The problem
+ is that customers may have a legacy driver that they don't want to upgrade
+ to WinUsb, so they may not have WINUSB.DLL installed on their machines, but
+ they still must be able to use SDB. So, the idea behind the split is as
+ such. When SdbWinApi.dll is loaded into a process, it will check WINUSB.DLL
+ installation (by checking existance of C:\Windows\System32\winusb.dll). If
+ WINUSB.DLL is installed, SdbWinApi will also load SdbWinUsbApi.dll (by
+ calling LoadLibrary), and will extract address of InstantiateWinUsbInterface
+ routine exported from SdbWinUsbApi.dll. Then this routine will be used to
+ instantiate SdbInterfaceObject instance on condition that it is confirmed
+ that USB driver underneath us is in deed WinUsb.
+*/
+typedef class SdbInterfaceObject* \
+ (__cdecl *PFN_INSTSDBWINUSBINTERFACE)(const wchar_t*);
+
+#endif // ANDROID_USB_API_SDBWINUSBAPI_H__
diff --git a/src/sdbwinapi/stdafx.cpp b/src/sdbwinapi/stdafx.cpp
new file mode 100644
index 0000000..a2701f6
--- /dev/null
+++ b/src/sdbwinapi/stdafx.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// SdbWinApi.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
diff --git a/src/sdbwinapi/stdafx.h b/src/sdbwinapi/stdafx.h
new file mode 100644
index 0000000..d57bec7
--- /dev/null
+++ b/src/sdbwinapi/stdafx.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+/** \file
+ Visual Studio generated include file for standard system include files, or
+ project specific include files that are used frequently, but are changed
+ infrequently.
+*/
+
+#pragma once
+
+#ifndef STRICT
+#define STRICT
+#endif
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.
+#define WINVER 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
+#endif
+
+#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.
+#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target Windows 2000 or later.
+#endif
+
+#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
+#define _WIN32_WINDOWS 0x0500 // Change this to the appropriate value to target Windows Me or later.
+#endif
+
+#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.
+#define _WIN32_IE 0x0501 // Change this to the appropriate value to target IE 5.0 or later.
+#endif
+
+// These defines prevent the MS header files from ejecting #pragma comment
+// statements with the manifest information of the used ATL, STL, and CRT
+#define _ATL_NOFORCE_MANIFEST
+#define _STL_NOFORCE_MANIFEST
+#define _CRT_NOFORCE_MANIFEST
+
+#define _ATL_APARTMENT_THREADED
+#define _ATL_NO_AUTOMATIC_NAMESPACE
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+
+// turns off ATL's hiding of some common and often safely ignored warning messages
+#define _ATL_ALL_WARNINGS
+
+// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <windows.h>
+#pragma warning(disable: 4702)
+#pragma warning(disable: 4201)
+#include <atlbase.h>
+#include <winioctl.h>
+#include <setupapi.h>
+#include <vector>
+#include <map>
+#include <string>
+#pragma warning(default: 4201)
+#pragma warning(disable: 4200)
+#include <usbdi.h>
+#include <usb100.h>
+
+#include "resource.h"
+
+using namespace ATL;
diff --git a/src/sdbwinusbapi/BUILDME.TXT b/src/sdbwinusbapi/BUILDME.TXT
new file mode 100644
index 0000000..b130472
--- /dev/null
+++ b/src/sdbwinusbapi/BUILDME.TXT
@@ -0,0 +1,7 @@
+In order to build SdbWinUsbApi.dll you will need to install Windows Driver Kit,
+which can be obtained from Microsoft. Assuming that WDK is installed, you
+need to set one of the WDK's build environments, "cd" back into this directory,
+and execute "build -cbeEIFZ" to clean and rebuild this project, or you can
+execute "build -befEIF" to do a minimal build.
+Note that you need to build SdbWinApi.dll (..\api) before you build
+SdbWinUsbApi.dll, as it depends on SdbWinApi.lib library.
diff --git a/src/sdbwinusbapi/MAKEFILE b/src/sdbwinusbapi/MAKEFILE
new file mode 100644
index 0000000..87a1295
--- /dev/null
+++ b/src/sdbwinusbapi/MAKEFILE
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2009 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.
+#
+
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/src/sdbwinusbapi/Resource.h b/src/sdbwinusbapi/Resource.h
new file mode 100644
index 0000000..7d883c7
--- /dev/null
+++ b/src/sdbwinusbapi/Resource.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by SdbWinApi.rc
+//
+
+#define IDS_PROJNAME 100
+#define IDR_SDBWINAPI 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 201
+#define _APS_NEXT_COMMAND_VALUE 32768
+#define _APS_NEXT_CONTROL_VALUE 201
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
diff --git a/src/sdbwinusbapi/SOURCES b/src/sdbwinusbapi/SOURCES
new file mode 100644
index 0000000..90be2c1
--- /dev/null
+++ b/src/sdbwinusbapi/SOURCES
@@ -0,0 +1,91 @@
+#
+# Copyright (C) 2009 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.
+#
+
+TARGETNAME = SdbWinUsbApi
+TARGETPATH = obj
+TARGETTYPE = DYNLINK
+
+UMTYPE = windows
+DLLDEF = SdbWinUsbApi.def
+
+# Use statically linked atl libraries:
+# - atls.lib for free build
+# - atlsd.lib for checked build
+USE_STATIC_ATL = 1
+# Use ATL v. 7.1
+ATL_VER = 71
+# Use STL v. 6.0
+USE_STL = 1
+STL_VER = 60
+# Use multithreaded libraries
+USE_LIBCMT = 1
+
+# Include directories
+INCLUDES = $(DDK_INC_PATH); \
+ $(SDK_INC_PATH); \
+ $(CRT_INC_PATH); \
+ $(SDK_INC_PATH)\crt; \
+ $(CRT_INC_PATH)\atl71; \
+ $(SDK_INC_PATH)\crt\stl60
+
+# Common target libraries
+TARGETLIBS = $(SDK_LIB_PATH)\ole32.lib \
+ $(SDK_LIB_PATH)\Advapi32.lib \
+ $(SDK_LIB_PATH)\Kernel32.lib \
+ $(SDK_LIB_PATH)\User32.lib \
+ $(SDK_LIB_PATH)\oleaut32.lib \
+ $(SDK_LIB_PATH)\wbemuuid.lib \
+ $(SDK_LIB_PATH)\uuid.lib \
+ $(SDK_LIB_PATH)\setupapi.lib \
+ $(SDK_LIB_PATH)\usbd.lib \
+ $(SDK_LIB_PATH)\winusb.lib \
+ ..\sdbwinapi\obj$(BUILD_ALT_DIR)\i386\SdbWinApi.lib
+
+!IF "$(DDKBUILDENV)" == "fre"
+# Libraries for release (free) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atls.lib
+!ELSE
+# Libraries for debug (checked) builds
+TARGETLIBS = $(TARGETLIBS) $(ATL_LIB_PATH)\atlsd.lib
+!ENDIF
+
+# Common C defines
+C_DEFINES= $(C_DEFINES) -DSDBWINUSB_EXPORTS -D_UNICODE \
+ -DUNICODE -DWIN32 -D_WINDOWS -D_USRDLL -D_WINDLL
+
+!IF "$(DDKBUILDENV)" == "fre"
+# C defines for release (free) builds
+C_DEFINES = $(C_DEFINES) -DNDEBUG
+!ELSE
+# C defines for debug (checked) builds
+C_DEFINES = $(C_DEFINES) -D_DEBUG
+!ENDIF
+
+# Turn on all warnings, and treat warnings as errors
+MSC_WARNING_LEVEL = /W4 /Wp64 /WX
+# Common C defines
+USER_C_FLAGS = $(USER_C_FLAGS) /FD /EHsc /wd4100 /wd4200 /wd4702 /nologo
+# Set precompiled header information
+PRECOMPILED_CXX = 1
+PRECOMPILED_INCLUDE = stdafx.h
+PRECOMPILED_SOURCEFILE = stdafx.cpp
+
+# Define source files for SdbWinUsbApi.dll
+SOURCES = sdb_winusb_endpoint_object.cpp \
+ sdb_winusb_interface.cpp \
+ sdb_winusb_io_completion.cpp \
+ SdbWinUsbApi.cpp \
+ SdbWinUsbApi.rc
diff --git a/src/sdbwinusbapi/SdbWinUsbApi.cpp b/src/sdbwinusbapi/SdbWinUsbApi.cpp
new file mode 100644
index 0000000..e656e94
--- /dev/null
+++ b/src/sdbwinusbapi/SdbWinUsbApi.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+// SdbWinUsbApi.cpp : Implementation of DLL Exports.
+
+#include "stdafx.h"
+#include "sdb_winusb_interface.h"
+
+class CSdbWinApiModule : public CAtlDllModuleT< CSdbWinApiModule > {
+public:
+};
+
+CSdbWinApiModule _AtlModule;
+
+// DLL Entry Point
+extern "C" BOOL WINAPI DllMain(HINSTANCE instance,
+ DWORD reason,
+ LPVOID reserved) {
+ return _AtlModule.DllMain(reason, reserved);
+}
+
+/** \brief Instantiates interface instance that uses WinUsb API to communicate
+ with USB driver.
+
+ This is the only exported routine from this DLL. This routine instantiates an
+ object of SdbWinUsbInterfaceObject on request from SdbWinApi.dll when it is
+ detected that underlying USB driver is WinUsb.sys.
+ @param[in] interface_name Name of the interface.
+ @return SdbInterfaceObject - casted instance of SdbWinUsbInterfaceObject
+ object on success, or NULL on failure with GetLastError providing
+ information on an error that occurred.
+*/
+extern "C" __declspec(dllexport)
+SdbInterfaceObject* __cdecl InstantiateSdbWinUsbInterface(
+ const wchar_t* interface_name) {
+ // Validate parameter.
+ if (NULL == interface_name) {
+ return NULL;
+ }
+
+ // Instantiate requested object.
+ try {
+ return new SdbWinUsbInterfaceObject(interface_name);
+ } catch (...) {
+ // We expect only OOM exceptions here.
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+}
diff --git a/src/sdbwinusbapi/SdbWinUsbApi.def b/src/sdbwinusbapi/SdbWinUsbApi.def
new file mode 100644
index 0000000..06aac0b
--- /dev/null
+++ b/src/sdbwinusbapi/SdbWinUsbApi.def
@@ -0,0 +1,5 @@
+; SdbWinUsbApi.def : Declares the module parameters.
+
+LIBRARY "SdbWinUsbApi.DLL"
+
+EXPORTS
diff --git a/src/sdbwinusbapi/SdbWinUsbApi.rc b/src/sdbwinusbapi/SdbWinUsbApi.rc
new file mode 100644
index 0000000..d19be52
--- /dev/null
+++ b/src/sdbwinusbapi/SdbWinUsbApi.rc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+//Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,0,0
+ PRODUCTVERSION 2,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "Google, inc"
+ VALUE "FileDescription", "Android SDB API (WinUsb)"
+ VALUE "FileVersion", "2.0.0.0"
+ VALUE "LegalCopyright", "Copyright (C) 2006 The Android Open Source Project"
+ VALUE "InternalName", "SdbWinUsbApi.dll"
+ VALUE "OriginalFilename", "SdbWinUsbApi.dll"
+ VALUE "ProductName", "Android SDK"
+ VALUE "ProductVersion", "2.0.0.0"
+ VALUE "OLESelfRegister", ""
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
+
+#endif // !_MAC
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_PROJNAME "SdbWinUsbApi"
+END
+
+////////////////////////////////////////////////////////////////////////////
+
+
+#endif
+
+#ifndef APSTUDIO_INVOKED
+#endif // not APSTUDIO_INVOKED
diff --git a/src/sdbwinusbapi/sdb_winusb_endpoint_object.cpp b/src/sdbwinusbapi/sdb_winusb_endpoint_object.cpp
new file mode 100644
index 0000000..8b5e891
--- /dev/null
+++ b/src/sdbwinusbapi/sdb_winusb_endpoint_object.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbWinUsbEndpointObject that
+ encapsulates a handle opened to a WinUsb endpoint on our device.
+*/
+
+#include "stdafx.h"
+#include "sdb_winusb_endpoint_object.h"
+#include "sdb_winusb_io_completion.h"
+
+SdbWinUsbEndpointObject::SdbWinUsbEndpointObject(
+ SdbWinUsbInterfaceObject* parent_interf,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index)
+ : SdbEndpointObject(parent_interf, endpoint_id, endpoint_index) {
+}
+
+SdbWinUsbEndpointObject::~SdbWinUsbEndpointObject() {
+}
+
+LONG SdbWinUsbEndpointObject::Release() {
+ ATLASSERT(ref_count_ > 0);
+ LONG ret = InterlockedDecrement(&ref_count_);
+ ATLASSERT(ret >= 0);
+ if (0 == ret) {
+ LastReferenceReleased();
+ delete this;
+ }
+ return ret;
+}
+
+SDBAPIHANDLE SdbWinUsbEndpointObject::CommonAsyncReadWrite(
+ bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ HANDLE event_handle,
+ ULONG time_out) {
+ if (!SetTimeout(time_out))
+ return false;
+
+ // Create completion i/o object
+ SdbIOCompletion* sdb_io_completion = NULL;
+
+ try {
+ sdb_io_completion = new SdbWinUsbIOCompletion(this,
+ bytes_to_transfer,
+ event_handle);
+ } catch (... ) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+
+ // Create a handle for it
+ SDBAPIHANDLE ret = sdb_io_completion->CreateHandle();
+ ULONG transferred = 0;
+ if (NULL != ret) {
+ BOOL res = TRUE;
+ // Go the read / write file way
+ res = is_read ?
+ WinUsb_ReadPipe(parent_winusb_interface()->winusb_handle(),
+ endpoint_id(),
+ reinterpret_cast<PUCHAR>(buffer),
+ bytes_to_transfer,
+ &transferred,
+ sdb_io_completion->overlapped()) :
+ WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(),
+ endpoint_id(),
+ reinterpret_cast<PUCHAR>(buffer),
+ bytes_to_transfer,
+ &transferred,
+ sdb_io_completion->overlapped());
+
+ if (NULL != bytes_transferred)
+ *bytes_transferred = transferred;
+
+ ULONG error = GetLastError();
+ if (!res && (ERROR_IO_PENDING != error)) {
+ // I/O failed immediatelly. We need to close i/o completion object
+ // before we return NULL to the caller.
+ sdb_io_completion->CloseHandle();
+ ret = NULL;
+ SetLastError(error);
+ }
+ }
+
+ // Offseting 'new'
+ sdb_io_completion->Release();
+
+ return ret;
+}
+
+bool SdbWinUsbEndpointObject::CommonSyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ ULONG time_out) {
+ if (!SetTimeout(time_out))
+ return false;
+
+ // This is synchronous I/O. Since we always open I/O items for
+ // overlapped I/O we're obligated to always provide OVERLAPPED
+ // structure to read / write routines. Prepare it now.
+ OVERLAPPED overlapped;
+ ZeroMemory(&overlapped, sizeof(overlapped));
+ overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ BOOL ret = TRUE;
+ ULONG transferred = 0;
+ // Go the read / write file way
+ ret = is_read ?
+ WinUsb_ReadPipe(parent_winusb_interface()->winusb_handle(),
+ endpoint_id(),
+ reinterpret_cast<PUCHAR>(buffer),
+ bytes_to_transfer,
+ &transferred,
+ &overlapped) :
+ WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(),
+ endpoint_id(),
+ reinterpret_cast<PUCHAR>(buffer),
+ bytes_to_transfer,
+ &transferred,
+ &overlapped);
+
+ // Lets see the result
+ if (!ret && (ERROR_IO_PENDING != GetLastError())) {
+ // I/O failed.
+ if (NULL != overlapped.hEvent)
+ ::CloseHandle(overlapped.hEvent);
+ return false;
+ }
+
+ // Lets wait till I/O completes
+ ret = WinUsb_GetOverlappedResult(parent_winusb_interface()->winusb_handle(), &overlapped,
+ &transferred, TRUE);
+ if (ret && (NULL != bytes_transferred)) {
+ *bytes_transferred = transferred;
+ }
+
+ if (NULL != overlapped.hEvent)
+ ::CloseHandle(overlapped.hEvent);
+
+ return ret ? true : false;
+}
+
+bool SdbWinUsbEndpointObject::SetTimeout(ULONG timeout) {
+ if (!WinUsb_SetPipePolicy(parent_winusb_interface()->winusb_handle(),
+ endpoint_id(), PIPE_TRANSFER_TIMEOUT,
+ sizeof(ULONG), &timeout)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/sdbwinusbapi/sdb_winusb_endpoint_object.h b/src/sdbwinusbapi/sdb_winusb_endpoint_object.h
new file mode 100644
index 0000000..bd7b38b
--- /dev/null
+++ b/src/sdbwinusbapi/sdb_winusb_endpoint_object.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_WINUSB_ENDPOINT_OBJECT_H__
+#define ANDROID_USB_API_SDB_WINUSB_ENDPOINT_OBJECT_H__
+/** \file
+ This file consists of declaration of class SdbWinUsbEndpointObject that
+ encapsulates a handle opened to a WinUsb endpoint on our device.
+*/
+
+#include "..\sdbwinapi\sdb_endpoint_object.h"
+#include "sdb_winusb_interface.h"
+
+/** Class SdbWinUsbEndpointObject encapsulates a handle opened to an endpoint on
+ our device.
+*/
+class SdbWinUsbEndpointObject : public SdbEndpointObject {
+ public:
+ /** \brief Constructs the object
+
+ @param[in] interface Parent WinUsb interface for this object.
+ @param[in] endpoint_id Endpoint ID (endpoint address) on the device.
+ @param[in] endpoint_index Zero-based endpoint index in the interface's
+ array of endpoints.
+ */
+ SdbWinUsbEndpointObject(SdbWinUsbInterfaceObject* parent_interf,
+ UCHAR endpoint_id,
+ UCHAR endpoint_index);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbWinUsbEndpointObject();
+
+ //
+ // Virtual overrides
+ //
+
+ public:
+ /** \brief Releases the object.
+
+ If refcount drops to zero as the result of this release, the object is
+ destroyed in this method. As a general rule, objects must not be touched
+ after this method returns even if returned value is not zero. We override
+ this method in order to make sure that objects of this class are deleted
+ in contect of the DLL they were created in. The problem is that since
+ objects of this class were created in context of SdbWinUsbApi module, they
+ are allocated from the heap assigned to that module. Now, if these objects
+ are deleted outside of SdbWinUsbApi module, this will lead to the heap
+ corruption in the module that deleted these objects. Since all objects of
+ this class are deleted in the Release method only, by overriding it we make
+ sure that we free memory in the context of the module where it was
+ allocated.
+ @return Value of the reference counter after object is released in this
+ method.
+ */
+ virtual LONG Release();
+
+ //
+ // Abstract overrides
+ //
+
+ protected:
+ /** \brief Common code for async read / write
+
+ @param[in] is_read Read or write selector.
+ @param[in,out] buffer Pointer to the buffer for read / write.
+ @param[in] bytes_to_transfer Number of bytes to be read / written.
+ @param[out] bytes_transferred Number of bytes read / written. Can be NULL.
+ @param[in] event_handle Event handle that should be signaled when async I/O
+ completes. Can be NULL. If it's not NULL this handle will be used to
+ initialize OVERLAPPED structure for this I/O.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return A handle to IO completion object or NULL on failure. If NULL is
+ returned GetLastError() provides extended error information.
+ */
+ virtual SDBAPIHANDLE CommonAsyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ HANDLE event_handle,
+ ULONG time_out);
+
+ /** \brief Common code for sync read / write
+
+ @param[in] is_read Read or write selector.
+ @param[in,out] buffer Pointer to the buffer for read / write.
+ @param[in] bytes_to_transfer Number of bytes to be read / written.
+ @param[out] bytes_transferred Number of bytes read / written. Can be NULL.
+ @param[in] time_out A timeout (in milliseconds) required for this I/O to
+ complete. Zero value in this parameter means that there is no
+ timeout set for this I/O.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool CommonSyncReadWrite(bool is_read,
+ void* buffer,
+ ULONG bytes_to_transfer,
+ ULONG* bytes_transferred,
+ ULONG time_out);
+
+ //
+ // Operations
+ //
+
+ protected:
+ /** \brief Sets read / write operation timeout.
+
+ @param[in] timeout Timeout value in milliseconds to use for current read
+ or write operation. Zero value passed in this parameters indicate
+ not timeout at all. Note that timeout that is set with this method is
+ global per endpoint (pipe). I.e. once set, it will be used against
+ all read / write operations performed on this endpoint, untill
+ another call to this method modifies it. This is a WinUsb design
+ flaw. Microsoft is aware of this and (hopefuly) future versions of
+ WinUsb framework will accept a timeout parameter in WinUsb_Read/Write
+ routines. For the purposes of SDB this flaw doesn't apperar to be an
+ issue, since we use single-threaded synchronous read / writes, so
+ there is no conflict in setting per-endpoint timeouts.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool SetTimeout(ULONG timeout);
+
+ public:
+ /// Gets parent WinUsb interface
+ SdbWinUsbInterfaceObject* parent_winusb_interface() const {
+ return reinterpret_cast<SdbWinUsbInterfaceObject*>(parent_interface());
+ }
+
+ /// Gets parent interface WinUsb handle
+ WINUSB_INTERFACE_HANDLE winusb_handle() const {
+ return parent_winusb_interface()->winusb_handle();
+ }
+};
+
+#endif // ANDROID_USB_API_SDB_WINUSB_ENDPOINT_OBJECT_H__
diff --git a/src/sdbwinusbapi/sdb_winusb_interface.cpp b/src/sdbwinusbapi/sdb_winusb_interface.cpp
new file mode 100644
index 0000000..3fcdce7
--- /dev/null
+++ b/src/sdbwinusbapi/sdb_winusb_interface.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbWinUsbInterfaceObject
+ that encapsulates an interface on our USB device that is accessible
+ via WinUsb API.
+*/
+
+#include "stdafx.h"
+#include "sdb_winusb_interface.h"
+#include "sdb_winusb_endpoint_object.h"
+
+SdbWinUsbInterfaceObject::SdbWinUsbInterfaceObject(const wchar_t* interf_name)
+ : SdbInterfaceObject(interf_name),
+ usb_device_handle_(INVALID_HANDLE_VALUE),
+ winusb_handle_(NULL),
+ interface_number_(0xFF),
+ def_read_endpoint_(0xFF),
+ read_endpoint_id_(0xFF),
+ def_write_endpoint_(0xFF),
+ write_endpoint_id_(0xFF) {
+}
+
+SdbWinUsbInterfaceObject::~SdbWinUsbInterfaceObject() {
+ ATLASSERT(NULL == winusb_handle_);
+ ATLASSERT(INVALID_HANDLE_VALUE == usb_device_handle_);
+}
+
+LONG SdbWinUsbInterfaceObject::Release() {
+ ATLASSERT(ref_count_ > 0);
+ LONG ret = InterlockedDecrement(&ref_count_);
+ ATLASSERT(ret >= 0);
+ if (0 == ret) {
+ LastReferenceReleased();
+ delete this;
+ }
+ return ret;
+}
+
+SDBAPIHANDLE SdbWinUsbInterfaceObject::CreateHandle() {
+ // Open USB device for this inteface Note that WinUsb API
+ // requires the handle to be opened for overlapped I/O.
+ usb_device_handle_ = CreateFile(interface_name().c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL);
+ if (INVALID_HANDLE_VALUE == usb_device_handle_)
+ return NULL;
+
+ // Initialize WinUSB API for this interface
+ if (!WinUsb_Initialize(usb_device_handle_, &winusb_handle_))
+ return NULL;
+
+ // Cache current interface number that will be used in
+ // WinUsb_Xxx calls performed on this interface.
+ if (!WinUsb_GetCurrentAlternateSetting(winusb_handle(), &interface_number_))
+ return false;
+
+ // Cache interface properties
+ unsigned long bytes_written;
+
+ // Cache USB device descriptor
+ if (!WinUsb_GetDescriptor(winusb_handle(), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0,
+ reinterpret_cast<PUCHAR>(&usb_device_descriptor_),
+ sizeof(usb_device_descriptor_), &bytes_written)) {
+ return false;
+ }
+
+ // Cache USB configuration descriptor
+ if (!WinUsb_GetDescriptor(winusb_handle(), USB_CONFIGURATION_DESCRIPTOR_TYPE,
+ 0, 0,
+ reinterpret_cast<PUCHAR>(&usb_config_descriptor_),
+ sizeof(usb_config_descriptor_), &bytes_written)) {
+ return false;
+ }
+
+ // Cache USB interface descriptor
+ if (!WinUsb_QueryInterfaceSettings(winusb_handle(), interface_number(),
+ &usb_interface_descriptor_)) {
+ return false;
+ }
+
+ // Save indexes and IDs for bulk read / write endpoints. We will use them to
+ // convert SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX and
+ // SDB_QUERY_BULK_READ_ENDPOINT_INDEX into actual endpoint indexes and IDs.
+ for (UCHAR endpoint = 0; endpoint < usb_interface_descriptor_.bNumEndpoints;
+ endpoint++) {
+ // Get endpoint information
+ WINUSB_PIPE_INFORMATION pipe_info;
+ if (!WinUsb_QueryPipe(winusb_handle(), interface_number(), endpoint,
+ &pipe_info)) {
+ return false;
+ }
+
+ if (UsbdPipeTypeBulk == pipe_info.PipeType) {
+ // This is a bulk endpoint. Cache its index and ID.
+ if (0 != (pipe_info.PipeId & USB_ENDPOINT_DIRECTION_MASK)) {
+ // Use this endpoint as default bulk read endpoint
+ ATLASSERT(0xFF == def_read_endpoint_);
+ def_read_endpoint_ = endpoint;
+ read_endpoint_id_ = pipe_info.PipeId;
+ } else {
+ // Use this endpoint as default bulk write endpoint
+ ATLASSERT(0xFF == def_write_endpoint_);
+ def_write_endpoint_ = endpoint;
+ write_endpoint_id_ = pipe_info.PipeId;
+ }
+ }
+ }
+
+ return SdbInterfaceObject::CreateHandle();
+}
+
+bool SdbWinUsbInterfaceObject::CloseHandle() {
+ if (NULL != winusb_handle_) {
+ WinUsb_Free(winusb_handle_);
+ winusb_handle_ = NULL;
+ }
+ if (INVALID_HANDLE_VALUE != usb_device_handle_) {
+ ::CloseHandle(usb_device_handle_);
+ usb_device_handle_ = INVALID_HANDLE_VALUE;
+ }
+
+ return SdbInterfaceObject::CloseHandle();
+}
+
+bool SdbWinUsbInterfaceObject::GetSerialNumber(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ if (NULL == buffer_char_size) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Calculate serial number string size. Note that WinUsb_GetDescriptor
+ // API will not return number of bytes needed to store serial number
+ // string. So we will have to start with a reasonably large preallocated
+ // buffer and then loop through WinUsb_GetDescriptor calls, doubling up
+ // string buffer size every time ERROR_INSUFFICIENT_BUFFER is returned.
+ union {
+ // Preallocate reasonably sized buffer on the stack.
+ char small_buffer[64];
+ USB_STRING_DESCRIPTOR initial_ser_num;
+ };
+ USB_STRING_DESCRIPTOR* ser_num = &initial_ser_num;
+ // Buffer byte size
+ unsigned long ser_num_size = sizeof(small_buffer);
+ // After successful call to WinUsb_GetDescriptor will contain serial
+ // number descriptor size.
+ unsigned long bytes_written;
+ while (!WinUsb_GetDescriptor(winusb_handle(), USB_STRING_DESCRIPTOR_TYPE,
+ usb_device_descriptor_.iSerialNumber,
+ 0x0409, // English (US)
+ reinterpret_cast<PUCHAR>(ser_num),
+ ser_num_size, &bytes_written)) {
+ // Any error other than ERROR_INSUFFICIENT_BUFFER is terminal here.
+ if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
+ if (ser_num != &initial_ser_num)
+ delete[] reinterpret_cast<char*>(ser_num);
+ return false;
+ }
+
+ // Double up buffer size and reallocate string buffer
+ ser_num_size *= 2;
+ if (ser_num != &initial_ser_num)
+ delete[] reinterpret_cast<char*>(ser_num);
+ try {
+ ser_num =
+ reinterpret_cast<USB_STRING_DESCRIPTOR*>(new char[ser_num_size]);
+ } catch (...) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return false;
+ }
+ }
+
+ // Serial number string length
+ unsigned long str_len = (ser_num->bLength -
+ FIELD_OFFSET(USB_STRING_DESCRIPTOR, bString)) /
+ sizeof(wchar_t);
+
+ // Lets see if requested buffer is big enough to fit the string
+ if ((NULL == buffer) || (*buffer_char_size < (str_len + 1))) {
+ // Requested buffer is too small.
+ if (ser_num != &initial_ser_num)
+ delete[] reinterpret_cast<char*>(ser_num);
+ *buffer_char_size = str_len + 1;
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return false;
+ }
+
+ bool ret = true;
+ if (ansi) {
+ // We need to convert name from wide char to ansi string
+ if (0 != WideCharToMultiByte(CP_ACP, 0, ser_num->bString,
+ static_cast<int>(str_len),
+ reinterpret_cast<PSTR>(buffer),
+ static_cast<int>(*buffer_char_size),
+ NULL, NULL)) {
+ // Zero-terminate output string.
+ reinterpret_cast<char*>(buffer)[str_len] = '\0';
+ } else {
+ ret = false;
+ }
+ } else {
+ // For wide char output just copy string buffer,
+ // and zero-terminate output string.
+ CopyMemory(buffer, ser_num->bString, bytes_written);
+ reinterpret_cast<wchar_t*>(buffer)[str_len] = L'\0';
+ }
+
+ if (ser_num != &initial_ser_num)
+ delete[] reinterpret_cast<char*>(ser_num);
+
+ return ret;
+}
+
+bool SdbWinUsbInterfaceObject::GetEndpointInformation(
+ UCHAR endpoint_index,
+ SdbEndpointInformation* info) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ if (NULL == info) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ // Get actual endpoint index for predefined read / write endpoints.
+ if (SDB_QUERY_BULK_READ_ENDPOINT_INDEX == endpoint_index) {
+ endpoint_index = def_read_endpoint_;
+ } else if (SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX == endpoint_index) {
+ endpoint_index = def_write_endpoint_;
+ }
+
+ // Query endpoint information
+ WINUSB_PIPE_INFORMATION pipe_info;
+ if (!WinUsb_QueryPipe(winusb_handle(), interface_number(), endpoint_index,
+ &pipe_info)) {
+ return false;
+ }
+
+ // Save endpoint information into output.
+ info->max_packet_size = pipe_info.MaximumPacketSize;
+ info->max_transfer_size = 0xFFFFFFFF;
+ info->endpoint_address = pipe_info.PipeId;
+ info->polling_interval = pipe_info.Interval;
+ info->setting_index = interface_number();
+ switch (pipe_info.PipeType) {
+ case UsbdPipeTypeControl:
+ info->endpoint_type = SdbEndpointTypeControl;
+ break;
+
+ case UsbdPipeTypeIsochronous:
+ info->endpoint_type = SdbEndpointTypeIsochronous;
+ break;
+
+ case UsbdPipeTypeBulk:
+ info->endpoint_type = SdbEndpointTypeBulk;
+ break;
+
+ case UsbdPipeTypeInterrupt:
+ info->endpoint_type = SdbEndpointTypeInterrupt;
+ break;
+
+ default:
+ info->endpoint_type = SdbEndpointTypeInvalid;
+ break;
+ }
+
+ return true;
+}
+
+SDBAPIHANDLE SdbWinUsbInterfaceObject::OpenEndpoint(
+ UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode) {
+ // Convert index into id
+ UCHAR endpoint_id;
+
+ if ((SDB_QUERY_BULK_READ_ENDPOINT_INDEX == endpoint_index) ||
+ (def_read_endpoint_ == endpoint_index)) {
+ endpoint_id = read_endpoint_id_;
+ endpoint_index = def_read_endpoint_;
+ } else if ((SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX == endpoint_index) ||
+ (def_write_endpoint_ == endpoint_index)) {
+ endpoint_id = write_endpoint_id_;
+ endpoint_index = def_write_endpoint_;
+ } else {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ return OpenEndpoint(endpoint_id, endpoint_index);
+}
+
+SDBAPIHANDLE SdbWinUsbInterfaceObject::OpenEndpoint(UCHAR endpoint_id,
+ UCHAR endpoint_index) {
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ SdbEndpointObject* sdb_endpoint = NULL;
+
+ try {
+ sdb_endpoint =
+ new SdbWinUsbEndpointObject(this, endpoint_id, endpoint_index);
+ } catch (...) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return NULL;
+ }
+
+ SDBAPIHANDLE ret = sdb_endpoint->CreateHandle();
+
+ sdb_endpoint->Release();
+
+ return ret;
+}
diff --git a/src/sdbwinusbapi/sdb_winusb_interface.h b/src/sdbwinusbapi/sdb_winusb_interface.h
new file mode 100644
index 0000000..6a37996
--- /dev/null
+++ b/src/sdbwinusbapi/sdb_winusb_interface.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_WINUSB_INTERFACE_H__
+#define ANDROID_USB_API_SDB_WINUSB_INTERFACE_H__
+/** \file
+ This file consists of declaration of class SdbWinUsbInterfaceObject
+ that encapsulates an interface on our USB device that is accessible
+ via WinUsb API.
+*/
+
+#include "..\sdbwinapi\sdb_interface.h"
+
+/** \brief Encapsulates an interface on our USB device that is accessible
+ via WinUsb API.
+*/
+class SdbWinUsbInterfaceObject : public SdbInterfaceObject {
+ public:
+ /** \brief Constructs the object.
+
+ @param[in] interf_name Name of the interface
+ */
+ explicit SdbWinUsbInterfaceObject(const wchar_t* interf_name);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbWinUsbInterfaceObject();
+
+ //
+ // Virtual overrides
+ //
+
+ public:
+ /** \brief Releases the object.
+
+ If refcount drops to zero as the result of this release, the object is
+ destroyed in this method. As a general rule, objects must not be touched
+ after this method returns even if returned value is not zero. We override
+ this method in order to make sure that objects of this class are deleted
+ in contect of the DLL they were created in. The problem is that since
+ objects of this class were created in context of SdbWinUsbApi module, they
+ are allocated from the heap assigned to that module. Now, if these objects
+ are deleted outside of SdbWinUsbApi module, this will lead to the heap
+ corruption in the module that deleted these objects. Since all objects of
+ this class are deleted in the Release method only, by overriding it we make
+ sure that we free memory in the context of the module where it was
+ allocated.
+ @return Value of the reference counter after object is released in this
+ method.
+ */
+ virtual LONG Release();
+
+ /** \brief Creates handle to this object.
+
+ In this call a handle for this object is generated and object is added
+ to the SdbObjectHandleMap. We override this method in order to initialize
+ WinUsb API for the given interface.
+ @return A handle to this object on success or NULL on an error.
+ If NULL is returned GetLastError() provides extended error
+ information. ERROR_GEN_FAILURE is set if an attempt was
+ made to create already opened object.
+ */
+ virtual SDBAPIHANDLE CreateHandle();
+
+ /** \brief This method is called when handle to this object gets closed.
+
+ In this call object is deleted from the SdbObjectHandleMap. We override
+ this method in order close WinUsb handle created in CreateHandle method
+ of this class.
+ @return true on success or false if object is already closed. If
+ false is returned GetLastError() provides extended error
+ information.
+ */
+ virtual bool CloseHandle();
+
+ //
+ // Abstract overrides
+ //
+
+ public:
+ /** \brief Gets serial number for interface's device.
+
+ @param[out] buffer Buffer for the serail number string. Can be NULL in
+ which case buffer_char_size will contain number of characters
+ required for the string.
+ @param[in,out] buffer_char_size On the way in supplies size (in characters)
+ of the buffer. On the way out, if method failed and GetLastError
+ reports ERROR_INSUFFICIENT_BUFFER, will contain number of characters
+ required for the name.
+ @param[in] ansi If true the name will be returned as single character
+ string. Otherwise name will be returned as wide character string.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetSerialNumber(void* buffer,
+ unsigned long* buffer_char_size,
+ bool ansi);
+
+ /** \brief Gets information about an endpoint on this interface.
+
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide infor about
+ (default?) bulk write and read endpoints respectively.
+ @param[out] info Upon successful completion will have endpoint information.
+ @return true on success, false on failure. If false is returned
+ GetLastError() provides extended error information.
+ */
+ virtual bool GetEndpointInformation(UCHAR endpoint_index,
+ SdbEndpointInformation* info);
+
+ /** \brief Opens an endpoint on this interface.
+
+ @param[in] endpoint_index Zero-based endpoint index. There are two
+ shortcuts for this parameter: SDB_QUERY_BULK_WRITE_ENDPOINT_INDEX
+ and SDB_QUERY_BULK_READ_ENDPOINT_INDEX that provide infor about
+ (default?) bulk write and read endpoints respectively.
+ @param[in] access_type Desired access type. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always read / write access.
+ @param[in] sharing_mode Desired share mode. In the current implementation
+ this parameter has no effect on the way endpoint is opened. It's
+ always shared for read / write.
+ @return Handle to the opened endpoint object or NULL on failure.
+ If NULL is returned GetLastError() provides extended information
+ about the error that occurred.
+ */
+ virtual SDBAPIHANDLE OpenEndpoint(UCHAR endpoint_index,
+ SdbOpenAccessType access_type,
+ SdbOpenSharingMode sharing_mode);
+
+ //
+ // Operations
+ //
+
+ protected:
+ /** \brief Opens an endpoint on this interface.
+
+ @param[in] endpoint_id Endpoint (pipe) address on the device.
+ @param[in] endpoint_index Zero-based endpoint index.
+ @return Handle to the opened endpoint object or NULL on failure.
+ If NULL is returned GetLastError() provides extended information
+ about the error that occurred.
+ */
+ SDBAPIHANDLE OpenEndpoint(UCHAR endpoint_id, UCHAR endpoint_index);
+
+ public:
+ /// Gets handle to the USB device
+ HANDLE usb_device_handle() const {
+ return usb_device_handle_;
+ }
+
+ /// Gets interface handle used by WinUSB API
+ WINUSB_INTERFACE_HANDLE winusb_handle() const {
+ return winusb_handle_;
+ }
+
+ /// Gets current interface number.
+ UCHAR interface_number() const {
+ return interface_number_;
+ }
+
+ protected:
+ /// Handle to the USB device
+ HANDLE usb_device_handle_;
+
+ /// Interface handle used by WinUSB API
+ WINUSB_INTERFACE_HANDLE winusb_handle_;
+
+ /// Current interface number. This value is obtained via call to
+ /// WinUsb_GetCurrentAlternateSetting and is used in WinUsb_Xxx
+ /// calls that require interface number.
+ UCHAR interface_number_;
+
+ /// Index for the default bulk read endpoint
+ UCHAR def_read_endpoint_;
+
+ /// ID for the default bulk read endpoint
+ UCHAR read_endpoint_id_;
+
+ /// Index for the default bulk write endpoint
+ UCHAR def_write_endpoint_;
+
+ /// ID for the default bulk write endpoint
+ UCHAR write_endpoint_id_;
+};
+
+#endif // ANDROID_USB_API_SDB_WINUSB_INTERFACE_H__
diff --git a/src/sdbwinusbapi/sdb_winusb_io_completion.cpp b/src/sdbwinusbapi/sdb_winusb_io_completion.cpp
new file mode 100644
index 0000000..283f689
--- /dev/null
+++ b/src/sdbwinusbapi/sdb_winusb_io_completion.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ This file consists of implementation of class SdbWinUsbIOCompletion that
+ encapsulates a wrapper around OVERLAPPED Win32 structure returned from
+ asynchronous I/O requests issued via WinUsb API.
+*/
+
+#include "stdafx.h"
+#include "sdb_winusb_io_completion.h"
+
+SdbWinUsbIOCompletion::SdbWinUsbIOCompletion(
+ SdbWinUsbEndpointObject* parent_io_obj,
+ ULONG expected_trans_size,
+ HANDLE event_hndl)
+ : SdbIOCompletion(parent_io_obj, expected_trans_size, event_hndl) {
+}
+
+SdbWinUsbIOCompletion::~SdbWinUsbIOCompletion() {
+}
+
+LONG SdbWinUsbIOCompletion::Release() {
+ ATLASSERT(ref_count_ > 0);
+ LONG ret = InterlockedDecrement(&ref_count_);
+ ATLASSERT(ret >= 0);
+ if (0 == ret) {
+ LastReferenceReleased();
+ delete this;
+ }
+ return ret;
+}
+
+bool SdbWinUsbIOCompletion::GetOvelappedIoResult(LPOVERLAPPED ovl_data,
+ ULONG* bytes_transferred,
+ bool wait) {
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = 0;
+ }
+
+ if (!IsOpened()) {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ ULONG transfer;
+ bool ret = WinUsb_GetOverlappedResult(
+ parent_winusb_io_object()->winusb_handle(),
+ overlapped(),
+ &transfer,
+ wait ? TRUE : FALSE) ? true : false;
+
+ // TODO: This is bizzare but I've seen it happening
+ // that GetOverlappedResult with wait set to true returns "prematurely",
+ // with wrong transferred bytes value and GetLastError reporting
+ // ERROR_IO_PENDING. So, lets give it an up to a 20 ms loop!
+ ULONG error = GetLastError();
+
+ if (wait && ret && (0 == transfer) && (0 != expected_transfer_size_) &&
+ ((ERROR_IO_INCOMPLETE == error) || (ERROR_IO_PENDING == error))) {
+ for (int trying = 0; trying < 10; trying++) {
+ Sleep(2);
+ ret = WinUsb_GetOverlappedResult(
+ parent_winusb_io_object()->winusb_handle(),
+ overlapped(),
+ &transfer,
+ wait ? TRUE : FALSE) ? true : false;
+ error = GetLastError();
+ if (!ret || (0 != transfer) ||
+ ((ERROR_IO_INCOMPLETE != error) && (ERROR_IO_PENDING != error))) {
+ break;
+ }
+ }
+ }
+
+ if (NULL != ovl_data) {
+ CopyMemory(ovl_data, overlapped(), sizeof(OVERLAPPED));
+ }
+
+ if (NULL != bytes_transferred) {
+ *bytes_transferred = transfer;
+ }
+
+ return ret;
+}
diff --git a/src/sdbwinusbapi/sdb_winusb_io_completion.h b/src/sdbwinusbapi/sdb_winusb_io_completion.h
new file mode 100644
index 0000000..e75a7b2
--- /dev/null
+++ b/src/sdbwinusbapi/sdb_winusb_io_completion.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2009 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_USB_API_SDB_WINUSB_IO_COMPLETION_H__
+#define ANDROID_USB_API_SDB_WINUSB_IO_COMPLETION_H__
+/** \file
+ This file consists of declaration of class SdbWinUsbIOCompletion that
+ encapsulates a wrapper around OVERLAPPED Win32 structure returned from
+ asynchronous I/O requests issued via WinUsb API.
+*/
+
+#include "..\sdbwinapi\sdb_io_completion.h"
+#include "sdb_winusb_endpoint_object.h"
+
+/** \brief Encapsulates encapsulates a wrapper around OVERLAPPED Win32
+ structure returned from asynchronous I/O requests issued via WinUsb API.
+
+ A handle to this object is returned to the caller of each successful
+ asynchronous I/O request. Just like all other handles this handle
+ must be closed after it's no longer needed.
+*/
+class SdbWinUsbIOCompletion : public SdbIOCompletion {
+ public:
+ /** \brief Constructs the object
+
+ @param[in] parent_io_obj Parent WinUsb I/O object that created this
+ instance.
+ @param[in] expected_trans_size Number of bytes expected to be transferred
+ with the I/O.
+ @param[in] event_hndl Event handle that should be signaled when I/O
+ completes. Can be NULL. If it's not NULL this handle will be
+ used to initialize OVERLAPPED structure for this object.
+ */
+ SdbWinUsbIOCompletion(SdbWinUsbEndpointObject* parent_io_obj,
+ ULONG expected_trans_size,
+ HANDLE event_hndl);
+
+ protected:
+ /** \brief Destructs the object.
+
+ We hide destructor in order to prevent ourseves from accidentaly allocating
+ instances on the stack. If such attemp occur, compiler will error.
+ */
+ virtual ~SdbWinUsbIOCompletion();
+
+ //
+ // Virtual overrides
+ //
+
+ public:
+ /** \brief Releases the object.
+
+ If refcount drops to zero as the result of this release, the object is
+ destroyed in this method. As a general rule, objects must not be touched
+ after this method returns even if returned value is not zero. We override
+ this method in order to make sure that objects of this class are deleted
+ in contect of the DLL they were created in. The problem is that since
+ objects of this class were created in context of SdbWinUsbApi module, they
+ are allocated from the heap assigned to that module. Now, if these objects
+ are deleted outside of SdbWinUsbApi module, this will lead to the heap
+ corruption in the module that deleted these objects. Since all objects of
+ this class are deleted in the Release method only, by overriding it we make
+ sure that we free memory in the context of the module where it was
+ allocated.
+ @return Value of the reference counter after object is released in this
+ method.
+ */
+ virtual LONG Release();
+
+ //
+ // Abstract overrides
+ //
+
+ public:
+ /** \brief Gets overlapped I/O result
+
+ This method uses WinUsb_GetOverlappedResult to get results of the
+ overlapped I/O operation.
+ @param[out] ovl_data Buffer for the copy of this object's OVERLAPPED
+ structure. Can be NULL.
+ @param[out] bytes_transferred Pointer to a variable that receives the
+ number of bytes that were actually transferred by a read or write
+ operation. See SDK doc on GetOvelappedResult for more information.
+ Unlike regular GetOvelappedResult call this parameter can be NULL.
+ @param[in] wait If this parameter is true, the method does not return
+ until the operation has been completed. If this parameter is false
+ and the operation is still pending, the method returns false and
+ the GetLastError function returns ERROR_IO_INCOMPLETE.
+ @return true if I/O has been completed or false on failure or if request
+ is not yet completed. If false is returned GetLastError() provides
+ extended error information. If GetLastError returns
+ ERROR_IO_INCOMPLETE it means that I/O is not yet completed.
+ */
+ virtual bool GetOvelappedIoResult(LPOVERLAPPED ovl_data,
+ ULONG* bytes_transferred,
+ bool wait);
+
+ public:
+ /// Gets WinUsb parent object
+ SdbWinUsbEndpointObject* parent_winusb_io_object() const {
+ return reinterpret_cast<SdbWinUsbEndpointObject*>(parent_io_object());
+ }
+};
+
+#endif // ANDROID_USB_API_SDB_WINUSB_IO_COMPLETION_H__
diff --git a/src/sdbwinusbapi/stdafx.cpp b/src/sdbwinusbapi/stdafx.cpp
new file mode 100644
index 0000000..83c6e13
--- /dev/null
+++ b/src/sdbwinusbapi/stdafx.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// SdbWinUsbApi.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
diff --git a/src/sdbwinusbapi/stdafx.h b/src/sdbwinusbapi/stdafx.h
new file mode 100644
index 0000000..88cd963
--- /dev/null
+++ b/src/sdbwinusbapi/stdafx.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+/** \file
+ Visual Studio generated include file for standard system include files, or
+ project specific include files that are used frequently, but are changed
+ infrequently.
+*/
+
+#pragma once
+
+#ifndef STRICT
+#define STRICT
+#endif
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER // Allow use of features specific to Windows 95 and Windows NT 4 or later.
+#define WINVER 0x0500 // Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
+#endif
+
+#ifndef _WIN32_WINNT // Allow use of features specific to Windows NT 4 or later.
+#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target Windows 2000 or later.
+#endif
+
+#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later.
+#define _WIN32_WINDOWS 0x0500 // Change this to the appropriate value to target Windows Me or later.
+#endif
+
+#ifndef _WIN32_IE // Allow use of features specific to IE 4.0 or later.
+#define _WIN32_IE 0x0501 // Change this to the appropriate value to target IE 5.0 or later.
+#endif
+
+// These defines prevent the MS header files from ejecting #pragma comment
+// statements with the manifest information of the used ATL, STL, and CRT
+#define _ATL_NOFORCE_MANIFEST
+#define _STL_NOFORCE_MANIFEST
+#define _CRT_NOFORCE_MANIFEST
+
+#define _ATL_APARTMENT_THREADED
+#define _ATL_NO_AUTOMATIC_NAMESPACE
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+
+// turns off ATL's hiding of some common and often safely ignored warning messages
+#define _ATL_ALL_WARNINGS
+
+// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <windows.h>
+#pragma warning(disable: 4702)
+#pragma warning(disable: 4201)
+#include <atlbase.h>
+#include <winioctl.h>
+#include <setupapi.h>
+#include <vector>
+#include <map>
+#include <string>
+#pragma warning(default: 4201)
+#pragma warning(disable: 4200)
+#include <winusb.h>
+
+#include "resource.h"
+
+using namespace ATL;
diff --git a/src/services.c b/src/services.c
new file mode 100755
index 0000000..555c91d
--- /dev/null
+++ b/src/services.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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_SERVICES
+#include "sdb.h"
+#include "file_sync_service.h"
+
+#if SDB_HOST
+# ifndef HAVE_WINSOCK
+# include <netinet/in.h>
+# include <netdb.h>
+# include <sys/ioctl.h>
+# endif
+#else
+# include "android_reboot.h"
+# include <sys/inotify.h>
+#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 SDB_HOST
+SDB_MUTEX_DEFINE( dns_lock );
+
+static void dns_service(int fd, void *cookie)
+{
+ char *hostname = cookie;
+ struct hostent *hp;
+ unsigned zero = 0;
+
+ sdb_mutex_lock(&dns_lock);
+ hp = gethostbyname(hostname);
+ free(cookie);
+ if(hp == 0) {
+ writex(fd, &zero, 4);
+ } else {
+ writex(fd, hp->h_addr, 4);
+ }
+ sdb_mutex_unlock(&dns_lock);
+ sdb_close(fd);
+}
+#else
+
+#if 0
+extern int recovery_mode;
+
+static void recover_service(int s, void *cookie)
+{
+ unsigned char buf[4096];
+ unsigned count = (unsigned) cookie;
+ int fd;
+
+ fd = sdb_creat("/tmp/update", 0644);
+ if(fd < 0) {
+ sdb_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);
+ }
+ sdb_close(fd);
+ sdb_close(s);
+
+ fd = sdb_creat("/tmp/update.begin", 0644);
+ sdb_close(fd);
+}
+
+void restart_root_service(int fd, void *cookie)
+{
+ char buf[100];
+ char value[PROPERTY_VALUE_MAX];
+
+ if (getuid() == 0) {
+ snprintf(buf, sizeof(buf), "sdbd is already running as root\n");
+ writex(fd, buf, strlen(buf));
+ sdb_close(fd);
+ } else {
+ property_get("ro.debuggable", value, "");
+ if (strcmp(value, "1") != 0) {
+ snprintf(buf, sizeof(buf), "sdbd cannot run as root in production builds\n");
+ writex(fd, buf, strlen(buf));
+ sdb_close(fd);
+ return;
+ }
+
+ property_set("service.sdb.root", "1");
+ snprintf(buf, sizeof(buf), "restarting sdbd as root\n");
+ writex(fd, buf, strlen(buf));
+ sdb_close(fd);
+ }
+}
+#endif
+
+void restart_tcp_service(int fd, void *cookie)
+{
+ char buf[100];
+ char value[PROPERTY_VALUE_MAX];
+ int port = (int)cookie;
+
+ if (port <= 0) {
+ snprintf(buf, sizeof(buf), "invalid port\n");
+ writex(fd, buf, strlen(buf));
+ sdb_close(fd);
+ return;
+ }
+
+ snprintf(value, sizeof(value), "%d", port);
+ property_set("service.sdb.tcp.port", value);
+ snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port);
+ writex(fd, buf, strlen(buf));
+ sdb_close(fd);
+}
+
+void restart_usb_service(int fd, void *cookie)
+{
+ char buf[100];
+
+ property_set("service.sdb.tcp.port", "0");
+ snprintf(buf, sizeof(buf), "restarting in USB mode\n");
+ writex(fd, buf, strlen(buf));
+ sdb_close(fd);
+}
+
+void reboot_service(int fd, void *arg)
+{
+ char buf[100];
+ int pid, ret;
+
+ sync();
+
+ /* Attempt to unmount the SD card first.
+ * No need to bother checking for errors.
+ */
+ pid = fork();
+ if (pid == 0) {
+ /* ask vdc to unmount it */
+ execl("/system/bin/vdc", "/system/bin/vdc", "volume", "unmount",
+ getenv("EXTERNAL_STORAGE"), "force", NULL);
+ } else if (pid > 0) {
+ /* wait until vdc succeeds or fails */
+ waitpid(pid, &ret, 0);
+ }
+
+ ret = android_reboot(ANDROID_RB_RESTART2, 0, (char *) arg);
+ if (ret < 0) {
+ snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno));
+ writex(fd, buf, strlen(buf));
+ }
+ free(arg);
+ sdb_close(fd);
+}
+
+#if !SDB_HOST
+#define EVENT_SIZE ( sizeof (struct inotify_event) )
+#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
+#define CS_PATH "/opt/usr/share/crash/report"
+static int inoti_once = 0;
+void inoti_service(int fd, void *arg)
+{
+ int wd;
+ int ifd;
+ char buffer[BUF_LEN];
+
+ D( "inoti_service start\n");
+ ifd = inotify_init();
+
+ if ( ifd < 0 ) {
+ D( "inotify_init failed\n");
+ return;
+ }
+
+ wd = inotify_add_watch( ifd, CS_PATH, IN_CREATE);
+ inoti_once = 1;
+ for ( ; ; ) {
+ int length, i = 0;
+ length = sdb_read( ifd, buffer, BUF_LEN );
+
+ if ( length < 0 ) {
+ D( "inoti read failed\n");
+ goto done;
+ }
+
+ while ( i < length ) {
+ struct inotify_event *event = ( struct inotify_event * )&buffer[i];
+ if (event->len) {
+ if ( event->mask & IN_CREATE) {
+ if (!(event->mask & IN_ISDIR)) {
+ char *cspath = NULL;
+ int len = asprintf(&cspath, "%s/%s", CS_PATH, event->name);
+ D( "The file %s was created.\n", cspath);
+ if (cspath != NULL) {
+ writex(fd, cspath, len);
+ free(cspath);
+ }
+ }
+ }
+ }
+ i += EVENT_SIZE + event->len;
+ }
+ }
+
+done:
+ inotify_rm_watch( ifd, wd );
+ sdb_close(ifd);
+ sdb_close(fd);
+ D( "inoti_service end\n");
+}
+#endif
+#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;
+ sdb_thread_t t;
+ int s[2];
+
+ if(sdb_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(sdb_thread_create( &t, service_bootstrap_func, sti)){
+ free(sti);
+ sdb_close(s[0]);
+ sdb_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];
+}
+
+#if !SDB_HOST
+static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
+{
+#ifdef HAVE_WIN32_PROC
+ D("create_subprocess(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
+ 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;
+
+ 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));
+ sdb_close(ptm);
+ return -1;
+ }
+
+ *pid = fork();
+ if(*pid < 0) {
+ printf("- fork failed: %s -\n", strerror(errno));
+ sdb_close(ptm);
+ return -1;
+ }
+
+ if(*pid == 0){
+ int pts;
+
+ setsid();
+
+ pts = unix_open(devname, O_RDWR);
+ if(pts < 0) {
+ fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname);
+ exit(-1);
+ }
+
+ dup2(pts, 0);
+ dup2(pts, 1);
+ dup2(pts, 2);
+
+ sdb_close(pts);
+ sdb_close(ptm);
+
+ // set OOM adjustment to zero
+ char text[64];
+ snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid());
+ int fd = sdb_open(text, O_WRONLY);
+ if (fd >= 0) {
+ sdb_write(fd, "0", 1);
+ sdb_close(fd);
+ } else {
+ D("sdb: unable to open %s\n", text);
+ }
+ execl(cmd, cmd, arg0, arg1, NULL);
+ fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
+ cmd, strerror(errno), errno);
+ exit(-1);
+ } else {
+ // Don't set child's OOM adjustment to zero.
+ // Let the child do it itself, as sometimes the parent starts
+ // running before the child has a /proc/pid/oom_adj.
+ // """sdb: unable to open /proc/644/oom_adj""" seen in some logs.
+ return ptm;
+ }
+#endif /* !HAVE_WIN32_PROC */
+}
+#endif /* !SDB_HOST */
+
+#if SDB_HOST
+#define SHELL_COMMAND "/bin/sh"
+#else
+#define SHELL_COMMAND "/bin/sh" /* tizen specific */
+#endif
+
+#if !SDB_HOST
+static void subproc_waiter_service(int fd, void *cookie)
+{
+ pid_t pid = (pid_t)cookie;
+
+ D("entered. fd=%d of pid=%d\n", fd, pid);
+ for (;;) {
+ int status;
+ pid_t p = waitpid(pid, &status, 0);
+ if (p == pid) {
+ D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
+ if (WIFSIGNALED(status)) {
+ D("*** Killed by signal %d\n", WTERMSIG(status));
+ break;
+ } else if (!WIFEXITED(status)) {
+ D("*** Didn't exit!!. status %d\n", status);
+ break;
+ } else if (WEXITSTATUS(status) >= 0) {
+ D("*** Exit code %d\n", WEXITSTATUS(status));
+ break;
+ }
+ }
+ }
+ D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
+ if (SHELL_EXIT_NOTIFY_FD >=0) {
+ int res;
+ res = writex(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd));
+ D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
+ SHELL_EXIT_NOTIFY_FD, pid, res, errno);
+ }
+}
+
+static int create_subproc_thread(const char *name)
+{
+ stinfo *sti;
+ sdb_thread_t t;
+ int ret_fd;
+ pid_t pid;
+ if(name) {
+ ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid);
+ } else {
+ ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid);
+ }
+ D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid);
+
+ sti = malloc(sizeof(stinfo));
+ if(sti == 0) fatal("cannot allocate stinfo");
+ sti->func = subproc_waiter_service;
+ sti->cookie = (void*)pid;
+ sti->fd = ret_fd;
+
+ if(sdb_thread_create( &t, service_bootstrap_func, sti)){
+ free(sti);
+ sdb_close(ret_fd);
+ printf("cannot create service thread\n");
+ return -1;
+ }
+
+ D("service thread started, fd=%d pid=%d\n",ret_fd, pid);
+ return ret_fd;
+}
+#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 SDB_HOST
+ sdb_mutex_lock(&dns_lock);
+ ret = socket_network_client(name + 1, port, SOCK_STREAM);
+ sdb_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 SDB_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 /* !SDB_HOST */
+ }/* else if(!strncmp("dev:", name, 4)) {// tizen specific
+ 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));
+ }*/ else if(!HOST && !strncmp(name, "shell:", 6)) {
+ if(name[6]) {
+ ret = create_subproc_thread(name + 6);
+ } else {
+ ret = create_subproc_thread(0);
+ }
+ } 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);
+ } else if(!strncmp(name, "reboot:", 7)) {
+ void* arg = strdup(name + 7);
+ if(arg == 0) return -1;
+ ret = create_service_thread(reboot_service, arg);
+ } else if(!strncmp(name, "root:", 5)) {
+ ret = create_service_thread(restart_root_service, NULL);
+ } else if(!strncmp(name, "backup:", 7)) {
+ char* arg = strdup(name+7);
+ if (arg == NULL) return -1;
+ ret = backup_service(BACKUP, arg);
+ } else if(!strncmp(name, "restore:", 8)) {
+ ret = backup_service(RESTORE, NULL);
+ }*/ else if(!strncmp(name, "tcpip:", 6)) {
+ int port;
+ /*if (sscanf(name + 6, "%d", &port) == 0) {
+ port = 0;
+ }*/
+ port = DEFAULT_SDB_LOCAL_TRANSPORT_PORT;
+ ret = create_service_thread(restart_tcp_service, (void *)port);
+ } else if(!strncmp(name, "usb:", 4)) {
+ ret = create_service_thread(restart_usb_service, NULL);
+ } else if(!strncmp(name, "cs:", 5)) {
+ if (inoti_once == 0) {
+ ret = create_service_thread(inoti_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 SDB_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);
+ sdb_close(fd);
+ D("wait_for_state is done\n");
+}
+#endif
+
+#if SDB_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, "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 /* SDB_HOST */
diff --git a/src/socket_inaddr_any_server.c b/src/socket_inaddr_any_server.c
new file mode 100644
index 0000000..41d7491
--- /dev/null
+++ b/src/socket_inaddr_any_server.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+// libs/cutils/socket_inaddr_any_server.c
+
+#include "sockets.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+#define LISTEN_BACKLOG 4
+
+/* open listen() port on any interface */
+int socket_inaddr_any_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ int s, n;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
diff --git a/src/socket_local.h b/src/socket_local.h
new file mode 100644
index 0000000..dc274ac
--- /dev/null
+++ b/src/socket_local.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __SOCKET_LOCAL_H
+#define __SOCKET_LOCAL_H
+
+#define FILESYSTEM_SOCKET_PREFIX "/tmp/"
+#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
+
+/*
+ * Set up a given sockaddr_un, to have it refer to the given
+ * name in the given namespace. The namespace must be one
+ * of <code>ANDROID_SOCKET_NAMESPACE_ABSTRACT</code>,
+ * <code>ANDROID_SOCKET_NAMESPACE_RESERVED</code>, or
+ * <code>ANDROID_SOCKET_NAMESPACE_FILESYSTEM</code>. Upon success,
+ * the pointed at sockaddr_un is filled in and the pointed at
+ * socklen_t is set to indicate the final length. This function
+ * will fail if the namespace is invalid (not one of the indicated
+ * constants) or if the name is too long.
+ *
+ * @return 0 on success or -1 on failure
+ */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen);
+
+#endif
diff --git a/src/socket_local_client.c b/src/socket_local_client.c
new file mode 100644
index 0000000..63d17ef
--- /dev/null
+++ b/src/socket_local_client.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sockets.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+/* Documented in header file. */
+int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr, socklen_t *alen)
+{
+ memset (p_addr, 0, sizeof (*p_addr));
+ size_t namelen;
+
+ switch (namespaceId) {
+ case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
+#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+ namelen = strlen(name);
+
+ // Test with length +1 for the *initial* '\0'.
+ if ((namelen + 1) > sizeof(p_addr->sun_path)) {
+ goto error;
+ }
+
+ /*
+ * Note: The path in this case is *not* supposed to be
+ * '\0'-terminated. ("man 7 unix" for the gory details.)
+ */
+
+ p_addr->sun_path[0] = 0;
+ memcpy(p_addr->sun_path + 1, name, namelen);
+#else /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+ /* this OS doesn't have the Linux abstract namespace */
+
+ namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+#endif /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_RESERVED:
+ namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
+ strcat(p_addr->sun_path, name);
+ break;
+
+ case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:
+ namelen = strlen(name);
+ /* unix_path_max appears to be missing on linux */
+ if (namelen > sizeof(*p_addr)
+ - offsetof(struct sockaddr_un, sun_path) - 1) {
+ goto error;
+ }
+
+ strcpy(p_addr->sun_path, name);
+ break;
+ default:
+ // invalid namespace id
+ return -1;
+ }
+
+ p_addr->sun_family = AF_LOCAL;
+ *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
+ return 0;
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name" on fd
+ * returns same fd or -1 on error.
+ * fd is not closed on error. that's your job.
+ *
+ * Used by AndroidSocketImpl
+ */
+int socket_local_client_connect(int fd, const char *name, int namespaceId,
+ int type)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ goto error;
+ }
+
+ if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
+ goto error;
+ }
+
+ return fd;
+
+error:
+ return -1;
+}
+
+/**
+ * connect to peer named "name"
+ * returns fd or -1 on error
+ */
+int socket_local_client(const char *name, int namespaceId, int type)
+{
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if(s < 0) return -1;
+
+ if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/src/socket_local_server.c b/src/socket_local_server.c
new file mode 100644
index 0000000..489d17c
--- /dev/null
+++ b/src/socket_local_server.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+// libs/cutils/socket_local_server.c
+
+#include "sockets.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifdef HAVE_WINSOCK
+
+int socket_local_server(const char *name, int namespaceId, int type)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+#else /* !HAVE_WINSOCK */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include "socket_local.h"
+
+#define LISTEN_BACKLOG 4
+
+
+/**
+ * Binds a pre-created socket(AF_LOCAL) 's' to 'name'
+ * returns 's' on success, -1 on fail
+ *
+ * Does not call listen()
+ */
+int socket_local_server_bind(int s, const char *name, int namespaceId)
+{
+ struct sockaddr_un addr;
+ socklen_t alen;
+ int n;
+ int err;
+
+ err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
+
+ if (err < 0) {
+ return -1;
+ }
+
+ /* basically: if this is a filesystem path, unlink first */
+#ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
+ if (1) {
+#else
+ if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
+ || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
+#endif
+ /*ignore ENOENT*/
+ unlink(addr.sun_path);
+ }
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
+ return -1;
+ }
+
+ return s;
+
+}
+
+
+/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem
+ * namespace
+ *
+ * Returns fd on success, -1 on fail
+ */
+
+int socket_local_server(const char *name, int namespace, int type)
+{
+ int err;
+ int s;
+
+ s = socket(AF_LOCAL, type, 0);
+ if (s < 0) return -1;
+
+ err = socket_local_server_bind(s, name, namespace);
+
+ if (err < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+#endif /* !HAVE_WINSOCK */
diff --git a/src/socket_loopback_client.c b/src/socket_loopback_client.c
new file mode 100644
index 0000000..69fd87f
--- /dev/null
+++ b/src/socket_loopback_client.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+// libs/cutils/socket_loopback_client.c
+
+#include "sockets.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#endif
+
+/* Connect to port on the loopback IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_loopback_client(int port, int type)
+{
+ struct sockaddr_in addr;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/src/socket_loopback_server.c b/src/socket_loopback_server.c
new file mode 100644
index 0000000..e38731d
--- /dev/null
+++ b/src/socket_loopback_server.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+// libs/cutils/socket_loopback_server.c
+
+#include "sockets.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#define LISTEN_BACKLOG 4
+#define LOOPBACK_UP 1
+#define LOOPBACK_DOWN 0
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#endif
+#include "sysdeps.h"
+
+int get_loopback_status(void) {
+
+ int s;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ int ifcnt;
+ char buf[1024];
+ int i;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if(s < 0)
+ {
+ perror("socket");
+ return LOOPBACK_DOWN;
+ }
+
+ // query available interfaces
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if(ioctl(s, SIOCGIFCONF, &ifc) < 0)
+ {
+ perror("ioctl(SIOCGIFCONF)");
+ return LOOPBACK_DOWN;
+ }
+
+ // iterate the list of interfaces
+ ifr = ifc.ifc_req;
+ ifcnt = ifc.ifc_len / sizeof(struct ifreq);
+ for(i = 0; i < ifcnt; i++)
+ {
+ struct sockaddr_in *addr;
+ addr = (struct sockaddr_in *)&ifr->ifr_addr;
+
+ if (ntohl(addr->sin_addr.s_addr) == INADDR_LOOPBACK)
+ {
+ return LOOPBACK_UP;
+ }
+ }
+ return LOOPBACK_DOWN;
+}
+
+/* open listen() port on loopback interface */
+int socket_loopback_server(int port, int type)
+{
+ struct sockaddr_in addr;
+ int s, n;
+ int cnt_max = 30;
+
+ /* tizen specific */
+#if !SDB_HOST
+ // check the loopback interface has been up in 30 sec
+ while(cnt_max > 0) {
+ if(get_loopback_status() == LOOPBACK_DOWN) {
+ cnt_max--;
+ sdb_sleep_ms(1000);
+ }
+ else {
+ break;
+ }
+ }
+#endif
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if(cnt_max ==0)
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ else
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s < 0) return -1;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
+
+
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ sdb_close(s);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+
+ if (ret < 0) {
+ sdb_close(s);
+ return -1;
+ }
+ }
+
+ return s;
+}
+
diff --git a/src/socket_network_client.c b/src/socket_network_client.c
new file mode 100644
index 0000000..d2b436a
--- /dev/null
+++ b/src/socket_network_client.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+// libs/cutils/socket_network_client.c
+
+#include "sockets.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+
+#ifndef HAVE_WINSOCK
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+
+
+/* Connect to port on the IP interface. type is
+ * SOCK_STREAM or SOCK_DGRAM.
+ * return is a file descriptor or -1 on error
+ */
+int socket_network_client(const char *host, int port, int type)
+{
+ struct hostent *hp;
+ struct sockaddr_in addr;
+ int s;
+
+ hp = gethostbyname(host);
+ if(hp == 0) return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ s = socket(hp->h_addrtype, type, 0);
+ if(s < 0) return -1;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return s;
+
+}
+
diff --git a/src/sockets.c b/src/sockets.c
new file mode 100644
index 0000000..c621f47
--- /dev/null
+++ b/src/sockets.c
@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sdb.h"
+
+SDB_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,
+};
+
+/* the the list of currently closing local sockets.
+** these have no peer anymore, but still packets to
+** write to their fd.
+*/
+static asocket local_socket_closing_list = {
+ .next = &local_socket_closing_list,
+ .prev = &local_socket_closing_list,
+};
+
+asocket *find_local_socket(unsigned id)
+{
+ asocket *s;
+ asocket *result = NULL;
+
+ sdb_mutex_lock(&socket_list_lock);
+ for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+ if (s->id == id) {
+ result = s;
+ break;
+ }
+ }
+ sdb_mutex_unlock(&socket_list_lock);
+
+ return result;
+}
+
+static void
+insert_local_socket(asocket* s, asocket* list)
+{
+ s->next = list;
+ s->prev = s->next->prev;
+ s->prev->next = s;
+ s->next->prev = s;
+}
+
+
+void install_local_socket(asocket *s)
+{
+ sdb_mutex_lock(&socket_list_lock);
+
+ s->id = local_socket_next_id++;
+ insert_local_socket(s, &local_socket_list);
+
+ sdb_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.
+ */
+ sdb_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;
+ }
+ }
+ sdb_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 = sdb_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)
+{
+ sdb_mutex_lock(&socket_list_lock);
+ local_socket_close_locked(s);
+ sdb_mutex_unlock(&socket_list_lock);
+}
+
+// be sure to hold the socket list lock when calling this
+static void local_socket_destroy(asocket *s)
+{
+ apacket *p, *n;
+ int exit_on_close = s->exit_on_close;
+
+ D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
+
+ /* 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);
+ }
+ remove_socket(s);
+ free(s);
+
+ if (exit_on_close) {
+ D("local_socket_destroy: exiting\n");
+ exit(1);
+ }
+}
+
+
+static void local_socket_close_locked(asocket *s)
+{
+ D("entered. LS(%d) fd=%d\n", s->id, s->fd);
+ if(s->peer) {
+ D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
+ s->id, s->peer->id, s->peer->fd);
+ 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);
+ }
+ s->peer = 0;
+ }
+
+ /* If we are already closing, or if there are no
+ ** pending packets, destroy immediately
+ */
+ if (s->closing || s->pkt_first == NULL) {
+ int id = s->id;
+ local_socket_destroy(s);
+ D("LS(%d): closed\n", id);
+ return;
+ }
+
+ /* otherwise, put on the closing list
+ */
+ D("LS(%d): closing\n", s->id);
+ s->closing = 1;
+ fdevent_del(&s->fde, FDE_READ);
+ remove_socket(s);
+ D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
+ insert_local_socket(s, &local_socket_closing_list);
+}
+
+static void local_socket_event_func(int fd, unsigned ev, void *_s)
+{
+ asocket *s = _s;
+
+ D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
+
+ /* put the FDE_WRITE processing before the FDE_READ
+ ** in order to simplify the code.
+ */
+ if(ev & FDE_WRITE){
+ apacket *p;
+
+ while((p = s->pkt_first) != 0) {
+ while(p->len > 0) {
+ int r = sdb_write(fd, p->ptr, p->len);
+ if(r > 0) {
+ p->ptr += r;
+ p->len -= r;
+ continue;
+ }
+ if(r < 0) {
+ /* returning here is ok because FDE_READ will
+ ** be processed in the next iteration loop
+ */
+ if(errno == EAGAIN) return;
+ if(errno == EINTR) continue;
+ }
+ D(" closing after write because r=%d and errno is %d\n", r, errno);
+ 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);
+ }
+ }
+
+ /* if we sent the last packet of a closing socket,
+ ** we can now destroy it.
+ */
+ if (s->closing) {
+ D(" closing because 'closing' is set after write\n");
+ s->close(s);
+ return;
+ }
+
+ /* 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);
+ }
+
+
+ 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 = sdb_read(fd, x, avail);
+ D("LS(%d): post sdb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, 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;
+ }
+ D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
+ s->id, s->fd, r, is_eof, s->fde.force_eof);
+ if((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+ put_apacket(p);
+ } else {
+ p->len = MAX_PAYLOAD - avail;
+
+ r = s->peer->enqueue(s->peer, p);
+ D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r);
+
+ if(r < 0) {
+ /* error return means they closed us as a side-effect
+ ** and we must return immediately.
+ **
+ ** note that if we still have buffered packets, the
+ ** socket will be placed on the closing socket list.
+ ** this handler function will be called again
+ ** to process FDE_WRITE events.
+ */
+ 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);
+ }
+ }
+ /* Don't allow a forced eof if data is still there */
+ if((s->fde.force_eof && !r) || is_eof) {
+ D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof);
+ s->close(s);
+ }
+ }
+
+ 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);
+ D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
+
+ return;
+ }
+}
+
+asocket *create_local_socket(int fd)
+{
+ asocket *s = calloc(1, sizeof(asocket));
+ if (s == NULL) fatal("cannot allocate socket");
+ s->fd = fd;
+ s->enqueue = local_socket_enqueue;
+ s->ready = local_socket_ready;
+ s->close = local_socket_close;
+ install_local_socket(s);
+
+ 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 0 /* not support in tizen */
+#if !SDB_HOST
+ if (!strcmp(name,"jdwp")) {
+ return create_jdwp_service_socket();
+ }
+ if (!strcmp(name,"track-jdwp")) {
+ return create_jdwp_tracker_service_socket();
+ }
+#endif
+#endif
+ fd = service_to_fd(name);
+ if(fd < 0) return 0;
+
+ s = create_local_socket(fd);
+ D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
+
+#if !SDB_HOST
+ if ((!strncmp(name, "root:", 5) && getuid() != 0)
+ || !strncmp(name, "usb:", 4)
+ || !strncmp(name, "tcpip:", 6)) {
+ D("LS(%d): enabling exit_on_close\n", s->id);
+ s->exit_on_close = 1;
+ }
+#endif
+
+ return s;
+}
+
+#if SDB_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 /* SDB_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("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
+ s->id, s->fd, s->peer->fd);
+ 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("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
+ s->id, s->fd, s->peer->fd);
+ 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("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+ s->id, s->fd, s->peer?s->peer->fd:-1);
+ apacket *p = get_apacket();
+ p->msg.command = A_CLSE;
+ if(s->peer) {
+ p->msg.arg0 = s->peer->id;
+ s->peer->peer = 0;
+ D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
+ s->id, s->peer->id, s->peer->fd);
+ 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 == NULL) 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 RS(%d) fd=%d\n", s->id, s->fd);
+ 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;
+ sdb_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;
+}
+
+/* skip_host_serial return the position in a string
+ skipping over the 'serial' parameter in the SDB protocol,
+ where parameter string may be a host:port string containing
+ the protocol delimiter (colon). */
+char *skip_host_serial(char *service) {
+ char *first_colon, *serial_end;
+
+ first_colon = strchr(service, ':');
+ if (!first_colon) {
+ /* No colon in service string. */
+ return NULL;
+ }
+ serial_end = first_colon;
+ if (isdigit(serial_end[1])) {
+ serial_end++;
+ while ((*serial_end) && isdigit(*serial_end)) {
+ serial_end++;
+ }
+ if ((*serial_end) != ':') {
+ // Something other than numbers was found, reset the end.
+ serial_end = first_colon;
+ }
+ }
+ return serial_end;
+}
+
+static int smart_socket_enqueue(asocket *s, apacket *p)
+{
+ unsigned len;
+#if SDB_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 SDB_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:" and could be a host:port string.
+ serial_end = skip_host_serial(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.
+ */
+ sdb_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 /* !SDB_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);
+ s->peer = 0;
+ }
+ 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 == NULL) fatal("cannot allocate socket");
+ 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);
+}
diff --git a/src/sockets.dia b/src/sockets.dia
new file mode 100644
index 0000000..c626f20
--- /dev/null
+++ b/src/sockets.dia
Binary files differ
diff --git a/src/sockets.h b/src/sockets.h
new file mode 100644
index 0000000..8e8196a
--- /dev/null
+++ b/src/sockets.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 */
diff --git a/src/sysdeps.h b/src/sysdeps.h
new file mode 100644
index 0000000..f2b4ad8
--- /dev/null
+++ b/src/sysdeps.h
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 SDB
+ * they're related to threads, sockets and file descriptors
+ */
+#ifndef _SDB_SYSDEPS_H
+#define _SDB_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 sdb_mutex_t;
+
+#define SDB_MUTEX_DEFINE(x) sdb_mutex_t x
+
+/* declare all mutexes */
+/* For win32, sdb_sysdeps_init() will do the mutex runtime initialization. */
+#define SDB_MUTEX(x) extern sdb_mutex_t x;
+#include "mutex_list.h"
+
+extern void sdb_sysdeps_init(void);
+
+static __inline__ char* ansi_to_utf8(const char *str)
+{
+ int len;
+ char *utf8;
+ wchar_t *unicode;
+
+ //ANSI( MutiByte ) -> UCS-2( WideByte ) -> UTF-8( MultiByte )
+ len = MultiByteToWideChar (CP_ACP, 0, str, -1, NULL, 0);
+ unicode = (wchar_t *)calloc (len+1, sizeof(wchar_t));
+ MultiByteToWideChar (CP_ACP, 0, str, -1, unicode, len);
+
+ len = WideCharToMultiByte (CP_UTF8, 0, unicode, -1, NULL, 0, NULL, NULL);
+ utf8 = (char *)calloc (len+1, sizeof(char));
+
+ WideCharToMultiByte (CP_UTF8, 0, unicode, -1, utf8, len, NULL, NULL);
+ free (unicode);
+
+ return utf8;
+}
+
+static __inline__ void sdb_mutex_lock( sdb_mutex_t* lock )
+{
+ EnterCriticalSection( lock );
+}
+
+static __inline__ void sdb_mutex_unlock( sdb_mutex_t* lock )
+{
+ LeaveCriticalSection( lock );
+}
+
+typedef struct { unsigned tid; } sdb_thread_t;
+
+typedef void* (*sdb_thread_func_t)(void* arg);
+
+typedef void (*win_thread_func_t)(void* arg);
+
+static __inline__ int sdb_thread_create( sdb_thread_t *thread, sdb_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 sdb_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 sdb_mkdir(const char* path, int mode)
+{
+ return _mkdir(path);
+}
+#undef mkdir
+#define mkdir ___xxx_mkdir
+
+extern int sdb_open(const char* path, int options);
+extern int sdb_creat(const char* path, int mode);
+extern int sdb_read(int fd, void* buf, int len);
+extern int sdb_write(int fd, const void* buf, int len);
+extern int sdb_lseek(int fd, int pos, int where);
+extern int sdb_shutdown(int fd);
+extern int sdb_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 sdb_open_mode(const char* path, int options, int mode)
+{
+ return sdb_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 "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;
+ int force_eof;
+
+ unsigned short state;
+ unsigned short events;
+
+ fd_func func;
+ void *arg;
+};
+
+static __inline__ void sdb_sleep_ms( int mseconds )
+{
+ Sleep( mseconds );
+}
+
+extern int sdb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen);
+
+#undef accept
+#define accept ___xxx_accept
+
+static __inline__ int sdb_socket_setbufsize( int fd, int bufsize )
+{
+ int opt = bufsize;
+ return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt));
+}
+
+extern int sdb_socketpair( int sv[2] );
+
+static __inline__ char* sdb_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* sdb_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 sdb_is_absolute_host_path( const char* path )
+{
+ return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
+}
+
+#else /* !_WIN32 a.k.a. Unix */
+
+#include "fdevent.h"
+#include "sockets.h"
+#include "properties.h"
+// tizen specific #include <cutils/misc.h>
+#include <stdio.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 sdb_mutex_t;
+
+#define SDB_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#define sdb_mutex_init pthread_mutex_init
+#define sdb_mutex_lock pthread_mutex_lock
+#define sdb_mutex_unlock pthread_mutex_unlock
+#define sdb_mutex_destroy pthread_mutex_destroy
+
+#define SDB_MUTEX_DEFINE(m) sdb_mutex_t m = PTHREAD_MUTEX_INITIALIZER
+
+#define sdb_cond_t pthread_cond_t
+#define sdb_cond_init pthread_cond_init
+#define sdb_cond_wait pthread_cond_wait
+#define sdb_cond_broadcast pthread_cond_broadcast
+#define sdb_cond_signal pthread_cond_signal
+#define sdb_cond_destroy pthread_cond_destroy
+
+/* declare all mutexes */
+#define SDB_MUTEX(x) extern sdb_mutex_t x;
+#include "mutex_list.h"
+
+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 sdb_open_mode( const char* pathname, int options, int mode )
+{
+ return open( pathname, options, mode );
+}
+
+
+static __inline__ int sdb_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 sdb_shutdown(int fd)
+{
+ return shutdown(fd, SHUT_RDWR);
+}
+#undef shutdown
+#define shutdown ____xxx_shutdown
+
+static __inline__ int sdb_close(int fd)
+{
+ return close(fd);
+}
+#undef close
+#define close ____xxx_close
+
+
+static __inline__ int sdb_read(int fd, void* buf, size_t len)
+{
+ return read(fd, buf, len);
+}
+
+#undef read
+#define read ___xxx_read
+
+static __inline__ int sdb_write(int fd, const void* buf, size_t len)
+{
+ return write(fd, buf, len);
+}
+#undef write
+#define write ___xxx_write
+
+static __inline__ int sdb_lseek(int fd, int pos, int where)
+{
+ return lseek(fd, pos, where);
+}
+#undef lseek
+#define lseek ___xxx_lseek
+
+static __inline__ int sdb_unlink(const char* path)
+{
+ return unlink(path);
+}
+#undef unlink
+#define unlink ___xxx_unlink
+
+static __inline__ int sdb_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 sdb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ int fd;
+
+ fd = accept(serverfd, addr, addrlen);
+ if (fd >= 0)
+ close_on_exec(fd);
+
+ return fd;
+}
+
+#undef accept
+#define accept ___xxx_accept
+
+#define unix_read sdb_read
+#define unix_write sdb_write
+#define unix_close sdb_close
+
+typedef pthread_t sdb_thread_t;
+
+typedef void* (*sdb_thread_func_t)( void* arg );
+
+static __inline__ int sdb_thread_create( sdb_thread_t *pthread, sdb_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 sdb_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 sdb_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 sdb_sleep_ms( int mseconds )
+{
+ usleep( mseconds*1000 );
+}
+
+static __inline__ int sdb_mkdir(const char* path, int mode)
+{
+ return mkdir(path, mode);
+}
+#undef mkdir
+#define mkdir ___xxx_mkdir
+
+static __inline__ void sdb_sysdeps_init(void)
+{
+}
+
+static __inline__ char* sdb_dirstart(const char* path)
+{
+ return strchr(path, '/');
+}
+
+static __inline__ char* sdb_dirstop(const char* path)
+{
+ return strrchr(path, '/');
+}
+
+static __inline__ int sdb_is_absolute_host_path( const char* path )
+{
+ return path[0] == '/';
+}
+
+static __inline__ char* ansi_to_utf8(const char *str)
+{
+ // Not implement!
+ // If need, use iconv later event though unix system is using utf8 encoding.
+ int len;
+ char *utf8;
+
+ len = strlen(str);
+ utf8 = (char *)calloc(len+1, sizeof(char));
+ strcpy(utf8, str);
+ return utf8;
+}
+
+#endif /* !_WIN32 */
+
+#endif /* _SDB_SYSDEPS_H */
diff --git a/src/sysdeps_win32.c b/src/sysdeps_win32.c
new file mode 100644
index 0000000..97399bf
--- /dev/null
+++ b/src/sysdeps_win32.c
@@ -0,0 +1,1982 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sysdeps.h"
+#include <windows.h>
+#include <winsock2.h>
+#include <stdio.h>
+#include <errno.h>
+#define TRACE_TAG TRACE_SYSDEPS
+#include "sdb.h"
+
+extern void fatal(const char *fmt, ...);
+
+#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replaces libs/cutils/load_file.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+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 + 1 );
+ if (data == NULL) {
+ D("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 )
+ {
+ D("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;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** common file descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+typedef const struct FHClassRec_* FHClass;
+
+typedef struct FHRec_* FH;
+
+typedef struct EventHookRec_* EventHook;
+
+typedef struct FHClassRec_
+{
+ void (*_fh_init) ( FH f );
+ int (*_fh_close)( FH f );
+ int (*_fh_lseek)( FH f, int pos, int origin );
+ int (*_fh_read) ( FH f, void* buf, int len );
+ int (*_fh_write)( FH f, const void* buf, int len );
+ void (*_fh_hook) ( FH f, int events, EventHook hook );
+
+} FHClassRec;
+
+/* used to emulate unix-domain socket pairs */
+typedef struct SocketPairRec_* SocketPair;
+
+typedef struct FHRec_
+{
+ FHClass clazz;
+ int used;
+ int eof;
+ union {
+ HANDLE handle;
+ SOCKET socket;
+ SocketPair pair;
+ } u;
+
+ HANDLE event;
+ int mask;
+
+ char name[32];
+
+} FHRec;
+
+#define fh_handle u.handle
+#define fh_socket u.socket
+#define fh_pair u.pair
+
+#define WIN32_FH_BASE 100
+
+#define WIN32_MAX_FHS 128
+
+static sdb_mutex_t _win32_lock;
+static FHRec _win32_fhs[ WIN32_MAX_FHS ];
+static int _win32_fh_count;
+
+static FH
+_fh_from_int( int fd )
+{
+ FH f;
+
+ fd -= WIN32_FH_BASE;
+
+ if (fd < 0 || fd >= _win32_fh_count) {
+ D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+ errno = EBADF;
+ return NULL;
+ }
+
+ f = &_win32_fhs[fd];
+
+ if (f->used == 0) {
+ D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+ errno = EBADF;
+ return NULL;
+ }
+
+ return f;
+}
+
+
+static int
+_fh_to_int( FH f )
+{
+ if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
+ return (int)(f - _win32_fhs) + WIN32_FH_BASE;
+
+ return -1;
+}
+
+static FH
+_fh_alloc( FHClass clazz )
+{
+ int nn;
+ FH f = NULL;
+
+ sdb_mutex_lock( &_win32_lock );
+
+ if (_win32_fh_count < WIN32_MAX_FHS) {
+ f = &_win32_fhs[ _win32_fh_count++ ];
+ goto Exit;
+ }
+
+ for (nn = 0; nn < WIN32_MAX_FHS; nn++) {
+ if ( _win32_fhs[nn].clazz == NULL) {
+ f = &_win32_fhs[nn];
+ goto Exit;
+ }
+ }
+ D( "_fh_alloc: no more free file descriptors\n" );
+Exit:
+ if (f) {
+ f->clazz = clazz;
+ f->used = 1;
+ f->eof = 0;
+ clazz->_fh_init(f);
+ }
+ sdb_mutex_unlock( &_win32_lock );
+ return f;
+}
+
+
+static int
+_fh_close( FH f )
+{
+ if ( f->used ) {
+ f->clazz->_fh_close( f );
+ f->used = 0;
+ f->eof = 0;
+ f->clazz = NULL;
+ }
+ return 0;
+}
+
+/* forward definitions */
+static const FHClassRec _fh_file_class;
+static const FHClassRec _fh_socket_class;
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void
+_fh_file_init( FH f )
+{
+ f->fh_handle = INVALID_HANDLE_VALUE;
+}
+
+static int
+_fh_file_close( FH f )
+{
+ CloseHandle( f->fh_handle );
+ f->fh_handle = INVALID_HANDLE_VALUE;
+ return 0;
+}
+
+static int
+_fh_file_read( FH f, void* buf, int len )
+{
+ DWORD read_bytes;
+
+ if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
+ D( "sdb_read: could not read %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (read_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)read_bytes;
+}
+
+static int
+_fh_file_write( FH f, const void* buf, int len )
+{
+ DWORD wrote_bytes;
+
+ if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
+ D( "sdb_file_write: could not write %d bytes from %s\n", len, f->name );
+ errno = EIO;
+ return -1;
+ } else if (wrote_bytes < (DWORD)len) {
+ f->eof = 1;
+ }
+ return (int)wrote_bytes;
+}
+
+static int
+_fh_file_lseek( FH f, int pos, int origin )
+{
+ DWORD method;
+ DWORD result;
+
+ switch (origin)
+ {
+ case SEEK_SET: method = FILE_BEGIN; break;
+ case SEEK_CUR: method = FILE_CURRENT; break;
+ case SEEK_END: method = FILE_END; break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ result = SetFilePointer( f->fh_handle, pos, NULL, method );
+ if (result == INVALID_SET_FILE_POINTER) {
+ errno = EIO;
+ return -1;
+ } else {
+ f->eof = 0;
+ }
+ return (int)result;
+}
+
+static void _fh_file_hook( FH f, int event, EventHook eventhook ); /* forward */
+
+static const FHClassRec _fh_file_class =
+{
+ _fh_file_init,
+ _fh_file_close,
+ _fh_file_lseek,
+ _fh_file_read,
+ _fh_file_write,
+ _fh_file_hook
+};
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** file-based descriptor handling *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+int sdb_open(const char* path, int options)
+{
+ FH f;
+
+ DWORD desiredAccess = 0;
+ DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+ switch (options) {
+ case O_RDONLY:
+ desiredAccess = GENERIC_READ;
+ break;
+ case O_WRONLY:
+ desiredAccess = GENERIC_WRITE;
+ break;
+ case O_RDWR:
+ desiredAccess = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ D("sdb_open: invalid options (0x%0x)\n", options);
+ errno = EINVAL;
+ return -1;
+ }
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
+ 0, NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ _fh_close(f);
+ D( "sdb_open: could not open '%s':", path );
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error\n" );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "sdb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+/* ignore mode on Win32 */
+int sdb_creat(const char* path, int mode)
+{
+ FH f;
+
+ f = _fh_alloc( &_fh_file_class );
+ if ( !f ) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+ _fh_close(f);
+ D( "sdb_creat: could not open '%s':", path );
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ D( "file not found\n" );
+ errno = ENOENT;
+ return -1;
+
+ case ERROR_PATH_NOT_FOUND:
+ D( "path not found\n" );
+ errno = ENOTDIR;
+ return -1;
+
+ default:
+ D( "unknown error\n" );
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
+ D( "sdb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int sdb_read(int fd, void* buf, int len)
+{
+ FH f = _fh_from_int(fd);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_read( f, buf, len );
+}
+
+
+int sdb_write(int fd, const void* buf, int len)
+{
+ FH f = _fh_from_int(fd);
+
+ if (f == NULL) {
+ return -1;
+ }
+
+ return f->clazz->_fh_write(f, buf, len);
+}
+
+
+int sdb_lseek(int fd, int pos, int where)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ return f->clazz->_fh_lseek(f, pos, where);
+}
+
+
+int sdb_shutdown(int fd)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ D( "sdb_shutdown: %s\n", f->name);
+ shutdown( f->fh_socket, SD_BOTH );
+ return 0;
+}
+
+
+int sdb_close(int fd)
+{
+ FH f = _fh_from_int(fd);
+
+ if (!f) {
+ return -1;
+ }
+
+ D( "sdb_close: %s\n", f->name);
+ _fh_close(f);
+ return 0;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** socket-based file descriptors *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+static void
+_socket_set_errno( void )
+{
+ switch (WSAGetLastError()) {
+ case 0: errno = 0; break;
+ case WSAEWOULDBLOCK: errno = EAGAIN; break;
+ case WSAEINTR: errno = EINTR; break;
+ default:
+ D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
+ errno = EINVAL;
+ }
+}
+
+static void
+_fh_socket_init( FH f )
+{
+ f->fh_socket = INVALID_SOCKET;
+ f->event = WSACreateEvent();
+ f->mask = 0;
+}
+
+static int
+_fh_socket_close( FH f )
+{
+ /* gently tell any peer that we're closing the socket */
+ shutdown( f->fh_socket, SD_BOTH );
+ closesocket( f->fh_socket );
+ f->fh_socket = INVALID_SOCKET;
+ CloseHandle( f->event );
+ f->mask = 0;
+ return 0;
+}
+
+static int
+_fh_socket_lseek( FH f, int pos, int origin )
+{
+ errno = EPIPE;
+ return -1;
+}
+
+static int
+_fh_socket_read( FH f, void* buf, int len )
+{
+ int result = recv( f->fh_socket, buf, len, 0 );
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+static int
+_fh_socket_write( FH f, const void* buf, int len )
+{
+ int result = send( f->fh_socket, buf, len, 0 );
+ if (result == SOCKET_ERROR) {
+ _socket_set_errno();
+ result = -1;
+ }
+ return result;
+}
+
+static void _fh_socket_hook( FH f, int event, EventHook hook ); /* forward */
+
+static const FHClassRec _fh_socket_class =
+{
+ _fh_socket_init,
+ _fh_socket_close,
+ _fh_socket_lseek,
+ _fh_socket_read,
+ _fh_socket_write,
+ _fh_socket_hook
+};
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** replacement for libs/cutils/socket_xxxx.c *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#include <winsock2.h>
+
+static int _winsock_init;
+
+static void
+_cleanup_winsock( void )
+{
+ WSACleanup();
+}
+
+static void
+_init_winsock( void )
+{
+ if (!_winsock_init) {
+ WSADATA wsaData;
+ int rc = WSAStartup( MAKEWORD(2,2), &wsaData);
+ if (rc != 0) {
+ fatal( "sdb: could not initialize Winsock\n" );
+ }
+ atexit( _cleanup_winsock );
+ _winsock_init = 1;
+ }
+}
+
+int socket_loopback_client(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ D("socket_loopback_client: could not create socket\n" );
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port );
+ _fh_close(f);
+ return -1;
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#define LISTEN_BACKLOG 4
+
+int socket_loopback_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f) {
+ return -1;
+ }
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) return -1;
+
+ f->fh_socket = s;
+
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_network_client(const char *host, int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct hostent *hp;
+ struct sockaddr_in addr;
+ SOCKET s;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ hp = gethostbyname(host);
+ if(hp == 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+ s = socket(hp->h_addrtype, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+ f->fh_socket = s;
+
+ if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+
+int socket_inaddr_any_server(int port, int type)
+{
+ FH f = _fh_alloc( &_fh_socket_class );
+ struct sockaddr_in addr;
+ SOCKET s;
+ int n;
+
+ if (!f)
+ return -1;
+
+ if (!_winsock_init)
+ _init_winsock();
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ s = socket(AF_INET, type, 0);
+ if(s == INVALID_SOCKET) {
+ _fh_close(f);
+ return -1;
+ }
+
+ f->fh_socket = s;
+ n = 1;
+ setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+
+ if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ _fh_close(f);
+ return -1;
+ }
+
+ if (type == SOCK_STREAM) {
+ int ret;
+
+ ret = listen(s, LISTEN_BACKLOG);
+ if (ret < 0) {
+ _fh_close(f);
+ return -1;
+ }
+ }
+ snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port );
+ D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) );
+ return _fh_to_int(f);
+}
+
+#undef accept
+int sdb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
+{
+ FH serverfh = _fh_from_int(serverfd);
+ FH fh;
+
+ if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
+ D( "sdb_socket_accept: invalid fd %d\n", serverfd );
+ return -1;
+ }
+
+ fh = _fh_alloc( &_fh_socket_class );
+ if (!fh) {
+ D( "sdb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+ return -1;
+ }
+
+ fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
+ if (fh->fh_socket == INVALID_SOCKET) {
+ _fh_close( fh );
+ D( "sdb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+ return -1;
+ }
+
+ snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name );
+ D( "sdb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) );
+ return _fh_to_int(fh);
+}
+
+
+void disable_tcp_nagle(int fd)
+{
+ FH fh = _fh_from_int(fd);
+ int on;
+
+ if ( !fh || fh->clazz != &_fh_socket_class )
+ return;
+
+ setsockopt( fh->fh_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on) );
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** emulated socketpairs *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+/* we implement socketpairs directly in use space for the following reasons:
+ * - it avoids copying data from/to the Nt kernel
+ * - it allows us to implement fdevent hooks easily and cheaply, something
+ * that is not possible with standard Win32 pipes !!
+ *
+ * basically, we use two circular buffers, each one corresponding to a given
+ * direction.
+ *
+ * each buffer is implemented as two regions:
+ *
+ * region A which is (a_start,a_end)
+ * region B which is (0, b_end) with b_end <= a_start
+ *
+ * an empty buffer has: a_start = a_end = b_end = 0
+ *
+ * a_start is the pointer where we start reading data
+ * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
+ * then you start writing at b_end
+ *
+ * the buffer is full when b_end == a_start && a_end == BUFFER_SIZE
+ *
+ * there is room when b_end < a_start || a_end < BUFER_SIZE
+ *
+ * when reading, a_start is incremented, it a_start meets a_end, then
+ * we do: a_start = 0, a_end = b_end, b_end = 0, and keep going on..
+ */
+
+#define BIP_BUFFER_SIZE 4096
+
+#if 0
+#include <stdio.h>
+# define BIPD(x) D x
+# define BIPDUMP bip_dump_hex
+
+static void bip_dump_hex( const unsigned char* ptr, size_t len )
+{
+ int nn, len2 = len;
+
+ if (len2 > 8) len2 = 8;
+
+ for (nn = 0; nn < len2; nn++)
+ printf("%02x", ptr[nn]);
+ printf(" ");
+
+ for (nn = 0; nn < len2; nn++) {
+ int c = ptr[nn];
+ if (c < 32 || c > 127)
+ c = '.';
+ printf("%c", c);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+#else
+# define BIPD(x) do {} while (0)
+# define BIPDUMP(p,l) BIPD(p)
+#endif
+
+typedef struct BipBufferRec_
+{
+ int a_start;
+ int a_end;
+ int b_end;
+ int fdin;
+ int fdout;
+ int closed;
+ int can_write; /* boolean */
+ HANDLE evt_write; /* event signaled when one can write to a buffer */
+ int can_read; /* boolean */
+ HANDLE evt_read; /* event signaled when one can read from a buffer */
+ CRITICAL_SECTION lock;
+ unsigned char buff[ BIP_BUFFER_SIZE ];
+
+} BipBufferRec, *BipBuffer;
+
+static void
+bip_buffer_init( BipBuffer buffer )
+{
+ D( "bit_buffer_init %p\n", buffer );
+ buffer->a_start = 0;
+ buffer->a_end = 0;
+ buffer->b_end = 0;
+ buffer->can_write = 1;
+ buffer->can_read = 0;
+ buffer->fdin = 0;
+ buffer->fdout = 0;
+ buffer->closed = 0;
+ buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
+ buffer->evt_read = CreateEvent( NULL, TRUE, FALSE, NULL );
+ InitializeCriticalSection( &buffer->lock );
+}
+
+static void
+bip_buffer_close( BipBuffer bip )
+{
+ bip->closed = 1;
+
+ if (!bip->can_read) {
+ SetEvent( bip->evt_read );
+ }
+ if (!bip->can_write) {
+ SetEvent( bip->evt_write );
+ }
+}
+
+static void
+bip_buffer_done( BipBuffer bip )
+{
+ BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+ CloseHandle( bip->evt_read );
+ CloseHandle( bip->evt_write );
+ DeleteCriticalSection( &bip->lock );
+}
+
+static int
+bip_buffer_write( BipBuffer bip, const void* src, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+ BIPDUMP( src, len );
+
+ EnterCriticalSection( &bip->lock );
+
+ while (!bip->can_write) {
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ /* spinlocking here is probably unfair, but let's live with it */
+ ret = WaitForSingleObject( bip->evt_write, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* buffer probably closed */
+ D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+ }
+
+ BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = BIP_BUFFER_SIZE - bip->a_end;
+ if (avail > 0)
+ {
+ /* we can append to region A */
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->a_end, src, avail );
+ src += avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_end += avail;
+ if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ goto Exit;
+ }
+ }
+
+ if (len == 0)
+ goto Exit;
+
+ avail = bip->a_start - bip->b_end;
+ assert( avail > 0 ); /* since can_write is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( bip->buff + bip->b_end, src, avail );
+ count += avail;
+ bip->b_end += avail;
+
+ if (bip->b_end == bip->a_start) {
+ bip->can_write = 0;
+ ResetEvent( bip->evt_write );
+ }
+
+Exit:
+ assert( count > 0 );
+
+ if ( !bip->can_read ) {
+ bip->can_read = 1;
+ SetEvent( bip->evt_read );
+ }
+
+ BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+ }
+
+static int
+bip_buffer_read( BipBuffer bip, void* dst, int len )
+{
+ int avail, count = 0;
+
+ if (len <= 0)
+ return 0;
+
+ BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ EnterCriticalSection( &bip->lock );
+ while ( !bip->can_read )
+ {
+#if 0
+ LeaveCriticalSection( &bip->lock );
+ errno = EAGAIN;
+ return -1;
+#else
+ int ret;
+ LeaveCriticalSection( &bip->lock );
+
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+
+ ret = WaitForSingleObject( bip->evt_read, INFINITE );
+ if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
+ D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
+ return 0;
+ }
+ if (bip->closed) {
+ errno = EPIPE;
+ return -1;
+ }
+ EnterCriticalSection( &bip->lock );
+#endif
+ }
+
+ BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+
+ avail = bip->a_end - bip->a_start;
+ assert( avail > 0 ); /* since can_read is TRUE */
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( dst, bip->buff + bip->a_start, avail );
+ dst += avail;
+ count += avail;
+ len -= avail;
+
+ bip->a_start += avail;
+ if (bip->a_start < bip->a_end)
+ goto Exit;
+
+ bip->a_start = 0;
+ bip->a_end = bip->b_end;
+ bip->b_end = 0;
+
+ avail = bip->a_end;
+ if (avail > 0) {
+ if (avail > len)
+ avail = len;
+ memcpy( dst, bip->buff, avail );
+ count += avail;
+ bip->a_start += avail;
+
+ if ( bip->a_start < bip->a_end )
+ goto Exit;
+
+ bip->a_start = bip->a_end = 0;
+ }
+
+ bip->can_read = 0;
+ ResetEvent( bip->evt_read );
+
+Exit:
+ assert( count > 0 );
+
+ if (!bip->can_write ) {
+ bip->can_write = 1;
+ SetEvent( bip->evt_write );
+ }
+
+ BIPDUMP( (const unsigned char*)dst - count, count );
+ BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+ bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
+ LeaveCriticalSection( &bip->lock );
+
+ return count;
+}
+
+typedef struct SocketPairRec_
+{
+ BipBufferRec a2b_bip;
+ BipBufferRec b2a_bip;
+ FH a_fd;
+ int used;
+
+} SocketPairRec;
+
+void _fh_socketpair_init( FH f )
+{
+ f->fh_pair = NULL;
+}
+
+static int
+_fh_socketpair_close( FH f )
+{
+ if ( f->fh_pair ) {
+ SocketPair pair = f->fh_pair;
+
+ if ( f == pair->a_fd ) {
+ pair->a_fd = NULL;
+ }
+
+ bip_buffer_close( &pair->b2a_bip );
+ bip_buffer_close( &pair->a2b_bip );
+
+ if ( --pair->used == 0 ) {
+ bip_buffer_done( &pair->b2a_bip );
+ bip_buffer_done( &pair->a2b_bip );
+ free( pair );
+ }
+ f->fh_pair = NULL;
+ }
+ return 0;
+}
+
+static int
+_fh_socketpair_lseek( FH f, int pos, int origin )
+{
+ errno = ESPIPE;
+ return -1;
+}
+
+static int
+_fh_socketpair_read( FH f, void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->b2a_bip;
+ else
+ bip = &pair->a2b_bip;
+
+ return bip_buffer_read( bip, buf, len );
+}
+
+static int
+_fh_socketpair_write( FH f, const void* buf, int len )
+{
+ SocketPair pair = f->fh_pair;
+ BipBuffer bip;
+
+ if (!pair)
+ return -1;
+
+ if ( f == pair->a_fd )
+ bip = &pair->a2b_bip;
+ else
+ bip = &pair->b2a_bip;
+
+ return bip_buffer_write( bip, buf, len );
+}
+
+
+static void _fh_socketpair_hook( FH f, int event, EventHook hook ); /* forward */
+
+static const FHClassRec _fh_socketpair_class =
+{
+ _fh_socketpair_init,
+ _fh_socketpair_close,
+ _fh_socketpair_lseek,
+ _fh_socketpair_read,
+ _fh_socketpair_write,
+ _fh_socketpair_hook
+};
+
+
+int sdb_socketpair( int sv[2] )
+{
+ FH fa, fb;
+ SocketPair pair;
+
+ fa = _fh_alloc( &_fh_socketpair_class );
+ fb = _fh_alloc( &_fh_socketpair_class );
+
+ if (!fa || !fb)
+ goto Fail;
+
+ pair = malloc( sizeof(*pair) );
+ if (pair == NULL) {
+ D("sdb_socketpair: not enough memory to allocate pipes\n" );
+ goto Fail;
+ }
+
+ bip_buffer_init( &pair->a2b_bip );
+ bip_buffer_init( &pair->b2a_bip );
+
+ fa->fh_pair = pair;
+ fb->fh_pair = pair;
+ pair->used = 2;
+ pair->a_fd = fa;
+
+ sv[0] = _fh_to_int(fa);
+ sv[1] = _fh_to_int(fb);
+
+ pair->a2b_bip.fdin = sv[0];
+ pair->a2b_bip.fdout = sv[1];
+ pair->b2a_bip.fdin = sv[1];
+ pair->b2a_bip.fdout = sv[0];
+
+ snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
+ snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
+ D( "sdb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+ return 0;
+
+Fail:
+ _fh_close(fb);
+ _fh_close(fa);
+ return -1;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/***** *****/
+/***** fdevents emulation *****/
+/***** *****/
+/***** this is a very simple implementation, we rely on the fact *****/
+/***** that SDB doesn't use FDE_ERROR. *****/
+/***** *****/
+/**************************************************************************/
+/**************************************************************************/
+
+#define FATAL(x...) fatal(__FUNCTION__, x)
+
+#if DEBUG
+static void dump_fde(fdevent *fde, const char *info)
+{
+ fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
+ fde->state & FDE_READ ? 'R' : ' ',
+ fde->state & FDE_WRITE ? 'W' : ' ',
+ fde->state & FDE_ERROR ? 'E' : ' ',
+ info);
+}
+#else
+#define dump_fde(fde, info) do { } while(0)
+#endif
+
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+#define FDE_CREATED 0x0400
+
+static void fdevent_plist_enqueue(fdevent *node);
+static void fdevent_plist_remove(fdevent *node);
+static fdevent *fdevent_plist_dequeue(void);
+
+static fdevent list_pending = {
+ .next = &list_pending,
+ .prev = &list_pending,
+};
+
+static fdevent **fd_table = 0;
+static int fd_table_max = 0;
+
+typedef struct EventLooperRec_* EventLooper;
+
+typedef struct EventHookRec_
+{
+ EventHook next;
+ FH fh;
+ HANDLE h;
+ int wanted; /* wanted event flags */
+ int ready; /* ready event flags */
+ void* aux;
+ void (*prepare)( EventHook hook );
+ int (*start) ( EventHook hook );
+ void (*stop) ( EventHook hook );
+ int (*check) ( EventHook hook );
+ int (*peek) ( EventHook hook );
+} EventHookRec;
+
+static EventHook _free_hooks;
+
+static EventHook
+event_hook_alloc( FH fh )
+{
+ EventHook hook = _free_hooks;
+ if (hook != NULL)
+ _free_hooks = hook->next;
+ else {
+ hook = malloc( sizeof(*hook) );
+ if (hook == NULL)
+ fatal( "could not allocate event hook\n" );
+ }
+ hook->next = NULL;
+ hook->fh = fh;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->h = INVALID_HANDLE_VALUE;
+ hook->aux = NULL;
+
+ hook->prepare = NULL;
+ hook->start = NULL;
+ hook->stop = NULL;
+ hook->check = NULL;
+ hook->peek = NULL;
+
+ return hook;
+}
+
+static void
+event_hook_free( EventHook hook )
+{
+ hook->fh = NULL;
+ hook->wanted = 0;
+ hook->ready = 0;
+ hook->next = _free_hooks;
+ _free_hooks = hook;
+}
+
+
+static void
+event_hook_signal( EventHook hook )
+{
+ FH f = hook->fh;
+ int fd = _fh_to_int(f);
+ fdevent* fde = fd_table[ fd - WIN32_FH_BASE ];
+
+ if (fde != NULL && fde->fd == fd) {
+ if ((fde->state & FDE_PENDING) == 0) {
+ fde->state |= FDE_PENDING;
+ fdevent_plist_enqueue( fde );
+ }
+ fde->events |= hook->wanted;
+ }
+}
+
+
+#define MAX_LOOPER_HANDLES WIN32_MAX_FHS
+
+typedef struct EventLooperRec_
+{
+ EventHook hooks;
+ HANDLE htab[ MAX_LOOPER_HANDLES ];
+ int htab_count;
+
+} EventLooperRec;
+
+static EventHook*
+event_looper_find_p( EventLooper looper, FH fh )
+{
+ EventHook *pnode = &looper->hooks;
+ EventHook node = *pnode;
+ for (;;) {
+ if ( node == NULL || node->fh == fh )
+ break;
+ pnode = &node->next;
+ node = *pnode;
+ }
+ return pnode;
+}
+
+static void
+event_looper_hook( EventLooper looper, int fd, int events )
+{
+ FH f = _fh_from_int(fd);
+ EventHook *pnode;
+ EventHook node;
+
+ if (f == NULL) /* invalid arg */ {
+ D("event_looper_hook: invalid fd=%d\n", fd);
+ return;
+ }
+
+ pnode = event_looper_find_p( looper, f );
+ node = *pnode;
+ if ( node == NULL ) {
+ node = event_hook_alloc( f );
+ node->next = *pnode;
+ *pnode = node;
+ }
+
+ if ( (node->wanted & events) != events ) {
+ /* this should update start/stop/check/peek */
+ D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
+ fd, node->wanted, events);
+ f->clazz->_fh_hook( f, events & ~node->wanted, node );
+ node->wanted |= events;
+ } else {
+ D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
+ events, fd, node->wanted);
+ }
+}
+
+static void
+event_looper_unhook( EventLooper looper, int fd, int events )
+{
+ FH fh = _fh_from_int(fd);
+ EventHook *pnode = event_looper_find_p( looper, fh );
+ EventHook node = *pnode;
+
+ if (node != NULL) {
+ int events2 = events & node->wanted;
+ if ( events2 == 0 ) {
+ D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+ return;
+ }
+ node->wanted &= ~events2;
+ if (!node->wanted) {
+ *pnode = node->next;
+ event_hook_free( node );
+ }
+ }
+}
+
+static EventLooperRec win32_looper;
+
+static void fdevent_init(void)
+{
+ win32_looper.htab_count = 0;
+ win32_looper.hooks = NULL;
+}
+
+static void fdevent_connect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_hook( looper, fde->fd, events );
+}
+
+static void fdevent_disconnect(fdevent *fde)
+{
+ EventLooper looper = &win32_looper;
+ int events = fde->state & FDE_EVENTMASK;
+
+ if (events != 0)
+ event_looper_unhook( looper, fde->fd, events );
+}
+
+static void fdevent_update(fdevent *fde, unsigned events)
+{
+ EventLooper looper = &win32_looper;
+ unsigned events0 = fde->state & FDE_EVENTMASK;
+
+ if (events != events0) {
+ int removes = events0 & ~events;
+ int adds = events & ~events0;
+ if (removes) {
+ D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+ event_looper_unhook( looper, fde->fd, removes );
+ }
+ if (adds) {
+ D("fdevent_update: add %x to %d\n", adds, fde->fd);
+ event_looper_hook ( looper, fde->fd, adds );
+ }
+ }
+}
+
+static void fdevent_process()
+{
+ EventLooper looper = &win32_looper;
+ EventHook hook;
+ int gotone = 0;
+
+ /* if we have at least one ready hook, execute it/them */
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ hook->ready = 0;
+ if (hook->prepare) {
+ hook->prepare(hook);
+ if (hook->ready != 0) {
+ event_hook_signal( hook );
+ gotone = 1;
+ }
+ }
+ }
+
+ /* nothing's ready yet, so wait for something to happen */
+ if (!gotone)
+ {
+ looper->htab_count = 0;
+
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if (hook->start && !hook->start(hook)) {
+ D( "fdevent_process: error when starting a hook\n" );
+ return;
+ }
+ if (hook->h != INVALID_HANDLE_VALUE) {
+ int nn;
+
+ for (nn = 0; nn < looper->htab_count; nn++)
+ {
+ if ( looper->htab[nn] == hook->h )
+ goto DontAdd;
+ }
+ looper->htab[ looper->htab_count++ ] = hook->h;
+ DontAdd:
+ ;
+ }
+ }
+
+ if (looper->htab_count == 0) {
+ D( "fdevent_process: nothing to wait for !!\n" );
+ return;
+ }
+
+ do
+ {
+ int wait_ret;
+
+ D( "sdb_win32: waiting for %d events\n", looper->htab_count );
+ if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
+ D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS, aborting!\n", looper->htab_count);
+ abort();
+ }
+ wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
+ if (wait_ret == (int)WAIT_FAILED) {
+ D( "sdb_win32: wait failed, error %ld\n", GetLastError() );
+ } else {
+ D( "sdb_win32: got one (index %d)\n", wait_ret );
+
+ /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
+ * like mouse movements. we need to filter these with the "check" function
+ */
+ if ((unsigned)wait_ret < (unsigned)looper->htab_count)
+ {
+ for (hook = looper->hooks; hook; hook = hook->next)
+ {
+ if ( looper->htab[wait_ret] == hook->h &&
+ (!hook->check || hook->check(hook)) )
+ {
+ D( "sdb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
+ event_hook_signal( hook );
+ gotone = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ while (!gotone);
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->stop)
+ hook->stop( hook );
+ }
+ }
+
+ for (hook = looper->hooks; hook; hook = hook->next) {
+ if (hook->peek && hook->peek(hook))
+ event_hook_signal( hook );
+ }
+}
+
+
+static void fdevent_register(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if(fd < 0) {
+ FATAL("bogus negative fd (%d)\n", fde->fd);
+ }
+
+ if(fd >= fd_table_max) {
+ int oldmax = fd_table_max;
+ if(fde->fd > 32000) {
+ FATAL("bogus huuuuge fd (%d)\n", fde->fd);
+ }
+ if(fd_table_max == 0) {
+ fdevent_init();
+ fd_table_max = 256;
+ }
+ while(fd_table_max <= fd) {
+ fd_table_max *= 2;
+ }
+ fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max);
+ if(fd_table == 0) {
+ FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+ }
+ memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
+ }
+
+ fd_table[fd] = fde;
+}
+
+static void fdevent_unregister(fdevent *fde)
+{
+ int fd = fde->fd - WIN32_FH_BASE;
+
+ if((fd < 0) || (fd >= fd_table_max)) {
+ FATAL("fd out of range (%d)\n", fde->fd);
+ }
+
+ if(fd_table[fd] != fde) {
+ FATAL("fd_table out of sync");
+ }
+
+ fd_table[fd] = 0;
+
+ if(!(fde->state & FDE_DONT_CLOSE)) {
+ dump_fde(fde, "close");
+ sdb_close(fde->fd);
+ }
+}
+
+static void fdevent_plist_enqueue(fdevent *node)
+{
+ fdevent *list = &list_pending;
+
+ node->next = list;
+ node->prev = list->prev;
+ node->prev->next = node;
+ list->prev = node;
+}
+
+static void fdevent_plist_remove(fdevent *node)
+{
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+}
+
+static fdevent *fdevent_plist_dequeue(void)
+{
+ fdevent *list = &list_pending;
+ fdevent *node = list->next;
+
+ if(node == list) return 0;
+
+ list->next = node->next;
+ list->next->prev = list;
+ node->next = 0;
+ node->prev = 0;
+
+ return node;
+}
+
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
+{
+ fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+ if(fde == 0) return 0;
+ fdevent_install(fde, fd, func, arg);
+ fde->state |= FDE_CREATED;
+ return fde;
+}
+
+void fdevent_destroy(fdevent *fde)
+{
+ if(fde == 0) return;
+ if(!(fde->state & FDE_CREATED)) {
+ FATAL("fde %p not created by fdevent_create()\n", fde);
+ }
+ fdevent_remove(fde);
+}
+
+void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
+{
+ memset(fde, 0, sizeof(fdevent));
+ fde->state = FDE_ACTIVE;
+ fde->fd = fd;
+ fde->func = func;
+ fde->arg = arg;
+
+ fdevent_register(fde);
+ dump_fde(fde, "connect");
+ fdevent_connect(fde);
+ fde->state |= FDE_ACTIVE;
+}
+
+void fdevent_remove(fdevent *fde)
+{
+ if(fde->state & FDE_PENDING) {
+ fdevent_plist_remove(fde);
+ }
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_disconnect(fde);
+ dump_fde(fde, "disconnect");
+ fdevent_unregister(fde);
+ }
+
+ fde->state = 0;
+ fde->events = 0;
+}
+
+
+void fdevent_set(fdevent *fde, unsigned events)
+{
+ events &= FDE_EVENTMASK;
+
+ if((fde->state & FDE_EVENTMASK) == (int)events) return;
+
+ if(fde->state & FDE_ACTIVE) {
+ fdevent_update(fde, events);
+ dump_fde(fde, "update");
+ }
+
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ if(fde->state & FDE_PENDING) {
+ /* if we're pending, make sure
+ ** we don't signal an event that
+ ** is no longer wanted.
+ */
+ fde->events &= (~events);
+ if(fde->events == 0) {
+ fdevent_plist_remove(fde);
+ fde->state &= (~FDE_PENDING);
+ }
+ }
+}
+
+void fdevent_add(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
+}
+
+void fdevent_del(fdevent *fde, unsigned events)
+{
+ fdevent_set(
+ fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
+}
+
+void fdevent_loop()
+{
+ fdevent *fde;
+
+ for(;;) {
+#if DEBUG
+ fprintf(stderr,"--- ---- waiting for events\n");
+#endif
+ fdevent_process();
+
+ while((fde = fdevent_plist_dequeue())) {
+ unsigned events = fde->events;
+ fde->events = 0;
+ fde->state &= (~FDE_PENDING);
+ dump_fde(fde, "callback");
+ fde->func(fde->fd, events, fde->arg);
+ }
+ }
+}
+
+/** FILE EVENT HOOKS
+ **/
+
+static void _event_file_prepare( EventHook hook )
+{
+ if (hook->wanted & (FDE_READ|FDE_WRITE)) {
+ /* we can always read/write */
+ hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
+ }
+}
+
+static int _event_file_peek( EventHook hook )
+{
+ return (hook->wanted & (FDE_READ|FDE_WRITE));
+}
+
+static void _fh_file_hook( FH f, int events, EventHook hook )
+{
+ hook->h = f->fh_handle;
+ hook->prepare = _event_file_prepare;
+ hook->peek = _event_file_peek;
+}
+
+/** SOCKET EVENT HOOKS
+ **/
+
+static void _event_socket_verify( EventHook hook, WSANETWORKEVENTS* evts )
+{
+ if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_READ)
+ hook->ready |= FDE_READ;
+ if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
+ if (hook->wanted & FDE_WRITE)
+ hook->ready |= FDE_WRITE;
+ if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+ if ( evts->lNetworkEvents & FD_OOB ) {
+ if (hook->wanted & FDE_ERROR)
+ hook->ready |= FDE_ERROR;
+ }
+}
+
+static void _event_socket_prepare( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
+ _event_socket_verify( hook, &evts );
+}
+
+static int _socket_wanted_to_flags( int wanted )
+{
+ int flags = 0;
+ if (wanted & FDE_READ)
+ flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
+
+ if (wanted & FDE_WRITE)
+ flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+ if (wanted & FDE_ERROR)
+ flags |= FD_OOB;
+
+ return flags;
+}
+
+static int _event_socket_start( EventHook hook )
+{
+ /* create an event which we're going to wait for */
+ FH fh = hook->fh;
+ long flags = _socket_wanted_to_flags( hook->wanted );
+
+ hook->h = fh->event;
+ if (hook->h == INVALID_HANDLE_VALUE) {
+ D( "_event_socket_start: no event for %s\n", fh->name );
+ return 0;
+ }
+
+ if ( flags != fh->mask ) {
+ D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
+ if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
+ D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
+ CloseHandle( hook->h );
+ hook->h = INVALID_HANDLE_VALUE;
+ exit(1);
+ return 0;
+ }
+ fh->mask = flags;
+ }
+ return 1;
+}
+
+static void _event_socket_stop( EventHook hook )
+{
+ hook->h = INVALID_HANDLE_VALUE;
+}
+
+static int _event_socket_check( EventHook hook )
+{
+ int result = 0;
+ FH fh = hook->fh;
+ WSANETWORKEVENTS evts;
+
+ if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
+ _event_socket_verify( hook, &evts );
+ result = (hook->ready != 0);
+ if (result) {
+ ResetEvent( hook->h );
+ }
+ }
+ D( "_event_socket_check %s returns %d\n", fh->name, result );
+ return result;
+}
+
+static int _event_socket_peek( EventHook hook )
+{
+ WSANETWORKEVENTS evts;
+ FH fh = hook->fh;
+
+ /* look if some of the events we want already happened ? */
+ if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
+ _event_socket_verify( hook, &evts );
+ if (hook->ready)
+ ResetEvent( hook->h );
+ }
+
+ return hook->ready != 0;
+}
+
+
+
+static void _fh_socket_hook( FH f, int events, EventHook hook )
+{
+ hook->prepare = _event_socket_prepare;
+ hook->start = _event_socket_start;
+ hook->stop = _event_socket_stop;
+ hook->check = _event_socket_check;
+ hook->peek = _event_socket_peek;
+
+ _event_socket_start( hook );
+}
+
+/** SOCKETPAIR EVENT HOOKS
+ **/
+
+static void _event_socketpair_prepare( EventHook hook )
+{
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted & FDE_READ && rbip->can_read)
+ hook->ready |= FDE_READ;
+
+ if (hook->wanted & FDE_WRITE && wbip->can_write)
+ hook->ready |= FDE_WRITE;
+ }
+
+ static int _event_socketpair_start( EventHook hook )
+ {
+ FH fh = hook->fh;
+ SocketPair pair = fh->fh_pair;
+ BipBuffer rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
+ BipBuffer wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
+
+ if (hook->wanted == FDE_READ)
+ hook->h = rbip->evt_read;
+
+ else if (hook->wanted == FDE_WRITE)
+ hook->h = wbip->evt_write;
+
+ else {
+ D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+ return 0;
+ }
+ D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
+ hook->fh->name, _fh_to_int(fh), hook->wanted);
+ return 1;
+}
+
+static int _event_socketpair_peek( EventHook hook )
+{
+ _event_socketpair_prepare( hook );
+ return hook->ready != 0;
+}
+
+static void _fh_socketpair_hook( FH fh, int events, EventHook hook )
+{
+ hook->prepare = _event_socketpair_prepare;
+ hook->start = _event_socketpair_start;
+ hook->peek = _event_socketpair_peek;
+}
+
+
+void
+sdb_sysdeps_init( void )
+{
+#define SDB_MUTEX(x) InitializeCriticalSection( & x );
+#include "mutex_list.h"
+ InitializeCriticalSection( &_win32_lock );
+}
diff --git a/src/threads.h b/src/threads.h
new file mode 100644
index 0000000..dc587b9
--- /dev/null
+++ b/src/threads.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 */
diff --git a/src/transport.c b/src/transport.c
new file mode 100755
index 0000000..4e7ff0b
--- /dev/null
+++ b/src/transport.c
@@ -0,0 +1,1217 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sdb.h"
+
+static void transport_unref(atransport *t);
+
+static atransport transport_list = {
+ .next = &transport_list,
+ .prev = &transport_list,
+};
+
+SDB_MUTEX_DEFINE( transport_lock );
+
+#ifdef _WIN32 /* FIXME : move to sysdeps.h later */
+int asprintf( char **, char *, ... );
+int vasprintf( char **, char *, va_list );
+
+int vasprintf( char **sptr, char *fmt, va_list argv )
+{
+ int wanted = vsnprintf( *sptr = NULL, 0, fmt, argv );
+
+ if( (wanted > 0) && ((*sptr = malloc( 1 + wanted )) != NULL) )
+ return vsprintf( *sptr, fmt, argv );
+
+ return wanted;
+}
+
+int asprintf( char **sptr, char *fmt, ... )
+{
+ int retval;
+
+ va_list argv;
+ va_start( argv, fmt );
+ retval = vasprintf( sptr, fmt, argv );
+ va_end( argv );
+
+ return retval;
+}
+#endif
+
+#if SDB_TRACE
+#define MAX_DUMP_HEX_LEN 16
+static void dump_hex( const unsigned char* ptr, size_t len )
+{
+ int nn, len2 = len;
+ // Build a string instead of logging each character.
+ // MAX chars in 2 digit hex, one space, MAX chars, one '\0'.
+ char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer;
+
+ if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN;
+
+ for (nn = 0; nn < len2; nn++) {
+ sprintf(pb, "%02x", ptr[nn]);
+ pb += 2;
+ }
+ sprintf(pb++, " ");
+
+ for (nn = 0; nn < len2; nn++) {
+ int c = ptr[nn];
+ if (c < 32 || c > 127)
+ c = '.';
+ *pb++ = c;
+ }
+ *pb++ = '\0';
+ DR("%s\n", buffer);
+}
+#endif
+
+void
+kick_transport(atransport* t)
+{
+ if (t && !t->kicked)
+ {
+ int kicked;
+
+ sdb_mutex_lock(&transport_lock);
+ kicked = t->kicked;
+ if (!kicked)
+ t->kicked = 1;
+ sdb_mutex_unlock(&transport_lock);
+
+ if (!kicked)
+ t->kick(t);
+ }
+}
+
+void
+run_transport_disconnects(atransport* t)
+{
+ adisconnect* dis = t->disconnects.next;
+
+ D("%s: run_transport_disconnects\n", t->serial);
+ while (dis != &t->disconnects) {
+ adisconnect* next = dis->next;
+ dis->func( dis->opaque, t );
+ dis = next;
+ }
+}
+
+#if SDB_TRACE
+static void
+dump_packet(const char* name, const char* func, apacket* p)
+{
+ unsigned command = p->msg.command;
+ int len = p->msg.data_length;
+ char cmd[9];
+ char arg0[12], arg1[12];
+ int n;
+
+ for (n = 0; n < 4; n++) {
+ int b = (command >> (n*8)) & 255;
+ if (b < 32 || b >= 127)
+ break;
+ cmd[n] = (char)b;
+ }
+ if (n == 4) {
+ cmd[4] = 0;
+ } else {
+ /* There is some non-ASCII name in the command, so dump
+ * the hexadecimal value instead */
+ snprintf(cmd, sizeof cmd, "%08x", command);
+ }
+
+ if (p->msg.arg0 < 256U)
+ snprintf(arg0, sizeof arg0, "%d", p->msg.arg0);
+ else
+ snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0);
+
+ if (p->msg.arg1 < 256U)
+ snprintf(arg1, sizeof arg1, "%d", p->msg.arg1);
+ else
+ snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
+
+ D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
+ name, func, cmd, arg0, arg1, len);
+ dump_hex(p->data, len);
+}
+#endif /* SDB_TRACE */
+
+static int
+read_packet(int fd, const char* name, apacket** ppacket)
+{
+ char *p = (char*)ppacket; /* really read a packet address */
+ int r;
+ int len = sizeof(*ppacket);
+ char buff[8];
+ if (!name) {
+ snprintf(buff, sizeof buff, "fd=%d", fd);
+ name = buff;
+ }
+ while(len > 0) {
+ r = sdb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+
+#if SDB_TRACE
+ if (SDB_TRACING) {
+ dump_packet(name, "from remote", *ppacket);
+ }
+#endif
+ return 0;
+}
+
+static int
+write_packet(int fd, const char* name, apacket** ppacket)
+{
+ char *p = (char*) ppacket; /* we really write the packet address */
+ int r, len = sizeof(ppacket);
+ char buff[8];
+ if (!name) {
+ snprintf(buff, sizeof buff, "fd=%d", fd);
+ name = buff;
+ }
+
+#if SDB_TRACE
+ if (SDB_TRACING) {
+ dump_packet(name, "to remote", *ppacket);
+ }
+#endif
+ len = sizeof(ppacket);
+ while(len > 0) {
+ r = sdb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ D("%s: write_packet (fd=%d) error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
+ if((r < 0) && (errno == EINTR)) continue;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void transport_socket_events(int fd, unsigned events, void *_t)
+{
+ atransport *t = _t;
+ D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
+ if(events & FDE_READ){
+ apacket *p = 0;
+ if(read_packet(fd, t->serial, &p)){
+ D("%s: failed to read packet from transport socket on fd %d\n", t->serial, 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) {
+ D("Transport is null \n");
+ // Zap errno because print_packet() and other stuff have errno effect.
+ errno = 0;
+ fatal_errno("Transport is null");
+ }
+
+ if(write_packet(t->transport_socket, t->serial, &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("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
+ t->serial, t->fd, 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, t->serial, &p)) {
+ put_apacket(p);
+ D("%s: failed to write SYNC packet\n", t->serial);
+ goto oops;
+ }
+
+ D("%s: data pump started\n", t->serial);
+ for(;;) {
+ p = get_apacket();
+
+ if(t->read_from_remote(p, t) == 0){
+ D("%s: received remote packet, sending to transport\n",
+ t->serial);
+ if(write_packet(t->fd, t->serial, &p)){
+ put_apacket(p);
+ D("%s: failed to write apacket to transport\n", t->serial);
+ goto oops;
+ }
+ } else {
+ D("%s: remote read failed for transport\n", t->serial);
+ put_apacket(p);
+ break;
+ }
+ }
+
+ D("%s: SYNC offline for transport\n", t->serial);
+ 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, t->serial, &p)) {
+ put_apacket(p);
+ D("%s: failed to write SYNC apacket to transport", t->serial);
+ }
+
+oops:
+ D("%s: transport output thread is exiting\n", t->serial);
+ kick_transport(t);
+ transport_unref(t);
+ return 0;
+}
+
+static void *input_thread(void *_t)
+{
+ atransport *t = _t;
+ apacket *p;
+ int active = 0;
+
+ D("%s: starting transport input thread, reading from fd %d\n",
+ t->serial, t->fd);
+
+ for(;;){
+ if(read_packet(t->fd, t->serial, &p)) {
+ D("%s: failed to read apacket from transport on fd %d\n",
+ t->serial, t->fd );
+ break;
+ }
+ if(p->msg.command == A_SYNC){
+ if(p->msg.arg0 == 0) {
+ D("%s: transport SYNC offline\n", t->serial);
+ put_apacket(p);
+ break;
+ } else {
+ if(p->msg.arg1 == t->sync_token) {
+ D("%s: transport SYNC online\n", t->serial);
+ active = 1;
+ } else {
+ D("%s: transport ignoring SYNC %d != %d\n",
+ t->serial, p->msg.arg1, t->sync_token);
+ }
+ }
+ } else {
+ if(active) {
+ D("%s: transport got packet, sending to remote\n", t->serial);
+ t->write_to_remote(p, t);
+ } else {
+ D("%s: transport ignoring packet while offline\n", t->serial);
+ }
+ }
+
+ 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("%s: transport input thread is exiting, fd %d\n", t->serial, 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 SDB_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;
+
+ sdb_mutex_lock( &transport_lock );
+ while (node) {
+ if (node == tracker) {
+ *pnode = node->next;
+ break;
+ }
+ pnode = &node->next;
+ node = *pnode;
+ }
+ sdb_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 // SDB_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 = sdb_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 = sdb_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;
+}
+
+#ifndef _WIN32 /* FIXME : change for windows build */
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+#endif
+static void transport_registration_func(int _fd, unsigned ev, void *data)
+{
+ tmsg m;
+ sdb_thread_t output_thread_ptr;
+ sdb_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: %s removing and free'ing %d\n", t->serial, t->transport_socket);
+
+ /* IMPORTANT: the remove closes one half of the
+ ** socket pair. The close closes the other half.
+ */
+ fdevent_remove(&(t->transport_fde));
+ sdb_close(t->fd);
+
+ sdb_mutex_lock(&transport_lock);
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+#ifndef _WIN32
+ pthread_cond_broadcast(&cond);
+#endif
+ sdb_mutex_unlock(&transport_lock);
+
+ run_transport_disconnects(t);
+
+ if (t->product)
+ free(t->product);
+ if (t->serial)
+ free(t->serial);
+ if (t->device_name)
+ free(t->device_name);
+
+ memset(t,0xee,sizeof(atransport));
+ free(t);
+
+ update_transports();
+ return;
+ }
+
+ /* don't create transport threads for inaccessible devices */
+ if (t->connection_state != CS_NOPERM) {
+ /* initial references are the two threads */
+ t->ref_count = 2;
+
+ if(sdb_socketpair(s)) {
+ fatal_errno("cannot open transport socketpair");
+ }
+
+ D("transport: %s (%d,%d) starting\n", t->serial, s[0], s[1]);
+
+ t->transport_socket = s[0];
+ t->fd = s[1];
+
+ fdevent_install(&(t->transport_fde),
+ t->transport_socket,
+ transport_socket_events,
+ t);
+
+ fdevent_set(&(t->transport_fde), FDE_READ);
+
+ if(sdb_thread_create(&input_thread_ptr, input_thread, t)){
+ fatal_errno("cannot create input thread");
+ }
+
+ if(sdb_thread_create(&output_thread_ptr, output_thread, t)){
+ fatal_errno("cannot create output thread");
+ }
+ }
+
+ /* put us on the master device list */
+ sdb_mutex_lock(&transport_lock);
+ t->next = &transport_list;
+ t->prev = transport_list.prev;
+ t->next->prev = t;
+ t->prev->next = t;
+#ifndef _WIN32
+ pthread_cond_broadcast(&cond);
+#endif
+ sdb_mutex_unlock(&transport_lock);
+
+ t->disconnects.next = t->disconnects.prev = &t->disconnects;
+
+ update_transports();
+}
+
+void init_transport_registration(void)
+{
+ int s[2];
+
+ if(sdb_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: %s registered\n", transport->serial);
+ 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: %s removed\n", transport->serial);
+ if(transport_write_action(transport_registration_send, &m)) {
+ fatal_errno("cannot write transport registration socket\n");
+ }
+}
+
+
+static void transport_unref_locked(atransport *t)
+{
+ atransport *tmp;
+ int nr;
+ t->ref_count--;
+ if (t->ref_count == 0) {
+ D("transport: %s unref (kicking and closing)\n", t->serial);
+ if (!t->kicked) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ t->close(t);
+ remove_transport(t);
+
+ /* update tizen specific device name */
+ for (tmp = t->next; tmp && tmp != &transport_list &&
+ tmp->type == kTransportUsb; tmp = tmp->next) {
+ D("update tizen specific device name: %s\n", tmp->device_name);
+ if (tmp->device_name && sscanf(tmp->device_name, "device-%d", &nr) == 1) {
+ free(tmp->device_name);
+ asprintf(&tmp->device_name, "device-%d", nr - 1);
+ }
+ }
+ } else {
+ D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+ }
+}
+
+static void transport_unref(atransport *t)
+{
+ if (t) {
+ sdb_mutex_lock(&transport_lock);
+ transport_unref_locked(t);
+ sdb_mutex_unlock(&transport_lock);
+ }
+}
+
+void add_transport_disconnect(atransport* t, adisconnect* dis)
+{
+ sdb_mutex_lock(&transport_lock);
+ dis->next = &t->disconnects;
+ dis->prev = dis->next->prev;
+ dis->prev->next = dis;
+ dis->next->prev = dis;
+ sdb_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";
+
+ sdb_mutex_lock(&transport_lock);
+ for (t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->connection_state == CS_NOPERM) {
+ if (error_out)
+ *error_out = "insufficient permissions for device";
+ continue;
+ }
+
+ /* 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;
+ }
+ }
+ }
+ sdb_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)) {
+ sdb_sleep_ms(1000);
+ goto retry;
+ }
+
+ return result;
+}
+
+#if SDB_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";
+ case CS_SIDELOAD: return "sideload";
+ case CS_NOPERM: return "no permissions";
+ default: return "unknown";
+ }
+}
+
+/*
+ * find number of devices which serial match with the prefix
+ */
+int find_transports(char **serial_out, const char *prefix)
+{
+ int nr = 0; // not found
+ char *match = NULL;
+ atransport *t;
+
+ if (!serial_out || !prefix)
+ return -1;
+
+ sdb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ char* serial = t->serial;
+ if (!serial || !serial[0])
+ continue;
+ if (!strncmp(prefix, serial, strlen(prefix))) {
+ match = serial;
+ nr++;
+ }
+
+ if (nr > 1) {
+ match = NULL;
+ break;
+ }
+ }
+ sdb_mutex_unlock(&transport_lock);
+
+ if (nr == 1 && match) {
+ *serial_out = strdup(match);
+ } else if (nr == 0) {
+ asprintf(serial_out, "device not found");
+ } else if (nr > 1) {
+ asprintf(serial_out, "more than one device and emulator");
+ }
+
+ return nr;
+}
+
+int list_transports(char *buf, size_t bufsize)
+{
+ char* p = buf;
+ char* end = buf + bufsize;
+ int len;
+ atransport *t;
+
+ /* XXX OVERRUN PROBLEMS XXX */
+ sdb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ const char* serial = t->serial;
+ const char* devicename = (t->device_name == NULL) ? DEFAULT_DEVICENAME : t->device_name; /* tizen specific */
+ if (!serial || !serial[0])
+ serial = "????????????";
+ len = snprintf(p, end - p, "%s\t%s\t%s\n", serial, statename(t), devicename);
+
+ if (p + len >= end) {
+ /* discard last line if buffer is too short */
+ break;
+ }
+ p += len;
+ }
+ p[0] = 0;
+ sdb_mutex_unlock(&transport_lock);
+ return p - buf;
+}
+
+
+/* hack for osx */
+void close_usb_devices()
+{
+ atransport *t;
+
+ sdb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if ( !t->kicked ) {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ }
+ sdb_mutex_unlock(&transport_lock);
+}
+#endif // SDB_HOST
+
+void register_socket_transport(int s, const char *serial, int port, int local, const char *device_name)
+{
+ atransport *t = calloc(1, sizeof(atransport));
+ char buff[32];
+
+ if (!serial) {
+ snprintf(buff, sizeof buff, "T-%p", t);
+ serial = buff;
+ }
+ D("transport: %s init'ing for socket %d, on port %d (%s)\n", serial, s, port, device_name);
+ if ( init_socket_transport(t, s, port, local) < 0 ) {
+ sdb_close(s);
+ free(t);
+#if SDB_HOST /* tizen specific */
+ atransport *old_t = find_transport(serial);
+ if (old_t) {
+ unregister_transport(old_t);
+ } else {
+ D("No such device %s", serial);
+ }
+#endif
+ return;
+ }
+ if(serial) {
+ t->serial = strdup(serial);
+ }
+#if SDB_HOST /* tizen specific */
+ if (device_name) {/* tizen specific */
+ t->device_name = strdup(device_name);
+ } else { // device_name could be null when sdb server was forked before qemu has sent the connect message.
+ char device_name[DEVICENAME_MAX];
+ if (get_devicename_from_shdmem(port, device_name) == 0) {
+ t->device_name = strdup(device_name);
+ }
+ }
+#endif
+ register_transport(t);
+}
+
+#if SDB_HOST
+atransport *find_transport(const char *serial)
+{
+ atransport *t;
+
+ sdb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->serial && !strcmp(serial, t->serial)) {
+ break;
+ }
+ }
+ sdb_mutex_unlock(&transport_lock);
+
+ if (t != &transport_list)
+ return t;
+ else
+ return 0;
+}
+
+void unregister_transport(atransport *t)
+{
+ sdb_mutex_lock(&transport_lock);
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+ sdb_mutex_unlock(&transport_lock);
+
+ kick_transport(t);
+ transport_unref(t);
+}
+
+// unregisters all non-emulator TCP transports
+void unregister_all_tcp_transports()
+{
+ atransport *t, *next;
+ sdb_mutex_lock(&transport_lock);
+ for (t = transport_list.next; t != &transport_list; t = next) {
+ next = t->next;
+ if (t->type == kTransportLocal && t->sdb_port == 0) {
+ t->next->prev = t->prev;
+ t->prev->next = next;
+ // we cannot call kick_transport when holding transport_lock
+ if (!t->kicked)
+ {
+ t->kicked = 1;
+ t->kick(t);
+ }
+ transport_unref_locked(t);
+ }
+ }
+
+ sdb_mutex_unlock(&transport_lock);
+}
+
+#endif
+
+int get_connected_device_count(transport_type type) /* tizen specific */
+{
+ int cnt = 0;
+ atransport *t;
+ sdb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if (type == t->type)
+ cnt++;
+ }
+ sdb_mutex_unlock(&transport_lock);
+ D("connected device count:%d\n",cnt);
+ return cnt;
+}
+
+void register_usb_transport(usb_handle *usb, const char *serial, unsigned writeable)
+{
+ atransport *t = calloc(1, sizeof(atransport));
+ char device_name[256];
+
+ D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+ serial ? serial : "");
+ init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
+ if(serial) {
+ t->serial = strdup(serial);
+ }
+ /*
+ * send register request to server thread and wait it finished
+ */
+ sdb_mutex_lock(&transport_lock);
+ register_transport(t);
+#ifndef _WIN32
+ pthread_cond_wait(&cond, &transport_lock);
+#endif
+ sdb_mutex_unlock(&transport_lock);
+ /* tizen specific */
+ sprintf(device_name, "device-%d",get_connected_device_count(kTransportUsb));
+ t->device_name = strdup(device_name);
+}
+
+/* this should only be used for transports with connection_state == CS_NOPERM */
+void unregister_usb_transport(usb_handle *usb)
+{
+ atransport *t;
+ sdb_mutex_lock(&transport_lock);
+ for(t = transport_list.next; t != &transport_list; t = t->next) {
+ if (t->usb == usb && t->connection_state == CS_NOPERM) {
+ t->next->prev = t->prev;
+ t->prev->next = t->next;
+ break;
+ }
+ }
+ sdb_mutex_unlock(&transport_lock);
+}
+
+#undef TRACE_TAG
+#define TRACE_TAG TRACE_RWX
+
+int readx(int fd, void *ptr, size_t len)
+{
+ char *p = ptr;
+ int r;
+#if SDB_TRACE
+ int len0 = len;
+#endif
+ D("readx: fd=%d wanted=%d\n", fd, (int)len);
+ while(len > 0) {
+ r = sdb_read(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ if (r < 0) {
+ D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+ if (errno == EINTR)
+ continue;
+ } else {
+ D("readx: fd=%d disconnected\n", fd);
+ }
+ return -1;
+ }
+ }
+
+#if SDB_TRACE
+ D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len);
+ dump_hex( ptr, len0 );
+#endif
+ return 0;
+}
+
+int writex(int fd, const void *ptr, size_t len)
+{
+ char *p = (char*) ptr;
+ int r;
+
+#if SDB_TRACE
+ D("writex: fd=%d len=%d: ", fd, (int)len);
+ dump_hex( ptr, len );
+#endif
+ while(len > 0) {
+ r = sdb_write(fd, p, len);
+ if(r > 0) {
+ len -= r;
+ p += r;
+ } else {
+ if (r < 0) {
+ D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+ if (errno == EINTR)
+ continue;
+ } else {
+ D("writex: fd=%d disconnected\n", fd);
+ }
+ return -1;
+ }
+ }
+ 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;
+ }
+}
diff --git a/src/transport.h b/src/transport.h
new file mode 100644
index 0000000..511b37e
--- /dev/null
+++ b/src/transport.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __TRANSPORT_H
+#define __TRANSPORT_H
+
+/* 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);
+#endif /* __TRANSPORT_H */
diff --git a/src/transport_local.c b/src/transport_local.c
new file mode 100644
index 0000000..da55a77
--- /dev/null
+++ b/src/transport_local.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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>
+
+#ifndef HAVE_WIN32_IPC
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <unistd.h>
+#endif
+
+#define TRACE_TAG TRACE_TRANSPORT
+#include "sdb.h"
+
+#ifdef HAVE_BIG_ENDIAN
+#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 SDB_HOST
+/* we keep a list of opened transports. The atransport struct knows to which
+ * local transport it is connected. The list is used to detect when we're
+ * trying to connect twice to a given local transport.
+ */
+#define SDB_LOCAL_TRANSPORT_MAX 16
+
+SDB_MUTEX_DEFINE( local_transports_lock );
+
+static atransport* local_transports[ SDB_LOCAL_TRANSPORT_MAX ];
+#endif /* SDB_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 HAVE_BIG_ENDIAN
+ 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 HAVE_BIG_ENDIAN
+ 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, const char *device_name) {
+ return local_connect_arbitrary_ports(port-1, port, device_name);
+}
+
+int local_connect_arbitrary_ports(int console_port, int sdb_port, const char *device_name)
+{
+ char buf[64];
+ int fd = -1;
+
+#if SDB_HOST
+ const char *host = getenv("SDBHOST");
+ if (host) {
+ fd = socket_network_client(host, sdb_port, SOCK_STREAM);
+ }
+#endif
+ if (fd < 0) {
+ fd = socket_loopback_client(sdb_port, SOCK_STREAM);
+ }
+
+ 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, console_port);
+ register_socket_transport(fd, buf, sdb_port, 1, device_name);
+ return 0;
+ }
+ return -1;
+}
+
+#if SDB_HOST /* tizen specific */
+int get_devicename_from_shdmem(int port, char *device_name)
+{
+ char *vms = NULL;
+#ifndef HAVE_WIN32_IPC
+ int shm_id;
+ void *shared_memory = (void *)0;
+
+ shm_id = shmget( (key_t)port-1, 0, 0);
+ if (shm_id == -1)
+ return -1;
+
+ shared_memory = shmat(shm_id, (void *)0, SHM_RDONLY);
+
+ if (shared_memory == (void *)-1)
+ {
+ D("faild to get shdmem key (%d) : %s\n", port, strerror(errno));
+ return -1;
+ }
+ vms = strstr((char*)shared_memory, VMS_PATH);
+ if (vms != NULL)
+ strncpy(device_name, vms+strlen(VMS_PATH), DEVICENAME_MAX);
+ else
+ strncpy(device_name, DEFAULT_DEVICENAME, DEVICENAME_MAX);
+
+#else /* _WIN32*/
+ HANDLE hMapFile;
+ char s_port[5];
+ char* pBuf;
+
+ sprintf(s_port, "%d", port-1);
+ hMapFile = OpenFileMapping(FILE_MAP_READ, TRUE, s_port);
+
+ if(hMapFile == NULL) {
+ D("faild to get shdmem key (%ld) : %s\n", port, GetLastError() );
+ return -1;
+ }
+ pBuf = (char*)MapViewOfFile(hMapFile,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 50);
+ if (pBuf == NULL) {
+ D("Could not map view of file (%ld)\n", GetLastError());
+ CloseHandle(hMapFile);
+ return -1;
+ }
+
+ vms = strstr((char*)pBuf, VMS_PATH);
+ if (vms != NULL)
+ strncpy(device_name, vms+strlen(VMS_PATH), DEVICENAME_MAX);
+ else
+ strncpy(device_name, DEFAULT_DEVICENAME, DEVICENAME_MAX);
+ CloseHandle(hMapFile);
+#endif
+ // apply for new vms path policy from Jan 23 2013
+ // vms path should be: ~/tizen-sdk-data/emulator-vms/vms/{name}/emulimg-em1.~~
+ vms = strtok(device_name, OS_PATH_SEPARATOR_STR);
+ if (vms != NULL) {
+ strncpy(device_name, vms, DEVICENAME_MAX);
+ }
+ D("init device name %s on port %d\n", device_name, port);
+
+ return 0;
+}
+
+int read_line(const int fd, char* ptr, size_t maxlen)
+{
+ unsigned int n = 0;
+ char c[2];
+ int rc;
+
+ while(n != maxlen) {
+ if((rc = sdb_read(fd, c, 1)) != 1)
+ return -1; // eof or read err
+
+ if(*c == '\n') {
+ ptr[n] = 0;
+ return n;
+ }
+ ptr[n++] = *c;
+ }
+ return -1; // no space
+}
+#endif
+
+static void *client_socket_thread(void *x)
+{
+#if SDB_HOST
+ int port = DEFAULT_SDB_LOCAL_TRANSPORT_PORT;
+ int count = SDB_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 SDB starts up. later, each new emulator */
+ /* will send a message to SDB to indicate that is is starting up */
+ for ( ; count > 0; count--, port += 10 ) { /* tizen specific */
+ (void) local_connect(port, NULL);
+ }
+#endif
+ return 0;
+}
+
+static void *server_socket_thread(void * arg)
+{
+ int serverfd, fd;
+ struct sockaddr addr;
+ socklen_t alen;
+ int port = (int)arg;
+
+ D("transport: server_socket_thread() starting\n");
+ serverfd = -1;
+ for(;;) {
+ if(serverfd == -1) {
+ serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
+ if(serverfd < 0) {
+ D("server: cannot bind socket yet\n");
+ sdb_sleep_ms(1000);
+ continue;
+ }
+ close_on_exec(serverfd);
+ }
+
+ alen = sizeof(addr);
+ D("server: trying to get new connection from %d\n", port);
+ fd = sdb_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", port, 1, NULL);
+ }
+ }
+ D("transport: server_socket_thread() exiting\n");
+ return 0;
+}
+
+/* This is relevant only for SDB daemon running inside the emulator. */
+#if !SDB_HOST
+/*
+ * Redefine open and write for qemu_pipe.h that contains inlined references
+ * to those routines. We will redifine them back after qemu_pipe.h inclusion.
+ */
+#undef open
+#undef write
+#define open sdb_open
+#define write sdb_write
+#include "qemu_pipe.h"
+#undef open
+#undef write
+#define open ___xxx_open
+#define write ___xxx_write
+
+/* A worker thread that monitors host connections, and registers a transport for
+ * every new host connection. This thread replaces server_socket_thread on
+ * condition that sdbd daemon runs inside the emulator, and emulator uses QEMUD
+ * pipe to communicate with sdbd daemon inside the guest. This is done in order
+ * to provide more robust communication channel between SDB host and guest. The
+ * main issue with server_socket_thread approach is that it runs on top of TCP,
+ * and thus is sensitive to network disruptions. For instance, the
+ * ConnectionManager may decide to reset all network connections, in which case
+ * the connection between SDB host and guest will be lost. To make SDB traffic
+ * independent from the network, we use here 'sdb' QEMUD service to transfer data
+ * between the host, and the guest. See external/qemu/android/sdb-*.* that
+ * implements the emulator's side of the protocol. Another advantage of using
+ * QEMUD approach is that SDB will be up much sooner, since it doesn't depend
+ * anymore on network being set up.
+ * The guest side of the protocol contains the following phases:
+ * - Connect with sdb QEMUD service. In this phase a handle to 'sdb' QEMUD service
+ * is opened, and it becomes clear whether or not emulator supports that
+ * protocol.
+ * - Wait for the SDB host to create connection with the guest. This is done by
+ * sending an 'accept' request to the sdb QEMUD service, and waiting on
+ * response.
+ * - When new SDB host connection is accepted, the connection with sdb QEMUD
+ * service is registered as the transport, and a 'start' request is sent to the
+ * sdb QEMUD service, indicating that the guest is ready to receive messages.
+ * Note that the guest will ignore messages sent down from the emulator before
+ * the transport registration is completed. That's why we need to send the
+ * 'start' request after the transport is registered.
+ */
+#if 0
+static void *qemu_socket_thread(void * arg)
+{
+/* 'accept' request to the sdb QEMUD service. */
+static const char _accept_req[] = "accept";
+/* 'start' request to the sdb QEMUD service. */
+static const char _start_req[] = "start";
+/* 'ok' reply from the sdb QEMUD service. */
+static const char _ok_resp[] = "ok";
+
+ const int port = (int)arg;
+ int res, fd;
+ char tmp[256];
+ char con_name[32];
+
+ D("transport: qemu_socket_thread() starting\n");
+
+ /* sdb QEMUD service connection request. */
+ snprintf(con_name, sizeof(con_name), "qemud:sdb:%d", port);
+
+ /* Connect to the sdb QEMUD service. */
+ fd = qemu_pipe_open(con_name);
+ if (fd < 0) {
+ /* This could be an older version of the emulator, that doesn't
+ * implement sdb QEMUD service. Fall back to the old TCP way. */
+ sdb_thread_t thr;
+ D("sdb service is not available. Falling back to TCP socket.\n");
+ sdb_thread_create(&thr, server_socket_thread, arg);
+ return 0;
+ }
+
+ for(;;) {
+ /*
+ * Wait till the host creates a new connection.
+ */
+
+ /* Send the 'accept' request. */
+ res = sdb_write(fd, _accept_req, strlen(_accept_req));
+ if (res == strlen(_accept_req)) {
+ /* Wait for the response. In the response we expect 'ok' on success,
+ * or 'ko' on failure. */
+ res = sdb_read(fd, tmp, sizeof(tmp));
+ if (res != 2 || memcmp(tmp, _ok_resp, 2)) {
+ D("Accepting SDB host connection has failed.\n");
+ sdb_close(fd);
+ } else {
+ /* Host is connected. Register the transport, and start the
+ * exchange. */
+ register_socket_transport(fd, "host", port, 1, NULL);
+ sdb_write(fd, _start_req, strlen(_start_req));
+ }
+
+ /* Prepare for accepting of the next SDB host connection. */
+ fd = qemu_pipe_open(con_name);
+ if (fd < 0) {
+ D("sdb service become unavailable.\n");
+ return 0;
+ }
+ } else {
+ D("Unable to send the '%s' request to SDB service.\n", _accept_req);
+ return 0;
+ }
+ }
+ D("transport: qemu_socket_thread() exiting\n");
+ return 0;
+}
+#endif // !SDB_HOST
+#endif
+
+void local_init(int port)
+{
+ sdb_thread_t thr;
+ void* (*func)(void *);
+
+ if(HOST) {
+ func = client_socket_thread;
+ } else {
+#if SDB_HOST
+ func = server_socket_thread;
+#else
+ /* For the sdbd daemon in the system image we need to distinguish
+ * between the device, and the emulator. */
+#if 0 /* tizen specific */
+ char is_qemu[PROPERTY_VALUE_MAX];
+ property_get("ro.kernel.qemu", is_qemu, "");
+ if (!strcmp(is_qemu, "1")) {
+ /* Running inside the emulator: use QEMUD pipe as the transport. */
+ func = qemu_socket_thread;
+ } else
+#endif
+ {
+ /* Running inside the device: use TCP socket as the transport. */
+ func = server_socket_thread;
+ }
+#endif // !SDB_HOST
+ }
+
+ D("transport: local %s init\n", HOST ? "client" : "server");
+
+ if(sdb_thread_create(&thr, func, (void *)port)) {
+ fatal_errno("cannot create local socket %s thread",
+ HOST ? "client" : "server");
+ }
+}
+
+static void remote_kick(atransport *t)
+{
+ int fd = t->sfd;
+ t->sfd = -1;
+ sdb_shutdown(fd);
+ sdb_close(fd);
+
+#if SDB_HOST
+ if(HOST) {
+ int nn;
+ sdb_mutex_lock( &local_transports_lock );
+ for (nn = 0; nn < SDB_LOCAL_TRANSPORT_MAX; nn++) {
+ if (local_transports[nn] == t) {
+ local_transports[nn] = NULL;
+ break;
+ }
+ }
+ sdb_mutex_unlock( &local_transports_lock );
+ }
+#endif
+}
+
+static void remote_close(atransport *t)
+{
+ sdb_close(t->fd);
+}
+
+
+#if SDB_HOST
+/* Only call this function if you already hold local_transports_lock. */
+atransport* find_emulator_transport_by_sdb_port_locked(int sdb_port)
+{
+ int i;
+ for (i = 0; i < SDB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] && local_transports[i]->sdb_port == sdb_port) {
+ return local_transports[i];
+ }
+ }
+ return NULL;
+}
+
+atransport* find_emulator_transport_by_sdb_port(int sdb_port)
+{
+ sdb_mutex_lock( &local_transports_lock );
+ atransport* result = find_emulator_transport_by_sdb_port_locked(sdb_port);
+ sdb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+
+/* Only call this function if you already hold local_transports_lock. */
+int get_available_local_transport_index_locked()
+{
+ int i;
+ for (i = 0; i < SDB_LOCAL_TRANSPORT_MAX; i++) {
+ if (local_transports[i] == NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int get_available_local_transport_index()
+{
+ sdb_mutex_lock( &local_transports_lock );
+ int result = get_available_local_transport_index_locked();
+ sdb_mutex_unlock( &local_transports_lock );
+ return result;
+}
+#endif
+
+int init_socket_transport(atransport *t, int s, int sdb_port, int local)
+{
+ 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;
+ t->sdb_port = 0;
+
+#if SDB_HOST
+ if (HOST && local) {
+ sdb_mutex_lock( &local_transports_lock );
+ {
+ t->sdb_port = sdb_port;
+ atransport* existing_transport =
+ find_emulator_transport_by_sdb_port_locked(sdb_port);
+ int index = get_available_local_transport_index_locked();
+ if (existing_transport != NULL) {
+ D("local transport for port %d already registered (%p)?\n",
+ sdb_port, existing_transport);
+ fail = -1;
+ } else if (index < 0) {
+ // Too many emulators.
+ D("cannot register more emulators. Maximum is %d\n",
+ SDB_LOCAL_TRANSPORT_MAX);
+ fail = -1;
+ } else {
+ local_transports[index] = t;
+ }
+ }
+ sdb_mutex_unlock( &local_transports_lock );
+ }
+#endif
+ return fail;
+}
diff --git a/src/transport_usb.c b/src/transport_usb.c
new file mode 100644
index 0000000..e4de29a
--- /dev/null
+++ b/src/transport_usb.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sdb.h"
+
+#if SDB_HOST
+#include "usb_vendors.h"
+#endif
+
+#ifdef HAVE_BIG_ENDIAN
+#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, int state)
+{
+ 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 = state;
+ t->type = kTransportUsb;
+ t->usb = h;
+
+#if SDB_HOST
+ HOST = 1;
+#else
+ HOST = 0;
+#endif
+}
+
+#if SDB_HOST
+int is_sdb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol)
+{
+ unsigned i;
+ for (i = 0; i < vendorIdCount; i++) {
+ if (vid == vendorIds[i]) {
+ if (usb_class == SDB_CLASS && usb_subclass == SDB_SUBCLASS &&
+ usb_protocol == SDB_PROTOCOL) {
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+#endif
diff --git a/src/usb_libusb.c b/src/usb_libusb.c
new file mode 100644
index 0000000..8d12ea7
--- /dev/null
+++ b/src/usb_libusb.c
@@ -0,0 +1,654 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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/endian.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <libusb.h>
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_USB
+#include "sdb.h"
+
+static sdb_mutex_t usb_lock = SDB_MUTEX_INITIALIZER;
+static libusb_context *ctx = NULL;
+
+struct usb_handle
+{
+ usb_handle *prev;
+ usb_handle *next;
+
+ libusb_device *dev;
+ libusb_device_handle *devh;
+ int interface;
+ uint8_t dev_bus;
+ uint8_t dev_addr;
+
+ int zero_mask;
+ unsigned char end_point_address[2];
+ char serial[128];
+
+ sdb_cond_t notify;
+ sdb_mutex_t lock;
+};
+
+static struct usb_handle handle_list = {
+ .prev = &handle_list,
+ .next = &handle_list,
+};
+
+void
+usb_cleanup()
+{
+ libusb_exit(ctx);
+}
+
+void
+report_bulk_libusb_error(int r)
+{
+ switch (r) {
+ case LIBUSB_ERROR_TIMEOUT:
+ D("Transfer timeout\n");
+ break;
+
+ case LIBUSB_ERROR_PIPE:
+ D("Control request is not supported\n");
+ break;
+
+ case LIBUSB_ERROR_OVERFLOW:
+ D("Device offered more data\n");
+ break;
+
+ case LIBUSB_ERROR_NO_DEVICE :
+ D("Device was disconnected\n");
+ break;
+
+ default:
+ D("Error %d during transfer\n", r);
+ break;
+ };
+}
+
+static int
+usb_bulk_write(usb_handle *uh, const void *data, int len)
+{
+ int r = 0;
+ int transferred = 0;
+
+ r = libusb_bulk_transfer(uh->devh, uh->end_point_address[1], (void *)data, len,
+ &transferred, 0);
+
+ if (r != 0) {
+ D("usb_bulk_write(): ");
+ report_bulk_libusb_error(r);
+ return r;
+ }
+
+ return (transferred);
+}
+
+static int
+usb_bulk_read(usb_handle *uh, void *data, int len)
+{
+ int r = 0;
+ int transferred = 0;
+
+ r = libusb_bulk_transfer(uh->devh, uh->end_point_address[0], data, len,
+ &transferred, 0);
+
+ if (r != 0) {
+ D("usb_bulk_read(): ");
+ report_bulk_libusb_error(r);
+ return r;
+ }
+
+ return (transferred);
+}
+
+int
+usb_write(struct usb_handle *uh, const void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+ int need_zero = 0;
+
+ if (uh->zero_mask == 1) {
+ if (!(len & uh->zero_mask)) {
+ need_zero = 1;
+ }
+ }
+
+ D("usb_write(): %p:%d -> transport %p\n", _data, len, uh);
+
+ while (len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ n = usb_bulk_write(uh, data, xfer);
+
+ if (n != xfer) {
+ D("usb_write(): failed for transport %p (%d bytes left)\n", uh, len);
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ if (need_zero){
+ n = usb_bulk_write(uh, _data, 0);
+
+ if (n < 0) {
+ D("usb_write(): failed to finish operation for transport %p\n", uh);
+ }
+ return n;
+ }
+
+ return 0;
+}
+
+int
+usb_read(struct usb_handle *uh, void *_data, int len)
+{
+ unsigned char *data = (unsigned char*) _data;
+ int n;
+
+ D("usb_read(): %p:%d <- transport %p\n", _data, len, uh);
+
+ while (len > 0) {
+ int xfer = (len > 4096) ? 4096 : len;
+
+ n = usb_bulk_read(uh, data, xfer);
+
+ if (n != xfer) {
+ if (n > 0) {
+ data += n;
+ len -= n;
+ continue;
+ }
+
+ D("usb_read(): failed for transport %p (%d bytes left)\n", uh, len);
+ return -1;
+ }
+
+ len -= xfer;
+ data += xfer;
+ }
+
+ return 0;
+ }
+
+int
+usb_close(struct usb_handle *h)
+{
+ D("usb_close(): closing transport %p\n", h);
+ sdb_mutex_lock(&usb_lock);
+
+ h->next->prev = h->prev;
+ h->prev->next = h->next;
+ h->prev = NULL;
+ h->next = NULL;
+
+ libusb_release_interface(h->devh, h->interface);
+ libusb_close(h->devh);
+ libusb_unref_device(h->dev);
+
+ sdb_mutex_unlock(&usb_lock);
+
+ free(h);
+
+ return (0);
+}
+
+void usb_kick(struct usb_handle *h)
+{
+ D("usb_cick(): kicking transport %p\n", h);
+
+ sdb_mutex_lock(&h->lock);
+ unregister_usb_transport(h);
+ sdb_mutex_unlock(&h->lock);
+
+ h->next->prev = h->prev;
+ h->prev->next = h->next;
+ h->prev = NULL;
+ h->next = NULL;
+
+ libusb_release_interface(h->devh, h->interface);
+ libusb_close(h->devh);
+ libusb_unref_device(h->dev);
+ free(h);
+}
+
+int
+check_usb_interface(libusb_interface *interface,
+ libusb_device_descriptor *desc,
+ struct usb_handle *uh)
+{
+ int e;
+
+ if (interface->num_altsetting == 0) {
+ D("check_usb_interface(): No interface settings\n");
+ return -1;
+ }
+
+ libusb_interface_descriptor *idesc = &interface->altsetting[0];
+
+ if (idesc->bNumEndpoints != 2) {
+ D("check_usb_interface(): Interface have not 2 endpoints, ignoring\n");
+ return -1;
+ }
+
+ for (e = 0; e < idesc->bNumEndpoints; e++) {
+ libusb_endpoint_descriptor *edesc = &idesc->endpoint[e];
+
+ if (edesc->bmAttributes != LIBUSB_TRANSFER_TYPE_BULK) {
+ D("check_usb_interface(): Endpoint (%u) is not bulk (%u), ignoring\n",
+ edesc->bmAttributes, LIBUSB_TRANSFER_TYPE_BULK);
+ return -1;
+ }
+
+ if (edesc->bEndpointAddress & LIBUSB_ENDPOINT_IN)
+ uh->end_point_address[0] = edesc->bEndpointAddress;
+ else
+ uh->end_point_address[1] = edesc->bEndpointAddress;
+
+ /* aproto 01 needs 0 termination */
+ if (idesc->bInterfaceProtocol == 0x01) {
+ uh->zero_mask = edesc->wMaxPacketSize - 1;
+ D("check_usb_interface(): Forced Android interface protocol v.1\n");
+ }
+ }
+
+ D("check_usb_interface(): Device: %04x:%04x "
+ "iclass: %x, isclass: %x, iproto: %x ep: %x/%x-> ",
+ desc->idVendor, desc->idProduct, idesc->bInterfaceClass,
+ idesc->bInterfaceSubClass, idesc->bInterfaceProtocol,
+ uh->end_point_address[0], uh->end_point_address[1]);
+
+ if (!is_sdb_interface(desc->idVendor, desc->idProduct,
+ idesc->bInterfaceClass, idesc->bInterfaceSubClass,
+ idesc->bInterfaceProtocol))
+ {
+ D("not matches\n");
+ return -1;
+ }
+
+ D("matches\n");
+ return 1;
+}
+
+int
+check_usb_interfaces(libusb_config_descriptor *config,
+ libusb_device_descriptor *desc, struct usb_handle *uh)
+{
+ int i;
+
+ for (i = 0; i < config->bNumInterfaces; ++i) {
+ if (check_usb_interface(&config->interface[i], desc, uh) != -1) {
+ /* found some interface and saved information about it */
+ D("check_usb_interfaces(): Interface %d of %04x:%04x "
+ "matches Android device\n", i, desc->idVendor,
+ desc->idProduct);
+
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int
+register_device(struct usb_handle *uh, const char *serial)
+{
+ D("register_device(): Registering %p [%s] as USB transport\n",
+ uh, serial);
+
+ struct usb_handle *usb= NULL;
+
+ usb = calloc(1, sizeof(struct usb_handle));
+ memcpy(usb, uh, sizeof(struct usb_handle));
+ strcpy(usb->serial, uh->serial);
+
+ sdb_cond_init(&usb->notify, 0);
+ sdb_mutex_init(&usb->lock, 0);
+
+ sdb_mutex_lock(&usb_lock);
+
+ usb->next = &handle_list;
+ usb->prev = handle_list.prev;
+ usb->prev->next = usb;
+ usb->next->prev = usb;
+
+ sdb_mutex_unlock(&usb_lock);
+
+ register_usb_transport(usb, serial, 1);
+
+ return (1);
+}
+
+int
+already_registered(usb_handle *uh)
+{
+ struct usb_handle *usb= NULL;
+ int exists = 0;
+
+ sdb_mutex_lock(&usb_lock);
+
+ for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+ if ((usb->dev_bus == uh->dev_bus) &&
+ (usb->dev_addr == uh->dev_addr))
+ {
+ exists = 1;
+ break;
+ }
+ }
+
+ sdb_mutex_unlock(&usb_lock);
+
+ return exists;
+}
+
+void
+check_device(libusb_device *dev)
+{
+ struct usb_handle uh;
+ int i = 0;
+ int found = -1;
+ char serial[256] = {0};
+
+ libusb_device_descriptor desc;
+ libusb_config_descriptor *config = NULL;
+
+ int r = libusb_get_device_descriptor(dev, &desc);
+
+ if (r != LIBUSB_SUCCESS) {
+ D("check_device(): Failed to get device descriptor\n");
+ return;
+ }
+
+ if ((desc.idVendor == 0) && (desc.idProduct == 0))
+ return;
+
+ D("check_device(): Probing usb device %04x:%04x\n",
+ desc.idVendor, desc.idProduct);
+
+ if (!is_sdb_interface (desc.idVendor, desc.idProduct,
+ SDB_CLASS, SDB_SUBCLASS, SDB_PROTOCOL))
+ {
+ D("check_device(): Ignored due unknown vendor id\n");
+ return;
+ }
+
+ uh.dev_bus = libusb_get_bus_number(dev);
+ uh.dev_addr = libusb_get_device_address(dev);
+
+ if (already_registered(&uh)) {
+ D("check_device(): Device (bus: %d, address: %d) "
+ "is already registered\n", uh.dev_bus, uh.dev_addr);
+ return;
+ }
+
+ D("check_device(): Device bus: %d, address: %d\n",
+ uh.dev_bus, uh.dev_addr);
+
+ r = libusb_get_active_config_descriptor(dev, &config);
+
+ if (r != 0) {
+ if (r == LIBUSB_ERROR_NOT_FOUND) {
+ D("check_device(): Device %4x:%4x is unconfigured\n",
+ desc.idVendor, desc.idProduct);
+ return;
+ }
+
+ D("check_device(): Failed to get configuration for %4x:%4x\n",
+ desc.idVendor, desc.idProduct);
+ return;
+ }
+
+ if (config == NULL) {
+ D("check_device(): Sanity check failed after "
+ "getting active config\n");
+ return;
+ }
+
+ if (config->interface != NULL) {
+ found = check_usb_interfaces(config, &desc, &uh);
+ }
+
+ /* not needed anymore */
+ libusb_free_config_descriptor(config);
+
+ r = libusb_open(dev, &uh.devh);
+ uh.dev = dev;
+
+ if (r != 0) {
+ switch (r) {
+ case LIBUSB_ERROR_NO_MEM:
+ D("check_device(): Memory allocation problem\n");
+ break;
+
+ case LIBUSB_ERROR_ACCESS:
+ D("check_device(): Permissions problem, "
+ "current user priveleges are messed up?\n");
+ break;
+
+ case LIBUSB_ERROR_NO_DEVICE:
+ D("check_device(): Device disconected, bad cable?\n");
+ break;
+
+ default:
+ D("check_device(): libusb triggered error %d\n", r);
+ }
+ // skip rest
+ found = -1;
+ }
+
+ if (found >= 0) {
+ D("check_device(): Device matches Android interface\n");
+ // read the device's serial number
+ memset(serial, 0, sizeof(serial));
+ uh.interface = found;
+
+ r = libusb_claim_interface(uh.devh, uh.interface);
+
+ if (r < 0) {
+ D("check_device(): Failed to claim interface %d\n",
+ uh.interface);
+
+ goto fail;
+ }
+
+ if (desc.iSerialNumber) {
+ // reading serial
+ uint16_t buffer[128] = {0};
+ uint16_t languages[128] = {0};
+ int languageCount = 0;
+
+ memset(languages, 0, sizeof(languages));
+ r = libusb_control_transfer(uh.devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+ LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8,
+ 0, (uint8_t *)languages, sizeof(languages), 0);
+
+ if (r <= 0) {
+ D("check_device(): Failed to get languages count\n");
+ goto fail;
+ }
+
+ languageCount = (r - 2) / 2;
+
+ for (i = 1; i <= languageCount; ++i) {
+ memset(buffer, 0, sizeof(buffer));
+
+ r = libusb_control_transfer(uh.devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+ LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber,
+ languages[i], (uint8_t *)buffer, sizeof(buffer), 0);
+
+ if (r > 0) { /* converting serial */
+ int j = 0;
+ r /= 2;
+
+ for (j = 1; j < r; ++j)
+ serial[j - 1] = buffer[j];
+
+ serial[j - 1] = '\0';
+ break; /* languagesCount cycle */
+ }
+ }
+
+ if (register_device(&uh, serial) == 0) {
+ D("check_device(): Failed to register device\n");
+ goto fail_interface;
+ }
+
+ libusb_ref_device(dev);
+ }
+ }
+
+ return;
+
+fail_interface:
+ libusb_release_interface(uh.devh, uh.interface);
+
+fail:
+ libusb_close(uh.devh);
+ uh.devh = NULL;
+}
+
+int
+check_device_connected(struct usb_handle *uh)
+{
+ int r = libusb_kernel_driver_active(uh->devh, uh->interface);
+
+ if (r == LIBUSB_ERROR_NO_DEVICE)
+ return 0;
+
+ if (r < 0)
+ return -1;
+
+ return 1;
+}
+
+void
+kick_disconnected()
+{
+ struct usb_handle *usb= NULL;
+
+ sdb_mutex_lock(&usb_lock);
+
+ for (usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+
+ if (check_device_connected(usb) == 0) {
+ D("kick_disconnected(): Transport %p is not online anymore\n",
+ usb);
+
+ usb_kick(usb);
+ }
+ }
+
+ sdb_mutex_unlock(&usb_lock);
+}
+
+void
+scan_usb_devices()
+{
+ D("scan_usb_devices(): started\n");
+
+ libusb_device **devs= NULL;
+ libusb_device *dev= NULL;
+ ssize_t cnt = libusb_get_device_list(ctx, &devs);
+
+ if (cnt < 0) {
+ D("scan_usb_devices(): Failed to get device list (error: %d)\n",
+ cnt);
+
+ return;
+ }
+
+ int i = 0;
+
+ while ((dev = devs[i++]) != NULL) {
+ check_device(dev);
+ }
+
+ libusb_free_device_list(devs, 1);
+}
+
+void *
+device_poll_thread(void* unused)
+{
+ D("device_poll_thread(): Created USB scan thread\n");
+
+ for (;;) {
+ sleep(5);
+ kick_disconnected();
+ scan_usb_devices();
+ }
+
+ /* never reaching this point */
+ return (NULL);
+}
+
+static void
+sigalrm_handler(int signo)
+{
+ /* nothing */
+}
+
+void
+usb_init()
+{
+ D("usb_init(): started\n");
+ sdb_thread_t tid;
+ struct sigaction actions;
+
+ int r = libusb_init(&ctx);
+
+ if (r != LIBUSB_SUCCESS) {
+ err(EX_IOERR, "Failed to init libusb\n");
+ }
+
+ memset(&actions, 0, sizeof(actions));
+
+ sigemptyset(&actions.sa_mask);
+
+ actions.sa_flags = 0;
+ actions.sa_handler = sigalrm_handler;
+
+ sigaction(SIGALRM, &actions, NULL);
+
+ /* initial device scan */
+ scan_usb_devices();
+
+ /* starting USB event polling thread */
+ if (sdb_thread_create(&tid, device_poll_thread, NULL)) {
+ err(EX_IOERR, "cannot create USB scan thread\n");
+ }
+
+ D("usb_init(): finished\n");
+}
+
diff --git a/src/usb_linux.c b/src/usb_linux.c
new file mode 100755
index 0000000..cd72c07
--- /dev/null
+++ b/src/usb_linux.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <sys/time.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 "sdb.h"
+
+
+/* usb scan debugging is waaaay too verbose */
+#define DBGX(x...)
+
+SDB_MUTEX_DEFINE( usb_lock );
+
+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;
+ unsigned writeable;
+
+ struct usbdevfs_urb urb_in;
+ struct usbdevfs_urb urb_out;
+
+ int urb_in_busy;
+ int urb_out_busy;
+ int dead;
+
+ sdb_cond_t notify;
+ sdb_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;
+
+ sdb_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;
+ sdb_mutex_unlock(&usb_lock);
+ return 1;
+ }
+ }
+ sdb_mutex_unlock(&usb_lock);
+ return 0;
+}
+
+static void kick_disconnected_devices()
+{
+ usb_handle *usb;
+
+ sdb_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;
+ }
+ }
+ sdb_mutex_unlock(&usb_lock);
+
+}
+
+static void register_device(const char *dev_name, unsigned char ep_in, unsigned char ep_out,
+ int ifc, int serial_index, unsigned zero_mask);
+
+static inline int badname(const char *name)
+{
+ while(*name) {
+ if(!isdigit(*name++)) return 1;
+ }
+ return 0;
+}
+
+static void find_usb_device(const char *base,
+ void (*register_device_callback)
+ (const char *, unsigned char, unsigned char, int, int, unsigned))
+{
+ char busname[32], devname[32];
+ unsigned char local_ep_in, local_ep_out;
+ DIR *busdir , *devdir ;
+ struct dirent *de;
+ int fd ;
+
+ busdir = opendir(base);
+ if(busdir == 0) return;
+
+ 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[4096];
+ unsigned char* bufptr = devdesc;
+ unsigned char* bufend;
+ 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;
+ unsigned int 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_RDONLY)) < 0) {
+ continue;
+ }
+
+ desclength = sdb_read(fd, devdesc, sizeof(devdesc));
+ bufend = bufptr + desclength;
+
+ // 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);
+ sdb_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)) {
+ sdb_close(fd);
+ continue;
+ }
+
+ vid = device->idVendor;
+ pid = device->idProduct;
+ DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
+
+ // should have config descriptor next
+ config = (struct usb_config_descriptor *)bufptr;
+
+ /* tizen specific */
+ if (device->bNumConfigurations > 1) {
+ bufptr += config->wTotalLength;
+ config = (struct usb_config_descriptor *)bufptr;
+ bufend = bufptr + config->wTotalLength;
+ }
+
+ bufptr += USB_DT_CONFIG_SIZE;
+ if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
+ D("usb_config_descriptor not found\n");
+ sdb_close(fd);
+ continue;
+ }
+
+ // loop through all the descriptors and look for the SDB interface
+ while (bufptr < bufend) {
+ unsigned char length = bufptr[0];
+ unsigned char type = bufptr[1];
+
+ if (type == USB_DT_INTERFACE) {
+ interface = (struct usb_interface_descriptor *)bufptr;
+ bufptr += length;
+
+ if (length != USB_DT_INTERFACE_SIZE) {
+ D("interface descriptor has wrong size\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_sdb_interface(vid, pid, interface->bInterfaceClass,
+ interface->bInterfaceSubClass, interface->bInterfaceProtocol)) {
+
+ D("looking for bulk endpoints\n");
+ // looks like SDB...
+ 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;
+ }
+
+ register_device_callback(devname, local_ep_in, local_ep_out,
+ interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
+ break;
+ }
+ } else {
+ bufptr += length;
+ }
+ } // end of while
+
+ sdb_close(fd);
+ } // end of devdir while
+ closedir(devdir);
+ } //end of busdir while
+ closedir(busdir);
+}
+
+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;
+ struct timeval tv;
+ struct timespec ts;
+
+ 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");
+
+ sdb_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(;;) {
+ /* time out after five seconds */
+ gettimeofday(&tv, NULL);
+ ts.tv_sec = tv.tv_sec + 5;
+ ts.tv_nsec = tv.tv_usec * 1000L;
+ res = pthread_cond_timedwait(&h->notify, &h->lock, &ts);
+ if(res < 0 || h->dead) {
+ break;
+ }
+ if(h->urb_out_busy == 0) {
+ if(urb->status == 0) {
+ res = urb->actual_length;
+ }
+ break;
+ }
+ }
+fail:
+ sdb_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;
+
+
+ sdb_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();
+ sdb_mutex_unlock(&h->lock);
+ res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
+ int saved_errno = errno;
+ sdb_mutex_lock(&h->lock);
+ h->reaper_thread = 0;
+ if(h->dead) {
+ res = -1;
+ break;
+ }
+ if(res < 0) {
+ if(saved_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;
+ sdb_cond_broadcast(&h->notify);
+ }
+ }
+fail:
+ sdb_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);
+ sdb_mutex_lock(&h->lock);
+ if(h->dead == 0) {
+ h->dead = 1;
+
+ if (h->writeable) {
+ /* 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;
+ sdb_cond_broadcast(&h->notify);
+ } else {
+ unregister_usb_transport(h);
+ }
+ }
+ sdb_mutex_unlock(&h->lock);
+}
+
+int usb_close(usb_handle *h)
+{
+ D("[ usb close ... ]\n");
+ sdb_mutex_lock(&usb_lock);
+ h->next->prev = h->prev;
+ h->prev->next = h->next;
+ h->prev = 0;
+ h->next = 0;
+
+ sdb_close(h->desc);
+ D("[ usb closed %p (fd = %d) ]\n", h, h->desc);
+ sdb_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, int serial_index, unsigned zero_mask)
+{
+ usb_handle* usb = 0;
+ int n = 0;
+ char serial[256];
+ int bConfigurationValue = 2; /* tizen specific : sdb needs 2nd configruation */
+
+ /* 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.
+ */
+ sdb_mutex_lock(&usb_lock);
+ for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
+ if(!strcmp(usb->fname, dev_name)) {
+ sdb_mutex_unlock(&usb_lock);
+ return;
+ }
+ }
+ sdb_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;
+ usb->writeable = 1;
+
+ sdb_cond_init(&usb->notify, 0);
+ sdb_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) {
+ /* if we fail, see if have read-only access */
+ usb->desc = unix_open(usb->fname, O_RDONLY);
+ if(usb->desc < 0) goto fail;
+ usb->writeable = 0;
+ D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc);
+ } else {
+ D("[ usb open %s fd = %d]\n", usb->fname, usb->desc);
+ /* tizen specific */
+ n = ioctl(usb->desc, USBDEVFS_RESET);
+ if(n != 0) {
+ D("[ usb reset failed %s fd = %d]\n", usb->fname, usb->desc);
+ goto fail;
+ }
+ n = ioctl(usb->desc, USBDEVFS_SETCONFIGURATION, &bConfigurationValue);
+ if (n != 0) {
+ D("[ usb set %d configuration failed %s fd = %d]\n", bConfigurationValue, usb->fname, usb->desc);
+ D("check kernel is supporting %dth configuration\n", bConfigurationValue);
+ }
+
+ n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface);
+ if(n != 0) {
+ D("[ usb claim failed %s fd = %d]\n", usb->fname, usb->desc);
+ goto fail;
+ }
+ }
+
+ /* read the device's serial number */
+ serial[0] = 0;
+ memset(serial, 0, sizeof(serial));
+ if (serial_index) {
+ struct usbdevfs_ctrltransfer ctrl;
+ __u16 buffer[128];
+ __u16 languages[128];
+ int i, result;
+ int languageCount = 0;
+
+ memset(languages, 0, sizeof(languages));
+ memset(&ctrl, 0, sizeof(ctrl));
+
+ // read list of supported languages
+ ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
+ ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
+ ctrl.wValue = (USB_DT_STRING << 8) | 0;
+ ctrl.wIndex = 0;
+ ctrl.wLength = sizeof(languages);
+ ctrl.data = languages;
+ ctrl.timeout = 1000;
+
+ result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl);
+ if (result > 0)
+ languageCount = (result - 2) / 2;
+
+ for (i = 1; i <= languageCount; i++) {
+ 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) | serial_index;
+ ctrl.wIndex = __le16_to_cpu(languages[i]);
+ ctrl.wLength = sizeof(buffer);
+ ctrl.data = buffer;
+ ctrl.timeout = 1000;
+
+ result = ioctl(usb->desc, 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] = __le16_to_cpu(buffer[i]);
+ serial[i - 1] = 0;
+ break;
+ }
+ }
+ }
+
+ /* add to the end of the active handles */
+ sdb_mutex_lock(&usb_lock);
+ usb->next = &handle_list;
+ usb->prev = handle_list.prev;
+ usb->prev->next = usb;
+ usb->next->prev = usb;
+ sdb_mutex_unlock(&usb_lock);
+
+ register_usb_transport(usb, serial, usb->writeable);
+ return;
+
+fail:
+ D("[ usb open %s error=%d, err_str = %s]\n",
+ usb->fname, errno, strerror(errno));
+ if(usb->desc >= 0) {
+ sdb_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()
+{
+ sdb_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(sdb_thread_create(&tid, device_poll_thread, NULL)){
+ fatal_errno("cannot create input thread");
+ }
+}
diff --git a/src/usb_linux_client.c b/src/usb_linux_client.c
new file mode 100644
index 0000000..e15690f
--- /dev/null
+++ b/src/usb_linux_client.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sdb.h"
+
+
+struct usb_handle
+{
+ int fd;
+ sdb_cond_t notify;
+ sdb_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
+ sdb_mutex_lock(&usb->lock);
+ while (usb->fd != -1) {
+ sdb_cond_wait(&usb->notify, &usb->lock);
+ }
+ sdb_mutex_unlock(&usb->lock);
+
+ D("[ usb_thread - opening device ]\n");
+ do {
+ /* XXX use inotify? */
+ fd = unix_open("/dev/samsung_sdb", O_RDWR); /* tizen-specific */
+ if (fd < 0) {
+ // to support older kernels
+ //fd = unix_open("/dev/android", O_RDWR);
+ D("[ opening /dev/samsung_sdb device failed ]\n");
+ }
+ if (fd < 0) {
+ sdb_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, 1);
+ }
+
+ // never gets here
+ return 0;
+}
+
+int usb_write(usb_handle *h, const void *data, int len)
+{
+ int n;
+
+ D("about to write (fd=%d, len=%d)\n", h->fd, len);
+ n = sdb_write(h->fd, data, len);
+ if(n != len) {
+ D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+ h->fd, n, errno, strerror(errno));
+ return -1;
+ }
+ D("[ done fd=%d ]\n", h->fd);
+ return 0;
+}
+
+int usb_read(usb_handle *h, void *data, int len)
+{
+ int n;
+
+ D("about to read (fd=%d, len=%d)\n", h->fd, len);
+ n = sdb_read(h->fd, data, len);
+ if(n != len) {
+ D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+ h->fd, n, errno, strerror(errno));
+ return -1;
+ }
+ D("[ done fd=%d ]\n", h->fd);
+ return 0;
+}
+
+void usb_init()
+{
+ usb_handle *h;
+ sdb_thread_t tid;
+// int fd;
+
+ h = calloc(1, sizeof(usb_handle));
+ h->fd = -1;
+ sdb_cond_init(&h->notify, 0);
+ sdb_mutex_init(&h->lock, 0);
+
+ // Open the file /dev/android_sdb_enable to trigger
+ // the enabling of the sdb 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.
+#if 0 /* tizen specific */
+ fd = unix_open("/dev/android_sdb_enable", O_RDWR);
+ if (fd < 0) {
+ D("failed to open /dev/android_sdb_enable\n");
+ } else {
+ close_on_exec(fd);
+ }
+#endif
+ D("[ usb_init - starting thread ]\n");
+ if(sdb_thread_create(&tid, usb_open_thread, h)){
+ fatal_errno("cannot create usb thread");
+ }
+}
+
+void usb_kick(usb_handle *h)
+{
+ D("usb_kick\n");
+ sdb_mutex_lock(&h->lock);
+ sdb_close(h->fd);
+ h->fd = -1;
+
+ // notify usb_open_thread that we are disconnected
+ sdb_cond_signal(&h->notify);
+ sdb_mutex_unlock(&h->lock);
+}
+
+int usb_close(usb_handle *h)
+{
+ // nothing to do here
+ return 0;
+}
diff --git a/src/usb_osx.c b/src/usb_osx.c
new file mode 100644
index 0000000..5fbe1a6
--- /dev/null
+++ b/src/usb_osx.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "sdb.h"
+#include "usb_vendors.h"
+
+#define DBG D
+
+static IONotificationPortRef notificationPort = 0;
+static io_iterator_t* notificationIterators;
+
+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 AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
+static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
+ natural_t messageType,
+ void *messageArgument);
+static usb_handle* CheckInterface(IOUSBInterfaceInterface **iface,
+ UInt16 vendor, UInt16 product);
+
+static int
+InitUSB()
+{
+ CFMutableDictionaryRef matchingDict;
+ CFRunLoopSourceRef runLoopSource;
+ SInt32 vendor, if_subclass, if_protocol;
+ unsigned 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 vendors
+ for (i = 0; i < vendorIdCount; i++) {
+ //* Create our matching dictionary to find the Android device's
+ //* sdb interface
+ //* IOServiceAddMatchingNotification consumes the reference, so we do
+ //* not need to release this
+ matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
+
+ if (!matchingDict) {
+ DBG("ERR: Couldn't create USB matching dictionary.\n");
+ return -1;
+ }
+
+ //* Match based on vendor id, interface subclass and protocol
+ vendor = vendorIds[i];
+ if_subclass = SDB_SUBCLASS;
+ if_protocol = SDB_PROTOCOL;
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID),
+ CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type, &vendor));
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceSubClass),
+ CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type, &if_subclass));
+ CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceProtocol),
+ CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type, &if_protocol));
+ IOServiceAddMatchingNotification(
+ notificationPort,
+ kIOFirstMatchNotification,
+ matchingDict,
+ AndroidInterfaceAdded,
+ NULL,
+ &notificationIterators[i]);
+
+ //* Iterate over set of matching interfaces to access already-present
+ //* devices and to arm the notification
+ AndroidInterfaceAdded(NULL, notificationIterators[i]);
+ }
+
+ return 0;
+}
+
+static void
+AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
+{
+ kern_return_t kr;
+ io_service_t usbDevice;
+ io_service_t usbInterface;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ IOUSBInterfaceInterface220 **iface = NULL;
+ IOUSBDeviceInterface197 **dev = NULL;
+ HRESULT result;
+ SInt32 score;
+ UInt16 vendor;
+ UInt16 product;
+ UInt8 serialIndex;
+ char serial[256];
+
+ while ((usbInterface = IOIteratorNext(iterator))) {
+ //* Create an intermediate interface plugin
+ kr = IOCreatePlugInInterfaceForService(usbInterface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ IOObjectRelease(usbInterface);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
+ continue;
+ }
+
+ //* This gets us the interface object
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID)
+ &iface);
+ //* We only needed the plugin to get the interface, so discard it
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !iface) {
+ DBG("ERR: Couldn't query the interface (%08x)\n", (int) result);
+ continue;
+ }
+
+ //* this gets us an ioservice, with which we will find the actual
+ //* device; after getting a plugin, and querying the interface, of
+ //* course.
+ //* Gotta love OS X
+ kr = (*iface)->GetDevice(iface, &usbDevice);
+ if (kIOReturnSuccess != kr || !usbDevice) {
+ DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
+ continue;
+ }
+
+ plugInInterface = NULL;
+ score = 0;
+ //* create an intermediate device plugin
+ kr = IOCreatePlugInInterfaceForService(usbDevice,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ //* only needed this to find the plugin
+ (void)IOObjectRelease(usbDevice);
+ if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
+ DBG("ERR: Unable to create a device plug-in (%08x)\n", kr);
+ continue;
+ }
+
+ result = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+ //* only needed this to query the plugin
+ (*plugInInterface)->Release(plugInInterface);
+ if (result || !dev) {
+ DBG("ERR: Couldn't create a device interface (%08x)\n",
+ (int) result);
+ continue;
+ }
+
+ //* Now after all that, we actually have a ref to the device and
+ //* the interface that matched our criteria
+
+ kr = (*dev)->GetDeviceVendor(dev, &vendor);
+ kr = (*dev)->GetDeviceProduct(dev, &product);
+ kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
+
+ if (serialIndex > 0) {
+ IOUSBDevRequest req;
+ UInt16 buffer[256];
+ UInt16 languages[128];
+
+ memset(languages, 0, sizeof(languages));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | 0;
+ req.wIndex = 0;
+ req.pData = languages;
+ req.wLength = sizeof(languages);
+ kr = (*dev)->DeviceRequest(dev, &req);
+
+ if (kr == kIOReturnSuccess && req.wLenDone > 0) {
+
+ int langCount = (req.wLenDone - 2) / 2, lang;
+
+ for (lang = 1; lang <= langCount; lang++) {
+
+ memset(buffer, 0, sizeof(buffer));
+ memset(&req, 0, sizeof(req));
+
+ req.bmRequestType =
+ USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
+ req.bRequest = kUSBRqGetDescriptor;
+ req.wValue = (kUSBStringDesc << 8) | serialIndex;
+ req.wIndex = languages[lang];
+ 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;
+ break;
+ }
+ }
+ }
+ }
+ (*dev)->Release(dev);
+
+ DBG("INFO: Found vid=%04x pid=%04x serial=%s\n", vendor, product,
+ serial);
+
+ usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
+ vendor, product);
+ if (handle == NULL) {
+ DBG("ERR: Could not find device interface: %08x\n", kr);
+ (*iface)->Release(iface);
+ continue;
+ }
+
+ DBG("AndroidDeviceAdded calling register_usb_transport\n");
+ register_usb_transport(handle, (serial[0] ? serial : NULL), 1);
+
+ // 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,
+ usbInterface,
+ kIOGeneralInterest,
+ AndroidInterfaceNotify,
+ handle,
+ &handle->usbNotification);
+
+ if (kIOReturnSuccess != kr) {
+ DBG("ERR: Unable to create interest notification (%08x)\n", kr);
+ }
+ }
+}
+
+static void
+AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
+{
+ usb_handle *handle = (usb_handle *)refCon;
+
+ if (messageType == kIOMessageServiceIsTerminated) {
+ if (!handle) {
+ DBG("ERR: NULL handle\n");
+ return;
+ }
+ DBG("AndroidInterfaceNotify\n");
+ IOObjectRelease(handle->usbNotification);
+ usb_kick(handle);
+ }
+}
+
+//* TODO: simplify this further since we only register to get SDB interface
+//* subclass+protocol events
+static usb_handle*
+CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
+{
+ usb_handle* handle = NULL;
+ IOReturn kr;
+ UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+ UInt8 endpoint;
+
+
+ //* 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);
+ return NULL;
+ }
+
+ //* 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 err_get_num_ep;
+ }
+
+ //* 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 err_get_interface_class;
+ }
+
+ //* check to make sure interface class, subclass and protocol match SDB
+ //* avoid opening mass storage endpoints
+ if (!is_sdb_interface(vendor, product, interfaceClass,
+ interfaceSubClass, interfaceProtocol))
+ goto err_bad_sdb_interface;
+
+ 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;
+
+ handle->zero_mask = maxPacketSize - 1;
+ } else {
+ DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
+ goto err_get_pipe_props;
+ }
+ }
+
+ handle->interface = interface;
+ return handle;
+
+err_get_pipe_props:
+ free(handle);
+err_bad_sdb_interface:
+err_get_interface_class:
+err_get_num_ep:
+ (*interface)->USBInterfaceClose(interface);
+ return NULL;
+}
+
+
+void* RunLoopThread(void* unused)
+{
+ unsigned i;
+
+ InitUSB();
+
+ currentRunLoop = CFRunLoopGetCurrent();
+
+ // Signal the parent that we are running
+ sdb_mutex_lock(&start_lock);
+ sdb_cond_signal(&start_cond);
+ sdb_mutex_unlock(&start_lock);
+
+ CFRunLoopRun();
+ currentRunLoop = 0;
+
+ for (i = 0; i < vendorIdCount; i++) {
+ IOObjectRelease(notificationIterators[i]);
+ }
+ IONotificationPortDestroy(notificationPort);
+
+ DBG("RunLoopThread done\n");
+ return NULL;
+}
+
+
+static int initialized = 0;
+void usb_init()
+{
+ if (!initialized)
+ {
+ sdb_thread_t tid;
+
+ notificationIterators = (io_iterator_t*)malloc(
+ vendorIdCount * sizeof(io_iterator_t));
+
+ sdb_mutex_init(&start_lock, NULL);
+ sdb_cond_init(&start_cond, NULL);
+
+ if(sdb_thread_create(&tid, RunLoopThread, NULL))
+ fatal_errno("cannot create input thread");
+
+ // Wait for initialization to finish
+ sdb_mutex_lock(&start_lock);
+ sdb_cond_wait(&start_cond, &start_lock);
+ sdb_mutex_unlock(&start_lock);
+
+ sdb_mutex_destroy(&start_lock);
+ sdb_cond_destroy(&start_cond);
+
+ initialized = 1;
+ }
+}
+
+void usb_cleanup()
+{
+ DBG("usb_cleanup\n");
+ close_usb_devices();
+ if (currentRunLoop)
+ CFRunLoopStop(currentRunLoop);
+
+ if (notificationIterators != NULL) {
+ free(notificationIterators);
+ notificationIterators = NULL;
+ }
+}
+
+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)
+ return;
+
+ if (handle->interface)
+ {
+ (*handle->interface)->USBInterfaceClose(handle->interface);
+ (*handle->interface)->Release(handle->interface);
+ handle->interface = 0;
+ }
+}
diff --git a/src/usb_vendors.c b/src/usb_vendors.c
new file mode 100644
index 0000000..6364828
--- /dev/null
+++ b/src/usb_vendors.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "usb_vendors.h"
+
+#include <stdio.h>
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include "windows.h"
+# include "shlobj.h"
+#else
+# include <unistd.h>
+# include <sys/stat.h>
+#endif
+
+#include "sysdeps.h"
+#include "sdb.h"
+
+#define ANDROID_PATH ".android"
+#define ANDROID_SDB_INI "sdb_usb.ini"
+
+#define TRACE_TAG TRACE_USB
+
+// Google's USB Vendor ID
+#define VENDOR_ID_GOOGLE 0x18d1
+// Intel's USB Vendor ID
+#define VENDOR_ID_INTEL 0x8087
+// HTC's USB Vendor ID
+#define VENDOR_ID_HTC 0x0bb4
+// Samsung's USB Vendor ID
+#define VENDOR_ID_SAMSUNG 0x04e8
+// Motorola's USB Vendor ID
+#define VENDOR_ID_MOTOROLA 0x22b8
+// LG's USB Vendor ID
+#define VENDOR_ID_LGE 0x1004
+// Huawei's USB Vendor ID
+#define VENDOR_ID_HUAWEI 0x12D1
+// Acer's USB Vendor ID
+#define VENDOR_ID_ACER 0x0502
+// Sony Ericsson's USB Vendor ID
+#define VENDOR_ID_SONY_ERICSSON 0x0FCE
+// Foxconn's USB Vendor ID
+#define VENDOR_ID_FOXCONN 0x0489
+// Dell's USB Vendor ID
+#define VENDOR_ID_DELL 0x413c
+// Nvidia's USB Vendor ID
+#define VENDOR_ID_NVIDIA 0x0955
+// Garmin-Asus's USB Vendor ID
+#define VENDOR_ID_GARMIN_ASUS 0x091E
+// Sharp's USB Vendor ID
+#define VENDOR_ID_SHARP 0x04dd
+// ZTE's USB Vendor ID
+#define VENDOR_ID_ZTE 0x19D2
+// Kyocera's USB Vendor ID
+#define VENDOR_ID_KYOCERA 0x0482
+// Pantech's USB Vendor ID
+#define VENDOR_ID_PANTECH 0x10A9
+// Qualcomm's USB Vendor ID
+#define VENDOR_ID_QUALCOMM 0x05c6
+// On-The-Go-Video's USB Vendor ID
+#define VENDOR_ID_OTGV 0x2257
+// NEC's USB Vendor ID
+#define VENDOR_ID_NEC 0x0409
+// Panasonic Mobile Communication's USB Vendor ID
+#define VENDOR_ID_PMC 0x04DA
+// Toshiba's USB Vendor ID
+#define VENDOR_ID_TOSHIBA 0x0930
+// SK Telesys's USB Vendor ID
+#define VENDOR_ID_SK_TELESYS 0x1F53
+// KT Tech's USB Vendor ID
+#define VENDOR_ID_KT_TECH 0x2116
+// Asus's USB Vendor ID
+#define VENDOR_ID_ASUS 0x0b05
+// Philips's USB Vendor ID
+#define VENDOR_ID_PHILIPS 0x0471
+// Texas Instruments's USB Vendor ID
+#define VENDOR_ID_TI 0x0451
+// Funai's USB Vendor ID
+#define VENDOR_ID_FUNAI 0x0F1C
+// Gigabyte's USB Vendor ID
+#define VENDOR_ID_GIGABYTE 0x0414
+// IRiver's USB Vendor ID
+#define VENDOR_ID_IRIVER 0x2420
+// Compal's USB Vendor ID
+#define VENDOR_ID_COMPAL 0x1219
+// T & A Mobile Phones' USB Vendor ID
+#define VENDOR_ID_T_AND_A 0x1BBB
+// LenovoMobile's USB Vendor ID
+#define VENDOR_ID_LENOVOMOBILE 0x2006
+// Lenovo's USB Vendor ID
+#define VENDOR_ID_LENOVO 0x17EF
+// Vizio's USB Vendor ID
+#define VENDOR_ID_VIZIO 0xE040
+// K-Touch's USB Vendor ID
+#define VENDOR_ID_K_TOUCH 0x24E3
+// Pegatron's USB Vendor ID
+#define VENDOR_ID_PEGATRON 0x1D4D
+// Archos's USB Vendor ID
+#define VENDOR_ID_ARCHOS 0x0E79
+// Positivo's USB Vendor ID
+#define VENDOR_ID_POSITIVO 0x1662
+// Fujitsu's USB Vendor ID
+#define VENDOR_ID_FUJITSU 0x04C5
+// Lumigon's USB Vendor ID
+#define VENDOR_ID_LUMIGON 0x25E3
+// Quanta's USB Vendor ID
+#define VENDOR_ID_QUANTA 0x0408
+// INQ Mobile's USB Vendor ID
+#define VENDOR_ID_INQ_MOBILE 0x2314
+// Sony's USB Vendor ID
+#define VENDOR_ID_SONY 0x054C
+// Lab126's USB Vendor ID
+#define VENDOR_ID_LAB126 0x1949
+// Yulong Coolpad's USB Vendor ID
+#define VENDOR_ID_YULONG_COOLPAD 0x1EBF
+
+/** built-in vendor list */
+int builtInVendorIds[] = {
+ VENDOR_ID_GOOGLE,
+ VENDOR_ID_INTEL,
+ VENDOR_ID_HTC,
+ VENDOR_ID_SAMSUNG,
+ VENDOR_ID_MOTOROLA,
+ VENDOR_ID_LGE,
+ VENDOR_ID_HUAWEI,
+ VENDOR_ID_ACER,
+ VENDOR_ID_SONY_ERICSSON,
+ VENDOR_ID_FOXCONN,
+ VENDOR_ID_DELL,
+ VENDOR_ID_NVIDIA,
+ VENDOR_ID_GARMIN_ASUS,
+ VENDOR_ID_SHARP,
+ VENDOR_ID_ZTE,
+ VENDOR_ID_KYOCERA,
+ VENDOR_ID_PANTECH,
+ VENDOR_ID_QUALCOMM,
+ VENDOR_ID_OTGV,
+ VENDOR_ID_NEC,
+ VENDOR_ID_PMC,
+ VENDOR_ID_TOSHIBA,
+ VENDOR_ID_SK_TELESYS,
+ VENDOR_ID_KT_TECH,
+ VENDOR_ID_ASUS,
+ VENDOR_ID_PHILIPS,
+ VENDOR_ID_TI,
+ VENDOR_ID_FUNAI,
+ VENDOR_ID_GIGABYTE,
+ VENDOR_ID_IRIVER,
+ VENDOR_ID_COMPAL,
+ VENDOR_ID_T_AND_A,
+ VENDOR_ID_LENOVOMOBILE,
+ VENDOR_ID_LENOVO,
+ VENDOR_ID_VIZIO,
+ VENDOR_ID_K_TOUCH,
+ VENDOR_ID_PEGATRON,
+ VENDOR_ID_ARCHOS,
+ VENDOR_ID_POSITIVO,
+ VENDOR_ID_FUJITSU,
+ VENDOR_ID_LUMIGON,
+ VENDOR_ID_QUANTA,
+ VENDOR_ID_INQ_MOBILE,
+ VENDOR_ID_SONY,
+ VENDOR_ID_LAB126,
+ VENDOR_ID_YULONG_COOLPAD,
+};
+
+#define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0]))
+
+/* max number of supported vendor ids (built-in + 3rd party). increase as needed */
+#define VENDOR_COUNT_MAX 128
+
+int vendorIds[VENDOR_COUNT_MAX];
+unsigned vendorIdCount = 0;
+
+int get_sdb_usb_ini(char* buff, size_t len);
+
+void usb_vendors_init(void)
+{
+ if (VENDOR_COUNT_MAX < BUILT_IN_VENDOR_COUNT) {
+ fprintf(stderr, "VENDOR_COUNT_MAX not big enough for built-in vendor list.\n");
+ exit(2);
+ }
+
+ /* add the built-in vendors at the beginning of the array */
+ memcpy(vendorIds, builtInVendorIds, sizeof(builtInVendorIds));
+
+ /* default array size is the number of built-in vendors */
+ vendorIdCount = BUILT_IN_VENDOR_COUNT;
+
+ if (VENDOR_COUNT_MAX == BUILT_IN_VENDOR_COUNT)
+ return;
+
+ char temp[PATH_MAX];
+ if (get_sdb_usb_ini(temp, sizeof(temp)) == 0) {
+ FILE * f = fopen(temp, "rt");
+
+ if (f != NULL) {
+ /* The vendor id file is pretty basic. 1 vendor id per line.
+ Lines starting with # are comments */
+ while (fgets(temp, sizeof(temp), f) != NULL) {
+ if (temp[0] == '#')
+ continue;
+
+ long value = strtol(temp, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE || value > INT_MAX || value < 0) {
+ fprintf(stderr, "Invalid content in %s. Quitting.\n", ANDROID_SDB_INI);
+ exit(2);
+ }
+
+ vendorIds[vendorIdCount++] = (int)value;
+
+ /* make sure we don't go beyond the array */
+ if (vendorIdCount == VENDOR_COUNT_MAX) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* Utils methods */
+
+/* builds the path to the sdb vendor id file. returns 0 if success */
+int build_path(char* buff, size_t len, const char* format, const char* home)
+{
+ if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_SDB_INI) >= (signed)len) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* fills buff with the path to the sdb vendor id file. returns 0 if success */
+int get_sdb_usb_ini(char* buff, size_t len)
+{
+#ifdef _WIN32
+ const char* home = getenv("ANDROID_SDK_HOME");
+ if (home != NULL) {
+ return build_path(buff, len, "%s\\%s\\%s", home);
+ } else {
+ char path[MAX_PATH];
+ SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, path);
+ return build_path(buff, len, "%s\\%s\\%s", path);
+ }
+#else
+ const char* home = getenv("HOME");
+ if (home == NULL)
+ home = "/tmp";
+
+ return build_path(buff, len, "%s/%s/%s", home);
+#endif
+}
diff --git a/src/usb_vendors.h b/src/usb_vendors.h
new file mode 100644
index 0000000..9e09d9a
--- /dev/null
+++ b/src/usb_vendors.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __USB_VENDORS_H
+#define __USB_VENDORS_H
+
+extern int vendorIds[];
+extern unsigned vendorIdCount;
+
+void usb_vendors_init(void);
+
+#endif
diff --git a/src/usb_windows.c b/src/usb_windows.c
new file mode 100644
index 0000000..37eef1b
--- /dev/null
+++ b/src/usb_windows.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <sdb_api.h>
+#include <stdio.h>
+
+#include "sysdeps.h"
+
+#define TRACE_TAG TRACE_USB
+#include "sdb.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
+ SDBAPIHANDLE sdb_interface;
+
+ /// Handle to USB read pipe (endpoint)
+ SDBAPIHANDLE sdb_read_pipe;
+
+ /// Handle to USB write pipe (endpoint)
+ SDBAPIHANDLE sdb_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
+SDB_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) {
+ sdb_mutex_lock(&usb_lock);
+ ret = known_device_locked(dev_name);
+ sdb_mutex_unlock(&usb_lock);
+ }
+
+ return ret;
+}
+
+int register_new_device(usb_handle* handle) {
+ if (NULL == handle)
+ return 0;
+
+ sdb_mutex_lock(&usb_lock);
+
+ // Check if device is already in the list
+ if (known_device_locked(handle->interface_name)) {
+ sdb_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;
+
+ sdb_mutex_unlock(&usb_lock);
+
+ return 1;
+}
+
+void* device_poll_thread(void* unused) {
+ D("Created device thread\n");
+
+ while(1) {
+ find_devices();
+ sdb_sleep_ms(1000);
+ }
+
+ return NULL;
+}
+
+void usb_init() {
+ sdb_thread_t tid;
+
+ if(sdb_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->sdb_interface = SdbCreateInterfaceByName(interface_name);
+
+ if (NULL == ret->sdb_interface) {
+ free(ret);
+ errno = GetLastError();
+ return NULL;
+ }
+
+ // Open read pipe (endpoint)
+ ret->sdb_read_pipe =
+ SdbOpenDefaultBulkReadEndpoint(ret->sdb_interface,
+ SdbOpenAccessTypeReadWrite,
+ SdbOpenSharingModeReadWrite);
+ if (NULL != ret->sdb_read_pipe) {
+ // Open write pipe (endpoint)
+ ret->sdb_write_pipe =
+ SdbOpenDefaultBulkWriteEndpoint(ret->sdb_interface,
+ SdbOpenAccessTypeReadWrite,
+ SdbOpenSharingModeReadWrite);
+ if (NULL != ret->sdb_write_pipe) {
+ // Save interface name
+ unsigned long name_len = 0;
+
+ // First get expected name length
+ SdbGetInterfaceName(ret->sdb_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 (SdbGetInterfaceName(ret->sdb_interface,
+ ret->interface_name,
+ &name_len,
+ true)) {
+ // We're done at this point
+ return ret;
+ }
+ } else {
+ SetLastError(ERROR_OUTOFMEMORY);
+ }
+ }
+ }
+ }
+
+ // Something went wrong.
+ int saved_errno = GetLastError();
+ usb_cleanup_handle(ret);
+ free(ret);
+ SetLastError(saved_errno);
+
+ return NULL;
+}
+
+int usb_write(usb_handle* handle, const void* data, int len) {
+ unsigned long time_out = 5000;
+ unsigned long written = 0;
+ int ret;
+
+ D("usb_write %d\n", len);
+ if (NULL != handle) {
+ // Perform write
+ ret = SdbWriteEndpointSync(handle->sdb_write_pipe,
+ (void*)data,
+ (unsigned long)len,
+ &written,
+ time_out);
+ int saved_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
+ SdbWriteEndpointSync(handle->sdb_write_pipe,
+ (void*)data,
+ 0,
+ &written,
+ time_out);
+ }
+ return 0;
+ }
+ } else {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (saved_errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ }
+ errno = saved_errno;
+ } 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 = 0;
+ 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 = SdbReadEndpointSync(handle->sdb_read_pipe,
+ (void*)data,
+ (unsigned long)xfer,
+ &read,
+ time_out);
+ int saved_errno = GetLastError();
+ D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno);
+ if (ret) {
+ data += read;
+ len -= read;
+
+ if (len == 0)
+ return 0;
+ } else {
+ // assume ERROR_INVALID_HANDLE indicates we are disconnected
+ if (saved_errno == ERROR_INVALID_HANDLE)
+ usb_kick(handle);
+ break;
+ }
+ errno = saved_errno;
+ }
+ } 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->sdb_write_pipe)
+ SdbCloseHandle(handle->sdb_write_pipe);
+ if (NULL != handle->sdb_read_pipe)
+ SdbCloseHandle(handle->sdb_read_pipe);
+ if (NULL != handle->sdb_interface)
+ SdbCloseHandle(handle->sdb_interface);
+
+ handle->interface_name = NULL;
+ handle->sdb_write_pipe = NULL;
+ handle->sdb_read_pipe = NULL;
+ handle->sdb_interface = NULL;
+ }
+}
+
+void usb_kick(usb_handle* handle) {
+ if (NULL != handle) {
+ sdb_mutex_lock(&usb_lock);
+
+ usb_cleanup_handle(handle);
+
+ sdb_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
+ sdb_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;
+ }
+
+ sdb_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 (!SdbGetUsbDeviceDescriptor(handle->sdb_interface,
+ &device_desc)) {
+ return 0;
+ }
+
+ // Then check interface properties
+ USB_INTERFACE_DESCRIPTOR interf_desc;
+
+ if (!SdbGetUsbInterfaceDescriptor(handle->sdb_interface,
+ &interf_desc)) {
+ return 0;
+ }
+
+ // Must have two endpoints
+ if (2 != interf_desc.bNumEndpoints) {
+ return 0;
+ }
+
+ if (is_sdb_interface(device_desc.idVendor, device_desc.idProduct,
+ interf_desc.bInterfaceClass, interf_desc.bInterfaceSubClass, interf_desc.bInterfaceProtocol)) {
+
+ if(interf_desc.bInterfaceProtocol == 0x01) {
+ SdbEndpointInformation endpoint_info;
+ // assuming zero is a valid bulk endpoint ID
+ if (SdbGetEndpointInformation(handle->sdb_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];
+ SdbInterfaceInfo* next_interface = (SdbInterfaceInfo*)(&entry_buffer[0]);
+ unsigned long entry_buffer_size = sizeof(entry_buffer);
+ char* copy_name;
+
+ // Enumerate all present and active interfaces.
+ SDBAPIHANDLE enum_handle =
+ SdbEnumInterfaces(usb_class_id, true, true, true);
+
+ if (NULL == enum_handle)
+ return;
+
+ while (SdbNextInterface(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 (SdbGetSerialNumber(handle->sdb_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, 1);
+ } 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);
+ }
+
+ SdbCloseHandle(enum_handle);
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..4b510f3
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#define STRING_MAXLEN 1024
+char*
+buff_addc (char* buff, char* buffEnd, int c)
+{
+ int avail = buffEnd - buff;
+
+ if (avail <= 0) /* already in overflow mode */
+ return buff;
+
+ if (avail == 1) { /* overflowing, the last byte is reserved for zero */
+ buff[0] = 0;
+ return buff + 1;
+ }
+
+ buff[0] = (char) c; /* add char and terminating zero */
+ buff[1] = 0;
+ return buff + 1;
+}
+
+char*
+buff_adds (char* buff, char* buffEnd, const char* s)
+{
+ int slen = strlen(s);
+
+ return buff_addb(buff, buffEnd, s, slen);
+}
+
+char*
+buff_addb (char* buff, char* buffEnd, const void* data, int len)
+{
+ int avail = (buffEnd - buff);
+
+ if (avail <= 0 || len <= 0) /* already overflowing */
+ return buff;
+
+ if (len > avail)
+ len = avail;
+
+ memcpy(buff, data, len);
+
+ buff += len;
+
+ /* ensure there is a terminating zero */
+ if (buff >= buffEnd) { /* overflow */
+ buff[-1] = 0;
+ } else
+ buff[0] = 0;
+
+ return buff;
+}
+
+char*
+buff_add (char* buff, char* buffEnd, const char* format, ... )
+{
+ int avail;
+
+ avail = (buffEnd - buff);
+
+ if (avail > 0) {
+ va_list args;
+ int nn;
+
+ va_start(args, format);
+ nn = vsnprintf( buff, avail, format, args);
+ va_end(args);
+
+ if (nn < 0) {
+ /* some C libraries return -1 in case of overflow,
+ * but they will also do that if the format spec is
+ * invalid. We assume SDB is not buggy enough to
+ * trigger that last case. */
+ nn = avail;
+ }
+ else if (nn > avail) {
+ nn = avail;
+ }
+
+ buff += nn;
+
+ /* ensure that there is a terminating zero */
+ if (buff >= buffEnd)
+ buff[-1] = 0;
+ else
+ buff[0] = 0;
+ }
+ return buff;
+}
+
+char *str_trim(const char* string)
+{
+ const char* s = string;
+ const char* e = string + (strlen(string) - 1);
+ char* ret;
+
+ while(*s == ' ' || *s == '\t') { // ltrim
+ s++;
+ }
+ while(*e == ' ' || *e == '\t') { // rtrim
+ e--;
+ }
+
+ ret = strdup(s);
+ ret[e - s + 1] = 0;
+
+ return ret;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..3df520e
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 _SDB_UTILS_H
+#define _SDB_UTILS_H
+
+/* bounded buffer functions */
+
+/* all these functions are used to append data to a bounded buffer.
+ *
+ * after each operation, the buffer is guaranteed to be zero-terminated,
+ * even in the case of an overflow. they all return the new buffer position
+ * which allows one to use them in succession, only checking for overflows
+ * at the end. For example:
+ *
+ * BUFF_DECL(temp,p,end,1024);
+ * char* p;
+ *
+ * p = buff_addc(temp, end, '"');
+ * p = buff_adds(temp, end, string);
+ * p = buff_addc(temp, end, '"');
+ *
+ * if (p >= end) {
+ * overflow detected. note that 'temp' is
+ * zero-terminated for safety.
+ * }
+ * return strdup(temp);
+ */
+
+/* tries to add a character to the buffer, in case of overflow
+ * this will only write a terminating zero and return buffEnd.
+ */
+char* buff_addc (char* buff, char* buffEnd, int c);
+
+/* tries to add a string to the buffer */
+char* buff_adds (char* buff, char* buffEnd, const char* s);
+
+/* tries to add a bytes to the buffer. the input can contain zero bytes,
+ * but a terminating zero will always be appended at the end anyway
+ */
+char* buff_addb (char* buff, char* buffEnd, const void* data, int len);
+
+/* tries to add a formatted string to a bounded buffer */
+char* buff_add (char* buff, char* buffEnd, const char* format, ... );
+
+/* convenience macro used to define a bounded buffer, as well as
+ * a 'cursor' and 'end' variables all in one go.
+ *
+ * note: this doesn't place an initial terminating zero in the buffer,
+ * you need to use one of the buff_ functions for this. or simply
+ * do _cursor[0] = 0 manually.
+ */
+#define BUFF_DECL(_buff,_cursor,_end,_size) \
+ char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size)
+
+char *str_trim(const char* string);
+#endif /* _SDB_UTILS_H */
diff --git a/test/install_udev.sh b/test/install_udev.sh
new file mode 100755
index 0000000..f405a22
--- /dev/null
+++ b/test/install_udev.sh
@@ -0,0 +1,14 @@
+TMP_FILE=99-samsung-device.rules
+echo "# Add a udev rules when you want to develop a Tizen application with your device." >> $TMP_FILE
+echo "# Use this format to add each vendor to the file:" >> $TMP_FILE
+echo "# SUBSYSTEM==\"usb\", ATTR{idVendor}==\"04e8\", ATTRS{idProduct}==\"6864\", MODE=\"0666\", GROUP=\"plugdev\"" >> $TMP_FILE
+echo "# In the example, the vendor ID is for Samsung manufacture. The mode specifies read/write permissions, and group defines which Unix group owns the device node." >> $TMP_FILE
+echo "#" >> $TMP_FILE
+echo "# Contact : Kangho Kim <kh5325.kim@samsung.com>, Yoonki Park<yoonki.park@samsung.com>, Ho Namkoong <ho.namkoong@samsung.com>" >> $TMP_FILE
+echo "# See also udev(7) for an overview of rule syntax." >> $TMP_FILE
+echo "" >> $TMP_FILE
+echo "# Samsung" >> $TMP_FILE
+echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"04e8\", ATTRS{idProduct}==\"6864\", MODE=\"0666\", GROUP=\"plugdev\"" >> $TMP_FILE
+echo "SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"04e8\", ATTRS{idProduct}==\"6863\", MODE=\"0666\", GROUP=\"plugdev\"" >> $TMP_FILE
+chmod +x $TMP_FILE
+#gksudo mv ${TMP_FILE} /etc/udev/rules.d/
diff --git a/test/test_inoti_service.c b/test/test_inoti_service.c
new file mode 100755
index 0000000..b3c63ef
--- /dev/null
+++ b/test/test_inoti_service.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 test program, connects to SDB 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* transport = "host:transport-a";
+ const char* request = "cs:";
+ int len;
+
+ memset( &server, 0, sizeof(server) );
+ server.sin_family = AF_INET;
+ server.sin_port = htons(26099);
+ 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 transport */
+ len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(transport), transport );
+ 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" );
+
+ /* 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 (;;) {
+ memset(buffer, 0, sizeof(buffer));
+ if (unix_read(s, buffer, 30) < 0)
+ panic("could not read length");
+
+ printf( "server answer: %s\n",buffer );
+ break;
+ }
+ close(s);
+}
diff --git a/test/test_track_devices.c b/test/test_track_devices.c
new file mode 100755
index 0000000..64c484f
--- /dev/null
+++ b/test/test_track_devices.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 test program, connects to SDB 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);
+}
diff --git a/test/test_track_jdwp.c b/test/test_track_jdwp.c
new file mode 100644
index 0000000..5187d5f
--- /dev/null
+++ b/test/test_track_jdwp.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 test program, connects to SDB 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);
+}