From: kh5325.kim Date: Tue, 18 Jun 2013 01:27:25 +0000 (+0900) Subject: Upload tizen_2.2 source X-Git-Tag: 2.2.1_release~11 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=cc836453a86e31fdb268b4e987482dda1ae5a918;p=sdk%2Ftools%2Fsdb.git Upload tizen_2.2 source Change-Id: I5f479b03ba18b94fa97910427b1407b59bf40be2 --- diff --git a/Makefile b/Makefile index 5cc84f3..2127c92 100644 --- a/Makefile +++ b/Makefile @@ -4,47 +4,39 @@ # # -HOST_OS := $(shell uname -s | tr A-Z a-z | cut -d'_' -f1) +HOST_OS := $(shell uname -s | tr A-Z a-z) # sdb host tool # ========================================================= -ifeq ($(HOST_OS),darwin) - CC := /Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/bin/llvm-gcc-4.2 -else - CC := gcc -endif +# Default to a virtual (sockets) usb interface +USB_SRCS := +EXTRA_SRCS := -# ifeq ($(HOST_OS),linux) - LOCAL_USB_SRC := src/libusb/linux.c src/sdb_usb.c src/libusb/error.c src/libusb/usb.c src/libusb/descriptors.c - LOCAL_UTIL_SRC := src/utils_unix.c - LOCAL_OTHER_SRC := src/fdevent.c src/fdevent_unix.c - LOCAL_LFLAGS := -lrt -lncurses -lpthread - LOCAL_CFLAGS := -DOS_LINUX -DHAVE_FORKEXEC -DHAVE_TERMIO_H -DHAVE_SYMLINKS -DSDB_HOST=1 -DSDB_HOST_ON_TARGET=1 + USB_SRCS := usb_linux.c + EXTRA_SRCS := get_my_path_linux.c + LOCAL_LDLIBS += -lrt -lncurses -lpthread endif ifeq ($(HOST_OS),darwin) - LOCAL_USB_SRC := src/libusb/darwin.c src/sdb_usb.c src/libusb/error.c src/libusb/usb.c src/libusb/descriptors.c - LOCAL_UTIL_SRC := src/utils_unix.c - LOCAL_OTHER_SRC := src/fdevent.c src/fdevent_unix.c - LOCAL_LFLAGS := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon - LOCAL_CFLAGS := -DOS_DARWIN -DHAVE_FORKEXEC -DHAVE_TERMIO_H -DHAVE_SYMLINKS -mmacosx-version-min=10.4 -DSDB_HOST=1 -DSDB_HOST_ON_TARGET=1 + 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),mingw32) - LOCAL_USB_SRC := src/libusb/windows.c - LOCAL_UTIL_SRC := src/utils_windows.c - LOCAL_OTHER_SRC := src/fdevent.c src/fdevent_windows.c - LOCAL_CFLAGS := -DOS_WINDOWS - LOCAL_IFLAGS := -I/mingw/include/ddk - LOCAL_LFLAGS := -lws2_32 - LOCAL_STATIC_LFLAGS := /mingw/lib/libsetupapi.a +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 \ @@ -53,34 +45,97 @@ SDB_SRC_FILES := \ src/sockets.c \ src/services.c \ src/file_sync_client.c \ - $(LOCAL_USB_SRC) \ - $(LOCAL_UTIL_SRC) \ - $(LOCAL_OTHER_SRC) \ + src/$(EXTRA_SRCS) \ + src/$(USB_SRCS) \ src/utils.c \ - src/strutils.c \ - src/linkedlist.c \ - src/sdb_model.c \ - src/sdb_constants.c \ - src/file_sync_functions.c \ - src/command_function.c - -SDB_CFLAGS := -O2 -g -Wall -Wno-unused-parameter + 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 \ + src/strutils.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 += -Iinclude -Isrc -SDB_CFLAGS += $(LOCAL_CFLAGS) -SDB_LFLAGS := $(LOCAL_LFLAGS) -STATIC_LFLAGS := $(LOCAL_STATIC_LFLAGS) +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 \ + src/strutils.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 -MODULE := sdb +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) - rm -rf $(OBJDIR) mkdir -p $(OBJDIR) - $(CC) $(SDB_CFLAGS) -o $(OBJDIR)/$(MODULE) $(SDB_SRC_FILES) $(LOCAL_IFLAGS) $(SDB_LFLAGS) $(STATIC_LFLAGS) + $(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..d9c8974 --- /dev/null +++ b/Makefile.win @@ -0,0 +1,50 @@ +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 \ + src/strutils.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/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/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 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 , Yoonki Park , Ho Namkoong +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/changelog b/package/changelog index 6010e7a..15f5079 100644 --- a/package/changelog +++ b/package/changelog @@ -1,3 +1,9 @@ +* 2.1.28 +- revert: change app path +== ho.namkoong 2013-05-31 +* 2.1.27 +- change app path +== ho.namkoong 2013-05-31 * 2.1.26 - support debug command == yoonki.park 2013-04-22 diff --git a/package/pkginfo.manifest b/package/pkginfo.manifest index cf3affb..bfa41c4 100644 --- a/package/pkginfo.manifest +++ b/package/pkginfo.manifest @@ -1,4 +1,4 @@ -Version:2.1.26 +Version:2.1.28 Source:sdb Maintainer:Kangho Kim , Yoonki Park, Hyunsik Noh, Gun Kim, Ho Namkoong, Taeyoung Son 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 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 + + 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:' request, + where '' 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: + This is a special query that is sent to the SDB server when a + new emulator starts up. 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: + Ask to switch the connection to the device/emulator identified by + . 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:: + This is a special form of query, where the 'host-serial::' + prefix can be used to indicate that the client is asking the SDB server + for information related to a specific device. can be in one + of the format described below. + +host-usb: + 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: + 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: + 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'. + +:get-product + XXX + +:get-serialno + Returns the serial number of the corresponding device/emulator. + Note that emulator serial numbers are of the form "emulator-5554" + +:get-state + Returns the state of a given device as a string. + +:forward:; + Asks the SDB server to forward local connections from + to the address on a given device. + + There, 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 is one of: + + tcp: -> TCP connection on localhost: + local: -> Unix local domain socket on + + the format of is one of: + + tcp: -> TCP localhost: on device + local: -> Unix local domain socket on device + jdwp: -> JDWP thread on VM process + + 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: + 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. is a full + path from the root of the filesystem. + +tcp: + Tries to connect to tcp port on localhost. + +tcp:: + Tries to connect to tcp port on machine from + the device. This can be useful to debug some networking/proxy + issues that can only be revealed on the device itself. + +local: + Tries to connect to a Unix domain socket on the device + +localreserved: +localabstract: +localfilesystem: + Variants of local: that are used to access other Android + socket namespaces. + +log: + Opens one of the system logs (/dev/log/) 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: + 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(
) on the host and return + the corresponding IP address as a 4-byte string. + +recover: + This service is used to upload a recovery image to the device. + 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: + Connects to the JDWP thread running in the VM of process . + +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: + + : the length of all content as a 4-char hexadecimal string + : a series of ASCII lines of the following format: + "\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//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 + */ +#ifdef __CYGWIN__ +# define HAVE_TERMIO_H +#endif + +/* + * Define this if you have + */ +#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 + */ +/*#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 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 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 exists. + */ +/* #define HAVE_STDINT_H */ + +/* + * Define if exists. + */ +#define HAVE_STDBOOL_H + +/* + * Define if 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..db8a4a9 --- /dev/null +++ b/src/android_reboot.c @@ -0,0 +1,132 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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; + /* 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; + } + 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/command_function.c b/src/command_function.c deleted file mode 100644 index 240833f..0000000 --- a/src/command_function.c +++ /dev/null @@ -1,702 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#include -#include -#include "utils.h" -#include "fdevent.h" -#include "sdb.h" - -#include "commandline.h" -#include "command_function.h" -#include "sdb_client.h" -#include "sdb_constants.h" -#include "file_sync_service.h" - -#include "strutils.h" -#include "file_sync_client.h" -#include "file_sync_functions.h" - -static const char *SDK_TOOL_PATH="/home/developer/sdk_tools"; -static const char *APP_PATH_PREFIX="/opt/apps"; - -static void __inline__ format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial); -static int get_pkgtype_file_name(const char* file_name); -static int get_pkgtype_from_app_id(const char* app_id, void** extargv); -static int kill_gdbserver_if_running(const char* process_cmd, void** extargv); -static int verify_gdbserver_exist(void** extargv); - -int da(int argc, char ** argv, void** extargv) { - char full_cmd[PATH_MAX] = "shell:/usr/bin/da_command"; - - append_args(full_cmd, --argc, (const char**)++argv, PATH_MAX-1); - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - int result = __sdb_command(full_cmd, extargv); - - if(result < 0) { - return 1; - } - return 0; -} - -int oprofile(int argc, char ** argv, void** extargv) { - char full_cmd[PATH_MAX] = "shell:/usr/bin/oprofile_command"; - - append_args(full_cmd, --argc, (const char**)++argv, PATH_MAX- 1); - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - int result = __sdb_command(full_cmd, extargv); - - if(result < 0) { - return 1; - } - return 0; -} - -int launch(int argc, char ** argv, void** extargv) { - int i; - int result = 0; - char pkgid[11] = {0,}; - char exe[512] = {0,}; - char args[512] = {0,}; - int mode = 0; - int port = 0; - int pid = 0; - int type = 0; - char fullcommand[PATH_MAX] = {'s','h','e','l','l',':',}; - char buf[128] = {0,}; - char flag = 0; - - if (argc < 7 || argc > 15 ) { - fprintf(stderr,"usage: sdb launch -p -e -m [-P ] [-attach ] [-t ] []\n"); - return -1; - } - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-p")) { - flag = 'p'; - continue; - } - if (!strcmp(argv[i], "-e")) { - flag = 'e'; - continue; - } - if (!strcmp(argv[i], "-m")) { - flag = 'm'; - continue; - } - if (!strcmp(argv[i], "-P")) { - flag = 'P'; - continue; - } - if (!strcmp(argv[i], "-t")) { - flag = 't'; - continue; - } - if (!strcmp(argv[i], "-attach")) { - flag = 'a'; - continue; - } - D("launch cmd args: %c : %s\n", flag, argv[i]); - - switch (flag) { - case 'p' : - s_strncpy(pkgid, argv[i], sizeof(pkgid)); - flag = 0; - break; - case 'e': - s_strncpy(exe, argv[i], sizeof(exe)); - flag = 0; - break; - case 'm': { - if (!strcmp(argv[i], "run")) { - mode = 0; - } else if (!strcmp(argv[i], "debug")) { - mode = 1; - } else { - fprintf(stderr,"The -m option accepts arguments only run or debug options\n"); - return -1; - } - flag = 0; - break; - } - case 'P': { - if (mode != 1) { - fprintf(stderr,"The -P option should be used in debug mode\n"); - return -1; - } - port = atoi(argv[i]); - flag = 0; - break; - } - case 'a': { - if (mode != 1) { - fprintf(stderr, "The -attach option should be used in debug mode\n"); - return -1; - } - pid = atoi(argv[i]); - flag = 0; - break; - } - case 't': { - char *str = argv[i]; - for (; *str; str++) { - if (!memcmp(str, "gtest", 5)) { - snprintf(buf, sizeof(buf), "export LD_LIBRARY_PATH=%s/gtest/usr/lib && ", SDK_TOOL_PATH); - strncat(fullcommand, buf, sizeof(fullcommand) - 1); - type = 1; - } - if (!memcmp(str, "gcov", 4)) { - snprintf(buf, sizeof(buf), "export GCOV_PREFIX=/tmp/%s/data && export GCOV_PREFIX_STRIP=0 && ", pkgid); - strncat(fullcommand, buf, sizeof(fullcommand) - 1); - type = 2; - } - char *ptr = strstr(str, ","); - if (ptr) { - str = ptr; - } - } - flag = 0; - } - break; - default : { - while (i < argc) { - strncat(args, " ", sizeof(args)-1); - strncat(args, argv[i], sizeof(args)-1); - i++; - } - break; - } - } - } - - if (mode == 0) { - if (type == 0) { - snprintf(buf, sizeof(buf), "/usr/bin/launch_app %s.%s", pkgid, exe); - strncat(fullcommand, buf, sizeof(fullcommand)-1); - } else { - snprintf(buf, sizeof(buf), "%s/%s/bin/%s", APP_PATH_PREFIX, pkgid, exe); - strncat(fullcommand, buf, sizeof(fullcommand)-1); - } - } else if (mode == 1) { - if (verify_gdbserver_exist(extargv) < 0) { - return -1; - } - if (!port) { - fprintf(stderr,"The port number is not valid\n"); - return -1; - } - if (pid) { - snprintf(buf, sizeof(buf), "%s/gdbserver/gdbserver :%d --attach %d", SDK_TOOL_PATH, port, pid); - } else { - snprintf(buf, sizeof(buf), "%s/gdbserver/gdbserver :%d %s/%s/bin/%s", SDK_TOOL_PATH, port, APP_PATH_PREFIX, pkgid, exe); - } - if (kill_gdbserver_if_running(buf, extargv) < 0) { - fprintf(stderr, "Gdbserver is already running on your target.\nAn gdb is going to connect the previous gdbserver process.\n"); - return -1; - } - strncat(fullcommand, buf, sizeof(fullcommand)-1); - } - if (strlen(args) > 1) { - strncat(fullcommand, " ", sizeof(fullcommand)-1); - strncat(fullcommand, args, sizeof(fullcommand)-1); - } - - D("launch command: [%s]\n", fullcommand); - result = __sdb_command(fullcommand, extargv); - sdb_close(result); - - return result; -} - -int devices(int argc, char ** argv, void** extargv) { - char *tmp; - char full_cmd[PATH_MAX]; - - snprintf(full_cmd, sizeof full_cmd, "host:%s", argv[0]); - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - tmp = sdb_query(full_cmd, extargv); - if(tmp) { - printf("List of devices attached \n"); - printf("%s", tmp); - return 0; - } else { - return 1; - } -} - -int __disconnect(int argc, char ** argv, void** extargv) { - char full_cmd[PATH_MAX]; - char* tmp; - - if (argc == 2) { - snprintf(full_cmd, sizeof full_cmd, "host:disconnect:%s", argv[1]); - } else { - snprintf(full_cmd, sizeof full_cmd, "host:disconnect:"); - } - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - tmp = sdb_query(full_cmd, extargv); - if(tmp) { - printf("%s\n", tmp); - return 0; - } else { - return 1; - } -} - -int __connect(int argc, char ** argv, void** extargv) { - char full_cmd[PATH_MAX]; - char * tmp; - snprintf(full_cmd, sizeof full_cmd, "host:connect:%s", argv[1]); - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - tmp = sdb_query(full_cmd, extargv); - if(tmp) { - printf("%s\n", tmp); - return 0; - } else { - return 1; - } -} - -int get_state_serialno(int argc, char ** argv, void** extargv) { - char* serial = (char *)extargv[0]; - transport_type* ttype = (transport_type*)extargv[1]; - char *tmp; - char full_cmd[PATH_MAX]; - - format_host_command(full_cmd, sizeof full_cmd, argv[0], *ttype, serial); - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - tmp = sdb_query(full_cmd, extargv); - if(tmp) { - printf("%s\n", tmp); - return 0; - } else { - return 1; - } -} - -int root(int argc, char ** argv, void** extargv) { - char full_cmd[20]; - snprintf(full_cmd, sizeof(full_cmd), "root:%s", argv[1]); - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - int fd = sdb_connect(full_cmd, extargv); - - if(fd >= 0) { - read_and_dump(fd); - sdb_close(fd); - return 0; - } - return 1; -} - -int status_window(int argc, char ** argv, void** extargv) { - char* serial = (char *)extargv[0]; - transport_type* ttype = (transport_type*)extargv[1]; - - char full_cmd[PATH_MAX]; - char *state = 0; - char *laststate = 0; - - /* silence stderr (It means 2>/dev/null) */ -#ifdef _WIN32 - /* XXX: TODO */ -#else - int fd; - fd = unix_open("/dev/null", O_WRONLY); - dup2(fd, 2); - sdb_close(fd); -#endif - - format_host_command(full_cmd, sizeof full_cmd, "get-state", *ttype, serial); - - for(;;) { - sdb_sleep_ms(250); - - if(state) { - free(state); - state = 0; - } - - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - state = sdb_query(full_cmd, extargv); - - 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); - } - - return 0; -} - -int kill_server(int argc, char ** argv, void** extargv) { - int fd; - fd = _sdb_connect("host:kill", extargv); - if(fd == -1) { - fprintf(stderr,"* server not running *\n"); - return 1; - } - return 0; -} - -int start_server(int argc, char ** argv, void** extargv) { - return sdb_connect("host:start-server", extargv); -} - -int version(int argc, char ** argv, void** extargv) { - transport_type ttype = *(transport_type*)extargv[1]; - - if (ttype == kTransportUsb || ttype == kTransportLocal) { - char* VERSION_QUERY ="shell:rpm -qa | grep sdbd"; - send_shellcommand(VERSION_QUERY, extargv); - } else { - fprintf(stdout, "Smart Development Bridge version %d.%d.%d\n", - SDB_VERSION_MAJOR, SDB_VERSION_MINOR, SDB_SERVER_VERSION); - } - return 0; -} - -int forward(int argc, char ** argv, void** extargv) { - char* serial = (char *)extargv[0]; - transport_type ttype = *(transport_type*)extargv[1]; - - char full_cmd[PATH_MAX]; - char prefix[NAME_MAX]; - - get_host_prefix(prefix, NAME_MAX, ttype, serial, host); - snprintf(full_cmd, sizeof full_cmd, "%sforward:%s;%s",prefix, argv[1], argv[2]); - - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - if(sdb_command(full_cmd, extargv) < 0) { - return 1; - } - return 0; -} - -int dlog(int argc, char ** argv, void** extargv) { - D("dlog with serial: %s\n", (char*)extargv[0]); - - char full_cmd[PATH_MAX] = "shell:/usr/bin/dlogutil"; - - int i; - for(i = 1; i 3 && !strcmp(argv[argc-1], "--with-utf8")) { - D("push with utf8"); - utf8 = 1; - --argc; - } - - for (i=1; i 0) { - strcat(buf, " "); - - /* quote empty strings and strings with spaces */ - int 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, extargv); - 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 { - r = 1; - } - - D("interactive shell loop. return r=%d\n", r); - return r; - } - return 1; -} - -int forkserver(int argc, char** argv, void** extargv) { - if(!strcmp(argv[1], "server")) { - int r = sdb_main(1, get_server_port()); - return r; - } - else { - fprintf(stderr,COMMANDLINE_ERROR_ARG_MISSING, "server", "forkserver"); - return 1; - } -} - -int install(int argc, char **argv, void** extargv) { - - char* srcpath = argv[1]; - const char* filename = sdb_dirstop(srcpath); - - const char* args[2]; - args[0] = DIR_APP_TMP; - char destination[PATH_MAX]; - if (filename) { - filename++; - args[1] = filename; - } else { - args[1] = srcpath; - } - append_args(destination, 2, args, sizeof destination); - - D("Install path%s\n", destination); - int tpk = get_pkgtype_file_name(srcpath); - if (tpk == -1) { - fprintf(stderr, "error: unknown package type\n"); - return 1; - } - - D("Push file: %s to %s\n", srcpath, destination); - int result = do_sync_copy(srcpath, destination, (FILE_FUNC*)&LOCAL_FILE_FUNC, (FILE_FUNC*)&REMOTE_FILE_FUNC, 0, extargv); - - if(result < 0) { - return 1; - } - - const char* SHELL_INSTALL_CMD ="shell:/usr/bin/pkgcmd -i -t %s -p %s -q"; - char full_cmd[PATH_MAX]; - - if(tpk == 1) { - snprintf(full_cmd, sizeof full_cmd, SHELL_INSTALL_CMD, "tpk", destination); - } - else if(tpk == 0){ - snprintf(full_cmd, sizeof full_cmd, SHELL_INSTALL_CMD, "wgt", destination); - } - - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - result = __sdb_command(full_cmd, extargv); - - if(result < 0) { - return 1; - } - - const char* SHELL_REMOVE_CMD = "shell:rm %s"; - snprintf(full_cmd, sizeof full_cmd, SHELL_REMOVE_CMD, destination); - D(COMMANDLINE_MSG_FULL_CMD, "remove", full_cmd); - result = __sdb_command(full_cmd, extargv); - - if(result < 0) { - return 1; - } - - return 0; -} - -int uninstall(int argc, char **argv, void** extargv) { - char* appid = argv[1]; - const char* SHELL_UNINSTALL_CMD ="shell:/usr/bin/pkgcmd -u -t %s -n %s -q"; - char full_cmd[PATH_MAX]; - int result = 0; - int tpk = get_pkgtype_from_app_id(appid, extargv); - if(tpk == 1) { - snprintf(full_cmd, sizeof full_cmd, SHELL_UNINSTALL_CMD, "tpk", appid); - } - else if(tpk == 0){ - snprintf(full_cmd, sizeof full_cmd, SHELL_UNINSTALL_CMD, "wgt", appid); - } - else { - return 1; - } - D(COMMANDLINE_MSG_FULL_CMD, argv[0], full_cmd); - result = __sdb_command(full_cmd, extargv); - - if(result < 0) { - return 1; - } - - return 0; -} - -// Returns 0 if pkg type is wgt. Returns 1 if pkg type is tpk. Returns minus if exception happens. -static int get_pkgtype_file_name(const char* file_name) { - - char* pkg_type; - - int result = -1; - - pkg_type = strrchr(file_name, '.')+1; - if (pkg_type != NULL) { - if(!strcmp(pkg_type, "wgt")) { - result = 0; - } - else if(!strcmp(pkg_type, "tpk")) { - result = 1; - } - } - - return result; -} - -// Returns 0 if pkg type is wgt. Returns 1 if pkg type is tpk. Returns minus if exception happens. -static int get_pkgtype_from_app_id(const char* app_id, void** extargv) { - - char* GET_PKG_TYPE_CMD = "shell:/usr/bin/pkgcmd -l | grep %s | awk '{print $2}'"; - char full_cmd[PATH_MAX]; - snprintf(full_cmd, sizeof full_cmd, GET_PKG_TYPE_CMD, app_id); - - int result = sdb_connect(full_cmd, extargv); - if(result < 0) { - return result; - } - char buf[100] = ""; - - int rl_result = read_line(result, buf, 100); - if(rl_result < 0) { - D("Error to read buffer (fd=%d)\n", rl_result); - return rl_result; - } - - sdb_close(result); - result = -1; - - if(strstr(buf, "[tpk]") != NULL) { - result = 1; - } else if(strstr(buf, "[wgt]") != NULL) { - result = 0; - } - return result; -} - - -/* - * kill gdbserver if running - */ - -static int kill_gdbserver_if_running(const char* process_cmd, void** extargv) { - char cmd[512] = {}; - char buf[512] = {}; - - // hopefully, it is not going to happen, but check executable gdbserver is existed - snprintf(cmd, sizeof(cmd), "shell:/usr/bin/da_command process | grep '%s' | grep -v grep | wc -l", process_cmd); - int result = sdb_connect(cmd, extargv); - - if(result < 0) { - return -1; - } - if (read_line(result, buf, sizeof(buf)) < 0) { - sdb_close(result); - return -1; - } - if(memcmp(buf, "0", 1)) { -/* - // TODO: check cmd return code - snprintf(cmd, sizeof(cmd), "shell:/usr/bin/da_command killapp '%s'", process_cmd); - result = sdb_connect(cmd); - if (read_line(result, buf, sizeof(buf)) < 0) { - sdb_close(result); - return -1; - } -*/ - } - sdb_close(result); - return 1; -} - -static void __inline__ format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial) -{ - char prefix[NAME_MAX]; - get_host_prefix(prefix, NAME_MAX, ttype, serial, host); - snprintf(buffer, buflen, "%s%s", prefix, command); -} - - -/* - * returns -1 if gdbserver exists - */ -static int verify_gdbserver_exist(void** extargv) { - char cmd[512] = {}; - char buf[512] = {}; - - snprintf(cmd, sizeof(cmd), "shell:%s/gdbserver/gdbserver --version 1>/dev/null", SDK_TOOL_PATH); - int result = sdb_connect(cmd, extargv); - - if(result < 0) { - sdb_close(result); - return -1; - } - if (read_line(result, buf, sizeof(buf)) > 0) { - fprintf(stderr, "error: %s\n", buf); - sdb_close(result); - return -1; - } - sdb_close(result); - return result; -} diff --git a/src/command_function.h b/src/command_function.h deleted file mode 100644 index 92e71ae..0000000 --- a/src/command_function.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef COMMAND_FUNCTION_H_ -#define COMMAND_FUNCTION_H_ - -#define TRACE_TAG TRACE_SDB - -int da(int argc, char ** argv, void** extargv); -int oprofile(int argc, char ** argv, void** extargv); -int launch(int argc, char ** argv, void** extargv); -int devices(int argc, char ** argv, void** extargv); -int __disconnect(int argc, char ** argv, void** extargv); -int __connect(int argc, char ** argv, void** extargv); -int get_state_serialno(int argc, char ** argv, void** extargv); -int root(int argc, char ** argv, void** extargv); -int status_window(int argc, char ** argv, void** extargv); -int start_server(int argc, char ** argv, void** extargv); -int kill_server(int argc, char ** argv, void** extargv); -int version(int argc, char ** argv, void** extargv); -int forward(int argc, char ** argv, void** extargv); -int push(int argc, char ** argv, void** extargv); -int pull(int argc, char ** argv, void** extargv); -int dlog(int argc, char ** argv, void** extargv); -int install(int argc, char **argv, void** extargv); -int uninstall(int argc, char **argv, void** extargv); -int forkserver(int argc, char** argv, void** extargv); -int shell(int argc, char ** argv, void** extargv); - -#endif /* COMMAND_FUNCTION_H_ */ diff --git a/src/commandline.c b/src/commandline.c index fafae79..400525b 100755 --- a/src/commandline.c +++ b/src/commandline.c @@ -26,42 +26,135 @@ #include #include -#define TRACE_TAG TRACE_SDB - -#include "fdevent.h" +#include "sysdeps.h" #include "strutils.h" #ifdef HAVE_TERMIO_H #include #endif -#include "utils.h" + +#define TRACE_TAG TRACE_SDB #include "sdb.h" #include "sdb_client.h" #include "file_sync_service.h" +#include "utils.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 get_pkgtype_file_name(const char* file_name); +int get_pkgtype_from_app_id(const char* app_id); +int sdb_command2(const char* cmd); +int launch_app(transport_type transport, char* serial, int argc, char** argv); +void version_sdbd(transport_type ttype, char* serial); +int verify_gdbserver_exist(); +int kill_gdbserver_if_running(); + +static const char *gProductOutPath = NULL; +static const char *APP_PATH_PREFIX="/opt/apps"; +static const char *SDK_TOOL_PATH="/home/developer/sdk_tools"; + +static char *product_file(const char *extra) +{ + int n; + char *x; -#include "linkedlist.h" -#include "sdb_constants.h" -#include "sdb_model.h" -#include "commandline.h" -#include "command_function.h" + if (gProductOutPath == NULL) { + fprintf(stderr, "sdb: Product directory not specified; " + "use -p or define ANDROID_PRODUCT_OUT\n"); + exit(1); + } -static void print_help(LIST_NODE* optlist, LIST_NODE* cmdlist); -static void create_opt_list(LIST_NODE** opt_list); -static void create_cmd_list(LIST_NODE** cmd_list); -static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); + n = strlen(gProductOutPath) + strlen(extra) + 2; + x = malloc(n); + if (x == 0) { + fprintf(stderr, "sdb: Out of memory (product_file())\n"); + exit(1); + } -#ifdef HAVE_TERMIO_H + 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] [parameters]\n" + "\n" + " options:\n" + " -d - direct command to the only connected USB device\n" + " return an error if more than one USB device is present.\n" + " -e - direct command to the only running emulator.\n" + " return an error if more than one emulator is running.\n" + " -s - direct command to the USB device or emulator with\n" + " the given serial number.\n" + " devices - list all connected devices\n" + " connect [:] - connect to a device via TCP/IP\n" + " Port 26101 is used by default if no port number is specified.\n" + " disconnect [[:]] - 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 [--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 [] - copy file/dir from device\n" + " sdb shell - run remote shell interactively\n" + " sdb shell - run remote shell \n" + " sdb dlog [] - view device log\n" + " sdb install - push package file and install it\n" + " sdb uninstall - uninstall this app from the device\n" + " sdb forward - forward socket connections\n" + + " forward spec is : \n" + " tcp:\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 - print: offline | bootloader | device\n" + " sdb get-serialno - print: \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" + " sdb root - switch to root or developer account mode\n" + " 'on' means to root mode, and vice versa" + "\n" + ); +} + +int usage() +{ + help(); + return 1; +} -static __inline__ void stdin_raw_init(int fd, struct termios* tio_save); -static __inline__ void stdin_raw_restore(int fd, struct termios* tio_save); +#ifdef HAVE_TERMIO_H +static struct termios tio_save; -static __inline__ void stdin_raw_init(int fd, struct termios* tio_save) +static void stdin_raw_init(int fd) { struct termios tio; if(tcgetattr(fd, &tio)) return; - memcpy(tio_save, &tio, sizeof(struct termios)); + if(tcgetattr(fd, &tio_save)) return; tio.c_lflag = 0; /* disable CANON, ECHO*, etc */ @@ -73,21 +166,21 @@ static __inline__ void stdin_raw_init(int fd, struct termios* tio_save) tcflush(fd, TCIFLUSH); } -static __inline__ void stdin_raw_restore(int fd, struct termios* tio_save) +static void stdin_raw_restore(int fd) { - tcsetattr(fd, TCSANOW, tio_save); + tcsetattr(fd, TCSANOW, &tio_save); tcflush(fd, TCIFLUSH); } #endif -void read_and_dump(int fd) +static void read_and_dump(int fd) { - char buf[PATH_MAX]; + char buf[4096]; int len; while(fd >= 0) { D("read_and_dump(): pre sdb_read(fd=%d)\n", fd); - len = sdb_read(fd, buf, PATH_MAX); + len = sdb_read(fd, buf, 4096); D("read_and_dump(): post sdb_read(fd=%d): len=%d\n", fd, len); if(len == 0) { break; @@ -102,43 +195,77 @@ void read_and_dump(int fd) } } +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); - void** args = (void**) x; - int fd = *(int*)args[0]; -#ifdef HAVE_TERMIO_H - struct termios* tio_save = args[1]; -#endif - free(args[0]); - free(args); 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", INPUT_FD); - r = unix_read(INPUT_FD, buf, 1024); - D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", INPUT_FD); + 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++){ - if(buf[n] == '\n' || buf[n] == '\r') { - n++; - if(buf[n] == '~') { - n++; - if(buf[n] == '.') { - fprintf(stderr,"\n* disconnect *\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(INPUT_FD, tio_save); - free(tio_save); + stdin_raw_restore(fdi); #endif - free(args); exit(0); - } } + default: + state = 0; } } r = sdb_write(fd, buf, r); @@ -149,406 +276,1715 @@ static void *stdin_read_thread(void *x) return 0; } -int interactive_shell(void** extargv) +int interactive_shell(void) { sdb_thread_t thr; + int fdi, fd; + int *fds; - int fd = sdb_connect("shell:", extargv); + fd = sdb_connect("shell:"); if(fd < 0) { + fprintf(stderr,"error: %s\n", sdb_error()); return 1; } - int* fd_p = malloc(sizeof(int)); - *fd_p = fd; + fdi = 0; //dup(0); + + fds = malloc(sizeof(int) * 2); + fds[0] = fd; + fds[1] = fdi; #ifdef HAVE_TERMIO_H - void** args = (void**)malloc(sizeof(void*)*2); - struct termios tio_save; - stdin_raw_init(INPUT_FD, &tio_save); - struct termios* tio_save_p = (struct termios*)malloc(sizeof(struct termios)); - memcpy(tio_save_p, &tio_save, sizeof(struct termios)); - args[1] = tio_save_p; -#else - void** args = (void**)malloc(sizeof(void*)); + stdin_raw_init(fdi); #endif - args[0] = fd_p; - sdb_thread_create(&thr, stdin_read_thread, args); + sdb_thread_create(&thr, stdin_read_thread, fds); read_and_dump(fd); #ifdef HAVE_TERMIO_H - stdin_raw_restore(INPUT_FD, &tio_save); + stdin_raw_restore(fdi); #endif return 0; } -int send_shellcommand(char* buf, void** extargv) + +static void format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial) { - int fd, ret; - char* serial = (char *)extargv[0]; - transport_type ttype = *(transport_type*)extargv[1]; + 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"; - for(;;) { - fd = sdb_connect(buf, extargv); - if(fd >= 0) - break; - fprintf(stderr,"- waiting for device -\n"); - sdb_sleep_ms(1000); - do_cmd(ttype, serial, "wait-for-device", 0); + 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; - read_and_dump(fd); - ret = sdb_close(fd); - if (ret) - perror("close"); + sprintf(buf,"%s:%d", service, sz); + fd = sdb_connect(buf); + if(fd < 0) { + fprintf(stderr,"error: %s\n", sdb_error()); + return -1; + } - return ret; -} + int opt = CHUNK_SIZE; + opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)); -static int do_cmd(transport_type ttype, char* serial, char *cmd, ...) -{ - char *argv[16]; - int argc; - va_list ap; + total = sz; + ptr = data; - va_start(ap, cmd); - argc = 0; + if(progress) { + char *x = strrchr(service, ':'); + if(x) service = x + 1; + } - if (serial) { - argv[argc++] = "-s"; - argv[argc++] = serial; - } else if (ttype == kTransportUsb) { - argv[argc++] = "-d"; - } else if (ttype == kTransportLocal) { - argv[argc++] = "-e"; + 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"); } - argv[argc++] = cmd; - while((argv[argc] = va_arg(ap, char*)) != 0) { - argc++; + 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; } - 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]); + 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 - return process_cmdline(argc, argv); + 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); + } } -int __sdb_command(const char* cmd, void** extargv) { - int result = sdb_connect(cmd, extargv); +/** 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; - if(result < 0) { - return result; + ts = s; + + alloc_len = 0; + + for( ;*ts != '\0'; ts++) { + alloc_len++; + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + alloc_len++; + } } - D("about to read_and_dump(fd=%d)\n", result); - read_and_dump(result); - D("read_and_dump() done.\n"); - sdb_close(result); + ret = (char *)malloc(alloc_len + 1); - return 0; + ts = s; + dest = ret; + + for ( ;*ts != '\0'; ts++) { + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + *dest++ = '\\'; + } + + *dest++ = *ts; + } + + *dest++ = '\0'; + + return ret; } -const char* get_basename(const char* filename) +/** + * Run ppp in "notty" mode against a resource listed as the first parameter + * eg: + * + * ppp dev:/dev/omap_csmi_tty0 + * + */ +int ppp(int argc, char **argv) { - const char* basename = sdb_dirstop(filename); - if (basename) { - basename++; - return basename; - } else { - return filename; +#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 [ppp opts]\n", + argv[0]); + + return 1; } -} -int __inline__ get_server_port() { - return DEFAULT_SDB_PORT; -} + 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(); -static void create_opt_list(LIST_NODE** opt_list) { + 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; - OPTION* serial = NULL; - create_option(&serial, COMMANDLINE_SERIAL_LONG_OPT, COMMANDLINE_SERIAL_SHORT_OPT, COMMANDLINE_SERIAL_DESC, - COMMANDLINE_SERIAL_DESC_SIZE, COMMANDLINE_SERIAL_ARG_DESC, COMMANDLINE_SERIAL_HAS_ARG); - append(opt_list, serial); + // child side - OPTION* device = NULL; - create_option(&device, COMMANDLINE_DEVICE_LONG_OPT, COMMANDLINE_DEVICE_SHORT_OPT, COMMANDLINE_DEVICE_DESC, - COMMANDLINE_DEVICES_DESC_SIZE, EMPTY_STRING, COMMANDLINE_DEVICE_HAS_ARG); - append(opt_list, device); + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + sdb_close(STDERR_FILENO); + sdb_close(fd); - OPTION* emulator = NULL; - create_option(&emulator, COMMANDLINE_EMULATOR_LONG_OPT, COMMANDLINE_EMULATOR_SHORT_OPT, COMMANDLINE_EMULATOR_DESC, - COMMANDLINE_EMULATOR_DESC_SIZE, EMPTY_STRING, COMMANDLINE_EMULATOR_HAS_ARG); - append(opt_list, emulator); -} - -static void create_cmd_list(LIST_NODE** cmd_list) { + err = execvp("pppd", (char * const *)ppp_args); - COMMAND* devices_cmd = NULL; - create_command(&devices_cmd, COMMANDLINE_DEVICES_NAME, COMMANDLINE_DEVICES_DESC, - COMMANDLINE_DEVICES_DESC_SIZE, EMPTY_STRING, devices, COMMANDLINE_DEVICES_MAX_ARG, COMMANDLINE_DEVICES_MIN_ARG); - append(cmd_list, devices_cmd); - - COMMAND* connect_cmd = NULL; - create_command(&connect_cmd, COMMANDLINE_CONNECT_NAME, COMMANDLINE_CONNECT_DESC, - COMMANDLINE_CONNECT_DESC_SIZE, COMMANDLINE_CONNECT_ARG_DESC, __connect, COMMANDLINE_CONNECT_MAX_ARG, COMMANDLINE_CONNECT_MIN_ARG); - append(cmd_list, connect_cmd); - - COMMAND* disconnect_cmd = NULL; - create_command(&disconnect_cmd, COMMANDLINE_DISCONNECT_NAME, COMMANDLINE_DISCONNECT_DESC, - COMMANDLINE_DISCONNECT_DESC_SIZE, COMMANDLINE_DISCONNECT_ARG_DESC, __disconnect, COMMANDLINE_DISCONNECT_MAX_ARG, COMMANDLINE_DISCONNECT_MIN_ARG); - append(cmd_list, disconnect_cmd); + if (err < 0) { + perror("execing pppd"); + } + exit(-1); + } else { + // parent side - COMMAND* push_cmd = NULL; - create_command(&push_cmd, COMMANDLINE_PUSH_NAME, COMMANDLINE_PUSH_DESC, - COMMANDLINE_PUSH_DESC_SIZE, COMMANDLINE_PUSH_ARG_DESC, push, COMMANDLINE_PUSH_MAX_ARG, COMMANDLINE_PUSH_MIN_ARG); - append(cmd_list, push_cmd); + sdb_close(fd); + return 0; + } +#endif /* !HAVE_WIN32_PROC */ +} - COMMAND* pull_cmd = NULL; - create_command(&pull_cmd, COMMANDLINE_PULL_NAME, COMMANDLINE_PULL_DESC, - COMMANDLINE_PULL_DESC_SIZE, COMMANDLINE_PULL_ARG_DESC, pull, COMMANDLINE_PULL_MAX_ARG, COMMANDLINE_PULL_MIN_ARG); - append(cmd_list, pull_cmd); +static int send_shellcommand(transport_type transport, char* serial, char* buf) +{ + int fd, ret; - COMMAND* shell_cmd = NULL; - create_command(&shell_cmd, COMMANDLINE_SHELL_NAME, COMMANDLINE_SHELL_DESC, - COMMANDLINE_SHELL_DESC_SIZE, COMMANDLINE_SHELL_ARG_DESC, shell, COMMANDLINE_SHELL_MAX_ARG, COMMANDLINE_SHELL_MIN_ARG); - append(cmd_list, shell_cmd); + 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); + } - COMMAND* dlog_cmd = NULL; - create_command(&dlog_cmd, COMMANDLINE_DLOG_NAME, COMMANDLINE_DLOG_DESC, - COMMANDLINE_DLOG_DESC_SIZE, COMMANDLINE_DLOG_ARG_DESC, dlog, COMMANDLINE_DLOG_MAX_ARG, COMMANDLINE_DLOG_MIN_ARG); - append(cmd_list, dlog_cmd); + read_and_dump(fd); + ret = sdb_close(fd); + if (ret) + perror("close"); - COMMAND* install_cmd = NULL; - create_command(&install_cmd, COMMANDLINE_INSTALL_NAME, COMMANDLINE_INSTALL_DESC, - COMMANDLINE_INSTALL_DESC_SIZE, COMMANDLINE_INSTALL_ARG_DESC, install, COMMANDLINE_INSTALL_MAX_ARG, COMMANDLINE_INSTALL_MIN_ARG); - append(cmd_list, install_cmd); + return ret; +} - COMMAND* uninstall_cmd = NULL; - create_command(&uninstall_cmd, COMMANDLINE_UNINSTALL_NAME, COMMANDLINE_UNINSTALL_DESC, - COMMANDLINE_UNINSTALL_DESC_SIZE, COMMANDLINE_UNINSTALL_ARG_DESC, uninstall, COMMANDLINE_UNINSTALL_MAX_ARG, COMMANDLINE_UNINSTALL_MIN_ARG); - append(cmd_list, uninstall_cmd); +static int logcat(transport_type transport, char* serial, int argc, char **argv) +{ + char buf[4096]; - COMMAND* forward_cmd = NULL; - create_command(&forward_cmd, COMMANDLINE_FORWARD_NAME, COMMANDLINE_FORWARD_DESC, - COMMANDLINE_FORWARD_DESC_SIZE, COMMANDLINE_FORWARD_ARG_DESC, forward, COMMANDLINE_FORWARD_MAX_ARG, COMMANDLINE_FORWARD_MIN_ARG); - append(cmd_list, forward_cmd); + snprintf(buf, sizeof(buf), + "shell:/usr/bin/dlogutil"); - COMMAND* help_cmd = NULL; - create_command(&help_cmd, COMMANDLINE_HELP_NAME, COMMANDLINE_HELP_DESC, - COMMANDLINE_HELP_DESC_SIZE, EMPTY_STRING, NULL, 0, 0); - append(cmd_list, help_cmd); +/* + if (!strcmp(argv[0],"longcat")) { + strncat(buf, " -v long", sizeof(buf)-1); + } +*/ - COMMAND* version_cmd = NULL; - create_command(&version_cmd, COMMANDLINE_VERSION_NAME, COMMANDLINE_VERSION_DESC, - COMMANDLINE_VERSION_DESC_SIZE, EMPTY_STRING, version, COMMANDLINE_VERSION_MAX_ARG, COMMANDLINE_VERSION_MIN_ARG); - append(cmd_list, version_cmd); + argc -= 1; + argv += 1; + while(argc-- > 0) { + char *quoted; - COMMAND* sserver_cmd = NULL; - create_command(&sserver_cmd, COMMANDLINE_SSERVER_NAME, COMMANDLINE_SSERVER_DESC, - COMMANDLINE_SSERVER_DESC_SIZE, EMPTY_STRING, start_server, COMMANDLINE_SSERVER_MAX_ARG, COMMANDLINE_SSERVER_MIN_ARG); - append(cmd_list, sserver_cmd); + quoted = dupAndQuote (*argv++); - COMMAND* kserver_cmd = NULL; - create_command(&kserver_cmd, COMMANDLINE_KSERVER_NAME, COMMANDLINE_KSERVER_DESC, - COMMANDLINE_KSERVER_DESC_SIZE, EMPTY_STRING, kill_server, COMMANDLINE_KSERVER_MAX_ARG, COMMANDLINE_KSERVER_MIN_ARG); - append(cmd_list, kserver_cmd); + strncat(buf, " ", sizeof(buf)-1); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + } - COMMAND* gstate_cmd = NULL; - create_command(&gstate_cmd, COMMANDLINE_GSTATE_NAME, COMMANDLINE_GSTATE_DESC, - COMMANDLINE_GSTATE_DESC_SIZE, EMPTY_STRING, get_state_serialno, COMMANDLINE_GSTATE_MAX_ARG, COMMANDLINE_GSTATE_MIN_ARG); - append(cmd_list, gstate_cmd); + send_shellcommand(transport, serial, buf); + return 0; +} - COMMAND* gserial_cmd = NULL; - create_command(&gserial_cmd, COMMANDLINE_GSERIAL_NAME, COMMANDLINE_GSERIAL_DESC, - COMMANDLINE_GSERIAL_DESC_SIZE, EMPTY_STRING, get_state_serialno, COMMANDLINE_GSERIAL_MAX_ARG, COMMANDLINE_GSERIAL_MIN_ARG); - append(cmd_list, gserial_cmd); - - COMMAND* swindow_cmd = NULL; - create_command(&swindow_cmd, COMMANDLINE_SWINDOW_NAME, COMMANDLINE_SWINDOW_DESC, - COMMANDLINE_SWINDOW_DESC_SIZE, EMPTY_STRING, status_window, COMMANDLINE_SWINDOW_MAX_ARG, COMMANDLINE_SWINDOW_MIN_ARG); - append(cmd_list, swindow_cmd); - - COMMAND* root_cmd = NULL; - create_command(&root_cmd, COMMANDLINE_ROOT_NAME, COMMANDLINE_ROOT_DESC, - COMMANDLINE_ROOT_DESC_SIZE, COMMANDLINE_ROOT_ARG_DESC, root, COMMANDLINE_ROOT_MAX_ARG, COMMANDLINE_ROOT_MIN_ARG); - append(cmd_list, root_cmd); - - COMMAND* launch_cmd = NULL; - create_command(&launch_cmd, COMMANDLINE_LAUNCH_NAME, NULL, - 0, EMPTY_STRING, launch, COMMANDLINE_LAUNCH_MAX_ARG, COMMANDLINE_LAUNCH_MIN_ARG); - append(cmd_list, launch_cmd); - - COMMAND* forkserver_cmd = NULL; - create_command(&forkserver_cmd, COMMANDLINE_FORKSERVER_NAME, NULL, - 0, EMPTY_STRING, forkserver, COMMANDLINE_FORKSERVER_MAX_ARG, COMMANDLINE_FORKSERVER_MIN_ARG); - append(cmd_list, forkserver_cmd); - - COMMAND* oprofile_cmd = NULL; - create_command(&oprofile_cmd, COMMANDLINE_OPROFILE_NAME, NULL, - 0, EMPTY_STRING, oprofile, COMMANDLINE_OPROFILE_MAX_ARG, COMMANDLINE_OPROFILE_MIN_ARG); - append(cmd_list, oprofile_cmd); - - COMMAND* da_cmd = NULL; - create_command(&da_cmd , COMMANDLINE_DA_NAME, NULL, - 0, EMPTY_STRING, da, COMMANDLINE_DA_MAX_ARG, COMMANDLINE_DA_MIN_ARG); - append(cmd_list, da_cmd ); -} - -int process_cmdline(int argc, char** argv) { +static int mkdirs(char *path) +{ + int ret; + char *x = path + 1; - transport_type ttype = kTransportAny; - char* serial = NULL; + 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; +} - // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint +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(); - LIST_NODE* cmd_list = NULL; - LIST_NODE* opt_list = NULL; - LIST_NODE* input_opt_list = NULL; + 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; + } - create_cmd_list(&cmd_list); - create_opt_list(&opt_list); - int parsed_argc = parse_opt(argc, argv, opt_list, &input_opt_list); + 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); + } - if(parsed_argc < 0) { + 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; } - D("Parsed %d arguments\n", parsed_argc); + printf("Now unlock your device and confirm the backup operation.\n"); + copy_to_file(fd, outFd); - argc = argc - parsed_argc; - argv = argv + parsed_argc; + sdb_close(fd); + sdb_close(outFd); + return 0; +} - int server_port = get_server_port(); - void* extraarg[3]; - extraarg[2] = &server_port; +static int restore(int argc, char** argv) { + const char* filename; + int fd, tarFd; - INPUT_OPTION* opt_s = get_inputopt(input_opt_list, (char*)COMMANDLINE_SERIAL_SHORT_OPT); - if(opt_s != NULL) { - serial = opt_s->value; + if (argc != 2) return usage(); - char buf[PATH_MAX]; - char *tmp; - snprintf(buf, sizeof(buf), "host:serial-match:%s", serial); + 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); - tmp = sdb_query(buf, extraarg); - if (tmp) { - serial = strdup(tmp); + 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 { - fprintf(stderr, "wrong serial number '%s'\n", serial); - return 1; + path_buf[0] = '\0'; + return NULL; } } - else { - INPUT_OPTION* opt_d = get_inputopt(input_opt_list, (char*)COMMANDLINE_DEVICE_SHORT_OPT); - if(opt_d != NULL) { - ttype = kTransportUsb; +} + +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 { - INPUT_OPTION* opt_e = get_inputopt(input_opt_list, (char*)COMMANDLINE_EMULATOR_SHORT_OPT); - if(opt_e != NULL) { - ttype = kTransportLocal; + } 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(argc > 0) { - if(!strcmp(argv[0], COMMANDLINE_HELP_NAME)) { - print_help(opt_list, cmd_list); - return 1; - } - COMMAND* command = get_command(cmd_list, argv[0]); + if (top != NULL) { + /* The environment pointed to a top directory that works. + */ + strcpy(path_buf, top); + return path_buf; + } - D("process command: %s\n", command->name); - int minargs = command->minargs; - int maxargs = command->maxargs; + /* 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; +} - if(argc < minargs + 1) { - fprintf(stderr, "%s command has following args: %s, and it require at least %d arguments\n", argv[0], command->argdesc, minargs); - return 1; +/* 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 , 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(argc > maxargs + 1 && maxargs > -1) { - fprintf(stderr, "command %s require at most %d arguments\n", argv[0], maxargs); - return 1; + if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) { + fprintf(stderr, "sdb: Couldn't assemble path\n"); + return NULL; } - extraarg[0] = serial; - extraarg[1] = &ttype; - int (*Func)(int, char**, void**) = command->Func; - free_list(cmd_list, NULL); - free_list(input_opt_list, NULL); - free_list(opt_list, NULL); - return Func(argc, argv, extraarg); + strcat(path_buf, OS_PATH_SEPARATOR_STR); + strcat(path_buf, hint); + return path_buf; } - print_help(opt_list, cmd_list); - return 1; + /* 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; } -static void print_help(LIST_NODE* optlist, LIST_NODE* cmdlist) { - - fprintf(stderr, "Smart Development Bridge version %d.%d.%d\n", - SDB_VERSION_MAJOR, SDB_VERSION_MINOR, SDB_SERVER_VERSION); - fprintf(stderr, "\n Usage : sdb [option] [parameters]\n\n"); - fprintf(stderr, " options:\n"); - - LIST_NODE* curptr = optlist; - int append_len = strlen(HELP_APPEND_STR); - char* append_str = (char*)malloc(sizeof(char)*append_len); - char* help_str = (char*)malloc(sizeof(char)*append_len*3); - while(curptr != NULL) { - OPTION* opt = (OPTION*)curptr->data; - const char** des = opt->desc; - if(des != NULL) { - snprintf(help_str, append_len*3, " -%s, --%s %s", opt->shortopt, opt->longopt, opt->argdesc); - int opt_len = strlen(help_str); - if(opt_len >= append_len) { - fprintf(stderr, "%s\n%s", help_str, HELP_APPEND_STR); - } - else { - snprintf(append_str, append_len - opt_len + 1, "%s", HELP_APPEND_STR); - fprintf(stderr, "%s%s", help_str, append_str); - } - } +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 - int array_len = opt->desc_size; - fprintf(stderr, "- %s\n", des[0]); - int i = 1; - for(; i< array_len; i++) { - fprintf(stderr, "%s %s\n", HELP_APPEND_STR, des[i]); + 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(); } - curptr = curptr->next_ptr; } - fprintf(stderr, "\n commands:\n"); - curptr = cmdlist; - while(curptr != NULL) { - COMMAND* cmd = (COMMAND*)curptr ->data; - const char** des = cmd->desc; - if(des != NULL) { - snprintf(help_str, append_len*3, " sdb %s %s", cmd->name, cmd->argdesc); - int cmd_len = strlen(help_str); - if(cmd_len >= append_len) { - fprintf(stderr, "%s\n%s", help_str, HELP_APPEND_STR); + /* 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; } - else { - snprintf(append_str, append_len - cmd_len + 1, "%s", HELP_APPEND_STR); - fprintf(stderr, "%s%s", help_str, append_str); + gProductOutPath = find_product_out_path(product); + if (gProductOutPath == NULL) { + fprintf(stderr, "sdb: could not resolve \"-p %s\"\n", + product); + return usage(); } - int array_len = cmd->desc_size; - fprintf(stderr, "- %s\n", des[0]); - int i = 1; - for(; i< array_len; i++) { - fprintf(stderr, "%s %s\n", HELP_APPEND_STR, des[i]); + } 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++; } - } - curptr = curptr->next_ptr; - } + } 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 [:]\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 [[:]]\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], "root")) { + char command[100]; + + if (argc != 2) { + return usage(); + } + if (strcmp(argv[1], "on") != 0 && strcmp(argv[1], "off") != 0) { + return usage(); + } + snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]); + int fd = sdb_connect(command); + if(fd >= 0) { + read_and_dump(fd); + sdb_close(fd); + return 0; + } + //TODO: it may be here due to version mis-match + 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= 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/usr/apps/tmp/%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); + } + + int tpk = get_pkgtype_file_name(srcpath); + if (tpk == -1) { + fprintf(stderr, "error: unknown package type\n"); + return -1; + } + 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:/usr/bin/pkgcmd -i -t %s -p %s -q"; + char full_cmd[PATH_MAX]; + + if(tpk == 1) { + snprintf(full_cmd, sizeof full_cmd, SHELL_INSTALL_CMD, "tpk", destination); + } + else if(tpk == 0){ + snprintf(full_cmd, sizeof full_cmd, SHELL_INSTALL_CMD, "wgt", destination); + } + else { + return tpk; + } + 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:/usr/bin/pkgcmd -u -t %s -n %s -q"; + char full_cmd[PATH_MAX]; + int result = 0; + int tpk = get_pkgtype_from_app_id(appid); + if(tpk == 1) { + snprintf(full_cmd, sizeof full_cmd, SHELL_UNINSTALL_CMD, "tpk", appid); + } + else if(tpk == 0){ + snprintf(full_cmd, sizeof full_cmd, SHELL_UNINSTALL_CMD, "wgt", appid); + } + else { + return tpk; + } + 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); +} + +// Returns 0 if pkg type is wgt. Returns 1 if pkg type is tpk. Returns minus if exception happens. +int get_pkgtype_file_name(const char* file_name) { + + char* pkg_type; + + int result = -1; + + pkg_type = strrchr(file_name, '.')+1; + if (pkg_type != NULL) { + if(!strcmp(pkg_type, "wgt")) { + result = 0; + } + else if(!strcmp(pkg_type, "tpk")) { + result = 1; + } + } + + return result; +} + +// Returns 0 if pkg type is wgt. Returns 1 if pkg type is tpk. Returns minus if exception happens. +int get_pkgtype_from_app_id(const char* app_id) { + + char* GET_PKG_TYPE_CMD = "shell:/usr/bin/pkgcmd -l | grep %s | awk '{print $2}'"; + char full_cmd[PATH_MAX]; + snprintf(full_cmd, sizeof full_cmd, GET_PKG_TYPE_CMD, app_id); + + int result = sdb_connect(full_cmd); + if(result < 0) { + return result; + } + char buf[100] = ""; + + int rl_result = read_line(result, buf, 100); + if(rl_result < 0) { + D("Error to read buffer (fd=%d)\n", rl_result); + return rl_result; + } + + sdb_close(result); + result = -1; + + if(strstr(buf, "[tpk]") != NULL) { + result = 1; + } else if(strstr(buf, "[wgt]") != NULL) { + result = 0; + } + return result; +} + +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; +} + +int launch_app(transport_type transport, char* serial, int argc, char** argv) +{ + int i; + int result = 0; + char pkgid[11] = {0,}; + char exe[512] = {0,}; + char args[512] = {0,}; + int mode = 0; + int port = 0; + int pid = 0; + int type = 0; + char fullcommand[4096] = {'s','h','e','l','l',':',}; + char buf[128] = {0,}; + char flag = 0; + + if (argc < 7 || argc > 15 ) { + fprintf(stderr,"usage: sdb launch -p -e -m [-P ] [-attach ] [-t ] []\n"); + return -1; + } + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-p")) { + flag = 'p'; + continue; + } + if (!strcmp(argv[i], "-e")) { + flag = 'e'; + continue; + } + if (!strcmp(argv[i], "-m")) { + flag = 'm'; + continue; + } + if (!strcmp(argv[i], "-P")) { + flag = 'P'; + continue; + } + if (!strcmp(argv[i], "-t")) { + flag = 't'; + continue; + } + if (!strcmp(argv[i], "-attach")) { + flag = 'a'; + continue; + } + D("launch cmd args: %c : %s\n", flag, argv[i]); + + switch (flag) { + case 'p' : + s_strncpy(pkgid, argv[i], sizeof(pkgid)); + flag = 0; + break; + case 'e': + s_strncpy(exe, argv[i], sizeof(exe)); + flag = 0; + break; + case 'm': { + if (!strcmp(argv[i], "run")) { + mode = 0; + } else if (!strcmp(argv[i], "debug")) { + mode = 1; + } else { + fprintf(stderr,"The -m option accepts arguments only run or debug options\n"); + return -1; + } + flag = 0; + break; + } + case 'P': { + if (mode != 1) { + fprintf(stderr,"The -P option should be used in debug mode\n"); + return -1; + } + port = atoi(argv[i]); + flag = 0; + break; + } + case 'a': { + if (mode != 1) { + fprintf(stderr, "The -attach option should be used in debug mode\n"); + return -1; + } + pid = atoi(argv[i]); + flag = 0; + break; + } + case 't': { + char *str = argv[i]; + for (; *str; str++) { + if (!memcmp(str, "gtest", 5)) { + snprintf(buf, sizeof(buf), "export LD_LIBRARY_PATH=%s/gtest/usr/lib && ", SDK_TOOL_PATH); + strncat(fullcommand, buf, sizeof(fullcommand) - 1); + type = 1; + } + if (!memcmp(str, "gcov", 4)) { + snprintf(buf, sizeof(buf), "export GCOV_PREFIX=/tmp/%s/data && export GCOV_PREFIX_STRIP=0 && ", pkgid); + strncat(fullcommand, buf, sizeof(fullcommand) - 1); + type = 2; + } + char *ptr = strstr(str, ","); + if (ptr) { + str = ptr; + } + } + flag = 0; + } + break; + default : { + while (i < argc) { + strncat(args, " ", sizeof(args)-1); + strncat(args, argv[i], sizeof(args)-1); + i++; + } + break; + } + } + } + + if (mode == 0) { + if (type == 0) { + snprintf(buf, sizeof(buf), "/usr/bin/launch_app %s.%s", pkgid, exe); + strncat(fullcommand, buf, sizeof(fullcommand)-1); + } else { + snprintf(buf, sizeof(buf), "%s/%s/bin/%s", APP_PATH_PREFIX, pkgid, exe); + strncat(fullcommand, buf, sizeof(fullcommand)-1); + } + } else if (mode == 1) { + if (verify_gdbserver_exist() < 0) { + return -1; + } + if (!port) { + fprintf(stderr,"The port number is not valid\n"); + return -1; + } + if (pid) { + snprintf(buf, sizeof(buf), "%s/gdbserver/gdbserver :%d --attach %d", SDK_TOOL_PATH, port, pid); + } else { + snprintf(buf, sizeof(buf), "%s/gdbserver/gdbserver :%d %s/%s/bin/%s", SDK_TOOL_PATH, port, APP_PATH_PREFIX, pkgid, exe); + } + if (kill_gdbserver_if_running(buf) < 0) { + fprintf(stderr, "Gdbserver is already running on your target.\nAn gdb is going to connect the previous gdbserver process.\n"); + return -1; + } + strncat(fullcommand, buf, sizeof(fullcommand)-1); + } + if (strlen(args) > 1) { + strncat(fullcommand, " ", sizeof(fullcommand)-1); + strncat(fullcommand, args, sizeof(fullcommand)-1); + } + + D("launch command: [%s]\n", fullcommand); + result = sdb_command2(fullcommand); + + if(result < 0) { + fprintf(stderr, "error: %s\n", sdb_error()); + } + sdb_close(result); + + return result; + +} + +/* + * kill gdbserver if running + */ + +int kill_gdbserver_if_running(const char* process_cmd) { + char cmd[512] = {}; + char buf[512] = {}; + + // hopefully, it is not going to happen, but check executable gdbserver is existed + snprintf(cmd, sizeof(cmd), "shell:/usr/bin/da_command process | grep '%s' | grep -v grep | wc -l", process_cmd); + int result = sdb_connect(cmd); + + if(result < 0) { + fprintf(stderr, "error: %s\n", sdb_error()); + return -1; + } + if (read_line(result, buf, sizeof(buf)) < 0) { + sdb_close(result); + return -1; + } + if(memcmp(buf, "0", 1)) { +/* + // TODO: check cmd return code + snprintf(cmd, sizeof(cmd), "shell:/usr/bin/da_command killapp '%s'", process_cmd); + result = sdb_connect(cmd); + if (read_line(result, buf, sizeof(buf)) < 0) { + sdb_close(result); + return -1; + } +*/ + } + sdb_close(result); + return 1; +} + +/* + * returns -1 if gdbserver exists + */ +int verify_gdbserver_exist() { + char cmd[512] = {}; + char buf[512] = {}; + + snprintf(cmd, sizeof(cmd), "shell:%s/gdbserver/gdbserver --version 1>/dev/null", SDK_TOOL_PATH); + int result = sdb_connect(cmd); + + if(result < 0) { + fprintf(stderr, "error: %s\n", sdb_error()); + sdb_close(result); + return -1; + } + if (read_line(result, buf, sizeof(buf)) > 0) { + fprintf(stderr, "error: %s\n", buf); + sdb_close(result); + return -1; + } + sdb_close(result); + return result; +} + +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/commandline.h b/src/commandline.h deleted file mode 100644 index ea018a9..0000000 --- a/src/commandline.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef COMMANDLINE_H_ -#define COMMANDLINE_H_ - -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -#ifndef NAME_MAX -#define NAME_MAX 255 -#endif - -#ifndef MAX_INPUT -#define MAX_INPUT 255 -#endif - -#ifndef INPUT_FD -#define INPUT_FD 0 -#endif - -int send_shellcommand(char* buf, void** extargv); -int process_cmdline(int argc, char** argv); -void read_and_dump(int fd); -int interactive_shell(void** extargv); -int __inline__ get_server_port(); -int __sdb_command(const char* cmd, void** extargv); - -#endif /* COMMANDLINE_H_ */ 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 + +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 index 32dd05f..4171acb 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include #include @@ -25,30 +27,23 @@ #include #include -#include "fdevent_backend.h" +#include "fdevent.h" #include "transport.h" -#include "utils.h" - -#if defined(OS_LINUX) -const struct fdevent_os_backend* fdevent_backend = &fdevent_unix_backend; -#elif defined(OS_DARWIN) -const struct fdevent_os_backend* fdevent_backend = &fdevent_unix_backend; -#elif defined(OS_WINDOWS) -const struct fdevent_os_backend* fdevent_backend = &fdevent_windows_backend; -#define WIN32_FH_BASE 100 -#else -#error "unsupported OS" -#endif +#include "sysdeps.h" -fdevent list_pending = { - .next = &list_pending, - .prev = &list_pending, -}; -fdevent **fd_table = 0; -int fd_table_max = 0; +/* !!! 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; -void _fatal(const char *fn, const char *fmt, ...) +static void fatal(const char *fn, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -58,28 +53,395 @@ void _fatal(const char *fn, const char *fmt, ...) abort(); } -void fdevent_register(fdevent *fde) +#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 + +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 +#else +#include +#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) { -#if defined(OS_WINDOWS) - int fd = fde->fd - WIN32_FH_BASE; + 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 - int fd = fde->fd; +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 - if(fd < 0) { +static void fdevent_register(fdevent *fde) +{ + if(fde->fd < 0) { FATAL("bogus negative fd (%d)\n", fde->fd); } - if(fd >= fd_table_max) { + 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_backend->fdevent_init(); + fdevent_init(); fd_table_max = 256; } - while(fd_table_max <= fd) { + while(fd_table_max <= fde->fd) { fd_table_max *= 2; } fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max); @@ -89,25 +451,20 @@ void fdevent_register(fdevent *fde) memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax)); } - fd_table[fd] = fde; + fd_table[fde->fd] = fde; } -void fdevent_unregister(fdevent *fde) +static void fdevent_unregister(fdevent *fde) { -#if defined(OS_WINDOWS) - int fd = fde->fd - WIN32_FH_BASE; -#else - int fd = fde->fd; -#endif - if((fd < 0) || (fd >= fd_table_max)) { + if((fde->fd < 0) || (fde->fd >= fd_table_max)) { FATAL("fd out of range (%d)\n", fde->fd); } - if(fd_table[fd] != fde) { - FATAL("fd_table out of sync"); + if(fd_table[fde->fd] != fde) { + FATAL("fd_table out of sync [%d]\n", fde->fd); } - fd_table[fd] = 0; + fd_table[fde->fd] = 0; if(!(fde->state & FDE_DONT_CLOSE)) { dump_fde(fde, "close"); @@ -115,7 +472,7 @@ void fdevent_unregister(fdevent *fde) } } -void fdevent_plist_enqueue(fdevent *node) +static void fdevent_plist_enqueue(fdevent *node) { fdevent *list = &list_pending; @@ -125,7 +482,7 @@ void fdevent_plist_enqueue(fdevent *node) list->prev = node; } -void fdevent_plist_remove(fdevent *node) +static void fdevent_plist_remove(fdevent *node) { node->prev->next = node->next; node->next->prev = node->prev; @@ -133,7 +490,7 @@ void fdevent_plist_remove(fdevent *node) node->prev = 0; } -fdevent *fdevent_plist_dequeue(void) +static fdevent *fdevent_plist_dequeue(void) { fdevent *list = &list_pending; fdevent *node = list->next; @@ -148,6 +505,74 @@ fdevent *fdevent_plist_dequeue(void) 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)); @@ -175,12 +600,12 @@ void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) fde->func = func; fde->arg = arg; -#ifndef OS_WINDOWS +#ifndef HAVE_WINSOCK fcntl(fd, F_SETFL, O_NONBLOCK); #endif fdevent_register(fde); dump_fde(fde, "connect"); - fdevent_backend->fdevent_connect(fde); + fdevent_connect(fde); fde->state |= FDE_ACTIVE; } @@ -191,7 +616,7 @@ void fdevent_remove(fdevent *fde) } if(fde->state & FDE_ACTIVE) { - fdevent_backend->fdevent_disconnect(fde); + fdevent_disconnect(fde); dump_fde(fde, "disconnect"); fdevent_unregister(fde); } @@ -208,7 +633,7 @@ void fdevent_set(fdevent *fde, unsigned events) if((fde->state & FDE_EVENTMASK) == events) return; if(fde->state & FDE_ACTIVE) { - fdevent_backend->fdevent_update(fde, events); + fdevent_update(fde, events); dump_fde(fde, "update"); } @@ -227,15 +652,47 @@ void fdevent_set(fdevent *fde, unsigned events) } } -void fdevent_add(fdevent *fde, unsigned events) { - fdevent_set(fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK)); +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_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_backend->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 index 6bb2d4b..a6db9ea 100644 --- a/src/fdevent.h +++ b/src/fdevent.h @@ -25,14 +25,6 @@ #define FDE_ERROR 0x0004 #define FDE_TIMEOUT 0x0008 - -#define FDE_EVENTMASK 0x00ff -#define FDE_STATEMASK 0xff00 - -#define FDE_ACTIVE 0x0100 -#define FDE_PENDING 0x0200 -#define FDE_CREATED 0x0400 - /* features that may be set (via the events set/add/del interface) */ #define FDE_DONT_CLOSE 0x0080 diff --git a/src/fdevent_backend.h b/src/fdevent_backend.h deleted file mode 100644 index 3213f58..0000000 --- a/src/fdevent_backend.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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_I_H -#define __FDEVENT_I_H - -#include "fdevent.h" - -extern fdevent list_pending; -extern fdevent **fd_table; -extern int fd_table_max; - -void fdevent_plist_enqueue(fdevent *node); -void fdevent_plist_remove(fdevent *node); -fdevent *fdevent_plist_dequeue(void); -void fdevent_register(fdevent *fde); -void fdevent_unregister(fdevent *fde); - -void _fatal(const char *fn, const char *fmt, ...); -#define FATAL(x...) _fatal(__FUNCTION__, x) - -#if DEBUG -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 - -struct fdevent_os_backend { - // human-readable name - const char *name; - void (*fdevent_loop)(void); - void (*fdevent_init)(void); - void (*fdevent_connect)(fdevent *fde); - void (*fdevent_disconnect)(fdevent *fde); - void (*fdevent_update)(fdevent *fde, unsigned events); -}; - -extern const struct fdevent_os_backend* fdevent_backend; -extern const struct fdevent_os_backend fdevent_unix_backend; -extern const struct fdevent_os_backend fdevent_windows_backend; - -#endif diff --git a/src/fdevent_unix.c b/src/fdevent_unix.c deleted file mode 100644 index d5b6807..0000000 --- a/src/fdevent_unix.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * 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 - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "fdevent_backend.h" -#include "transport.h" -#include "utils.h" - -#define D(...) ((void)0) -// 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; - -#include - - -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; -} - -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); - } -} - -static 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); -} - -static 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); - } - } -} - -const struct fdevent_os_backend fdevent_unix_backend = { - .name = "unix fdevent", - .fdevent_init = _fdevent_init, - .fdevent_connect = _fdevent_connect, - .fdevent_disconnect = _fdevent_disconnect, - .fdevent_update = _fdevent_update, - .fdevent_loop = _fdevent_loop -}; diff --git a/src/fdevent_windows.c b/src/fdevent_windows.c deleted file mode 100644 index 5f750a4..0000000 --- a/src/fdevent_windows.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "utils.h" -#include "utils_backend.h" -#include "fdevent_backend.h" -#include "transport.h" - -#define D(...) ((void)0) - -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" ); - //TODO: - } - } - 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; - } -} - -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 ); - } -} - -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); - } - } -} - - -const struct fdevent_os_backend fdevent_windows_backend = { - .name = "windows fdevent", - .fdevent_init = _fdevent_init, - .fdevent_connect = _fdevent_connect, - .fdevent_disconnect = _fdevent_disconnect, - .fdevent_update = _fdevent_update, - .fdevent_loop = _fdevent_loop -}; - - diff --git a/src/file_sync_client.c b/src/file_sync_client.c old mode 100644 new mode 100755 index b5c3e54..0a1c4ab --- a/src/file_sync_client.c +++ b/src/file_sync_client.c @@ -1,274 +1,1053 @@ /* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ + * 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 #include -#include +#include +#include +#include +#include +#include +#include #include -#include +#include +// tizen specific #include + +#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); -#include "file_sync_client.h" -#include "file_sync_functions.h" -#include "utils.h" -#include "strutils.h" +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 __inline__ void finalize(int srcfd, int dstfd, FILE_FUNC* srcF, FILE_FUNC* dstF); +static void END(const char* filename) +{ + long long t = NOW() - start_time; + if(total_bytes == 0) return; -__inline__ void create_copy_info(COPY_INFO** info, char* srcp, char* dstp) { - *info = (COPY_INFO*)malloc(sizeof(COPY_INFO)); - (*info)->src = srcp; - (*info)->dst = dstp; + 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); } -static __inline__ void finalize(int srcfd, int dstfd, FILE_FUNC* srcF, FILE_FUNC* dstF) { - srcF->finalize(srcfd); - dstF->finalize(dstfd); + +void sync_quit(int fd) +{ + syncmsg msg; + + msg.req.id = ID_QUIT; + msg.req.namelen = 0; + + writex(fd, &msg.req, sizeof(msg.req)); } -static int file_copy(int src_fd, int dst_fd, char* srcp, char* dstp, FILE_FUNC* srcF, FILE_FUNC* dstF, unsigned* total_bytes) { - void* srcstat; - if(srcF->_stat(src_fd, srcp, &srcstat, 1) < 0) { +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; } - src_fd = srcF->readopen(src_fd, srcp, srcstat); - if(src_fd < 0) { + if(readx(fd, &msg.stat, sizeof(msg.stat))) { return -1; } - dst_fd = dstF->writeopen(dst_fd, dstp, srcstat); - if(dst_fd < 0) { + if(msg.stat.id != ID_STAT) { return -1; } - FILE_BUFFER srcbuf; - srcbuf.id = ID_DATA; + *timestamp = ltohl(msg.stat.time); + return 0; +} + +static int sync_start_readtime(int fd, const char *path) +{ + syncmsg msg; + int len = strlen(path); - while(1) { - int ret = srcF->readfile(src_fd, srcp, srcstat, &srcbuf); - if(ret == 0) { + 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; } - else if(ret == 1) { - ret = dstF->writefile(dst_fd, dstp, &srcbuf, total_bytes); - if(ret < 0) { - srcF->readclose(src_fd); - dstF->writeclose(dst_fd, dstp, srcstat); - return -1; - } + + sbuf->size = htoll(ret); + if(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){ + err = -1; + break; } - else if(ret == 2) { - continue; + 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; } - else if(ret == 3) { - ret = dstF->writefile(dst_fd, dstp, &srcbuf, total_bytes); - if(ret < 0) { - srcF->readclose(src_fd); - dstF->writeclose(dst_fd, dstp, srcstat); - return -1; - } + + memcpy(sbuf->data, &file_buffer[total], count); + sbuf->size = htoll(count); + if(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){ + err = -1; break; } - else { - srcF->readclose(src_fd); - dstF->writeclose(dst_fd, dstp, srcstat); - if(ret == 4) { - return 0; - } + 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++; } - srcF->readclose(src_fd); - dstF->writeclose(dst_fd, dstp, srcstat); - free(srcstat); - return 1; + return 0; } -static void free_copyinfo(void* data) { - COPY_INFO* info = (COPY_INFO*)data; - if(info != NULL) { - if(info->src != NULL) { - free(info->src); +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; } - if(info->dst != NULL) { - free(info->dst); + + 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; } - free(info); + 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; } -int do_sync_copy(char* srcp, char* dstp, FILE_FUNC* srcF, FILE_FUNC* dstF, int is_utf8, void** ext_argv) { - unsigned total_bytes = 0; - long long start_time = NOW(); - int src_fd = 0; - int dst_fd = 0; +/* --- */ - void* srcstat = NULL; - void* dststat = NULL; - int pushed = 0; - int skiped = 0; - src_fd = srcF->initialize(srcp, ext_argv); - dst_fd = dstF->initialize(dstp, ext_argv); - if(src_fd < 0 || dst_fd < 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(srcF->_stat(src_fd, srcp, &srcstat, 1) < 0) { - finalize(src_fd, dst_fd, srcF, dstF); + + if(sync_ls(fd, path, do_sync_ls_cb, 0)) { return 1; + } else { + sync_quit(fd); + return 0; } - int src_dir = srcF->is_dir(srcp, srcstat, 1); - int dst_dir = 1; +} - if(dstF->_stat(dst_fd, dstp, &dststat, 0) >= 0) { - dst_dir = dstF->is_dir(dstp, dststat, 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(); } - free(dststat); - free(srcstat); - if(src_dir == -1 || dst_dir == -1) { - finalize(src_fd, dst_fd, srcF, dstF); - return 1; + + 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; } - //copy file - else if(src_dir == 0) { - /* if we're copying a local file to a remote directory, - ** we *really* want to copy to remotedir + "/" + localfilename - */ - if(dst_dir == 1) { - char* src_filename = get_filename(srcp); - char full_dstpath[PATH_MAX]; - append_file(full_dstpath, dstp, src_filename); - - if(is_utf8 != 0) { - dstp = ansi_to_utf8(full_dstpath); + + 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; } - else { - dstp = full_dstpath; + 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; } } - int result = file_copy(src_fd, dst_fd, srcp, dstp, srcF, dstF, &total_bytes); - if(result < 0) { - finalize(src_fd, dst_fd, srcF, dstF); - return 1; + } + + 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, ×tamp, &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; + } } - if(result == 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 { - skiped++; + } 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; } } - else if(src_dir == 2) { - skiped++; + return 0; +cleanup: + if (tmp != NULL) { + free(tmp); } - //copy directory - else { - LIST_NODE* dir_list = NULL; - //for free later, do strncpy - int len = strlen(srcp); - char* _srcp = (char*)malloc(sizeof(char)*len + 1); - s_strncpy(_srcp, srcp, len+1); + if (utf8 != NULL) { + free(utf8); + } + return ret; +} - len = strlen(dstp); - char* _dstp = (char*)malloc(sizeof(char)*len + 1); - s_strncpy(_dstp, dstp, len+1); - COPY_INFO* __info; - create_copy_info(&__info, _srcp, _dstp); - append(&dir_list, __info); +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; + } - while(dir_list != NULL) { - LIST_NODE* entry_list = NULL; - COPY_INFO* _info = (COPY_INFO*)dir_list->data; - if(srcF->get_dirlist(src_fd, _info->src, _info->dst, &entry_list) < 0) { - finalize(src_fd, dst_fd, srcF, dstF); + 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, ×tamp, &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; } - LIST_NODE* curptr = entry_list; + pulled++; + } else { + skipped++; + } + free(ci); + } - while(curptr != NULL) { - COPY_INFO* info = (COPY_INFO*)curptr->data; - char* src_p = (char*)info->src; - char* dst_p = (char*)info->dst; + fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n", + pulled, (pulled == 1) ? "" : "s", + skipped, (skipped == 1) ? "" : "s"); - if(srcF->_stat(src_fd, src_p, &srcstat, 1) < 0) { - finalize(src_fd, dst_fd, srcF, dstF); - return 1; - } + return 0; +} - src_dir = srcF->is_dir(src_p, srcstat, 1); - free(srcstat); - if(src_dir < 0) { - finalize(src_fd, dst_fd, srcF, dstF); - return 1; - } - if(src_dir == 1) { - append(&dir_list, info); - } - else { - if(src_dir == 0) { - fprintf(stderr,"push: %s -> %s\n", src_p, dst_p); - int result = file_copy(src_fd, dst_fd, src_p, dst_p, srcF, dstF, &total_bytes); - if(result < 0) { - finalize(src_fd, dst_fd, srcF, dstF); - return 1; - } - if(result == 1) { - pushed++; - } - else { - skiped++; - } - } - else if(src_dir == 2) { - skiped++; - } - free(src_p); - free(dst_p); - free(info); +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,"'%s': No such file or directory\n", rpath); + sync_quit(fd); + 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++; } - curptr = curptr->next_ptr; + 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; } - free_list(entry_list, no_free); - remove_first(&dir_list, free_copyinfo); } + 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,"'%s': No such file or directory\n", rpath); + return 1; } +} - fprintf(stderr,"%d file(s) pushed. %d file(s) skipped.\n", - pushed, skiped); - - long long end_time = NOW() - start_time; +int do_sync_sync(const char *lpath, const char *rpath, int listonly) +{ + fprintf(stderr,"syncing %s...\n",rpath); - if(end_time != 0) { - fprintf(stderr,"%-30s %lld KB/s (%lld bytes in %lld.%03llds)\n", - srcp, - ((((long long) total_bytes) * 1000000LL) / end_time) / 1024LL, - (long long) total_bytes, (end_time / 1000000LL), (end_time % 1000000LL) / 1000LL); + int fd = sdb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", sdb_error()); + return 1; } - finalize(src_fd, dst_fd, srcF, dstF); - return 0; + 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_client.h b/src/file_sync_client.h deleted file mode 100644 index 7bb53bb..0000000 --- a/src/file_sync_client.h +++ /dev/null @@ -1,35 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef FILE_SYNC_CLIENT_H_ -#define FILE_SYNC_CLIENT_H_ - -#include "file_sync_functions.h" - -__inline__ void create_copy_info(COPY_INFO** info, char* srcp, char* dstp); -int do_sync_copy(char* srcp, char* dstp, FILE_FUNC* srcF, FILE_FUNC* dstF, int is_utf8, void** ext_argv); - -#endif /* FILE_SYNC_CLIENT_H_ */ diff --git a/src/file_sync_functions.c b/src/file_sync_functions.c deleted file mode 100644 index 7d2ae12..0000000 --- a/src/file_sync_functions.c +++ /dev/null @@ -1,566 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#include -#include -#include -#include "fdevent.h" -#include "utils.h" -#include "sdb_client.h" -#include "file_sync_functions.h" -#include "linkedlist.h" - -#include "strutils.h" -#include "file_sync_client.h" - -const FILE_FUNC LOCAL_FILE_FUNC = { - initialize_local, - finalize_local, - _stat_local, - is_directory_local, - readopen_local, - readclose_local, - writeopen_local, - writeclose_local, - readfile_local, - writefile_local, - getdirlist_local, -}; - -const FILE_FUNC REMOTE_FILE_FUNC = { - initialize_remote, - finalize_remote, - _stat_remote, - is_directory_remote, - readopen_remote, - readclose_remote, - writeopen_remote, - writeclose_remote, - readfile_remote, - writefile_remote, - getdirlist_remote, -}; - -static int sync_readmode(int fd, const char *path, unsigned *mode); - -//return > 0 fd, = 0 success, < 0 fail. -int initialize_local(char* path, void** extargv) { - return 0; -} - -//return fd -int initialize_remote(char* path, void** extargv) { - - int fd = sdb_connect("sync:", extargv); - - if(fd < 0) { - fprintf(stderr,"cannot sync remote: %s\n", strerror(errno)); - return -1; - } - - return fd; -} - -void finalize_local(int fd) { - -} - -void finalize_remote(int fd) { - syncmsg msg; - - msg.req.id = ID_QUIT; - msg.req.namelen = 0; - - writex(fd, &msg.req, sizeof(msg.req)); - - if(fd > 0) { - sdb_close(fd); - } -} - -//TODO stat should be freed. -int _stat_local(int fd, char* path, void** _stat, int show_error) { - struct stat* st = (struct stat*)malloc(sizeof(struct stat)); - if(lstat(path, st)) { - if(show_error) { - fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno)); - } - return -1; - } - - *_stat = st; - return 1; -} - -int _stat_remote(int fd, char* path, void** stat, int show_error) { - - unsigned* mode = (unsigned*)malloc(sizeof(unsigned)); - if(sync_readmode(fd, path, mode)) { - if(show_error) { - fprintf(stderr,"cannot read mode '%s': %s\n", path, strerror(errno)); - } - return -1; - } - - *stat = mode; - return 1; -} - -int is_directory_local(char* path, void* stat, int show_error) { - struct stat* st = (struct stat*)stat; - - if(st == NULL) { - if(show_error) { - fprintf(stderr,"'%s': No such file or directory\n", path); - } - return -1; - } - - if(S_ISDIR(st->st_mode)) { - return 1; - } - if(S_ISREG(st->st_mode) || S_ISLNK(st->st_mode)) { - return 0; - } - - return 2; -} - -int is_directory_remote(char* path, void* stat, int show_error) { - int* mode = (int*)stat; - - if(mode == NULL) { - if(show_error) { - fprintf(stderr,"'%s': No such file or directory\n", path); - } - return -1; - } - if(S_ISREG(*mode) || S_ISLNK(*mode)) { - return 0; - } - if(S_ISDIR(*mode)) { - return 1; - } - return 2; -} - -//return fd. -int readopen_local(int fd, char* srcp, void* srcstat) { - - struct stat* st = srcstat; - if(S_ISREG(st->st_mode)) { - fd = sdb_open(srcp, O_RDONLY); - - if(fd < 0) { - fprintf(stderr,"cannot open local file '%s': %s\n", srcp, strerror(errno)); - return -1; - } - - return fd; - } - - return fd; -} - -int readopen_remote(int fd, char* srcp, void* srcstat) { - syncmsg msg; - int len; - - len = strlen(srcp); - if(len > SYNC_CHAR_MAX) { - fprintf(stderr,"protocol failure while opening remote file '%s' for reading. request should not exceeds %d\n", srcp, SYNC_CHAR_MAX); - return -1; - } - - msg.req.id = ID_RECV; - msg.req.namelen = htoll(len); - - if(writex(fd, &msg.req, sizeof(msg.req)) || writex(fd, srcp, len)) { - fprintf(stderr,"exception occurred while sending read request to remote file'%s': %s\n", srcp, strerror(errno)); - return -1; - } - return fd; -} - -int readclose_local(int lfd) { - if(lfd > 0) { - sdb_close(lfd); - } - return lfd; -} - -int readclose_remote(int fd) { - return fd; -} - -int writeopen_local(int fd, char* dstp, void* stat) { - sdb_unlink(dstp); - mkdirs(dstp); - fd = sdb_creat(dstp, 0644); - - if(fd < 0) { - fprintf(stderr,"cannot create '%s': %s\n", dstp, strerror(errno)); - return -1; - } - - return fd; -} - -//return fd. -int writeopen_remote(int fd, char* dstp, void* stat) { - syncmsg msg; - struct stat* st = (struct stat*)stat; - - int len, r; - int total_len; - char tmp[64]; - - snprintf(tmp, sizeof(tmp), ",%d", st->st_mode); - len = strlen(dstp); - r = strlen(tmp); - total_len = len + r; - - if(total_len > PATH_MAX) { - fprintf(stderr,"protocol failure. buffer [%s%s] exceeds limits: %d\n", dstp, tmp, PATH_MAX); - return -1; - } - - msg.req.id = ID_SEND; - msg.req.namelen = htoll(total_len); - - if(writex(fd, &msg.req, sizeof(msg.req)) || - writex(fd, dstp, len) || - writex(fd, tmp, r)) { - fprintf(stderr,"exception occurred while sending read msg: %s\n", dstp); - return -1; - } - - return fd; -} - -int writeclose_local(int fd, char*dstp, void* stat) { - if(fd > 0) { - sdb_close(fd); - } - return fd; -} - -int writeclose_remote(int fd, char* dstp, void* stat) { - struct stat* st = (struct stat*)stat; - syncmsg msg; - msg.data.id = ID_DONE; - msg.data.size = htoll(st->st_mtime); - - if(writex(fd, &msg.data, sizeof(msg.data))) { - fprintf(stderr,"exception occurred while sending close msg: %s\n", dstp); - return -1; - } - - if(readx(fd, &msg.status, sizeof(msg.status))) { - fprintf(stderr,"exception occurred while receiving close msg: %s\n", dstp); - return -1; - } - - if(msg.status.id != ID_OKAY) { - char buf[256]; - if(msg.status.id == ID_FAIL) { - int len = ltohl(msg.status.msglen); - if(len > 256) { - len = 255; - } - if(readx(fd, buf, len)) { - fprintf(stderr, "cannot close remote file '%s' and its failed msg. %s\n", dstp, strerror(errno)); - return -1; - } - buf[len] = 0; - } else { - strcpy(buf, "unknown reason"); - } - - fprintf(stderr,"cannot close remote file '%s' with failed msg '%s'. %s\n", dstp, buf, strerror(errno)); - return -1; - } - - return fd; -} - -//4: finish skipped -//0: finish normally. -//2: continue load but don't write. -//-1:fail -//1: write and continue load -//3: write and stop -int readfile_local(int lfd, char* srcpath, void* stat, FILE_BUFFER* sbuf) { - struct stat* st = (struct stat*)stat; - - if (S_ISREG(st->st_mode)) { - int ret; - - ret = sdb_read(lfd, sbuf->data, SYNC_DATA_MAX); - if (!ret) { - //Finish normally. - return 0; - } - if (ret < 0) { - //continue load but don't write - if (errno == EINTR) { - return 2; - } - //fail. - fprintf(stderr, "cannot read local file '%s': %s\n", srcpath, strerror(errno)); - return -1; - } - - sbuf->size = htoll(ret); - //write and continue load. - return 1; - } -#ifdef HAVE_SYMLINKS - else if (S_ISLNK(st->st_mode)) { - int len; - - len = readlink(srcpath, sbuf->data, SYNC_DATA_MAX-1); - //fail - if(len < 0) { - fprintf(stderr, "cannot read local link '%s': %s\n", srcpath, strerror(errno)); - return -1; - } - sbuf->data[len] = '\0'; - sbuf->size = htoll(len + 1); - - //write and stop. - return 3; - } -#endif - - fprintf(stderr,"protocol failure\n"); - - //fail - return -1; -} - -int readfile_remote(int fd, char* srcpath, void* stat, FILE_BUFFER* buffer) { - syncmsg msg; - unsigned id; - - if(readx(fd, &(msg.data), sizeof(msg.data))) { - fprintf(stderr, "cannot read remote file status '%s': %s\n", srcpath, strerror(errno)); - return -1; - } - id = msg.data.id; - buffer->size = ltohl(msg.data.size); - - if(id == ID_DONE) { - //Finish normally. - return 0; - } - //fail - if(id != ID_DATA) { - int len; - if(id == ID_FAIL) { - int len = buffer->size; - if(len > 256) { - len = 255; - } - if(readx(fd, buffer->data, len)) { - fprintf(stderr, "cannot read remote file '%s' and its failed msg. %s\n", srcpath, strerror(errno)); - return -1; - } - } - else { - len = 4; - memcpy(buffer->data, &id, len); - } - buffer->data[len] = 0; - - fprintf(stderr, "cannot read remote file '%s' with its failed msg '%s'. %s\n", srcpath, buffer->data, strerror(errno)); - return 4; - } - //fail - if(buffer->size > SYNC_DATA_MAX) { - fprintf(stderr,"data overrun\n"); - return -1; - } - - //fail - if(readx(fd, buffer->data, buffer->size)) { - fprintf(stderr, "cannot read remote file '%s': %s\n", srcpath, strerror(errno)); - return -1; - } - - //write and continue load - return 1; -} - -int writefile_local(int fd, char* dstp, FILE_BUFFER* sbuf, unsigned* total_bytes) { - char* data = sbuf->data; - unsigned len = sbuf->size; - - if(writex(fd, data, len)) { - fprintf(stderr,"cannot write '%s': %s\n", dstp, strerror(errno)); - return -1; - } - - *total_bytes += len; - return 0; -} - -int writefile_remote(int fd, char* dstp, FILE_BUFFER* sbuf, unsigned* total_bytes) { - int size = ltohl(sbuf->size); - - if(writex(fd, sbuf, sizeof(unsigned)*2 + size)) { - fprintf(stderr, "cannot write remote file '%s': %s\n", dstp, strerror(errno)); - return -1; - } - - *total_bytes += size; - return 0; -} - -int getdirlist_local(int fd, char* src_dir, char* dst_dir, LIST_NODE** dirlist) { - DIR* d; - - d = opendir(src_dir); - if(d == 0) { - fprintf(stderr,"cannot open '%s': %s\n", src_dir, strerror(errno)); - readclose_local(fd); - return -1; - } - struct dirent* de; - - while((de = readdir(d))) { - char* file_name = de->d_name; - - if(file_name[0] == '.') { - if(file_name[1] == 0) { - continue; - } - if((file_name[1] == '.') && (file_name[2] == 0)) { - continue; - } - } - - char* src_full_path = (char*)malloc(sizeof(char)*PATH_MAX); - append_file(src_full_path, src_dir, file_name); - - char* dst_full_path = (char*)malloc(sizeof(char)*PATH_MAX); - append_file(dst_full_path, dst_dir, file_name); - - COPY_INFO* info; - create_copy_info(&info, src_full_path, dst_full_path); - append(dirlist, info); - } - - closedir(d); - return fd; -} - -int getdirlist_remote(int fd, char* src_dir, char* dst_dir, LIST_NODE** dirlist) { - syncmsg msg; - int len; - - len = strlen(src_dir); - - if(len > SYNC_CHAR_MAX) { - fprintf(stderr,"protocol failure while getting dirlist of remote file '%s'. request should not exceeds %d\n", src_dir, SYNC_CHAR_MAX); - return -1; - } - - msg.req.id = ID_LIST; - msg.req.namelen = htoll(len); - - if(writex(fd, &msg.req, sizeof(msg.req)) || - writex(fd, src_dir, len)) { - fprintf(stderr,"cannot request directory entry: '%s'", src_dir); - return -1; - } - - while(1) { - if(readx(fd, &msg.dent, sizeof(msg.dent))) { - fprintf(stderr,"cannot read dirlist: '%s'", src_dir); - return -1; - } - if(msg.dent.id == ID_DONE) { - return fd; - } - if(msg.dent.id != ID_DENT) { - fprintf(stderr,"received dent msg '%d' is not DENT", msg.dent.id); - return -1; - } - len = ltohl(msg.dent.namelen); - if(len > 256) { - fprintf(stderr,"some file in the remote '%s' exceeds 256", src_dir); - return -1; - } - - char file_name[257]; - if(readx(fd, file_name, len)) { - fprintf(stderr,"cannot read file in the remote directory '%s'", src_dir); - return -1; - } - file_name[len] = 0; - - if(file_name[0] == '.') { - if(file_name[1] == 0) { - continue; - } - if((file_name[1] == '.') && (file_name[2] == 0)) { - continue; - } - } - - char* src_full_path = (char*)malloc(sizeof(char)*PATH_MAX); - append_file(src_full_path, src_dir, file_name); - - char* dst_full_path = (char*)malloc(sizeof(char)*PATH_MAX); - append_file(dst_full_path, dst_dir, file_name); - - COPY_INFO* info; - create_copy_info(&info, src_full_path, dst_full_path); - append(dirlist, info); - } - return fd; -} - -static 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; -} diff --git a/src/file_sync_functions.h b/src/file_sync_functions.h deleted file mode 100644 index 34be58e..0000000 --- a/src/file_sync_functions.h +++ /dev/null @@ -1,97 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef FILE_SYNC_FUNCTIONS_H_ -#define FILE_SYNC_FUNCTIONS_H_ - -#include "file_sync_service.h" -#include "linkedlist.h" - -struct copy_info { - char* src; - char* dst; -}; -typedef struct copy_info COPY_INFO; - -struct _file_buf { - unsigned id; - unsigned size; - char data[SYNC_DATA_MAX]; -}; -typedef struct _file_buf FILE_BUFFER; - -int initialize_local(char* path, void** extargv); -int initialize_remote(char* path, void** extargv); - -void finalize_local(int fd); -void finalize_remote(int fd); - -int _stat_local(int fd, char* path, void** _stat, int show_error); -int _stat_remote(int fd, char* path, void** stat, int show_error); - -int is_directory_local(char* path, void* stat, int show_error); -int is_directory_remote(char* path, void* stat, int show_error); - -int readopen_local(int fd, char* srcp, void* srcstat); -int readopen_remote(int fd, char* srcp, void* srcstat); - -int readclose_local(int lfd); -int readclose_remote(int fd); - -int writeopen_local(int fd, char* dstp, void* stat); -int writeopen_remote(int fd, char* dstp, void* stat); - -int writeclose_local(int fd, char*dstp, void* stat); -int writeclose_remote(int fd, char* dstp, void* stat); - -int readfile_local(int lfd, char* srcpath, void* stat, FILE_BUFFER* sbuf); -int readfile_remote(int fd, char* srcpath, void* stat, FILE_BUFFER* buffer); - -int writefile_local(int fd, char* dstp, FILE_BUFFER* sbuf, unsigned* total_bytes); -int writefile_remote(int fd, char* dstp, FILE_BUFFER* sbuf, unsigned* total_bytes); - -int getdirlist_local(int fd, char* src_dir, char* dst_dir, LIST_NODE** dirlist); -int getdirlist_remote(int fd, char* src_dir, char* dst_dir, LIST_NODE** dirlist); - -struct file_function { - int(*initialize)(char* path, void** extargv); - void(*finalize)(int fd); - int(*_stat)(int fd, char* path, void** stat, int show_error); - int(*is_dir)(char* path, void* stat, int show_error); - int(*readopen)(int fd, char* dstp, void* stat); - int(*readclose)(int fd); - int(*writeopen)(int fd, char* dstp, void* stat); - int(*writeclose)(int fd, char* dstp, void* stat); - int(*readfile)(int fd, char* path, void* stat, FILE_BUFFER* buf); - int(*writefile)(int fd, char* path, FILE_BUFFER* buf, unsigned* total_bytes); - int(*get_dirlist)(int fd, char* src_dir, char* dst_dir, LIST_NODE** dirlist); -}; -typedef struct file_function FILE_FUNC; - -const FILE_FUNC LOCAL_FILE_FUNC; -const FILE_FUNC REMOTE_FILE_FUNC; - -#endif /* FILE_SYNC_FUNCTIONS_H_ */ diff --git a/src/file_sync_service.c b/src/file_sync_service.c new file mode 100644 index 0000000..5871a8c --- /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 +#include +#include + +#include +#include +#include +#include + +#include + +#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); + } + D("link? %s\n", (is_link==0) ? "no" : "yes"); + 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 index 6f01979..84f06bb 100644 --- a/src/file_sync_service.h +++ b/src/file_sync_service.h @@ -78,11 +78,10 @@ typedef union { 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 isUtf8); +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) -#define SYNC_CHAR_MAX 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 +#include +#include +#include +#include +#include +#include +#include + +#include "fdevent.h" +#include "sdb.h" + +#include +#include +#include + +/* 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 +#include + +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 +#include +#include +#include + +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 +#include +#include +#include + +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 +#include +#include + +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..ab6f11b --- /dev/null +++ b/src/jdwp_service.c @@ -0,0 +1,751 @@ +/* + * 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 +#include +#include +#include + +/* 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 | + | | + | | + + 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: jdwp:" + + "jdwp:" 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 +#include + +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/libusb/darwin.c b/src/libusb/darwin.c deleted file mode 100644 index c330eb0..0000000 --- a/src/libusb/darwin.c +++ /dev/null @@ -1,1329 +0,0 @@ -/* - * 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. - */ -/* - * Darwin/MacOS X Support - * - * (c) 2002-2007 Nathan Hjelm - * - * (07/25/2007): - * - Added a fix to ignore buggy (virtual?) hubs. - * (06/26/2006): - * - Bulk functions no longer use async transfer functions. - * (04/17/2005): - * - Lots of minor fixes. - * - Endpoint table now made by claim_interface to fix a bug. - * - Merged Read/Write to make modifications easier. - * (03/25/2005): - * - Fixed a bug when using asynchronous callbacks within a multi-threaded application. - * (03/14/2005): - * - Added an endpoint table to speed up bulk transfers. - * 0.1.11 (02/22/2005): - * - Updated error checking in read/write routines to check completion codes. - * - Updated set_configuration so that the open interface is reclaimed before completion. - * - Fixed several typos. - * 0.1.8 (01/12/2004): - * - Fixed several memory leaks. - * - Readded 10.0 support - * - Added support for USB fuctions defined in 10.3 and above - * (01/02/2003): - * - Applied a patch by Philip Edelbrock that fixes a bug in usb_control_msg. - * (12/16/2003): - * - Even better error printing. - * - Devices that cannot be opened can have their interfaces opened. - * 0.1.6 (05/12/2002): - * - Fixed problem where libusb holds resources after program completion. - * - Mouse should no longer freeze up now. - * 0.1.2 (02/13/2002): - * - Bulk functions should work properly now. - * 0.1.1 (02/11/2002): - * - Fixed major bug (device and interface need to be released after use) - * 0.1.0 (01/06/2002): - * - Tested driver with gphoto (works great as long as Image Capture isn't running) - * 0.1d (01/04/2002): - * - Implimented clear_halt and resetep - * - Uploaded to CVS. - * 0.1b (01/04/2002): - * - Added usb_debug line to bulk read and write function. - * 0.1a (01/03/2002): - * - Driver mostly completed using the macosx driver I wrote for my rioutil software. - * - * Derived from Linux version by Richard Tobin. - * Also partly derived from BSD version. - * - * This library is covered by the LGPL, read LICENSE for details. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include - -/* standard includes for darwin/os10 (IOKit) */ -#include -#include -#include -#include - -#include "usbi.h" - -/* some defines */ -/* IOUSBInterfaceInferface */ -#if defined (kIOUSBInterfaceInterfaceID220) - -// #warning "libusb being compiled for 10.4 or later" -#define usb_interface_t IOUSBInterfaceInterface220 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 -#define InterfaceVersion 220 - -#elif defined (kIOUSBInterfaceInterfaceID197) - -// #warning "libusb being compiled for 10.3 or later" -#define usb_interface_t IOUSBInterfaceInterface197 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197 -#define InterfaceVersion 197 - -#elif defined (kIOUSBInterfaceInterfaceID190) - -// #warning "libusb being compiled for 10.2 or later" -#define usb_interface_t IOUSBInterfaceInterface190 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190 -#define InterfaceVersion 190 - -#elif defined (kIOUSBInterfaceInterfaceID182) - -// #warning "libusb being compiled for 10.1 or later" -#define usb_interface_t IOUSBInterfaceInterface182 -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182 -#define InterfaceVersion 182 - -#else - -/* No timeout functions available! Time to upgrade your os. */ -#warning "libusb being compiled without support for timeout bulk functions! 10.0 and up" -#define usb_interface_t IOUSBInterfaceInterface -#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID -#define LIBUSB_NO_TIMEOUT_INTERFACE -#define InterfaceVersion 180 - -#endif - -/* IOUSBDeviceInterface */ -#if defined (kIOUSBDeviceInterfaceID197) - -#define usb_device_t IOUSBDeviceInterface197 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 -#define DeviceVersion 197 - -#elif defined (kIOUSBDeviceInterfaceID187) - -#define usb_device_t IOUSBDeviceInterface187 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID187 -#define DeviceVersion 187 - -#elif defined (kIOUSBDeviceInterfaceID182) - -#define usb_device_t IOUSBDeviceInterface182 -#define DeviceInterfaceID kIOUSBDeviceInterfaceID182 -#define DeviceVersion 182 - -#else - -#define usb_device_t IOUSBDeviceInterface -#define DeviceInterfaceID kIOUSBDeviceInterfaceID -#define LIBUSB_NO_TIMEOUT_DEVICE -#define LIBUSB_NO_SEIZE_DEVICE -#define DeviceVersion 180 - -#endif - -typedef IOReturn io_return_t; -typedef IOCFPlugInInterface *io_cf_plugin_ref_t; -typedef SInt32 s_int32_t; -typedef IOReturn (*rw_async_func_t)(void *self, UInt8 pipeRef, void *buf, UInt32 size, - IOAsyncCallback1 callback, void *refcon); -typedef IOReturn (*rw_async_to_func_t)(void *self, UInt8 pipeRef, void *buf, UInt32 size, - UInt32 noDataTimeout, UInt32 completionTimeout, - IOAsyncCallback1 callback, void *refcon); - -#if !defined(IO_OBJECT_NULL) -#define IO_OBJECT_NULL ((io_object_t)0) -#endif - -struct darwin_dev_handle { - usb_device_t **device; - usb_interface_t **interface; - int open; - - /* stored translation table for pipes to endpoints */ - int num_endpoints; - unsigned char *endpoint_addrs; -}; - -static IONotificationPortRef gNotifyPort; -static mach_port_t masterPort = MACH_PORT_NULL; - -static void darwin_cleanup (void) -{ - IONotificationPortDestroy(gNotifyPort); - mach_port_deallocate(mach_task_self(), masterPort); -} - -static char *darwin_error_str (int result) { - switch (result) { - case kIOReturnSuccess: - return "no error"; - case kIOReturnNotOpen: - return "device not opened for exclusive access"; - case kIOReturnNoDevice: - return "no connection to an IOService"; - case kIOUSBNoAsyncPortErr: - return "no async port has been opened for interface"; - case kIOReturnExclusiveAccess: - return "another process has device opened for exclusive access"; - case kIOUSBPipeStalled: - return "pipe is stalled"; - case kIOReturnError: - return "could not establish a connection to the Darwin kernel"; - case kIOUSBTransactionTimeout: - return "transaction timed out"; - case kIOReturnBadArgument: - return "invalid argument"; - case kIOReturnAborted: - return "transaction aborted"; - case kIOReturnNotResponding: - return "device not responding"; - default: - return "unknown error"; - } -} - -/* not a valid errorno outside darwin.c */ -#define LUSBDARWINSTALL (ELAST+1) - -static int darwin_to_errno (int result) { - switch (result) { - case kIOReturnSuccess: - return 0; - case kIOReturnNotOpen: - return EBADF; - case kIOReturnNoDevice: - case kIOUSBNoAsyncPortErr: - return ENXIO; - case kIOReturnExclusiveAccess: - return EBUSY; - case kIOUSBPipeStalled: - return LUSBDARWINSTALL; - case kIOReturnBadArgument: - return EINVAL; - case kIOUSBTransactionTimeout: - return ETIMEDOUT; - case kIOReturnNotResponding: - return EIO; - case kIOReturnError: - default: - return 1; - } -} - -static int usb_setup_iterator (io_iterator_t *deviceIterator) -{ - int result; - CFMutableDictionaryRef matchingDict; - - /* set up the matching dictionary for class IOUSBDevice and its subclasses. - It will be consumed by the IOServiceGetMatchingServices call */ - if ((matchingDict = IOServiceMatching(kIOUSBDeviceClassName)) == NULL) { - darwin_cleanup (); - - USB_ERROR_STR(-1, "libusb/darwin.c usb_setup_iterator: Could not create a matching dictionary.\n"); - } - - result = IOServiceGetMatchingServices(masterPort, matchingDict, deviceIterator); - matchingDict = NULL; - - if (result != kIOReturnSuccess) - USB_ERROR_STR (-darwin_to_errno (result), "libusb/darwin.c usb_setup_iterator: IOServiceGetMatchingServices: %s\n", - darwin_error_str(result)); - - return 0; -} - -static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) -{ - io_cf_plugin_ref_t *plugInInterface = NULL; - usb_device_t **device; - io_service_t usbDevice; - long result, score; - - if (!IOIteratorIsValid (deviceIterator) || !(usbDevice = IOIteratorNext(deviceIterator))) - return NULL; - - result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, - kIOCFPlugInInterfaceID, &plugInInterface, - &score); - - result = IOObjectRelease(usbDevice); - if (result || !plugInInterface) - return NULL; - - (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID), - (LPVOID)&device); - - (*plugInInterface)->Stop(plugInInterface); - IODestroyPlugInInterface (plugInInterface); - plugInInterface = NULL; - - (*(device))->GetLocationID(device, locationp); - - return device; -} - -int usb_os_open(usb_dev_handle *dev) -{ - struct darwin_dev_handle *device; - - io_return_t result; - io_iterator_t deviceIterator; - - usb_device_t **darwin_device; - - UInt32 location = *((UInt32 *)dev->device->dev); - UInt32 dlocation; - - if (!dev) - USB_ERROR(-ENXIO); - - if (masterPort == MACH_PORT_NULL) - USB_ERROR(-EINVAL); - - device = calloc(1, sizeof(struct darwin_dev_handle)); - if (!device) - USB_ERROR(-ENOMEM); - - if (usb_debug > 3) - fprintf(stderr, "usb_os_open: %04x:%04x\n", - dev->device->descriptor.idVendor, - dev->device->descriptor.idProduct); - - if ((result = usb_setup_iterator (&deviceIterator)) < 0) - return result; - - /* This port of libusb uses locations to keep track of devices. */ - while ((darwin_device = usb_get_next_device (deviceIterator, &dlocation)) != NULL) { - if (dlocation == location) - break; - - (*darwin_device)->Release(darwin_device); - } - - IOObjectRelease(deviceIterator); - device->device = darwin_device; - - if (device->device == NULL) - USB_ERROR_STR (-ENOENT, "usb_os_open: %s\n", "Device not found!"); - -#if !defined (LIBUSB_NO_SEIZE_DEVICE) - result = (*(device->device))->USBDeviceOpenSeize (device->device); -#else - /* No Seize in OS X 10.0 (Darwin 1.4) */ - result = (*(device->device))->USBDeviceOpen (device->device); -#endif - - if (result != kIOReturnSuccess) { - switch (result) { - case kIOReturnExclusiveAccess: - if (usb_debug > 0) - fprintf (stderr, "usb_os_open(USBDeviceOpenSeize): %s\n", darwin_error_str(result)); - break; - default: - (*(device->device))->Release (device->device); - USB_ERROR_STR(-darwin_to_errno (result), "usb_os_open(USBDeviceOpenSeize): %s", - darwin_error_str(result)); - } - - device->open = 0; - } else - device->open = 1; - - dev->impl_info = device; - dev->interface = -1; - dev->altsetting = -1; - - device->num_endpoints = 0; - device->endpoint_addrs = NULL; - - return 0; -} - -int usb_os_close(usb_dev_handle *dev) -{ - struct darwin_dev_handle *device; - io_return_t result; - - if (!dev) - USB_ERROR(-ENXIO); - - if ((device = dev->impl_info) == NULL) - USB_ERROR(-ENOENT); - - usb_release_interface(dev, dev->interface); - - if (usb_debug > 3) - fprintf(stderr, "usb_os_close: %04x:%04x\n", - dev->device->descriptor.idVendor, - dev->device->descriptor.idProduct); - - if (device->open == 1) - result = (*(device->device))->USBDeviceClose(device->device); - else - result = kIOReturnSuccess; - - /* device may not need to be released, but if it has to... */ - (*(device->device))->Release(device->device); - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "usb_os_close(USBDeviceClose): %s", darwin_error_str(result)); - - free (device); - - return 0; -} - -static int get_endpoints (struct darwin_dev_handle *device) -{ - io_return_t ret; - - u_int8_t numep, direction, number; - u_int8_t dont_care1, dont_care3; - u_int16_t dont_care2; - - int i; - - if (device == NULL || device->interface == NULL) - return -EINVAL; - - if (usb_debug > 1) - fprintf(stderr, "libusb/darwin.c get_endpoints: building table of endpoints.\n"); - - /* retrieve the total number of endpoints on this interface */ - ret = (*(device->interface))->GetNumEndpoints(device->interface, &numep); - if ( ret ) { - if ( usb_debug > 1 ) - fprintf ( stderr, "get_endpoints: interface is %p\n", device->interface ); - - USB_ERROR_STR ( -ret, "get_endpoints: can't get number of endpoints for interface" ); - } - - free (device->endpoint_addrs); - device->endpoint_addrs = calloc (sizeof (unsigned char), numep); - - /* iterate through pipe references */ - for (i = 1 ; i <= numep ; i++) { - ret = (*(device->interface))->GetPipeProperties(device->interface, i, &direction, &number, - &dont_care1, &dont_care2, &dont_care3); - - if (ret != kIOReturnSuccess) { - fprintf (stderr, "get_endpoints: an error occurred getting pipe information on pipe %d\n", - i ); - USB_ERROR_STR(-darwin_to_errno(ret), "get_endpoints(GetPipeProperties): %s", darwin_error_str(ret)); - } - - if (usb_debug > 1) - fprintf (stderr, "get_endpoints: Pipe %i: DIR: %i number: %i\n", i, direction, number); - - device->endpoint_addrs[i - 1] = ((direction << 7 & USB_ENDPOINT_DIR_MASK) | - (number & USB_ENDPOINT_ADDRESS_MASK)); - } - - device->num_endpoints = numep; - - if (usb_debug > 1) - fprintf(stderr, "libusb/darwin.c get_endpoints: complete.\n"); - - return 0; -} - -static int claim_interface (usb_dev_handle *dev, int interface) -{ - io_iterator_t interface_iterator; - io_service_t usbInterface = IO_OBJECT_NULL; - io_return_t result; - io_cf_plugin_ref_t *plugInInterface = NULL; - - IOUSBFindInterfaceRequest request; - - struct darwin_dev_handle *device; - long score; - int current_interface; - - device = dev->impl_info; - - request.bInterfaceClass = kIOUSBFindInterfaceDontCare; - request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; - request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; - request.bAlternateSetting = kIOUSBFindInterfaceDontCare; - - result = (*(device->device))->CreateInterfaceIterator(device->device, &request, &interface_iterator); - if (result != kIOReturnSuccess) - USB_ERROR_STR (-darwin_to_errno(result), "claim_interface(CreateInterfaceIterator): %s", - darwin_error_str(result)); - - for ( current_interface=0 ; current_interface <= interface ; current_interface++ ) { - usbInterface = IOIteratorNext(interface_iterator); - if ( usb_debug > 3 ) - fprintf ( stderr, "Interface %d of device is 0x%08x\n", - current_interface, usbInterface ); - } - - current_interface--; - - /* the interface iterator is no longer needed, release it */ - IOObjectRelease(interface_iterator); - - if (!usbInterface) { - u_int8_t nConfig; /* Index of configuration to use */ - IOUSBConfigurationDescriptorPtr configDesc; /* to describe which configuration to select */ - /* Only a composite class device with no vendor-specific driver will - be configured. Otherwise, we need to do it ourselves, or there - will be no interfaces for the device. */ - - if ( usb_debug > 3 ) - fprintf ( stderr,"claim_interface: No interface found; selecting configuration\n" ); - - result = (*(device->device))->GetNumberOfConfigurations ( device->device, &nConfig ); - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(GetNumberOfConfigurations): %s", - darwin_error_str(result)); - - if (nConfig < 1) - USB_ERROR_STR(-ENXIO ,"claim_interface(GetNumberOfConfigurations): no configurations"); - else if ( nConfig > 1 && usb_debug > 0 ) - fprintf ( stderr, "claim_interface: device has more than one" - " configuration, using the first (warning)\n" ); - - if ( usb_debug > 3 ) - fprintf ( stderr, "claim_interface: device has %d configuration%s\n", - (int)nConfig, (nConfig>1?"s":"") ); - - /* Always use the first configuration */ - result = (*(device->device))->GetConfigurationDescriptorPtr ( (device->device), 0, &configDesc ); - if (result != kIOReturnSuccess) { - if (device->open == 1) { - (*(device->device))->USBDeviceClose ( (device->device) ); - (*(device->device))->Release ( (device->device) ); - } - - USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(GetConfigurationDescriptorPtr): %s", - darwin_error_str(result)); - } else if ( usb_debug > 3 ) - fprintf ( stderr, "claim_interface: configuration value is %d\n", - configDesc->bConfigurationValue ); - - if (device->open == 1) { - result = (*(device->device))->SetConfiguration ( (device->device), configDesc->bConfigurationValue ); - - if (result != kIOReturnSuccess) { - (*(device->device))->USBDeviceClose ( (device->device) ); - (*(device->device))->Release ( (device->device) ); - - USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(SetConfiguration): %s", - darwin_error_str(result)); - } - - dev->config = configDesc->bConfigurationValue; - } - - request.bInterfaceClass = kIOUSBFindInterfaceDontCare; - request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; - request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; - request.bAlternateSetting = kIOUSBFindInterfaceDontCare; - - /* Now go back and get the chosen interface */ - result = (*(device->device))->CreateInterfaceIterator(device->device, &request, &interface_iterator); - if (result != kIOReturnSuccess) - USB_ERROR_STR (-darwin_to_errno(result), "claim_interface(CreateInterfaceIterator): %s", - darwin_error_str(result)); - - for (current_interface = 0 ; current_interface <= interface ; current_interface++) { - usbInterface = IOIteratorNext(interface_iterator); - - if ( usb_debug > 3 ) - fprintf ( stderr, "claim_interface: Interface %d of device is 0x%08x\n", - current_interface, usbInterface ); - } - current_interface--; - - /* the interface iterator is no longer needed, release it */ - IOObjectRelease(interface_iterator); - - if (!usbInterface) - USB_ERROR_STR (-ENOENT, "claim_interface: interface iterator returned NULL"); - } - - result = IOCreatePlugInInterfaceForService(usbInterface, - kIOUSBInterfaceUserClientTypeID, - kIOCFPlugInInterfaceID, - &plugInInterface, &score); - /* No longer need the usbInterface object after getting the plug-in */ - result = IOObjectRelease(usbInterface); - if (result || !plugInInterface) - USB_ERROR(-ENOENT); - - /* Now create the device interface for the interface */ - result = (*plugInInterface)->QueryInterface(plugInInterface, - CFUUIDGetUUIDBytes(InterfaceInterfaceID), - (LPVOID) &device->interface); - - /* No longer need the intermediate plug-in */ - (*plugInInterface)->Stop(plugInInterface); - IODestroyPlugInInterface (plugInInterface); - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(QueryInterface): %s", - darwin_error_str(result)); - - if (!device->interface) - USB_ERROR(-EACCES); - - if ( usb_debug > 3 ) - fprintf ( stderr, "claim_interface: Interface %d of device from QueryInterface is %p\n", - current_interface, device->interface); - - /* claim the interface */ - result = (*(device->interface))->USBInterfaceOpen(device->interface); - if (result) - USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(USBInterfaceOpen): %s", - darwin_error_str(result)); - - result = get_endpoints (device); - - if (result) { - /* this should not happen */ - usb_release_interface (dev, interface); - USB_ERROR_STR ( result, "claim_interface: could not build endpoint table"); - } - - return 0; -} - -int usb_set_configuration (usb_dev_handle *dev, int configuration) -{ - struct darwin_dev_handle *device; - io_return_t result; - int interface; - - if ( usb_debug > 3 ) - fprintf ( stderr, "usb_set_configuration: called for config %x\n", configuration ); - - if (!dev) - USB_ERROR_STR ( -ENXIO, "usb_set_configuration: called with null device\n" ); - - if ((device = dev->impl_info) == NULL) - USB_ERROR_STR ( -ENOENT, "usb_set_configuration: device not properly initialized" ); - - /* Setting configuration will invalidate the interface, so we need - to reclaim it. First, dispose of existing interface, if any. */ - interface = dev->interface; - - if ( device->interface ) - usb_release_interface(dev, dev->interface); - - result = (*(device->device))->SetConfiguration(device->device, configuration); - - if (result) - USB_ERROR_STR(-darwin_to_errno(result), "usb_set_configuration(SetConfiguration): %s", - darwin_error_str(result)); - - /* Reclaim interface */ - if (interface != -1) - result = usb_claim_interface (dev, interface); - - dev->config = configuration; - - return result; -} - -int usb_claim_interface(usb_dev_handle *dev, int interface) -{ - struct darwin_dev_handle *device = dev->impl_info; - - io_return_t result; - - if ( usb_debug > 3 ) - fprintf ( stderr, "usb_claim_interface: called for interface %d\n", interface ); - - if (!device) - USB_ERROR_STR ( -ENOENT, "usb_claim_interface: device is NULL" ); - - if (!(device->device)) - USB_ERROR_STR ( -EINVAL, "usb_claim_interface: device->device is NULL" ); - - /* If we have already claimed an interface, release it */ - if ( device->interface ) - usb_release_interface(dev, dev->interface); - - result = claim_interface ( dev, interface ); - if ( result ) - USB_ERROR_STR ( result, "usb_claim_interface: couldn't claim interface" ); - - dev->interface = interface; - - /* interface is claimed and async IO is set up: return 0 */ - return 0; -} - -int usb_release_interface(usb_dev_handle *dev, int interface) -{ - struct darwin_dev_handle *device; - io_return_t result; - - if (!dev) - USB_ERROR(-ENXIO); - - if ((device = dev->impl_info) == NULL) - USB_ERROR(-ENOENT); - - /* interface is not open */ - if (!device->interface) - return 0; - - result = (*(device->interface))->USBInterfaceClose(device->interface); - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "usb_release_interface(USBInterfaceClose): %s", - darwin_error_str(result)); - - result = (*(device->interface))->Release(device->interface); - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "usb_release_interface(Release): %s", - darwin_error_str(result)); - - device->interface = NULL; - - free (device->endpoint_addrs); - - device->num_endpoints = 0; - device->endpoint_addrs = NULL; - - dev->interface = -1; - dev->altsetting = -1; - - return 0; -} - -int usb_set_altinterface(usb_dev_handle *dev, int alternate) -{ - struct darwin_dev_handle *device; - io_return_t result; - - if (!dev) - USB_ERROR(-ENXIO); - - if ((device = dev->impl_info) == NULL) - USB_ERROR(-ENOENT); - - /* interface is not open */ - if (!device->interface) - USB_ERROR_STR(-EACCES, "usb_set_altinterface: interface used without being claimed"); - - result = (*(device->interface))->SetAlternateInterface(device->interface, alternate); - - if (result) - USB_ERROR_STR(result, "usb_set_altinterface: could not set alternate interface"); - - dev->altsetting = alternate; - - result = get_endpoints (device); - if (result) { - /* this should not happen */ - USB_ERROR_STR ( result, "usb_set_altinterface: could not build endpoint table"); - } - - return 0; -} - -/* simple function that figures out what pipeRef is associated with an endpoint */ -static int ep_to_pipeRef (struct darwin_dev_handle *device, int ep) -{ - int i; - - if (usb_debug > 1) - fprintf(stderr, "libusb/darwin.c ep_to_pipeRef: Converting ep address to pipeRef.\n"); - - for (i = 0 ; i < device->num_endpoints ; i++) - if (device->endpoint_addrs[i] == ep) - return i + 1; - - /* No pipe found with the correct endpoint address */ - if (usb_debug > 1) - fprintf(stderr, "libusb/darwin.c ep_to_pipeRef: No pipeRef found with endpoint address 0x%02x.\n", ep); - - return -1; -} - -static int usb_bulk_transfer (usb_dev_handle *dev, int ep, char *bytes, u_int32_t size, int timeout, int usb_bt_read) -{ - struct darwin_dev_handle *device; - - io_return_t result = -1; - - int pipeRef; - - u_int8_t transferType, direction, number, interval; - u_int16_t maxPacketSize; - - if (!dev) - USB_ERROR_STR ( -ENXIO, "libusb/darwin.c usb_bulk_transfer: Called with NULL device" ); - - if ((device = dev->impl_info) == NULL) - USB_ERROR_STR ( -ENOENT, "libusb/darwin.c usb_bulk_transfer: Device not open" ); - - /* interface is not open */ - if (!device->interface) - USB_ERROR_STR(-EACCES, "libusb/darwin.c usb_bulk_transfer: Interface used before it was opened"); - - - /* Set up transfer */ - if ((pipeRef = ep_to_pipeRef(device, ep)) < 0) - USB_ERROR_STR ( -EINVAL, "libusb/darwin.c usb_bulk_transfer: Invalid pipe reference" ); - - (*(device->interface))->GetPipeProperties (device->interface, pipeRef, &direction, &number, - &transferType, &maxPacketSize, &interval); - /* Transfer set up complete */ - - if (usb_debug > 0) - fprintf (stderr, "libusb/darwin.c usb_bulk_transfer: Transfering %i bytes of data on endpoint 0x%02x\n", size, ep); - - /* Do bulk transfer */ - if (transferType == kUSBInterrupt && usb_debug > 3) - fprintf (stderr, "libusb/darwin.c usb_bulk_transfer: USB pipe is an interrupt pipe. Timeouts will not be used.\n"); - -#if !defined(LIBUSB_NO_TIMEOUT_INTERFACE) - if ( transferType != kUSBInterrupt) { - if (usb_bt_read != 0) - result = (*(device->interface))->ReadPipeTO (device->interface, pipeRef, bytes, (UInt32 *)&size, timeout, timeout); - else - result = (*(device->interface))->WritePipeTO (device->interface, pipeRef, bytes, size, timeout, timeout); - - /* pipe bits may need to be cleared after a timeout. should this be done here or in user code? */ - if (result == kIOUSBTransactionTimeout && (*(device->interface))->GetPipeStatus (device->interface, pipeRef) == kIOUSBPipeStalled) - usb_clear_halt (dev, ep); - } else -#endif - { - if (usb_bt_read != 0) - result = (*(device->interface))->ReadPipe (device->interface, pipeRef, bytes, (UInt32 *)&size); - else - result = (*(device->interface))->WritePipe (device->interface, pipeRef, bytes, size); - } - - if (result != kIOReturnSuccess) - USB_ERROR_STR (-darwin_to_errno (result), "libusb/darwin.c usb_bulk_transfer: %s", darwin_error_str (result)); - - return size; -} - -#if 0 -/* NOT USED */ -/* argument to handle multiple parameters to rw_completed */ -struct rw_complete_arg { - UInt32 io_size; - IOReturn result; - CFRunLoopRef cf_loop; -}; - -static void rw_completed(void *refcon, io_return_t result, void *io_size) -{ - struct rw_complete_arg *rw_arg = (struct rw_complete_arg *)refcon; - - if (usb_debug > 2) - fprintf(stderr, "io async operation completed: %s, size=%lu, result=0x%08x\n", darwin_error_str(result), - (UInt32)io_size, result); - - rw_arg->io_size = (UInt32)io_size; - rw_arg->result = result; - - CFRunLoopStop(rw_arg->cf_loop); -} - -static int usb_bulk_transfer_async (usb_dev_handle *dev, int ep, char *bytes, int size, int timeout, - rw_async_func_t rw_async, rw_async_to_func_t rw_async_to) -{ - struct darwin_dev_handle *device; - - io_return_t result = -1; - - CFRunLoopSourceRef cfSource; - int pipeRef; - - struct rw_complete_arg rw_arg; - - u_int8_t transferType; - - /* None of the values below are used in libusb for bulk transfers */ - u_int8_t direction, number, interval; - u_int16_t maxPacketSize; - - if (!dev) - USB_ERROR_STR ( -ENXIO, "usb_bulk_transfer: Called with NULL device" ); - - if ((device = dev->impl_info) == NULL) - USB_ERROR_STR ( -ENOENT, "usb_bulk_transfer: Device not open" ); - - /* interface is not open */ - if (!device->interface) - USB_ERROR_STR(-EACCES, "usb_bulk_transfer: Interface used before it was opened"); - - - /* Set up transfer */ - if ((pipeRef = ep_to_pipeRef(device, ep)) < 0) - USB_ERROR_STR ( -EINVAL, "usb_bulk_transfer: Invalid pipe reference" ); - - (*(device->interface))->GetPipeProperties (device->interface, pipeRef, &direction, &number, - &transferType, &maxPacketSize, &interval); - - bzero((void *)&rw_arg, sizeof(struct rw_complete_arg)); - rw_arg.cf_loop = CFRunLoopGetCurrent(); - CFRetain (rw_arg.cf_loop); - - (*(device->interface))->CreateInterfaceAsyncEventSource(device->interface, &cfSource); - CFRunLoopAddSource(rw_arg.cf_loop, cfSource, kCFRunLoopDefaultMode); - /* Transfer set up complete */ - - if (usb_debug > 0) - fprintf (stderr, "libusb/darwin.c usb_bulk_transfer: Transfering %i bytes of data on endpoint 0x%02x\n", - size, ep); - - /* Bulk transfer */ - if (transferType == kUSBInterrupt && usb_debug > 3) - fprintf (stderr, "libusb/darwin.c usb_bulk_transfer: USB pipe is an interrupt pipe. Timeouts will not be used.\n"); - - if ( transferType != kUSBInterrupt && rw_async_to != NULL) - - result = rw_async_to (device->interface, pipeRef, bytes, size, timeout, timeout, - (IOAsyncCallback1)rw_completed, (void *)&rw_arg); - else - result = rw_async (device->interface, pipeRef, bytes, size, (IOAsyncCallback1)rw_completed, - (void *)&rw_arg); - - if (result == kIOReturnSuccess) { - /* wait for write to complete */ - if (CFRunLoopRunInMode(kCFRunLoopDefaultMode, (timeout+999)/1000, true) == kCFRunLoopRunTimedOut) { - (*(device->interface))->AbortPipe(device->interface, pipeRef); - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true); /* Pick up aborted callback */ - if (usb_debug) - fprintf(stderr, "usb_bulk_transfer: timed out\n"); - } - } - - CFRunLoopRemoveSource(rw_arg.cf_loop, cfSource, kCFRunLoopDefaultMode); - CFRelease (rw_arg.cf_loop); - - /* Check the return code of both the write and completion functions. */ - if (result != kIOReturnSuccess || (rw_arg.result != kIOReturnSuccess && - rw_arg.result != kIOReturnAborted) ) { - int error_code; - char *error_str; - - if (result == kIOReturnSuccess) { - error_code = darwin_to_errno (rw_arg.result); - error_str = darwin_error_str (rw_arg.result); - } else { - error_code = darwin_to_errno(result); - error_str = darwin_error_str (result); - } - - if (transferType != kUSBInterrupt && rw_async_to != NULL) - USB_ERROR_STR(-error_code, "usb_bulk_transfer (w/ Timeout): %s", error_str); - else - USB_ERROR_STR(-error_code, "usb_bulk_transfer (No Timeout): %s", error_str); - } - - return rw_arg.io_size; -} -#endif - -int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) -{ - int result; - - if (dev == NULL || dev->impl_info == NULL) - return -EINVAL; - - if ((result = usb_bulk_transfer (dev, ep, bytes, size, timeout, 0)) < 0) - USB_ERROR_STR (result, "usb_bulk_write: An error occured during write (see messages above)"); - - return result; -} - -int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) -{ - int result; - - if (dev == NULL || dev->impl_info == NULL) - return -EINVAL; - - ep |= 0x80; - - if ((result = usb_bulk_transfer (dev, ep, bytes, size, timeout, 1)) < 0) - USB_ERROR_STR (result, "usb_bulk_read: An error occured during read (see messages above)"); - - return result; -} - -/* interrupt endpoints appear to be treated the same as non-interrupt endpoints under OSX/Darwin */ -int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - return usb_bulk_write (dev, ep, bytes, size, timeout); -} - -int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - return usb_bulk_read (dev, ep, bytes, size, timeout); -} - -int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, int timeout) -{ - struct darwin_dev_handle *device = dev->impl_info; - - io_return_t result; - -#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) - IOUSBDevRequestTO urequest; -#else - IOUSBDevRequest urequest; -#endif - - if (usb_debug >= 3) - fprintf(stderr, "libusb/darwin.c usb_control_msg (device: %s): %d %d %d %d %p %d %d\n", - dev->device->filename, requesttype, request, value, index, bytes, size, timeout); - - bzero(&urequest, sizeof(urequest)); - - urequest.bmRequestType = requesttype; - urequest.bRequest = request; - urequest.wValue = value; - urequest.wIndex = index; - urequest.wLength = size; - urequest.pData = bytes; -#if !defined (LIBUSB_NO_TIMEOUT_DEVICE) - urequest.completionTimeout = timeout; - urequest.noDataTimeout = timeout; - - result = (*(device->device))->DeviceRequestTO(device->device, &urequest); -#else - result = (*(device->device))->DeviceRequest(device->device, &urequest); -#endif - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "libusb/darwin.c usb_control_msg(DeviceRequestTO): %s", darwin_error_str(result)); - - /* Bytes transfered is stored in the wLenDone field*/ - return urequest.wLenDone; -} - -int usb_os_find_busses(struct usb_bus **busses) -{ - struct usb_bus *fbus = NULL; - - io_iterator_t deviceIterator; - io_return_t result; - - usb_device_t **device; - - UInt32 location; - - char buf[20]; - int i = 1; - - /* Create a master port for communication with IOKit (this should - have been created if the user called usb_init() )*/ - if (masterPort == MACH_PORT_NULL) { - usb_init (); - - if (masterPort == MACH_PORT_NULL) - USB_ERROR(-ENOENT); - } - - if ((result = usb_setup_iterator (&deviceIterator)) < 0) - return result; - - while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) { - struct usb_bus *bus; - - if (location & 0x00ffffff) - continue; - - bus = calloc(1, sizeof(struct usb_bus)); - if (bus == NULL) - USB_ERROR(-ENOMEM); - - sprintf(buf, "%03i", i++); - bus->location = location; - - strncpy(bus->dirname, buf, sizeof(bus->dirname) - 1); - bus->dirname[sizeof(bus->dirname) - 1] = 0; - - LIST_ADD(fbus, bus); - - if (usb_debug >= 2) - fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname); - - (*(device))->Release(device); - } - - IOObjectRelease(deviceIterator); - - *busses = fbus; - - return 0; -} - -int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices) -{ - struct usb_device *fdev = NULL; - - io_iterator_t deviceIterator; - io_return_t result; - - usb_device_t **device; - - u_int16_t address; - UInt32 location; - UInt32 bus_loc = bus->location; - int devnum; - - /* for use in retrieving device description */ - IOUSBDevRequest req; - - /* a master port should have been created by usb_os_init */ - if (masterPort == MACH_PORT_NULL) - USB_ERROR(-ENOENT); - - if ((result = usb_setup_iterator (&deviceIterator)) < 0) - return result; - - /* Set up request for device descriptor */ - req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); - req.bRequest = kUSBRqGetDescriptor; - req.wValue = kUSBDeviceDesc << 8; - req.wIndex = 0; - req.wLength = sizeof(IOUSBDeviceDescriptor); - - devnum = 0; - - while ((device = usb_get_next_device (deviceIterator, &location)) != NULL) { - unsigned char device_desc[DEVICE_DESC_LENGTH]; - UInt8 bDeviceClass, bDeviceSubClass; - UInt16 idVendor, idProduct; - - result = (*(device))->GetDeviceAddress(device, (USBDeviceAddress *)&address); - - if (result == kIOReturnSuccess) { - (*(device))->GetDeviceClass (device, &bDeviceClass); - (*(device))->GetDeviceSubClass (device, &bDeviceSubClass); - (*(device))->GetDeviceProduct (device, &idProduct); - (*(device))->GetDeviceVendor (device, &idVendor); - - if ((location >> 24) == (bus_loc >> 24)) { - struct usb_device *dev; - - if (usb_debug >= 2) - fprintf(stderr, "libusb/darwin.c usb_os_find_devices: Found USB device on bus 0x%08lx: 0x%08lx\n", - bus_loc, location); - - dev = calloc(1, sizeof(struct usb_device)); - if (dev == NULL) - USB_ERROR(-ENOMEM); - - dev->bus = bus; - - /* retrieve device descriptor */ - req.pData = device_desc; - result = (*(device))->DeviceRequest(device, &req); - - if (result != kIOReturnSuccess) { - free (dev); - - if (usb_debug) - fprintf (stderr, "libusb/darwin.c usb_os_find_devices: Could not retrieve device descriptor: %s. Skipping device.\n", - darwin_error_str(result)); - - /* release the device now */ - (*(device))->Release(device); - continue; /* can't continue without a descriptor */ - } - - usb_parse_descriptor(device_desc, "bbwbbbbwwwbbbb", &dev->descriptor); - - /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices */ - if (dev->descriptor.idProduct != idProduct) { - free (dev); - - if (usb_debug) - fprintf (stderr, "libusb/darwin.c usb_os_find_devices: idProduct from iokit does not match idProduct in descriptor. Skipping device\n"); - - /* release the device now */ - (*(device))->Release(device); - continue; /* can't continue without a descriptor */ - } - - dev->devnum = devnum++; - - sprintf(dev->filename, "%03i-%04x-%04x-%02x-%02x", address, idVendor, idProduct, bDeviceClass, bDeviceSubClass); - - dev->dev = (USBDeviceAddress *) calloc (1, 4); - if (dev->dev == NULL) - USB_ERROR(-ENOMEM); - - memcpy(dev->dev, &location, 4); - - LIST_ADD(fdev, dev); - - if (usb_debug >= 2) - fprintf(stderr, "libusb/darwin.c usb_os_find_devices: Found %s on %s at location 0x%08lx\n", - dev->filename, bus->dirname, location); - } - } else if (usb_debug) - fprintf (stderr, "libusb/darwin.c usb_os_find_devices: Could not retrieve device address: %s\n", - darwin_error_str(result)); - - /* release the device now */ - (*(device))->Release(device); - } - - IOObjectRelease(deviceIterator); - - *devices = fdev; - - if (usb_debug) - fprintf (stderr, "libusb/darwin.c usb_os_find_devices: Complete\n"); - - return 0; -} - -int usb_os_determine_children(struct usb_bus *bus) -{ - /* Nothing yet */ - return 0; -} - -void usb_os_init(void) -{ - if (masterPort == MACH_PORT_NULL) { - IOMasterPort(masterPort, &masterPort); - - gNotifyPort = IONotificationPortCreate(masterPort); - } -} - -void usb_os_cleanup (void) -{ - if (masterPort != MACH_PORT_NULL) - darwin_cleanup (); -} - -int usb_resetep(usb_dev_handle *dev, unsigned int ep) -{ - struct darwin_dev_handle *device; - - io_return_t result = -1; - - int pipeRef; - - if (!dev) - USB_ERROR(-ENXIO); - - if ((device = dev->impl_info) == NULL) - USB_ERROR(-ENOENT); - - /* interface is not open */ - if (!device->interface) - USB_ERROR_STR(-EACCES, "usb_resetep: interface used without being claimed"); - - if ((pipeRef = ep_to_pipeRef(device, ep)) == -1) - USB_ERROR(-EINVAL); - - result = (*(device->interface))->ResetPipe(device->interface, pipeRef); - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "usb_resetep(ResetPipe): %s", darwin_error_str(result)); - - return 0; -} - -int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) -{ - struct darwin_dev_handle *device; - - io_return_t result = -1; - - int pipeRef; - - if (!dev) - USB_ERROR(-ENXIO); - - if ((device = dev->impl_info) == NULL) - USB_ERROR(-ENOENT); - - /* interface is not open */ - if (!device->interface) - USB_ERROR_STR(-EACCES, "usb_clear_halt: interface used without being claimed"); - - if ((pipeRef = ep_to_pipeRef(device, ep)) == -1) - USB_ERROR(-EINVAL); - -#if (InterfaceVersion < 190) - result = (*(device->interface))->ClearPipeStall(device->interface, pipeRef); -#else - /* newer versions of darwin support clearing additional bits on the device's endpoint */ - result = (*(device->interface))->ClearPipeStallBothEnds(device->interface, pipeRef); -#endif - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "usb_clear_halt(ClearPipeStall): %s", darwin_error_str(result)); - - return 0; -} - -int usb_reset(usb_dev_handle *dev) -{ - struct darwin_dev_handle *device; - - io_return_t result; - - if (!dev) - USB_ERROR(-ENXIO); - - if ((device = dev->impl_info) == NULL) - USB_ERROR(-ENOENT); - - if (!device->device) - USB_ERROR_STR(-ENOENT, "usb_reset: no such device"); - - result = (*(device->device))->ResetDevice(device->device); - - if (result != kIOReturnSuccess) - USB_ERROR_STR(-darwin_to_errno(result), "usb_reset(ResetDevice): %s", darwin_error_str(result)); - - return 0; -} diff --git a/src/libusb/descriptors.c b/src/libusb/descriptors.c deleted file mode 100644 index f7fc784..0000000 --- a/src/libusb/descriptors.c +++ /dev/null @@ -1,535 +0,0 @@ -/* - * 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. - */ -/* - * Parses descriptors - * - * Copyright (c) 2001 Johannes Erdfelt - * - * This library is covered by the LGPL, read LICENSE for details. - */ - -#include -#include -#include -#include "usbi.h" - -int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, - unsigned char type, unsigned char index, void *buf, int size) -{ - memset(buf, 0, size); - - return usb_control_msg(udev, ep | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, - (type << 8) + index, 0, buf, size, 1000); -} - -int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size) -{ - memset(buf, 0, size); - - return usb_control_msg(udev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, - (type << 8) + index, 0, buf, size, 1000); -} - -int usb_parse_descriptor(unsigned char *source, char *description, void *dest) -{ - unsigned char *sp = source, *dp = dest; - uint16_t w; - uint32_t d; - char *cp; - - for (cp = description; *cp; cp++) { - switch (*cp) { - case 'b': /* 8-bit byte */ - *dp++ = *sp++; - break; - case 'w': /* 16-bit word, convert from little endian to CPU */ - w = (sp[1] << 8) | sp[0]; sp += 2; - dp += ((unsigned long)dp & 1); /* Align to word boundary */ - *((uint16_t *)dp) = w; dp += 2; - break; - case 'd': /* 32-bit dword, convert from little endian to CPU */ - d = (sp[3] << 24) | (sp[2] << 16) | (sp[1] << 8) | sp[0]; sp += 4; - dp += ((unsigned long)dp & 2); /* Align to dword boundary */ - *((uint32_t *)dp) = d; dp += 4; - break; - /* These two characters are undocumented and just a hack for Linux */ - case 'W': /* 16-bit word, keep CPU endianess */ - dp += ((unsigned long)dp & 1); /* Align to word boundary */ - memcpy(dp, sp, 2); sp += 2; dp += 2; - break; - case 'D': /* 32-bit dword, keep CPU endianess */ - dp += ((unsigned long)dp & 2); /* Align to dword boundary */ - memcpy(dp, sp, 4); sp += 4; dp += 4; - break; - } - } - - return sp - source; -} - -/* - * This code looks surprisingly similar to the code I wrote for the Linux - * kernel. It's not a coincidence :) - */ - -static int usb_parse_endpoint(struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size) -{ - struct usb_descriptor_header header; - unsigned char *begin; - int parsed = 0, len, numskipped; - - usb_parse_descriptor(buffer, "bb", &header); - - /* Everything should be fine being passed into here, but we sanity */ - /* check JIC */ - if (header.bLength > size) { - if (usb_debug >= 1) - fprintf(stderr, "ran out of descriptors parsing\n"); - return -1; - } - - if (header.bDescriptorType != USB_DT_ENDPOINT) { - if (usb_debug >= 2) - fprintf(stderr, "unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X\n", - header.bDescriptorType, USB_DT_ENDPOINT); - return parsed; - } - - if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH) - usb_parse_descriptor(buffer, "bbbbwbbb", endpoint); - else if (header.bLength >= ENDPOINT_DESC_LENGTH) - usb_parse_descriptor(buffer, "bbbbwb", endpoint); - - buffer += header.bLength; - size -= header.bLength; - parsed += header.bLength; - - /* Skip over the rest of the Class Specific or Vendor Specific */ - /* descriptors */ - begin = buffer; - numskipped = 0; - while (size >= DESC_HEADER_LENGTH) { - usb_parse_descriptor(buffer, "bb", &header); - - if (header.bLength < 2) { - if (usb_debug >= 1) - fprintf(stderr, "invalid descriptor length of %d\n", header.bLength); - return -1; - } - - /* If we find another "proper" descriptor then we're done */ - if ((header.bDescriptorType == USB_DT_ENDPOINT) || - (header.bDescriptorType == USB_DT_INTERFACE) || - (header.bDescriptorType == USB_DT_CONFIG) || - (header.bDescriptorType == USB_DT_DEVICE)) - break; - - if (usb_debug >= 1) - fprintf(stderr, "skipping descriptor 0x%X\n", header.bDescriptorType); - numskipped++; - - buffer += header.bLength; - size -= header.bLength; - parsed += header.bLength; - } - - if (numskipped && usb_debug >= 2) - fprintf(stderr, "skipped %d class/vendor specific endpoint descriptors\n", numskipped); - - /* Copy any unknown descriptors into a storage area for drivers */ - /* to later parse */ - len = (int)(buffer - begin); - if (!len) { - endpoint->extra = NULL; - endpoint->extralen = 0; - return parsed; - } - - endpoint->extra = malloc(len); - if (!endpoint->extra) { - if (usb_debug >= 1) - fprintf(stderr, "couldn't allocate memory for endpoint extra descriptors\n"); - endpoint->extralen = 0; - return parsed; - } - - memcpy(endpoint->extra, begin, len); - endpoint->extralen = len; - - return parsed; -} - -static int usb_parse_interface(struct usb_interface *interface, - unsigned char *buffer, int size) -{ - int i, len, numskipped, retval, parsed = 0; - struct usb_descriptor_header header; - struct usb_interface_descriptor *ifp; - unsigned char *begin; - - interface->num_altsetting = 0; - - while (size >= INTERFACE_DESC_LENGTH) { - interface->altsetting = realloc(interface->altsetting, sizeof(struct usb_interface_descriptor) * (interface->num_altsetting + 1)); - if (!interface->altsetting) { - if (usb_debug >= 1) - fprintf(stderr, "couldn't malloc interface->altsetting\n"); - return -1; - } - - ifp = interface->altsetting + interface->num_altsetting; - interface->num_altsetting++; - - usb_parse_descriptor(buffer, "bbbbbbbbb", ifp); - - /* Skip over the interface */ - buffer += ifp->bLength; - parsed += ifp->bLength; - size -= ifp->bLength; - - begin = buffer; - numskipped = 0; - - /* Skip over any interface, class or vendor descriptors */ - while (size >= DESC_HEADER_LENGTH) { - usb_parse_descriptor(buffer, "bb", &header); - - if (header.bLength < 2) { - if (usb_debug >= 1) - fprintf(stderr, "invalid descriptor length of %d\n", header.bLength); - return -1; - } - - /* If we find another "proper" descriptor then we're done */ - if ((header.bDescriptorType == USB_DT_INTERFACE) || - (header.bDescriptorType == USB_DT_ENDPOINT) || - (header.bDescriptorType == USB_DT_CONFIG) || - (header.bDescriptorType == USB_DT_DEVICE)) - break; - - numskipped++; - - buffer += header.bLength; - parsed += header.bLength; - size -= header.bLength; - } - - if (numskipped && usb_debug >= 2) - fprintf(stderr, "skipped %d class/vendor specific interface descriptors\n", numskipped); - - /* Copy any unknown descriptors into a storage area for */ - /* drivers to later parse */ - len = (int)(buffer - begin); - if (!len) { - ifp->extra = NULL; - ifp->extralen = 0; - } else { - ifp->extra = malloc(len); - if (!ifp->extra) { - if (usb_debug >= 1) - fprintf(stderr, "couldn't allocate memory for interface extra descriptors\n"); - ifp->extralen = 0; - return -1; - } - memcpy(ifp->extra, begin, len); - ifp->extralen = len; - } - - /* Did we hit an unexpected descriptor? */ - usb_parse_descriptor(buffer, "bb", &header); - if ((size >= DESC_HEADER_LENGTH) && - ((header.bDescriptorType == USB_DT_CONFIG) || - (header.bDescriptorType == USB_DT_DEVICE))) - return parsed; - - if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { - if (usb_debug >= 1) - fprintf(stderr, "too many endpoints\n"); - return -1; - } - - if (ifp->bNumEndpoints > 0) { - ifp->endpoint = (struct usb_endpoint_descriptor *) - malloc(ifp->bNumEndpoints * - sizeof(struct usb_endpoint_descriptor)); - if (!ifp->endpoint) { - if (usb_debug >= 1) - fprintf(stderr, "couldn't allocate memory for ifp->endpoint\n"); - return -1; - } - - memset(ifp->endpoint, 0, ifp->bNumEndpoints * - sizeof(struct usb_endpoint_descriptor)); - - for (i = 0; i < ifp->bNumEndpoints; i++) { - usb_parse_descriptor(buffer, "bb", &header); - - if (header.bLength > size) { - if (usb_debug >= 1) - fprintf(stderr, "ran out of descriptors parsing\n"); - return -1; - } - - retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size); - if (retval < 0) - return retval; - - buffer += retval; - parsed += retval; - size -= retval; - } - } else - ifp->endpoint = NULL; - - /* We check to see if it's an alternate to this one */ - ifp = (struct usb_interface_descriptor *)buffer; - if (size < USB_DT_INTERFACE_SIZE || - ifp->bDescriptorType != USB_DT_INTERFACE || - !ifp->bAlternateSetting) - return parsed; - } - - return parsed; -} - -int usb_parse_configuration(struct usb_config_descriptor *config, - unsigned char *buffer) -{ - int i, retval, size; - struct usb_descriptor_header header; - - usb_parse_descriptor(buffer, "bbwbbbbb", config); - size = config->wTotalLength; - - if (config->bNumInterfaces > USB_MAXINTERFACES) { - if (usb_debug >= 1) - fprintf(stderr, "too many interfaces\n"); - return -1; - } - - config->interface = (struct usb_interface *) - malloc(config->bNumInterfaces * - sizeof(struct usb_interface)); - if (!config->interface) { - if (usb_debug >= 1) - fprintf(stderr, "out of memory\n"); - return -1; - } - - memset(config->interface, 0, config->bNumInterfaces * sizeof(struct usb_interface)); - - buffer += config->bLength; - size -= config->bLength; - - config->extra = NULL; - config->extralen = 0; - - for (i = 0; i < config->bNumInterfaces; i++) { - int numskipped, len; - unsigned char *begin; - - /* Skip over the rest of the Class Specific or Vendor */ - /* Specific descriptors */ - begin = buffer; - numskipped = 0; - while (size >= DESC_HEADER_LENGTH) { - usb_parse_descriptor(buffer, "bb", &header); - - if ((header.bLength > size) || (header.bLength < DESC_HEADER_LENGTH)) { - if (usb_debug >= 1) - fprintf(stderr, "invalid descriptor length of %d\n", header.bLength); - return -1; - } - - /* If we find another "proper" descriptor then we're done */ - if ((header.bDescriptorType == USB_DT_ENDPOINT) || - (header.bDescriptorType == USB_DT_INTERFACE) || - (header.bDescriptorType == USB_DT_CONFIG) || - (header.bDescriptorType == USB_DT_DEVICE)) - break; - - if (usb_debug >= 2) - fprintf(stderr, "skipping descriptor 0x%X\n", header.bDescriptorType); - numskipped++; - - buffer += header.bLength; - size -= header.bLength; - } - - if (numskipped && usb_debug >= 2) - fprintf(stderr, "skipped %d class/vendor specific endpoint descriptors\n", numskipped); - - /* Copy any unknown descriptors into a storage area for */ - /* drivers to later parse */ - len = (int)(buffer - begin); - if (len) { - /* FIXME: We should realloc and append here */ - if (!config->extralen) { - config->extra = malloc(len); - if (!config->extra) { - if (usb_debug >= 1) - fprintf(stderr, "couldn't allocate memory for config extra descriptors\n"); - config->extralen = 0; - return -1; - } - - memcpy(config->extra, begin, len); - config->extralen = len; - } - } - - retval = usb_parse_interface(config->interface + i, buffer, size); - if (retval < 0) - return retval; - - buffer += retval; - size -= retval; - } - - return size; -} - -void usb_destroy_configuration(struct usb_device *dev) -{ - int c, i, j, k; - - if (!dev->config) - return; - - for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { - struct usb_config_descriptor *cf = &dev->config[c]; - - if (!cf->interface) - continue; - - for (i = 0; i < cf->bNumInterfaces; i++) { - struct usb_interface *ifp = &cf->interface[i]; - - if (!ifp->altsetting) - continue; - - for (j = 0; j < ifp->num_altsetting; j++) { - struct usb_interface_descriptor *as = &ifp->altsetting[j]; - - if (as->extra) - free(as->extra); - - if (!as->endpoint) - continue; - - for (k = 0; k < as->bNumEndpoints; k++) { - if (as->endpoint[k].extra) - free(as->endpoint[k].extra); - } - free(as->endpoint); - } - - free(ifp->altsetting); - } - - free(cf->interface); - } - - free(dev->config); -} - -void usb_fetch_and_parse_descriptors(usb_dev_handle *udev) -{ - struct usb_device *dev = udev->device; - int i; - - if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) { - if (usb_debug >= 1) - fprintf(stderr, "Too many configurations (%d > %d)\n", dev->descriptor.bNumConfigurations, USB_MAXCONFIG); - return; - } - - if (dev->descriptor.bNumConfigurations < 1) { - if (usb_debug >= 1) - fprintf(stderr, "Not enough configurations (%d < %d)\n", dev->descriptor.bNumConfigurations, 1); - return; - } - - dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); - if (!dev->config) { - if (usb_debug >= 1) - fprintf(stderr, "Unable to allocate memory for config descriptor\n"); - return; - } - - memset(dev->config, 0, dev->descriptor.bNumConfigurations * - sizeof(struct usb_config_descriptor)); - - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - unsigned char buffer[8], *bigbuffer; - struct usb_config_descriptor config; - int res; - - /* Get the first 8 bytes so we can figure out what the total length is */ - res = usb_get_descriptor(udev, USB_DT_CONFIG, i, buffer, 8); - if (res < 8) { - if (usb_debug >= 1) { - if (res < 0) - fprintf(stderr, "Unable to get descriptor (%d)\n", res); - else - fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, res); - } - - goto err; - } - - usb_parse_descriptor(buffer, "bbw", &config); - - bigbuffer = malloc(config.wTotalLength); - if (!bigbuffer) { - if (usb_debug >= 1) - fprintf(stderr, "Unable to allocate memory for descriptors\n"); - goto err; - } - - res = usb_get_descriptor(udev, USB_DT_CONFIG, i, bigbuffer, config.wTotalLength); - if (res < config.wTotalLength) { - if (usb_debug >= 1) { - if (res < 0) - fprintf(stderr, "Unable to get descriptor (%d)\n", res); - else - fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, res); - } - - free(bigbuffer); - goto err; - } - - res = usb_parse_configuration(&dev->config[i], bigbuffer); - if (usb_debug >= 2) { - if (res > 0) - fprintf(stderr, "Descriptor data still left\n"); - else if (res < 0) - fprintf(stderr, "Unable to parse descriptors\n"); - } - - free(bigbuffer); - } - - return; - -err: - free(dev->config); - - dev->config = NULL; -} - diff --git a/src/libusb/error.c b/src/libusb/error.c deleted file mode 100644 index 9212e86..0000000 --- a/src/libusb/error.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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. - */ -/* - * USB Error messages - * - * Copyright (c) 2000-2001 Johannes Erdfelt - * - * This library is covered by the LGPL, read LICENSE for details. - */ - -#include -#include - -#include "usb.h" -#include "error.h" - -char usb_error_str[1024] = ""; -int usb_error_errno = 0; -usb_error_type_t usb_error_type = USB_ERROR_TYPE_NONE; - -char *usb_strerror(void) -{ - switch (usb_error_type) { - case USB_ERROR_TYPE_NONE: - return "No error"; - case USB_ERROR_TYPE_STRING: - return usb_error_str; - case USB_ERROR_TYPE_ERRNO: - if (usb_error_errno > -USB_ERROR_BEGIN) - return strerror(usb_error_errno); - else - /* Any error we don't know falls under here */ - return "Unknown error"; - } - - return "Unknown error"; -} - diff --git a/src/libusb/error.h b/src/libusb/error.h deleted file mode 100644 index c980e22..0000000 --- a/src/libusb/error.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 _ERROR_H_ -#define _ERROR_H_ - -typedef enum { - USB_ERROR_TYPE_NONE = 0, - USB_ERROR_TYPE_STRING, - USB_ERROR_TYPE_ERRNO, -} usb_error_type_t; - -extern char usb_error_str[1024]; -extern int usb_error_errno; -extern usb_error_type_t usb_error_type; - -#define USB_ERROR(x) \ - do { \ - usb_error_type = USB_ERROR_TYPE_ERRNO; \ - usb_error_errno = x; \ - return x; \ - } while (0) - -#define USB_ERROR_STR(x, format, args...) \ - do { \ - usb_error_type = USB_ERROR_TYPE_STRING; \ - snprintf(usb_error_str, sizeof(usb_error_str) - 1, format, ## args); \ - if (usb_debug >= 2) \ - fprintf(stderr, "USB error: %s\n", usb_error_str); \ - return x; \ - } while (0) - -#endif /* _ERROR_H_ */ - diff --git a/src/libusb/linux.c b/src/libusb/linux.c deleted file mode 100644 index 6b13afb..0000000 --- a/src/libusb/linux.c +++ /dev/null @@ -1,748 +0,0 @@ -/* - * 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. - */ -/* - * Linux USB support - * - * Copyright (c) 2000-2003 Johannes Erdfelt - * - * This library is covered by the LGPL, read LICENSE for details. - */ - -#include /* getenv, etc */ -#include -#include -#include -#include -#include -#include -#include - -#include "linux.h" -#include "usbi.h" - -static char usb_path[PATH_MAX + 1] = ""; - -static int device_open(struct usb_device *dev) -{ - char filename[PATH_MAX + 1]; - int fd; - - snprintf(filename, sizeof(filename) - 1, "%s/%s/%s", - usb_path, dev->bus->dirname, dev->filename); - - fd = open(filename, O_RDWR); - if (fd < 0) { - fd = open(filename, O_RDONLY); - if (fd < 0) - USB_ERROR_STR(-errno, "failed to open %s: %s", - filename, strerror(errno)); - } - - return fd; -} - -int usb_os_open(usb_dev_handle *dev) -{ - dev->fd = device_open(dev->device); - - return 0; -} - -int usb_os_close(usb_dev_handle *dev) -{ - if (dev->fd < 0) - return 0; - - if (close(dev->fd) == -1) - /* Failing trying to close a file really isn't an error, so return 0 */ - USB_ERROR_STR(0, "tried to close device fd %d: %s", dev->fd, - strerror(errno)); - - return 0; -} - -int usb_set_configuration(usb_dev_handle *dev, int configuration) -{ - int ret; - - ret = ioctl(dev->fd, IOCTL_USB_SETCONFIG, &configuration); - if (ret < 0) - USB_ERROR_STR(-errno, "could not set config %d: %s", configuration, - strerror(errno)); - - dev->config = configuration; - - return 0; -} - -int usb_claim_interface(usb_dev_handle *dev, int interface) -{ - int ret; - - ret = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface); - if (ret < 0) { - if (errno == EBUSY && usb_debug > 0) - fprintf(stderr, "Check that you have permissions to write to %s/%s and, if you don't, that you set up hotplug (http://linux-hotplug.sourceforge.net/) correctly.\n", dev->bus->dirname, dev->device->filename); - - USB_ERROR_STR(-errno, "could not claim interface %d: %s", interface, - strerror(errno)); - } - - dev->interface = interface; - - return 0; -} - -int usb_release_interface(usb_dev_handle *dev, int interface) -{ - int ret; - - ret = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &interface); - if (ret < 0) - USB_ERROR_STR(-errno, "could not release intf %d: %s", interface, - strerror(errno)); - - dev->interface = -1; - - return 0; -} - -int usb_set_altinterface(usb_dev_handle *dev, int alternate) -{ - int ret; - struct usb_setinterface setintf; - - if (dev->interface < 0) - USB_ERROR(-EINVAL); - - setintf.interface = dev->interface; - setintf.altsetting = alternate; - - ret = ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf); - if (ret < 0) - USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s", - dev->interface, alternate, strerror(errno)); - - dev->altsetting = alternate; - - return 0; -} - -/* - * Linux usbfs has a limit of one page size for synchronous bulk read/write. - * 4096 is the most portable maximum we can do for now. - * Linux usbfs has a limit of 16KB for the URB interface. We use this now - * to get better performance for USB 2.0 devices. - */ -#define MAX_READ_WRITE (16 * 1024) - -int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, int timeout) -{ - struct usb_ctrltransfer ctrl; - int ret; - - ctrl.bRequestType = requesttype; - ctrl.bRequest = request; - ctrl.wValue = value; - ctrl.wIndex = index; - ctrl.wLength = size; - - ctrl.data = bytes; - ctrl.timeout = timeout; - - ret = ioctl(dev->fd, IOCTL_USB_CONTROL, &ctrl); - if (ret < 0) - USB_ERROR_STR(-errno, "error sending control message: %s", strerror(errno)); - - return ret; -} - -#define URB_USERCONTEXT_COOKIE ((void *)0x1) - -/* Reading and writing are the same except for the endpoint */ -static int usb_urb_transfer(usb_dev_handle *dev, int ep, int urbtype, - char *bytes, int size, int timeout) -{ - struct usb_urb urb; - int bytesdone = 0, requested; - struct timeval tv, tv_ref, tv_now; - struct usb_urb *context; - int ret, waiting; - - /* - * HACK: The use of urb.usercontext is a hack to get threaded applications - * sort of working again. Threaded support is still not recommended, but - * this should allow applications to work in the common cases. Basically, - * if we get the completion for an URB we're not waiting for, then we update - * the usercontext pointer to 1 for the other threads URB and it will see - * the change after it wakes up from the the timeout. Ugly, but it works. - */ - - /* - * Get actual time, and add the timeout value. The result is the absolute - * time where we have to quit waiting for an message. - */ - gettimeofday(&tv_ref, NULL); - tv_ref.tv_sec = tv_ref.tv_sec + timeout / 1000; - tv_ref.tv_usec = tv_ref.tv_usec + (timeout % 1000) * 1000; - - if (tv_ref.tv_usec > 1000000) { - tv_ref.tv_usec -= 1000000; - tv_ref.tv_sec++; - } - - do { - fd_set writefds; - - requested = size - bytesdone; - if (requested > MAX_READ_WRITE) - requested = MAX_READ_WRITE; - - urb.type = urbtype; - urb.endpoint = ep; - urb.flags = 0; - urb.buffer = bytes + bytesdone; - urb.buffer_length = requested; - urb.signr = 0; - urb.actual_length = 0; - urb.number_of_packets = 0; /* don't do isochronous yet */ - urb.usercontext = NULL; - - ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb); - if (ret < 0) { - USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno)); - return ret; - } - - FD_ZERO(&writefds); - FD_SET(dev->fd, &writefds); - -restart: - waiting = 1; - context = NULL; - while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) { - tv.tv_sec = 0; - tv.tv_usec = 1000; // 1 msec - select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait - - if (timeout) { - /* compare with actual time, as the select timeout is not that precise */ - gettimeofday(&tv_now, NULL); - - if ((tv_now.tv_sec > tv_ref.tv_sec) || - ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec))) - waiting = 0; - } - } - - if (context && context != &urb) { - context->usercontext = URB_USERCONTEXT_COOKIE; - /* We need to restart since we got a successful URB, but not ours */ - goto restart; - } - - /* - * If there was an error, that wasn't EAGAIN (no completion), then - * something happened during the reaping and we should return that - * error now - */ - if (ret < 0 && !urb.usercontext && errno != EAGAIN) - USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno)); - - bytesdone += urb.actual_length; - } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested); - - /* If the URB didn't complete in success or error, then let's unlink it */ - if (ret < 0 && !urb.usercontext) { - int rc; - - if (!waiting) - rc = -ETIMEDOUT; - else - rc = urb.status; - - ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb); - if (ret < 0 && errno != EINVAL && usb_debug >= 1) - fprintf(stderr, "error discarding URB: %s", strerror(errno)); - - /* - * When the URB is unlinked, it gets moved to the completed list and - * then we need to reap it or else the next time we call this function, - * we'll get the previous completion and exit early - */ - ioctl(dev->fd, IOCTL_USB_REAPURB, &context); - - return rc; - } - - return bytesdone; -} - -int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - /* Ensure the endpoint address is correct */ - return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, - timeout); -} - -int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - /* Ensure the endpoint address is correct */ - ep |= USB_ENDPOINT_IN; - return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size, - timeout); -} - -/* - * FIXME: Packetize large buffers here. 2.4 HCDs (atleast, haven't checked - * 2.5 HCDs yet) don't handle multi-packet Interrupt transfers. So we need - * to lookup the endpoint packet size and packetize appropriately here. - */ -int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - /* Ensure the endpoint address is correct */ - return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size, - timeout); -} - -int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout) -{ - /* Ensure the endpoint address is correct */ - ep |= USB_ENDPOINT_IN; - return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size, - timeout); -} - -int usb_os_find_busses(struct usb_bus **busses) -{ - struct usb_bus *fbus = NULL; - DIR *dir; - struct dirent *entry; - - dir = opendir(usb_path); - if (!dir) - USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", usb_path, - strerror(errno)); - - while ((entry = readdir(dir)) != NULL) { - struct usb_bus *bus; - - /* Skip anything starting with a . */ - if (entry->d_name[0] == '.') - continue; - - if (!strchr("0123456789", entry->d_name[strlen(entry->d_name) - 1])) { - if (usb_debug >= 2) - fprintf(stderr, "usb_os_find_busses: Skipping non bus directory %s\n", - entry->d_name); - continue; - } - - bus = malloc(sizeof(*bus)); - if (!bus) - USB_ERROR(-ENOMEM); - - memset((void *)bus, 0, sizeof(*bus)); - - strncpy(bus->dirname, entry->d_name, sizeof(bus->dirname) - 1); - bus->dirname[sizeof(bus->dirname) - 1] = 0; - - LIST_ADD(fbus, bus); - - if (usb_debug >= 2) - fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname); - } - - closedir(dir); - - *busses = fbus; - - return 0; -} - -int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices) -{ - struct usb_device *fdev = NULL; - DIR *dir; - struct dirent *entry; - char dirpath[PATH_MAX + 1]; - - snprintf(dirpath, PATH_MAX, "%s/%s", usb_path, bus->dirname); - - dir = opendir(dirpath); - if (!dir) - USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", dirpath, - strerror(errno)); - - while ((entry = readdir(dir)) != NULL) { - unsigned char device_desc[DEVICE_DESC_LENGTH]; - char filename[PATH_MAX + 1]; - struct usb_device *dev; - struct usb_connectinfo connectinfo; - int i, fd, ret; - - /* Skip anything starting with a . */ - if (entry->d_name[0] == '.') - continue; - - dev = malloc(sizeof(*dev)); - if (!dev) - USB_ERROR(-ENOMEM); - - memset((void *)dev, 0, sizeof(*dev)); - - dev->bus = bus; - - strncpy(dev->filename, entry->d_name, sizeof(dev->filename) - 1); - dev->filename[sizeof(dev->filename) - 1] = 0; - - snprintf(filename, sizeof(filename) - 1, "%s/%s", dirpath, entry->d_name); - fd = open(filename, O_RDWR); - if (fd < 0) { - fd = open(filename, O_RDONLY); - if (fd < 0) { - if (usb_debug >= 2) - fprintf(stderr, "usb_os_find_devices: Couldn't open %s\n", - filename); - - free(dev); - continue; - } - } - - /* Get the device number */ - ret = ioctl(fd, IOCTL_USB_CONNECTINFO, &connectinfo); - if (ret < 0) { - if (usb_debug) - fprintf(stderr, "usb_os_find_devices: couldn't get connect info\n"); - } else - dev->devnum = connectinfo.devnum; - - ret = read(fd, (void *)device_desc, DEVICE_DESC_LENGTH); - if (ret < 0) { - if (usb_debug) - fprintf(stderr, "usb_os_find_devices: Couldn't read descriptor\n"); - - free(dev); - - goto err; - } - - /* - * Linux kernel converts the words in this descriptor to CPU endian, so - * we use the undocumented W character for usb_parse_descriptor() that - * doesn't convert endianess when parsing the descriptor - */ - usb_parse_descriptor(device_desc, "bbWbbbbWWWbbbb", &dev->descriptor); - - LIST_ADD(fdev, dev); - - if (usb_debug >= 2) - fprintf(stderr, "usb_os_find_devices: Found %s on %s\n", - dev->filename, bus->dirname); - - /* Now try to fetch the rest of the descriptors */ - if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) - /* Silent since we'll try again later */ - goto err; - - if (dev->descriptor.bNumConfigurations < 1) - /* Silent since we'll try again later */ - goto err; - - dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor)); - if (!dev->config) - /* Silent since we'll try again later */ - goto err; - - memset(dev->config, 0, dev->descriptor.bNumConfigurations * - sizeof(struct usb_config_descriptor)); - - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - unsigned char buffer[8], *bigbuffer; - struct usb_config_descriptor config; - - /* Get the first 8 bytes so we can figure out what the total length is */ - ret = read(fd, (void *)buffer, 8); - if (ret < 8) { - if (usb_debug >= 1) { - if (ret < 0) - fprintf(stderr, "Unable to get descriptor (%d)\n", ret); - else - fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, ret); - } - - goto err; - } - - usb_parse_descriptor(buffer, "bbw", &config); - - bigbuffer = malloc(config.wTotalLength); - if (!bigbuffer) { - if (usb_debug >= 1) - fprintf(stderr, "Unable to allocate memory for descriptors\n"); - goto err; - } - - /* Read the rest of the config descriptor */ - memcpy(bigbuffer, buffer, 8); - - ret = read(fd, (void *)(bigbuffer + 8), config.wTotalLength - 8); - if (ret < config.wTotalLength - 8) { - if (usb_debug >= 1) { - if (ret < 0) - fprintf(stderr, "Unable to get descriptor (%d)\n", ret); - else - fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, ret); - } - - free(bigbuffer); - goto err; - } - - ret = usb_parse_configuration(&dev->config[i], bigbuffer); - if (usb_debug >= 2) { - if (ret > 0) - fprintf(stderr, "Descriptor data still left\n"); - else if (ret < 0) - fprintf(stderr, "Unable to parse descriptors\n"); - } - - free(bigbuffer); - } - -err: - close(fd); - } - - closedir(dir); - - *devices = fdev; - - return 0; -} - -int usb_os_determine_children(struct usb_bus *bus) -{ - struct usb_device *dev, *devices[256]; - struct usb_ioctl command; - int ret, i, i1; - - /* Create a list of devices first */ - memset(devices, 0, sizeof(devices)); - for (dev = bus->devices; dev; dev = dev->next) - if (dev->devnum) - devices[dev->devnum] = dev; - - /* Now fetch the children for each device */ - for (dev = bus->devices; dev; dev = dev->next) { - struct usb_hub_portinfo portinfo; - int fd; - - fd = device_open(dev); - if (fd < 0) - continue; - - /* Query the hub driver for the children of this device */ - if (dev->config && dev->config->interface && dev->config->interface->altsetting) - command.ifno = dev->config->interface->altsetting->bInterfaceNumber; - else - command.ifno = 0; - command.ioctl_code = IOCTL_USB_HUB_PORTINFO; - command.data = &portinfo; - ret = ioctl(fd, IOCTL_USB_IOCTL, &command); - if (ret < 0) { - /* errno == ENOSYS means the device probably wasn't a hub */ - if (errno != ENOSYS && usb_debug > 1) - fprintf(stderr, "error obtaining child information: %s\n", - strerror(errno)); - - close(fd); - continue; - } - - dev->num_children = 0; - for (i = 0; i < portinfo.numports; i++) - if (portinfo.port[i]) - dev->num_children++; - - /* Free any old children first */ - free(dev->children); - - dev->children = malloc(sizeof(struct usb_device *) * dev->num_children); - if (!dev->children) { - if (usb_debug > 1) - fprintf(stderr, "error allocating %zu bytes memory for dev->children\n", - sizeof(struct usb_device *) * dev->num_children); - - dev->num_children = 0; - close(fd); - continue; - } - - for (i = 0, i1 = 0; i < portinfo.numports; i++) { - if (!portinfo.port[i]) - continue; - - dev->children[i1++] = devices[portinfo.port[i]]; - - devices[portinfo.port[i]] = NULL; - } - - close(fd); - } - - /* - * There should be one device left in the devices list and that should be - * the root device - */ - for (i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) { - if (devices[i]) - bus->root_dev = devices[i]; - } - - return 0; -} - -static int check_usb_vfs(const char *dirname) -{ - DIR *dir; - struct dirent *entry; - int found = 0; - - dir = opendir(dirname); - if (!dir) - return 0; - - while ((entry = readdir(dir)) != NULL) { - /* Skip anything starting with a . */ - if (entry->d_name[0] == '.') - continue; - - /* We assume if we find any files that it must be the right place */ - found = 1; - break; - } - - closedir(dir); - - return found; -} - -void usb_os_init(void) -{ - /* Find the path to the virtual filesystem */ - if (getenv("USB_DEVFS_PATH")) { - if (check_usb_vfs(getenv("USB_DEVFS_PATH"))) { - strncpy(usb_path, getenv("USB_DEVFS_PATH"), sizeof(usb_path) - 1); - usb_path[sizeof(usb_path) - 1] = 0; - } else if (usb_debug) - fprintf(stderr, "usb_os_init: couldn't find USB VFS in USB_DEVFS_PATH\n"); - } - - if (!usb_path[0]) { - if (check_usb_vfs("/dev/bus/usb")) { - strncpy(usb_path, "/dev/bus/usb", sizeof(usb_path) - 1); - usb_path[sizeof(usb_path) - 1] = 0; - } else if (check_usb_vfs("/proc/bus/usb")) { - strncpy(usb_path, "/proc/bus/usb", sizeof(usb_path) - 1); - usb_path[sizeof(usb_path) - 1] = 0; - } else - usb_path[0] = 0; /* No path, no USB support */ - } - - if (usb_debug) { - if (usb_path[0]) - fprintf(stderr, "usb_os_init: Found USB VFS at %s\n", usb_path); - else - fprintf(stderr, "usb_os_init: No USB VFS found, is it mounted?\n"); - } -} - -int usb_resetep(usb_dev_handle *dev, unsigned int ep) -{ - int ret; - - ret = ioctl(dev->fd, IOCTL_USB_RESETEP, &ep); - if (ret) - USB_ERROR_STR(-errno, "could not reset ep %d: %s", ep, - strerror(errno)); - - return 0; -} - -int usb_clear_halt(usb_dev_handle *dev, unsigned int ep) -{ - int ret; - - ret = ioctl(dev->fd, IOCTL_USB_CLEAR_HALT, &ep); - if (ret) - USB_ERROR_STR(-errno, "could not clear/halt ep %d: %s", ep, - strerror(errno)); - - return 0; -} - -int usb_reset(usb_dev_handle *dev) -{ - int ret; - - ret = ioctl(dev->fd, IOCTL_USB_RESET, NULL); - if (ret) - USB_ERROR_STR(-errno, "could not reset: %s", strerror(errno)); - - return 0; -} - -int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name, - unsigned int namelen) -{ - struct usb_getdriver getdrv; - int ret; - - getdrv.interface = interface; - ret = ioctl(dev->fd, IOCTL_USB_GETDRIVER, &getdrv); - if (ret) - USB_ERROR_STR(-errno, "could not get bound driver: %s", strerror(errno)); - - strncpy(name, getdrv.driver, namelen - 1); - name[namelen - 1] = 0; - - return 0; -} - -int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface) -{ - struct usb_ioctl command; - int ret; - - command.ifno = interface; - command.ioctl_code = IOCTL_USB_DISCONNECT; - command.data = NULL; - - ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command); - if (ret) - USB_ERROR_STR(-errno, "could not detach kernel driver from interface %d: %s", - interface, strerror(errno)); - - return 0; -} - diff --git a/src/libusb/linux.h b/src/libusb/linux.h deleted file mode 100644 index e544c09..0000000 --- a/src/libusb/linux.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 __LINUX_H__ -#define __LINUX_H__ - -#include -#include -#include - -struct usb_ctrltransfer { - /* keep in sync with usbdevice_fs.h:usbdevfs_ctrltransfer */ - u_int8_t bRequestType; - u_int8_t bRequest; - u_int16_t wValue; - u_int16_t wIndex; - u_int16_t wLength; - - u_int32_t timeout; /* in milliseconds */ - - /* pointer to data */ - void *data; -}; - -struct usb_bulktransfer { - /* keep in sync with usbdevice_fs.h:usbdevfs_bulktransfer */ - unsigned int ep; - unsigned int len; - unsigned int timeout; /* in milliseconds */ - - /* pointer to data */ - void *data; -}; - -struct usb_setinterface { - /* keep in sync with usbdevice_fs.h:usbdevfs_setinterface */ - unsigned int interface; - unsigned int altsetting; -}; - -#define USB_MAXDRIVERNAME 255 - -struct usb_getdriver { - unsigned int interface; - char driver[USB_MAXDRIVERNAME + 1]; -}; - -#define USB_URB_DISABLE_SPD 1 -#define USB_URB_ISO_ASAP 2 -#define USB_URB_QUEUE_BULK 0x10 - -#define USB_URB_TYPE_ISO 0 -#define USB_URB_TYPE_INTERRUPT 1 -#define USB_URB_TYPE_CONTROL 2 -#define USB_URB_TYPE_BULK 3 - -struct usb_iso_packet_desc { - unsigned int length; - unsigned int actual_length; - unsigned int status; -}; - -struct usb_urb { - unsigned char type; - unsigned char endpoint; - int status; - unsigned int flags; - void *buffer; - int buffer_length; - int actual_length; - int start_frame; - int number_of_packets; - int error_count; - unsigned int signr; /* signal to be sent on error, -1 if none should be sent */ - void *usercontext; - struct usb_iso_packet_desc iso_frame_desc[0]; -}; - -struct usb_connectinfo { - unsigned int devnum; - unsigned char slow; -}; - -struct usb_ioctl { - int ifno; /* interface 0..N ; negative numbers reserved */ - int ioctl_code; /* MUST encode size + direction of data so the - * macros in give correct values */ - void *data; /* param buffer (in, or out) */ -}; - -struct usb_hub_portinfo { - unsigned char numports; - unsigned char port[127]; /* port to device num mapping */ -}; - -#define IOCTL_USB_CONTROL _IOWR('U', 0, struct usb_ctrltransfer) -#define IOCTL_USB_BULK _IOWR('U', 2, struct usb_bulktransfer) -#define IOCTL_USB_RESETEP _IOR('U', 3, unsigned int) -#define IOCTL_USB_SETINTF _IOR('U', 4, struct usb_setinterface) -#define IOCTL_USB_SETCONFIG _IOR('U', 5, unsigned int) -#define IOCTL_USB_GETDRIVER _IOW('U', 8, struct usb_getdriver) -#define IOCTL_USB_SUBMITURB _IOR('U', 10, struct usb_urb) -#define IOCTL_USB_DISCARDURB _IO('U', 11) -#define IOCTL_USB_REAPURB _IOW('U', 12, void *) -#define IOCTL_USB_REAPURBNDELAY _IOW('U', 13, void *) -#define IOCTL_USB_CLAIMINTF _IOR('U', 15, unsigned int) -#define IOCTL_USB_RELEASEINTF _IOR('U', 16, unsigned int) -#define IOCTL_USB_CONNECTINFO _IOW('U', 17, struct usb_connectinfo) -#define IOCTL_USB_IOCTL _IOWR('U', 18, struct usb_ioctl) -#define IOCTL_USB_HUB_PORTINFO _IOR('U', 19, struct usb_hub_portinfo) -#define IOCTL_USB_RESET _IO('U', 20) -#define IOCTL_USB_CLEAR_HALT _IOR('U', 21, unsigned int) -#define IOCTL_USB_DISCONNECT _IO('U', 22) -#define IOCTL_USB_CONNECT _IO('U', 23) - -/* - * IOCTL_USB_HUB_PORTINFO, IOCTL_USB_DISCONNECT and IOCTL_USB_CONNECT - * all work via IOCTL_USB_IOCTL - */ - -#endif - diff --git a/src/libusb/usb.c b/src/libusb/usb.c deleted file mode 100644 index ea4b375..0000000 --- a/src/libusb/usb.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * 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. - */ -/* - * Main API entry point - * - * Copyright (c) 2000-2003 Johannes Erdfelt - * - * This library is covered by the LGPL, read LICENSE for details. - */ - -#include /* getenv */ -#include /* stderr */ -#include /* strcmp */ -#include - -#include "usbi.h" - -int usb_debug = 0; -struct usb_bus *usb_busses = NULL; - -int usb_find_busses(void) -{ - struct usb_bus *busses, *bus; - int ret, changes = 0; - - ret = usb_os_find_busses(&busses); - if (ret < 0) - return ret; - - /* - * Now walk through all of the busses we know about and compare against - * this new list. Any duplicates will be removed from the new list. - * If we don't find it in the new list, the bus was removed. Any - * busses still in the new list, are new to us. - */ - bus = usb_busses; - while (bus) { - int found = 0; - struct usb_bus *nbus, *tbus = bus->next; - - nbus = busses; - while (nbus) { - struct usb_bus *tnbus = nbus->next; - - if (!strcmp(bus->dirname, nbus->dirname)) { - /* Remove it from the new busses list */ - LIST_DEL(busses, nbus); - - usb_free_bus(nbus); - found = 1; - break; - } - - nbus = tnbus; - } - - if (!found) { - /* The bus was removed from the system */ - LIST_DEL(usb_busses, bus); - usb_free_bus(bus); - changes++; - } - - bus = tbus; - } - - /* - * Anything on the *busses list is new. So add them to usb_busses and - * process them like the new bus it is. - */ - bus = busses; - while (bus) { - struct usb_bus *tbus = bus->next; - - /* - * Remove it from the temporary list first and add it to the real - * usb_busses list. - */ - LIST_DEL(busses, bus); - - LIST_ADD(usb_busses, bus); - - changes++; - - bus = tbus; - } - - return changes; -} - -int usb_find_devices(void) -{ - struct usb_bus *bus; - int ret, changes = 0; - - for (bus = usb_busses; bus; bus = bus->next) { - struct usb_device *devices, *dev; - - /* Find all of the devices and put them into a temporary list */ - ret = usb_os_find_devices(bus, &devices); - if (ret < 0) - return ret; - - /* - * Now walk through all of the devices we know about and compare - * against this new list. Any duplicates will be removed from the new - * list. If we don't find it in the new list, the device was removed. - * Any devices still in the new list, are new to us. - */ - dev = bus->devices; - while (dev) { - int found = 0; - struct usb_device *ndev, *tdev = dev->next; - - ndev = devices; - while (ndev) { - struct usb_device *tndev = ndev->next; - - if (!strcmp(dev->filename, ndev->filename)) { - /* Remove it from the new devices list */ - LIST_DEL(devices, ndev); - - usb_free_dev(ndev); - found = 1; - break; - } - - ndev = tndev; - } - - if (!found) { - /* The device was removed from the system */ - LIST_DEL(bus->devices, dev); - usb_free_dev(dev); - changes++; - } - - dev = tdev; - } - - /* - * Anything on the *devices list is new. So add them to bus->devices and - * process them like the new device it is. - */ - dev = devices; - while (dev) { - struct usb_device *tdev = dev->next; - - /* - * Remove it from the temporary list first and add it to the real - * bus->devices list. - */ - LIST_DEL(devices, dev); - - LIST_ADD(bus->devices, dev); - - /* - * Some ports fetch the descriptors on scanning (like Linux) so we don't - * need to fetch them again. - */ - if (!dev->config) { - usb_dev_handle *udev; - - udev = usb_open(dev); - if (udev) { - usb_fetch_and_parse_descriptors(udev); - - usb_close(udev); - } - } - - changes++; - - dev = tdev; - } - - usb_os_determine_children(bus); - } - - return changes; -} - -void usb_set_debug(int level) -{ - if (usb_debug || level) - fprintf(stderr, "usb_set_debug: Setting debugging level to %d (%s)\n", - level, level ? "on" : "off"); - - usb_debug = level; -} - -void usb_init(void) -{ - if (getenv("USB_DEBUG")) - usb_set_debug(atoi(getenv("USB_DEBUG"))); - - usb_os_init(); -} - -usb_dev_handle *usb_open(struct usb_device *dev) -{ - usb_dev_handle *udev; - - udev = malloc(sizeof(*udev)); - if (!udev) - return NULL; - - udev->fd = -1; - udev->device = dev; - udev->bus = dev->bus; - udev->config = udev->interface = udev->altsetting = -1; - - if (usb_os_open(udev) < 0) { - free(udev); - return NULL; - } - - return udev; -} - -int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, - size_t buflen) -{ - /* - * We can't use usb_get_descriptor() because it's lacking the index - * parameter. This will be fixed in libusb 1.0 - */ - return usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, - (USB_DT_STRING << 8) + index, langid, buf, buflen, 1000); -} - -int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen) -{ - char tbuf[255]; /* Some devices choke on size > 255 */ - int ret, langid, si, di; - - /* - * Asking for the zero'th index is special - it returns a string - * descriptor that contains all the language IDs supported by the - * device. Typically there aren't many - often only one. The - * language IDs are 16 bit numbers, and they start at the third byte - * in the descriptor. See USB 2.0 specification, section 9.6.7, for - * more information on this. */ - ret = usb_get_string(dev, 0, 0, tbuf, sizeof(tbuf)); - if (ret < 0) - return ret; - - if (ret < 4) - return -EIO; - - langid = tbuf[2] | (tbuf[3] << 8); - - ret = usb_get_string(dev, index, langid, tbuf, sizeof(tbuf)); - if (ret < 0) - return ret; - - if (tbuf[1] != USB_DT_STRING) - return -EIO; - - if (tbuf[0] > ret) - return -EFBIG; - - for (di = 0, si = 2; si < tbuf[0]; si += 2) { - if (di >= (buflen - 1)) - break; - - if (tbuf[si + 1]) /* high byte */ - buf[di++] = '?'; - else - buf[di++] = tbuf[si]; - } - - buf[di] = 0; - - return di; -} - -int usb_close(usb_dev_handle *dev) -{ - int ret; - - ret = usb_os_close(dev); - free(dev); - - return ret; -} - -struct usb_device *usb_device(usb_dev_handle *dev) -{ - return dev->device; -} - -void usb_free_dev(struct usb_device *dev) -{ - usb_destroy_configuration(dev); - free(dev->children); - free(dev); -} - -struct usb_bus *usb_get_busses(void) -{ - return usb_busses; -} - -void usb_free_bus(struct usb_bus *bus) -{ - free(bus); -} - diff --git a/src/libusb/usb.h b/src/libusb/usb.h deleted file mode 100644 index 35955b7..0000000 --- a/src/libusb/usb.h +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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. - */ -/* - * Prototypes, structure definitions and macros. - * - * Copyright (c) 2000-2003 Johannes Erdfelt - * - * This library is covered by the LGPL, read LICENSE for details. - * - * This file (and only this file) may alternatively be licensed under the - * BSD license as well, read LICENSE for details. - */ -#ifndef __USB_H__ -#define __USB_H__ - -#include -#include -#include - -#include - -/* - * USB spec information - * - * This is all stuff grabbed from various USB specs and is pretty much - * not subject to change - */ - -/* - * Device and/or Interface Class codes - */ -#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_PTP 6 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_DATA 10 -#define USB_CLASS_VENDOR_SPEC 0xff - -/* - * Descriptor types - */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_DT_HID 0x21 -#define USB_DT_REPORT 0x22 -#define USB_DT_PHYSICAL 0x23 -#define USB_DT_HUB 0x29 - -/* - * Descriptor sizes per descriptor type - */ -#define USB_DT_DEVICE_SIZE 18 -#define USB_DT_CONFIG_SIZE 9 -#define USB_DT_INTERFACE_SIZE 9 -#define USB_DT_ENDPOINT_SIZE 7 -#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define USB_DT_HUB_NONVAR_SIZE 7 - -/* All standard descriptors have these 2 fields in common */ -struct usb_descriptor_header { - unsigned char bLength; - unsigned char bDescriptorType; -}; - -/* String descriptor */ -struct usb_string_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wData[1]; -}; - -/* HID descriptor */ -struct usb_hid_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdHID; - unsigned char bCountryCode; - unsigned char bNumDescriptors; - /* unsigned char bReportDescriptorType; */ - /* unsigned short wDescriptorLength; */ - /* ... */ -}; - -/* Endpoint descriptor */ -#define USB_MAXENDPOINTS 32 -struct usb_endpoint_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bEndpointAddress; - unsigned char bmAttributes; - unsigned short wMaxPacketSize; - unsigned char bInterval; - unsigned char bRefresh; - unsigned char bSynchAddress; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ -#define USB_ENDPOINT_DIR_MASK 0x80 - -#define USB_ENDPOINT_TYPE_MASK 0x03 /* in bmAttributes */ -#define USB_ENDPOINT_TYPE_CONTROL 0 -#define USB_ENDPOINT_TYPE_ISOCHRONOUS 1 -#define USB_ENDPOINT_TYPE_BULK 2 -#define USB_ENDPOINT_TYPE_INTERRUPT 3 - -/* Interface descriptor */ -#define USB_MAXINTERFACES 32 -struct usb_interface_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bInterfaceNumber; - unsigned char bAlternateSetting; - unsigned char bNumEndpoints; - unsigned char bInterfaceClass; - unsigned char bInterfaceSubClass; - unsigned char bInterfaceProtocol; - unsigned char iInterface; - - struct usb_endpoint_descriptor *endpoint; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -#define USB_MAXALTSETTING 128 /* Hard limit */ -struct usb_interface { - struct usb_interface_descriptor *altsetting; - - int num_altsetting; -}; - -/* Configuration descriptor information.. */ -#define USB_MAXCONFIG 8 -struct usb_config_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wTotalLength; - unsigned char bNumInterfaces; - unsigned char bConfigurationValue; - unsigned char iConfiguration; - unsigned char bmAttributes; - unsigned char MaxPower; - - struct usb_interface *interface; - - unsigned char *extra; /* Extra descriptors */ - int extralen; -}; - -/* Device descriptor */ -struct usb_device_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdUSB; - unsigned char bDeviceClass; - unsigned char bDeviceSubClass; - unsigned char bDeviceProtocol; - unsigned char bMaxPacketSize0; - unsigned short idVendor; - unsigned short idProduct; - unsigned short bcdDevice; - unsigned char iManufacturer; - unsigned char iProduct; - unsigned char iSerialNumber; - unsigned char bNumConfigurations; -}; - -struct usb_ctrl_setup { - unsigned char bRequestType; - unsigned char bRequest; - unsigned short wValue; - unsigned short wIndex; - unsigned short wLength; -}; - -/* - * Standard requests - */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -/* 0x02 is reserved */ -#define USB_REQ_SET_FEATURE 0x03 -/* 0x04 is reserved */ -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* - * Various libusb API related stuff - */ - -#define USB_ENDPOINT_IN 0x80 -#define USB_ENDPOINT_OUT 0x00 - -/* Error codes */ -#define USB_ERROR_BEGIN 500000 - -/* - * This is supposed to look weird. This file is generated from autoconf - * and I didn't want to make this too complicated. - */ -#if _BIGENDIAN -#define USB_LE16_TO_CPU(x) do { x = ((x & 0xff) << 8) | ((x & 0xff00) >> 8); } while(0) -#else -#define USB_LE16_TO_CPU(x) -#endif - -/* Data types */ -struct usb_device; -struct usb_bus; - -/* - * To maintain compatibility with applications already built with libusb, - * we must only add entries to the end of this structure. NEVER delete or - * move members and only change types if you really know what you're doing. - */ -struct usb_device { - struct usb_device *next, *prev; - - char filename[PATH_MAX + 1]; - - struct usb_bus *bus; - - struct usb_device_descriptor descriptor; - struct usb_config_descriptor *config; - - void *dev; /* Darwin support */ - - unsigned char devnum; - - unsigned char num_children; - struct usb_device **children; -}; - -struct usb_bus { - struct usb_bus *next, *prev; - - char dirname[PATH_MAX + 1]; - - struct usb_device *devices; - unsigned long location; - - struct usb_device *root_dev; -}; - -struct usb_dev_handle; -typedef struct usb_dev_handle usb_dev_handle; - -/* Variables */ -extern struct usb_bus *usb_busses; - -#ifdef __cplusplus -extern "C" { -#endif - -/* Function prototypes */ - -/* usb.c */ -usb_dev_handle *usb_open(struct usb_device *dev); -int usb_close(usb_dev_handle *dev); -int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, - size_t buflen); -int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, - size_t buflen); - -/* descriptors.c */ -int usb_get_descriptor_by_endpoint(usb_dev_handle *udev, int ep, - unsigned char type, unsigned char index, void *buf, int size); -int usb_get_descriptor(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size); - -/* .c */ -int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); -int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); -int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); -int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, - int timeout); -int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, - int value, int index, char *bytes, int size, int timeout); -int usb_set_configuration(usb_dev_handle *dev, int configuration); -int usb_claim_interface(usb_dev_handle *dev, int interface); -int usb_release_interface(usb_dev_handle *dev, int interface); -int usb_set_altinterface(usb_dev_handle *dev, int alternate); -int usb_resetep(usb_dev_handle *dev, unsigned int ep); -int usb_clear_halt(usb_dev_handle *dev, unsigned int ep); -int usb_reset(usb_dev_handle *dev); - -#if LINUX_API -#define LIBUSB_HAS_GET_DRIVER_NP 1 -int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name, - unsigned int namelen); -#define LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP 1 -int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface); -#endif - -char *usb_strerror(void); - -void usb_init(void); -void usb_set_debug(int level); -int usb_find_busses(void); -int usb_find_devices(void); -struct usb_device *usb_device(usb_dev_handle *dev); -struct usb_bus *usb_get_busses(void); - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_H__ */ - diff --git a/src/libusb/usbi.h b/src/libusb/usbi.h deleted file mode 100644 index d71b5c4..0000000 --- a/src/libusb/usbi.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 _USBI_H_ -#define _USBI_H_ - -#include "usb.h" - -#include "error.h" - -extern int usb_debug; - -/* Some quick and generic macros for the simple kind of lists we use */ -#define LIST_ADD(begin, ent) \ - do { \ - if (begin) { \ - ent->next = begin; \ - ent->next->prev = ent; \ - } else \ - ent->next = NULL; \ - ent->prev = NULL; \ - begin = ent; \ - } while(0) - -#define LIST_DEL(begin, ent) \ - do { \ - if (ent->prev) \ - ent->prev->next = ent->next; \ - else \ - begin = ent->next; \ - if (ent->next) \ - ent->next->prev = ent->prev; \ - ent->prev = NULL; \ - ent->next = NULL; \ - } while (0) - -#define DESC_HEADER_LENGTH 2 -#define DEVICE_DESC_LENGTH 18 -#define CONFIG_DESC_LENGTH 9 -#define INTERFACE_DESC_LENGTH 9 -#define ENDPOINT_DESC_LENGTH 7 -#define ENDPOINT_AUDIO_DESC_LENGTH 9 - -struct usb_dev_handle { - int fd; - - struct usb_bus *bus; - struct usb_device *device; - - int config; - int interface; - int altsetting; - - /* Added by RMT so implementations can store other per-open-device data */ - void *impl_info; -}; - -/* descriptors.c */ -int usb_parse_descriptor(unsigned char *source, char *description, void *dest); -int usb_parse_configuration(struct usb_config_descriptor *config, - unsigned char *buffer); -void usb_fetch_and_parse_descriptors(usb_dev_handle *udev); -void usb_destroy_configuration(struct usb_device *dev); - -/* OS specific routines */ -int usb_os_find_busses(struct usb_bus **busses); -int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices); -int usb_os_determine_children(struct usb_bus *bus); -void usb_os_init(void); -int usb_os_open(usb_dev_handle *dev); -int usb_os_close(usb_dev_handle *dev); - -void usb_free_dev(struct usb_device *dev); -void usb_free_bus(struct usb_bus *bus); - -#endif /* _USBI_H_ */ - diff --git a/src/libusb/windows.c b/src/libusb/windows.c deleted file mode 100644 index 9c79b24..0000000 --- a/src/libusb/windows.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * 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 -#include -#include -#include "winusb.h" - -#include -#include -#include -#include "fdevent.h" -#include "utils.h" -#include "strutils.h" - -#define TRACE_TAG TRACE_USB -#include "sdb.h" -#include "sdb_usb.h" - -struct usb_handle { - usb_handle *prev; - usb_handle *next; - - HANDLE hnd; - WINUSB_INTERFACE_HANDLE fd; - char unique_node_path[PATH_MAX+1]; //MAX_DEVICE_ID_LEN \ - UCHAR end_point[2]; - unsigned int zero_mask; -}; -usb_handle *usb_open(const char *device_path); -int win_usb_close(usb_handle *dev); -int usb_get_string_simple(usb_handle *dev, int index, char *buf, size_t buflen); -void *device_poll_thread(void* unused); -int usb_bulk_transfer(usb_handle* handle, BOOL is_read, const void *data, unsigned long length, unsigned long *actual_length, unsigned long timeout); -void do_lsusb(void); - -// Class ID assigned to the device by aNdrOiduSb.sys -static const GUID TIZEN_CLASSID = {0x9ca29f37, 0xdd62, 0x4aad, {0x82, 0x65, 0xcf, 0xf7, 0x88, 0xc8, 0xba, 0x89}}; - -static usb_handle handle_list = { - .prev = &handle_list, - .next = &handle_list, -}; - -SDB_MUTEX_DEFINE( usb_lock ); - - -// winusb.dll entrypoints -DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR)); -DLL_DECLARE(WINAPI, BOOL, WinUsb_GetOverlappedResult, (WINUSB_INTERFACE_HANDLE, LPOVERLAPPED, LPDWORD, BOOL)); - -void win_usb_init(void) { - // Initialize DLL functions - if (!WinUsb_Initialize) { - DLL_LOAD(winusb.dll, WinUsb_Initialize); - DLL_LOAD(winusb.dll, WinUsb_Free); - DLL_LOAD(winusb.dll, WinUsb_GetAssociatedInterface); - DLL_LOAD(winusb.dll, WinUsb_GetDescriptor); - DLL_LOAD(winusb.dll, WinUsb_QueryInterfaceSettings); - DLL_LOAD(winusb.dll, WinUsb_QueryDeviceInformation); - DLL_LOAD(winusb.dll, WinUsb_SetCurrentAlternateSetting); - DLL_LOAD(winusb.dll, WinUsb_GetCurrentAlternateSetting); - DLL_LOAD(winusb.dll, WinUsb_QueryPipe); - DLL_LOAD(winusb.dll, WinUsb_SetPipePolicy); - DLL_LOAD(winusb.dll, WinUsb_GetPipePolicy); - DLL_LOAD(winusb.dll, WinUsb_ReadPipe); - DLL_LOAD(winusb.dll, WinUsb_WritePipe); - DLL_LOAD(winusb.dll, WinUsb_ControlTransfer); - DLL_LOAD(winusb.dll, WinUsb_ResetPipe); - DLL_LOAD(winusb.dll, WinUsb_AbortPipe); - DLL_LOAD(winusb.dll, WinUsb_FlushPipe); - DLL_LOAD(winusb.dll, WinUsb_GetOverlappedResult); - } -} - -int is_device_registered(const char *node_path) -{ - usb_handle *usb; - int r = 0; - sdb_mutex_lock(&usb_lock); - for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ - if(!strcmp(usb->unique_node_path, node_path)) { - r = 1; - break; - } - } - sdb_mutex_unlock(&usb_lock); - return r; -} - -usb_handle *usb_open(const char *device_path) { - // Allocate storage for handles - usb_handle* usb = calloc(1, sizeof(usb_handle)); - s_strncpy(usb->unique_node_path, device_path, sizeof(usb->unique_node_path)); - - // Open generic handle to device - HANDLE hnd = CreateFile(device_path, GENERIC_WRITE | GENERIC_READ, - FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (INVALID_HANDLE_VALUE == hnd) { - return NULL; - } - // Initialize WinUSB for this device and get a WinUSB handle for it - WINUSB_INTERFACE_HANDLE fd; - if (!WinUsb_Initialize(hnd, &fd)) { - return NULL; - } - - USB_INTERFACE_DESCRIPTOR usb_interface_descriptor; - - // fetch usb interface descriptor - UCHAR interface_number; - if (!WinUsb_GetCurrentAlternateSetting(fd, &interface_number)) { - return NULL; - } - - if (!WinUsb_QueryInterfaceSettings(fd, interface_number, &usb_interface_descriptor)) { - return NULL; - } - - if (2 != usb_interface_descriptor.bNumEndpoints) { - return NULL; - } - - if (!is_sdb_interface(usb_interface_descriptor.bInterfaceClass, usb_interface_descriptor.bInterfaceSubClass, - usb_interface_descriptor.bInterfaceProtocol)) { - return NULL; - } - UCHAR endpoint_index = 0; - - for (endpoint_index = 0; endpoint_index < usb_interface_descriptor.bNumEndpoints; endpoint_index++) { - // fetch endpoint information - WINUSB_PIPE_INFORMATION pipe_info; - if (!WinUsb_QueryPipe(fd, interface_number, endpoint_index, &pipe_info)) { - return NULL; - } - - if(usb_interface_descriptor.bInterfaceProtocol == 0x01) { - if (endpoint_index == 0) { - usb->zero_mask = pipe_info.MaximumPacketSize - 1; - } - } - // only interested in bulk type - if (UsbdPipeTypeBulk == pipe_info.PipeType) { - if (USB_ENDPOINT_DIRECTION_IN(pipe_info.PipeId)) { - D("builk in endpoint index: %d, id: %04x\n", endpoint_index, pipe_info.PipeId); - usb->end_point[0] = pipe_info.PipeId; - } - - if (USB_ENDPOINT_DIRECTION_OUT(pipe_info.PipeId)) { - D("builk out endpoint index: %d, id: %04x\n", endpoint_index, pipe_info.PipeId); - usb->end_point[1] = pipe_info.PipeId; - } - - } - } - usb->hnd = hnd; - usb->fd = fd; - - return usb; -} - -int register_device(usb_handle *hnd) { - if (is_device_registered(hnd->unique_node_path)) { - return 0; - } - - sdb_mutex_lock(&usb_lock); - - hnd->next = &handle_list; - hnd->prev = handle_list.prev; - hnd->prev->next = hnd; - hnd->next->prev = hnd; - - sdb_mutex_unlock(&usb_lock); - - return 1; -} - -int usb_get_string_simple(usb_handle *dev, int index, char *buf, size_t buflen) { - unsigned char temp[MAXIMUM_USB_STRING_LENGTH]; - - ULONG actlen = 0; - //0x0409 for English (US) - if (!WinUsb_GetDescriptor(dev->fd, USB_STRING_DESCRIPTOR_TYPE, index, 0x0409, temp, sizeof(temp), &actlen)) { - return -GetLastError(); - } - // Skip first two bytes of result (descriptor id and length), then take - // every other byte as a cheap way to convert Unicode to ASCII - unsigned int i, j; - for (i = 2, j = 0; i < actlen && j < (buflen - 1); i += 2, ++j) - buf[j] = temp[i]; - buf[j] = '\0'; - - return strlen(buf); -} - -int is_sdb_interface(int usb_class, int usb_subclass, int usb_protocol) -{ - if (usb_class == SDB_INTERFACE_CLASS && usb_subclass == SDB_INTERFACE_SUBCLASS - && usb_protocol == SDB_INTERFACE_PROTOCOL) { - return 1; - } - - return 0; -} - -static int get_serial_number(usb_handle *dev, char *buf, int buflen) { - USB_DEVICE_DESCRIPTOR usb_device_descriptor; - ULONG actlen = 0; - - if (!WinUsb_GetDescriptor(dev->fd, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, (unsigned char*) &usb_device_descriptor, - sizeof(usb_device_descriptor), &actlen)) { - return 0; - } - - return usb_get_string_simple(dev, usb_device_descriptor.iSerialNumber, buf, buflen); -} - -int usb_find_devices(GUID deviceClassID) { - SP_DEVINFO_DATA deviceInfoData; - char devicePath[PATH_MAX + 1]; - BOOL bResult = TRUE; - PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL; - int index = 0; - - // from http://msdn.microsoft.com/en-us/library/windows/hardware/ff540174(v=vs.85).aspx - // Get information about all the installed devices for the specified device interface class. - HDEVINFO hDeviceInfo = SetupDiGetClassDevs(&deviceClassID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - if (hDeviceInfo == INVALID_HANDLE_VALUE) { - D("fail to find any device: %d\n", GetLastError()); - return 0; - } - - //Enumerate all the device interfaces in the device information set. - deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); - - for (index = 0; ; index++) { - - //Get information about the device interface. - SP_DEVICE_INTERFACE_DATA interfaceData; - interfaceData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA); - - bResult = SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, &deviceClassID, index, &interfaceData); - // Check if last item - if (GetLastError() == ERROR_NO_MORE_ITEMS) { - break; - } - - //Check for some other error - if (!bResult) - { - D("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError()); - break; - } - - // Determine required size for interface detail data - ULONG requiredLength = 0; - - //Interface data is returned in SP_DEVICE_INTERFACE_DETAIL_DATA - //which we need to allocate, so we have to call this function twice. - //First to get the size so that we know how much to allocate - //Second, the actual call with the allocated buffer - bResult = SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL); - - //Check for some other error - if (!bResult) { - if ((ERROR_INSUFFICIENT_BUFFER == GetLastError()) && (requiredLength > 0)) { - // Allocate storage for interface detail data - detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredLength); - - if (detailData == NULL) { - D("fail to allocate memory\n"); - break; - } - } else { - D("Error SetupDiEnumDeviceInterfaces: %d.\n", GetLastError()); - break; - } - } - - // Finally, do fetch interface detail data - detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); - //Now call it with the correct size and allocated buffer - bResult = SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, detailData, requiredLength, NULL, - &deviceInfoData); - - //Check for some other error - if (!bResult) { - D("fail to setdup get device interface detail: %d\n", GetLastError()); - if (detailData != NULL) { - free(detailData); - } - break; - } - - //copy device path - s_strncpy(devicePath, detailData->DevicePath, sizeof(devicePath)); - - if (detailData != NULL) { - free(detailData); - } - - if (!is_device_registered(devicePath)) { - struct usb_handle *hnd = usb_open(devicePath); - if (hnd != NULL) { - if (register_device(hnd)) { - char serial[256]; - if (get_serial_number(hnd, serial, sizeof(serial)) > 0) { - D("register usb for: %s\n", serial); - register_usb_transport(hnd, serial, 1); - } else { - D("fail to get usb serial name%s\n"); - win_usb_close(hnd); - } - } else { - D("fail to register_new_device for %s\n", devicePath); - win_usb_close(hnd); - } - } else { - win_usb_close(hnd); - } - } - } - // Cleanup - bResult = SetupDiDestroyDeviceInfoList(hDeviceInfo); - - return bResult; -} - -void* device_poll_thread(void* sleep_msec) { - D("Created device thread\n"); - - int mseconds = (int) sleep_msec; - while (1) { - do_lsusb(); - sdb_sleep_ms(mseconds); - } - - return NULL; -} - -void sdb_usb_init() { - sdb_thread_t tid; - - win_usb_init(); - if (sdb_thread_create(&tid, device_poll_thread, (void*)1000)) { - fatal_errno("cannot create input thread"); - } -} - -void sdb_usb_cleanup() { - D("TODO: not imple yet\n"); -} - -int usb_bulk_transfer(usb_handle* handle, BOOL is_read, const void *data, unsigned long length, unsigned long *actual_length, unsigned long timeout) { - ULONG tmp = timeout; - UCHAR endpoint; - - if (is_read) { - endpoint = handle->end_point[0]; - } else { - endpoint = handle->end_point[1]; - } - if (handle->fd == NULL) { - D("invalid handle\n"); - return 0; - } - - // do not complete within the specified time-out interval - if (!WinUsb_SetPipePolicy(handle->fd, endpoint, PIPE_TRANSFER_TIMEOUT, sizeof(tmp), &tmp)) { - D("fail to set timeout\n"); - SetLastError(ERROR_SEM_TIMEOUT); - return 0; - } - - // manual reset must be true (second param) as the reset occurs in read - - OVERLAPPED overlapped; - ZeroMemory(&overlapped, sizeof(overlapped)); - overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - BOOL ret = TRUE; - ULONG transferred = 0; - - if (is_read) { - ret = WinUsb_ReadPipe(handle->fd, endpoint, (UCHAR*) data, length, &transferred, &overlapped); - } else { - ret = WinUsb_WritePipe(handle->fd, endpoint, (UCHAR*) data, length, &transferred, &overlapped); - } - - if (!ret && (ERROR_IO_PENDING != GetLastError())) { - if (NULL != overlapped.hEvent){ - CloseHandle(overlapped.hEvent); - } - D("pipe error: (%ld/ld) error:%d\n", length, transferred, GetLastError()); - return 0; - } - - // wait for the operation to be IO completed - ret = WinUsb_GetOverlappedResult(handle->fd, &overlapped, &transferred, TRUE); - - if (ret && (NULL != actual_length)) { - *actual_length = transferred; - } - - if (NULL != overlapped.hEvent) { - CloseHandle(overlapped.hEvent); - } - - return ret ? 1 : 0; -} - -int sdb_usb_write(usb_handle* handle, const void* data, int len) { - unsigned long time_out = 5000;//5000; - unsigned long written = 0; - int ret; - - D("+sdb_usb_write %d\n", len); - - if (NULL != handle) { - ret = usb_bulk_transfer(handle, FALSE, (void*)data, (unsigned long)len, &written, time_out); - int saved_errno = GetLastError(); - D("sdb_usb_write got(ret:%d): %ld, expected: %d, errno: %d\n",ret, written, len, saved_errno); - - if (ret) { - if (written == (unsigned long) len) { - if (handle->zero_mask && (len & handle->zero_mask) == 0) { - // Send a zero length packet - usb_bulk_transfer(handle, FALSE, (void*)data, 0, &written, time_out); - } - return 0; - } - } else { - // assume ERROR_INVALID_HANDLE indicates we are disconnected - if (saved_errno == ERROR_INVALID_HANDLE) { - sdb_usb_kick(handle); - } - } - errno = saved_errno; - } else { - D("usb_write NULL handle\n"); - SetLastError(ERROR_INVALID_HANDLE); - } - - D("-sdb_usb_write failed: %d\n", errno); - - return -1; -} - -int sdb_usb_read(usb_handle *handle, void* data, int len) { - unsigned long n = 0; - int ret; - - D("+sdb_usb_read %d\n", len); - if (NULL != handle) { - while (len > 0) { - int xfer = (len > 4096) ? 4096 : len; - ret = usb_bulk_transfer(handle, TRUE, (void*)data, (unsigned long)xfer, &n, (unsigned long)0); - int saved_errno = GetLastError(); - D("sdb_usb_read got(ret:%d): %ld, expected: %d, errno: %d\n", ret, n, xfer, saved_errno); - - if (ret) { - data += n; - len -= n; - - if (len == 0) - return 0; - } else { - // assume ERROR_INVALID_HANDLE indicates we are disconnected - if (saved_errno == ERROR_INVALID_HANDLE) { - sdb_usb_kick(handle); - } - break; - } - errno = saved_errno; - } - } else { - D("sdb_usb_read NULL handle\n"); - SetLastError(ERROR_INVALID_HANDLE); - } - - D("-sdb_usb_read failed: %d\n", errno); - - return -1; -} - -void usb_cleanup_handle(usb_handle* handle) { - if (NULL != handle) { - if (NULL != handle->fd) { - WinUsb_Free(handle->fd); - handle->fd = NULL; - } - if (NULL != handle->hnd) { - CloseHandle(handle->hnd); - handle->hnd = NULL; - } - handle = NULL; - } -} - -int win_usb_close(usb_handle *handle) { - D("+usb win_usb_close\n"); - if (NULL != handle) { - usb_cleanup_handle(handle); - free(handle); - handle = NULL; - } - D("-usb win_usb_close\n"); - return 0; -} - -void sdb_usb_kick(usb_handle* handle) { - D("+sdb_usb_kick: %p\n", 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; - } - D("-sdb_usb_kick: %p\n", handle); -} - -int sdb_usb_close(usb_handle* handle) { - D("+sdb_usb_close: %p\n", handle); - - if (NULL != handle) { - sdb_mutex_lock(&usb_lock); - handle->next->prev = handle->prev; - handle->prev->next = handle->next; - handle->prev = 0; - handle->next = 0; - sdb_mutex_unlock(&usb_lock); - win_usb_close(handle); - } - D("-sdb_usb_close: %p\n", handle); - return 0; -} - -void do_lsusb() { - usb_find_devices(TIZEN_CLASSID); -} diff --git a/src/libusb/winusb.h b/src/libusb/winusb.h deleted file mode 100755 index 8e5cf8a..0000000 --- a/src/libusb/winusb.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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 __WINUSB_H -#define __WINUSB_H - -#include "ddk/usb100.h" -#include "ddk/usbioctl.h" - -// Windows API default is uppercase - ugh! -#if !defined(bool) -#define bool BOOLEAN -#endif -#if !defined(true) -#define true TRUE -#endif -#if !defined(false) -#define false FALSE -#endif - - -/* - * Some of the EX stuff is not yet in MinGW => define it - */ -#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX -#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 -#endif - -#ifndef IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX -#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ - CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, \ - METHOD_BUFFERED, FILE_ANY_ACCESS) -#endif - -#ifndef USB_NODE_CONNECTION_INFORMATION_EX -typedef struct _USB_NODE_CONNECTION_INFORMATION_EX { - ULONG ConnectionIndex; - USB_DEVICE_DESCRIPTOR DeviceDescriptor; - UCHAR CurrentConfigurationValue; - UCHAR Speed; - BOOLEAN DeviceIsHub; - USHORT DeviceAddress; - ULONG NumberOfOpenPipes; - USB_CONNECTION_STATUS ConnectionStatus; - USB_PIPE_INFO PipeList[0]; -} USB_NODE_CONNECTION_INFORMATION_EX, *PUSB_NODE_CONNECTION_INFORMATION_EX; -#endif - -#ifndef USB_HUB_CAP_FLAGS -typedef union _USB_HUB_CAP_FLAGS { - ULONG ul; - struct { - ULONG HubIsHighSpeedCapable:1; - ULONG HubIsHighSpeed:1; - ULONG HubIsMultiTtCapable:1; - ULONG HubIsMultiTt:1; - ULONG HubIsRoot:1; - ULONG HubIsArmedWakeOnConnect:1; - ULONG ReservedMBZ:26; - }; -} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS; -#endif - -#ifndef USB_HUB_CAPABILITIES_EX -typedef struct _USB_HUB_CAPABILITIES_EX { - USB_HUB_CAP_FLAGS CapabilityFlags; -} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX; -#endif - -#ifndef USB_GET_HUB_CAPABILITIES_EX -#define USB_GET_HUB_CAPABILITIES_EX 276 -#endif - -#ifndef IOCTL_USB_GET_HUB_CAPABILITIES_EX -#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ - CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, \ - METHOD_BUFFERED, FILE_ANY_ACCESS ) -#endif - -/* - * WinUSB macros - from libusb-win32 1.x - */ - -#define DLL_DECLARE(api, ret, name, args) \ - typedef ret (api * __dll_##name##_t)args; \ - static __dll_##name##_t name - -#define DLL_LOAD(dll, name) \ - do { \ - HMODULE h = GetModuleHandle(#dll); \ - if(!h) \ - h = LoadLibrary(#dll); \ - if(!h) \ - break; \ - if((name = (__dll_##name##_t)GetProcAddress(h, #name))) \ - break; \ - if((name = (__dll_##name##_t)GetProcAddress(h, #name "A"))) \ - break; \ - if((name = (__dll_##name##_t)GetProcAddress(h, #name "W"))) \ - break; \ - } while(0) - - -/* winusb.dll interface */ - -#define SHORT_PACKET_TERMINATE 0x01 -#define AUTO_CLEAR_STALL 0x02 -#define PIPE_TRANSFER_TIMEOUT 0x03 -#define IGNORE_SHORT_PACKETS 0x04 -#define ALLOW_PARTIAL_READS 0x05 -#define AUTO_FLUSH 0x06 -#define RAW_IO 0x07 -#define MAXIMUM_TRANSFER_SIZE 0x08 -#define AUTO_SUSPEND 0x81 -#define SUSPEND_DELAY 0x83 -#define DEVICE_SPEED 0x01 -#define LowSpeed 0x01 -#define FullSpeed 0x02 -#define HighSpeed 0x03 - -typedef enum _USBD_PIPE_TYPE { - UsbdPipeTypeControl, - UsbdPipeTypeIsochronous, - UsbdPipeTypeBulk, - UsbdPipeTypeInterrupt -} USBD_PIPE_TYPE; - -typedef struct { - USBD_PIPE_TYPE PipeType; - UCHAR PipeId; - USHORT MaximumPacketSize; - UCHAR Interval; -} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION; - -#pragma pack(1) -typedef struct { - UCHAR RequestType; - UCHAR Request; - USHORT Value; - USHORT Index; - USHORT Length; -} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; -#pragma pack() - -typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; - -#endif // __WINUSB_H diff --git a/src/linkedlist.c b/src/linkedlist.c deleted file mode 100644 index 93aa7bb..0000000 --- a/src/linkedlist.c +++ /dev/null @@ -1,97 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#include -#include -#include "linkedlist.h" -#include "file_sync_functions.h" - -static void default_free(void* data); - -void append(LIST_NODE** listptr, void* value) { - - LIST_NODE* prev_ptr = NULL; - LIST_NODE* current_ptr = *listptr; - - LIST_NODE* new_ptr = (LIST_NODE*)malloc(sizeof(LIST_NODE)); - new_ptr->data = value; - new_ptr->next_ptr = NULL; - - while(current_ptr != NULL) { - prev_ptr = current_ptr; - current_ptr = prev_ptr->next_ptr; - } - - //listptr is empty. - if(prev_ptr == NULL) { - *listptr = new_ptr; - } - else { - prev_ptr->next_ptr = new_ptr; - } -} - -void no_free() { - //do nothing. -} - -void free_list(LIST_NODE* listptr, void(free_func)(void*)) { - - if(free_func == NULL) { - free_func = default_free; - } - - LIST_NODE* nextptr = NULL; - LIST_NODE* currentptr = listptr; - - while(currentptr != NULL) { - nextptr = currentptr->next_ptr; - free_func(currentptr->data); - free(currentptr); - currentptr = nextptr; - } -} - -static void default_free(void* data) { - if(data != NULL) { - free(data); - } -} - -void remove_first(LIST_NODE** listptr, void(free_func)(void*)) { - - if(free_func == NULL) { - free_func = default_free; - } - - if(*listptr != NULL) { - LIST_NODE* curptr = (*listptr)->next_ptr; - LIST_NODE* removeptr = *listptr; - *listptr = curptr; - free_func(removeptr->data); - free(removeptr); - } -} diff --git a/src/linkedlist.h b/src/linkedlist.h deleted file mode 100644 index b88a5de..0000000 --- a/src/linkedlist.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef LINKEDLIST_H_ -#define LINKEDLIST_H_ - -struct list_node { - void* data; - struct list_node* next_ptr; -}; - -typedef struct list_node LIST_NODE; - -void no_free(); -void append( LIST_NODE** listptr, void* value); -void free_list(LIST_NODE* listptr, void(free_func)(void*)); -void remove_first(LIST_NODE** listptr, void(free_func)(void*)); - -#endif /* LINKEDLIST_H_ */ 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..2c36dde --- /dev/null +++ b/src/properties.c @@ -0,0 +1,457 @@ +/* + * 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 +#include +#include +#include "sockets.h" +#include +#include + +#include "properties.h" +//#include "loghack.h" +#include "sysdeps.h" +#define TRACE_TAG TRACE_PROPERTIES +#include "sdb.h" +#include "strutils.h" + +#define HAVE_TIZEN_PROPERTY + +#ifdef HAVE_TIZEN_PROPERTY + +#define HAVE_PTHREADS +#include +#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; +} + +#elif defined(HAVE_LIBC_SYSTEM_PROPERTIES) + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +//#include + +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 +#include +#include +#include +#include + +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 +#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 "::" +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 may be omitted to indicate localhost +* "udp::" - host may be omitted to indicate localhost +* "local-dgram:" +* "local-stream:" +* "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......" + + +--- 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 WRITE WRITE WRITE +server: "OKAY" + +client: +server: "FAIL" + diff --git a/src/pthread_win32.h b/src/pthread_win32.h deleted file mode 100644 index cf1ae9e..0000000 --- a/src/pthread_win32.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 _PTHREAD_WIN32_H -#define _PTHREAD_WIN32_H - -//#include - -// implementing posix thread on win32 - -typedef struct { - HANDLE handle; - void *(*start_routine)(void*); - void *arg; - DWORD tid; -} pthread_t; - -typedef struct { - LONG waiters; - int was_broadcast; - CRITICAL_SECTION waiters_lock; - HANDLE sema; - HANDLE continue_broadcast; -} pthread_cond_t; - - -typedef CRITICAL_SECTION pthread_mutex_t; -typedef pthread_mutex_t CRITICAL_SECTION; - -#define SDB_MUTEX_DEFINE(x) sdb_mutex_t x - -#endif /* _PTHREAD_WIN32_H */ 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 +#include +#include +#include +#include /* for pthread_once() */ +#include +#include +#include + +#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..21ed259 --- /dev/null +++ b/src/remount_service.c @@ -0,0 +1,110 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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; + 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'; + 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 index d33b6b8..a92ab5b 100755 --- a/src/sdb.c +++ b/src/sdb.c @@ -26,10 +26,17 @@ #include #include -#include "utils.h" -#include "fdevent.h" +#include "sysdeps.h" #include "sdb.h" -#include "commandline.h" + +#if !SDB_HOST +//#include eric +#include +#include +#define SDB_PIDPATH "/var/run/sdbd.pid" +#else +#include "usb_vendors.h" +#endif #if SDB_TRACE SDB_MUTEX_DEFINE( D_lock ); @@ -37,6 +44,19 @@ SDB_MUTEX_DEFINE( D_lock ); 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, ...) @@ -127,6 +147,61 @@ void sdb_trace_init(void) } } +#if !SDB_HOST +/* + * Implements SDB tracing inside the emulator. + */ + +#include + +/* + * 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)); @@ -223,11 +298,11 @@ static void send_connect(atransport *t) HOST ? "host" : sdb_device_banner); cp->msg.data_length = strlen((char*) cp->data) + 1; send_packet(cp, t); - - /* XXX why sleep here? */ +#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) @@ -489,10 +564,22 @@ int local_name_to_fd(const char *name) int ret; port = atoi(name + 4); ret = socket_loopback_server(port, SOCK_STREAM); - D("add loopback listen to %d", port); 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; } @@ -518,7 +605,7 @@ static int install_listener(const char *local_name, const char *connect_to, atra { alistener *l; - D("install_listener('%s','%s')\n", local_name, connect_to); + //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) { @@ -586,7 +673,7 @@ nomem: return 0; } -#ifdef OS_WINDOWS +#ifdef HAVE_WIN32_PROC static BOOL WINAPI ctrlc_handler(DWORD type) { exit(STATUS_CONTROL_C_EXIT); @@ -596,8 +683,259 @@ static BOOL WINAPI ctrlc_handler(DWORD type) static void sdb_cleanup(void) { - sdb_usb_cleanup(); + 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. @@ -609,20 +947,66 @@ 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) { -#ifdef OS_WINDOWS +#if !SDB_HOST + int port; + char value[PROPERTY_VALUE_MAX]; + + umask(000); +#endif + + atexit(sdb_cleanup); +#ifdef HAVE_WIN32_PROC SetConsoleCtrlHandler( ctrlc_handler, TRUE ); -#else +#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(); - sdb_usb_init(); + usb_vendors_init(); + usb_init(); local_init(DEFAULT_SDB_LOCAL_TRANSPORT_PORT); char local_name[30]; @@ -630,13 +1014,98 @@ int sdb_main(int is_daemon, int 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 OS_WINDOWS +#ifdef HAVE_WIN32_PROC DWORD count; WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL ); -#else +#elif defined(HAVE_FORKEXEC) fprintf(stderr, "OK\n"); #endif start_logging(); @@ -645,11 +1114,12 @@ int sdb_main(int is_daemon, int server_port) fdevent_loop(); - atexit(sdb_cleanup); + usb_cleanup(); return 0; } +#if SDB_HOST void connect_device(char* host, char* buffer, int buffer_size) { int port, fd; @@ -746,6 +1216,7 @@ void connect_emulator(char* port_spec, char* buffer, int buffer_size) console_port, sdb_port); } } +#endif int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) { @@ -756,10 +1227,11 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r fprintf(stderr,"sdb server killed by remote request\n"); fflush(stdout); sdb_write(reply_fd, "OKAY", 4); - sdb_cleanup(); + 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 @@ -912,6 +1384,7 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r local_connect(port, NULL); return 0; } +#endif // SDB_HOST if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) { char *local, *remote, *err; @@ -968,10 +1441,37 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r return -1; } +#if !SDB_HOST +int recovery_mode = 0; +#endif + int main(int argc, char **argv) { - sdb_trace_init(); + 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 - return process_cmdline(argc - 1, argv + 1); + 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 index b45134a..f7cf3c1 100755 --- a/src/sdb.h +++ b/src/sdb.h @@ -20,7 +20,6 @@ #include #include "transport.h" /* readx(), writex() */ -#include "sdb_usb.h" #define MAX_PAYLOAD 4096 @@ -34,7 +33,7 @@ #define A_VERSION 0x01000000 // SDB protocol version #define SDB_VERSION_MAJOR 2 // Used for help/version information -#define SDB_VERSION_MINOR 2 // Used for help/version information +#define SDB_VERSION_MINOR 1 // Used for help/version information #define SDB_SERVER_VERSION 0 // Increment this when we want to force users to start a new sdb server @@ -45,6 +44,7 @@ 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 */ @@ -86,7 +86,7 @@ struct asocket { */ int closing; - /* flag: quit sdbd when both ends close the + /* flag: quit adbd when both ends close the ** local service socket */ int exit_on_close; @@ -245,6 +245,8 @@ 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); @@ -270,11 +272,14 @@ 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); -void close_usb_devices(); +/* 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); @@ -289,10 +294,33 @@ void register_usb_transport(usb_handle *h, const char *serial, unsigned writeabl 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); @@ -325,8 +353,20 @@ typedef enum { #if SDB_TRACE -#define DQ(...) ((void)0) +#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; @@ -372,17 +412,38 @@ typedef enum { #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 */ +# 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); @@ -406,6 +467,12 @@ extern int SHELL_EXIT_NOTIFY_FD; 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 "" void register_device_name(const char *device_type, const char *device_name, int port); int get_devicename_from_shdmem(int port, char *device_name); #endif +#endif diff --git a/src/sdb_client.c b/src/sdb_client.c index dd26d29..18fd20b 100644 --- a/src/sdb_client.c +++ b/src/sdb_client.c @@ -20,64 +20,125 @@ #include #include #include +// tizen specific #include #include #include -#include "fdevent.h" -#include "sdb_constants.h" -#include "utils.h" + +#include "sysdeps.h" #define TRACE_TAG TRACE_SDB #include "sdb_client.h" -static int switch_socket_transport(int fd, void** extra_args); -static int send_service_with_length(int fd, const char* service); -static int sdb_status(int fd); +static transport_type __sdb_transport = kTransportAny; +static const char* __sdb_serial = NULL; -static int send_service_with_length(int fd, const char* service) { - char tmp[5]; - int len; - len = strlen(service); +static int __sdb_server_port = DEFAULT_SDB_PORT; - if(len < 1) { - fprintf(stderr,"error: service name is empty\n"); - return -1; - } - else if (len > 1024) { - fprintf(stderr,"error: service name too long\n"); - return -1; - } +void sdb_set_transport(transport_type type, const char* serial) +{ + __sdb_transport = type; + __sdb_serial = serial; +} - if(write_msg_size(fd, len) < 0) { - return -1; - } +void sdb_set_tcp_specifics(int server_port) +{ + __sdb_server_port = server_port; +} - if(writex(fd, service, len)) { - fprintf(stderr,"error: write failure during connection\n"); - return -1; +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 */ } - return 0; + serial += sizeof(LOCAL_CLIENT_PREFIX)-1; + port = strtol(serial, NULL, 10); + return port; } -static int switch_socket_transport(int fd, void** extra_args) +static char __sdb_error[256] = { 0 }; + +const char *sdb_error(void) { - char* serial = (char *)extra_args[0]; - transport_type ttype = *(transport_type*)extra_args[1]; + return __sdb_error; +} +static int switch_socket_transport(int fd) +{ char service[64]; + char tmp[5]; + int len; - get_host_prefix(service, sizeof service, ttype, serial, transport); + 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; + default: + D("unknown transport type\n"); + break; + } - if(!strcmp(service, PREFIX_HOST)) { - // no switch necessary - return 0; + snprintf(service, sizeof service, "host:%s", transport_type); } + len = strlen(service); + snprintf(tmp, sizeof tmp, "%04x", len); - if(send_service_with_length(fd, service) < 0) { + 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)) { @@ -89,12 +150,13 @@ static int switch_socket_transport(int fd, void** extra_args) return 0; } -static int sdb_status(int fd) +int sdb_status(int fd) { unsigned char buf[5]; + unsigned len; if(readx(fd, buf, 4)) { - fprintf(stderr,"error: protocol fault (no status)\n"); + strcpy(__sdb_error, "protocol fault (no status)"); return -1; } @@ -103,48 +165,53 @@ static int sdb_status(int fd) } if(memcmp(buf, "FAIL", 4)) { - fprintf(stderr,"error: protocol fault (status %02x %02x %02x %02x?!)\n", + sprintf(__sdb_error, + "protocol fault (status %02x %02x %02x %02x?!)", buf[0], buf[1], buf[2], buf[3]); return -1; } - int len = read_msg_size(fd); - if(len < 0) { - fprintf(stderr,"error: protocol fault (status len)\n"); + 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; - - - char error[255]; - if(readx(fd, error, len)) { - fprintf(stderr,"error: protocol fault (status read)\n"); + if(readx(fd, __sdb_error, len)) { + strcpy(__sdb_error, "protocol fault (status read)"); return -1; } - error[len] = '\0'; - fprintf(stderr,"error: %s\n", error); + __sdb_error[len] = 0; return -1; } -int _sdb_connect(const char *service, void** ext_args) +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); - int server_port = *(int*)ext_args[2]; - - fd = socket_loopback_client(server_port, SOCK_STREAM); + fd = socket_loopback_client(__sdb_server_port, SOCK_STREAM); if(fd < 0) { - D("error: cannot connect to daemon\n"); + strcpy(__sdb_error, "cannot connect to daemon"); return -2; } - if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd, ext_args)) { + if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) { return -1; } - if(send_service_with_length(fd, service) < 0) { + if(writex(fd, tmp, 4) || writex(fd, service, len)) { + strcpy(__sdb_error, "write failure during connection"); sdb_close(fd); return -1; } @@ -158,40 +225,17 @@ int _sdb_connect(const char *service, void** ext_args) return fd; } -int __inline__ read_msg_size(int fd) { - char buf[5]; - - if(readx(fd, buf, 4)) { - return -1; - } - - buf[4] = 0; - return strtoul(buf, NULL, 16); -} - -int __inline__ write_msg_size(int fd, int size) { - char tmp[5]; - snprintf(tmp, sizeof tmp, "%04x", size); - - if(writex(fd, tmp, 4)) { - fprintf(stderr,"error: write msg size failure\n"); - return -1; - } - return 1; -} - -int sdb_connect(const char *service, void** ext_args) +int sdb_connect(const char *service) { // first query the sdb server's version - int fd = _sdb_connect("host:version", ext_args); - int server_port = *(int*)ext_args[2]; + 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", - server_port); + __sdb_server_port); start_server: - if(launch_server(server_port)) { + if(launch_server(__sdb_server_port)) { fprintf(stderr,"* failed to start daemon *\n"); return -1; } else { @@ -202,23 +246,31 @@ int sdb_connect(const char *service, void** ext_args) // 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) { - int n = read_msg_size(fd); - char buf[100]; - if(n < 0 || readx(fd, buf, n) || sscanf(buf, "%04x", &version) != 1) { - goto error; - } + 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", ext_args); + fd = _sdb_connect("host:kill"); sdb_close(fd); /* XXX can we better detect its death? */ @@ -231,7 +283,7 @@ int sdb_connect(const char *service, void** ext_args) if (!strcmp(service, "host:start-server")) return 0; - fd = _sdb_connect(service, ext_args); + fd = _sdb_connect(service); if(fd == -2) { fprintf(stderr,"** daemon still not running"); } @@ -244,9 +296,9 @@ error: } -int sdb_command(const char *service, void** extra_args) +int sdb_command(const char *service) { - int fd = sdb_connect(service, extra_args); + int fd = sdb_connect(service); if(fd < 0) { return -1; } @@ -259,20 +311,24 @@ int sdb_command(const char *service, void** extra_args) return 0; } -char *sdb_query(const char *service, void** extra_args) +char *sdb_query(const char *service) { + char buf[5]; + unsigned n; char *tmp; D("sdb_query: %s\n", service); - int fd = sdb_connect(service, extra_args); + int fd = sdb_connect(service); if(fd < 0) { + fprintf(stderr,"error: %s\n", __sdb_error); return 0; } - int n = read_msg_size(fd); - if(n < 0 || n > 1024) { - goto oops; - } + 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; @@ -288,45 +344,3 @@ oops: sdb_close(fd); return 0; } - -void get_host_prefix(char* prefix, int size, transport_type ttype, const char* serial, HOST_TYPE host_type) { - if(serial) { - if(host_type == host) { - snprintf(prefix, size, "%s%s:", PREFIX_HOST_SERIAL, serial); - } - else if(host_type == transport) { - snprintf(prefix, size, "%s%s", PREFIX_TRANSPORT_SERIAL, serial); - } - } - else { - char* temp_prefix; - if(ttype == kTransportUsb) { - if(host_type == host) { - temp_prefix = (char*)PREFIX_HOST_USB; - } - else if(host_type == transport) { - temp_prefix = (char*)PREFIX_TRANSPORT_USB; - } - } - else if(ttype == kTransportLocal) { - if(host_type == host) { - temp_prefix = (char*)PREFIX_HOST_LOCAL; - } - else if(host_type == transport) { - temp_prefix = (char*)PREFIX_TRANSPORT_LOCAL; - } - } - else if(ttype == kTransportHost) { - temp_prefix = (char*)PREFIX_HOST; - } - else if(ttype == kTransportAny) { - if(host_type == host) { - temp_prefix = (char*)PREFIX_HOST; - } - else if(host_type == transport) { - temp_prefix = (char*)PREFIX_TRANSPORT_ANY; - } - } - snprintf(prefix, size, "%s", temp_prefix); - } -} diff --git a/src/sdb_client.h b/src/sdb_client.h index f20b672..86e9ef1 100644 --- a/src/sdb_client.h +++ b/src/sdb_client.h @@ -18,26 +18,45 @@ #define _SDB_CLIENT_H_ #include "sdb.h" -#include "sdb_constants.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, void** ext_args); -int _sdb_connect(const char *service, void** ext_args); +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, void** ext_args); +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, void** ext_args); +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 + * with a 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); @@ -46,8 +65,6 @@ const char *sdb_error(void); ** return 0 in the event of OKAY, -1 in the event of FAIL ** or protocol error */ -int __inline__ read_msg_size(int fd); -int __inline__ write_msg_size(int fd, int size); +int sdb_status(int fd); -void get_host_prefix(char* prefix, int size, transport_type ttype, const char* serial, HOST_TYPE host_type); #endif diff --git a/src/sdb_constants.c b/src/sdb_constants.c deleted file mode 100644 index aeac8e2..0000000 --- a/src/sdb_constants.c +++ /dev/null @@ -1,248 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#include "sdb_constants.h" - - const char* HELP_APPEND_STR = " "; - - const char* EMPTY_STRING = ""; - const char* DIR_APP_TMP = "/opt/usr/apps/tmp/"; - const char* QUOTE_CHAR = " \"\\()"; - - const char* PREFIX_HOST = "host:"; - const char* PREFIX_HOST_USB = "host-usb:"; - const char* PREFIX_HOST_LOCAL = "host-local:"; - const char* PREFIX_HOST_SERIAL = "host-serial:"; - - const char* PREFIX_TRANSPORT_ANY = "host:transport-any"; - const char* PREFIX_TRANSPORT_USB = "host:transport-usb"; - const char* PREFIX_TRANSPORT_LOCAL = "host:transport-local"; - const char* PREFIX_TRANSPORT_SERIAL = "host:transport:"; - - const char* COMMANDLINE_MSG_FULL_CMD = "full command of %s: %s\n"; - const char* COMMANDLINE_OPROFILE_NAME = "oprofile"; - const int COMMANDLINE_OPROFILE_MAX_ARG = -1; - const int COMMANDLINE_OPROFILE_MIN_ARG = 0; - - const char* COMMANDLINE_DA_NAME = "da"; - const int COMMANDLINE_DA_MAX_ARG = -1; - const int COMMANDLINE_DA_MIN_ARG = 0; - - const char* COMMANDLINE_LAUNCH_NAME = "launch"; - const int COMMANDLINE_LAUNCH_MAX_ARG = -1; - const int COMMANDLINE_LAUNCH_MIN_ARG = 0; - - const char* COMMANDLINE_DEVICES_NAME = "devices"; - const char* COMMANDLINE_DEVICES_DESC[] = { - "list all connected devices" - }; - const int COMMANDLINE_DEVICES_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_DEVICES_DESC, char*); - const int COMMANDLINE_DEVICES_MAX_ARG = 0; - const int COMMANDLINE_DEVICES_MIN_ARG = 0; - - const char* COMMANDLINE_DISCONNECT_NAME = "disconnect"; - const char* COMMANDLINE_DISCONNECT_DESC[] = { - "disconnect from a TCP/IP device.", - "Port 26101 is used by default if no port number is specified", - "Using this command with no additional arguments", - "will disconnect from all connected TCP/IP devices", - }; - const int COMMANDLINE_DISCONNECT_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_DISCONNECT_DESC, char*); - const char* COMMANDLINE_DISCONNECT_ARG_DESC = "[[:]]"; - const int COMMANDLINE_DISCONNECT_MAX_ARG = 1; - const int COMMANDLINE_DISCONNECT_MIN_ARG = 0; - - const char* COMMANDLINE_CONNECT_NAME = "connect"; - const char* COMMANDLINE_CONNECT_DESC[] = { - "connect to a device via TCP/IP", - "Port 26101 is used by default if no port number is specified" - }; - const int COMMANDLINE_CONNECT_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_CONNECT_DESC, char*); - const char* COMMANDLINE_CONNECT_ARG_DESC = "[:]"; - const int COMMANDLINE_CONNECT_MAX_ARG = 1; - const int COMMANDLINE_CONNECT_MIN_ARG = 1; - - const char* COMMANDLINE_GSERIAL_NAME = "get-serialno"; - const char* COMMANDLINE_GSERIAL_DESC[] = { - "print: " - }; - const int COMMANDLINE_GSERIAL_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_GSERIAL_DESC, char*); - const int COMMANDLINE_GSERIAL_MAX_ARG = 0; - const int COMMANDLINE_GSERIAL_MIN_ARG = 0; - - const char* COMMANDLINE_GSTATE_NAME = "get-state"; - const char* COMMANDLINE_GSTATE_DESC[] = { - "print: offline | bootloader | device" - }; - const int COMMANDLINE_GSTATE_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_GSTATE_DESC, char*); - const int COMMANDLINE_GSTATE_MAX_ARG = 0; - const int COMMANDLINE_GSTATE_MIN_ARG = 0; - - const char* COMMANDLINE_ROOT_NAME = "root"; - const char* COMMANDLINE_ROOT_DESC[] = { - "switch to root or developer account mode", - "'on' means to root mode, and vice versa" - }; - const int COMMANDLINE_ROOT_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_ROOT_DESC, char*); - const char* COMMANDLINE_ROOT_ARG_DESC = ""; - const int COMMANDLINE_ROOT_MAX_ARG = 1; - const int COMMANDLINE_ROOT_MIN_ARG = 1; - - const char* COMMANDLINE_SWINDOW_NAME = "status-window"; - const char* COMMANDLINE_SWINDOW_DESC[] = { - "continuously print device status for a specified device" - }; - const int COMMANDLINE_SWINDOW_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_SWINDOW_DESC, char*); - const int COMMANDLINE_SWINDOW_MAX_ARG = 0; - const int COMMANDLINE_SWINDOW_MIN_ARG = 0; - - const char* COMMANDLINE_SSERVER_NAME = "start-server"; - const char* COMMANDLINE_SSERVER_DESC[] = { - "ensure that there is a server running" - }; - const int COMMANDLINE_SSERVER_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_SSERVER_DESC, char*); - const int COMMANDLINE_SSERVER_MAX_ARG = 0; - const int COMMANDLINE_SSERVER_MIN_ARG = 0; - - const char* COMMANDLINE_KSERVER_NAME = "kill-server"; - const char* COMMANDLINE_KSERVER_DESC[] = { - "kill the server if it is running" - }; - const int COMMANDLINE_KSERVER_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_KSERVER_DESC, char*); - const int COMMANDLINE_KSERVER_MAX_ARG = 0; - const int COMMANDLINE_KSERVER_MIN_ARG = 0; - - const char* COMMANDLINE_HELP_NAME = "help"; - const char* COMMANDLINE_HELP_DESC[] = { - "show this help message" - }; - const int COMMANDLINE_HELP_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_HELP_DESC, char*); - - const char* COMMANDLINE_VERSION_NAME = "version"; - const char* COMMANDLINE_VERSION_DESC[] = { - "show version num" - }; - const int COMMANDLINE_VERSION_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_VERSION_DESC, char*); - const int COMMANDLINE_VERSION_MAX_ARG = 0; - const int COMMANDLINE_VERSION_MIN_ARG = 0; - - const char* COMMANDLINE_DLOG_NAME = "dlog"; - const char* COMMANDLINE_DLOG_DESC[] = { - "view device log" - }; - const int COMMANDLINE_DLOG_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_DLOG_DESC, char*); - const char* COMMANDLINE_DLOG_ARG_DESC = "[]"; - const int COMMANDLINE_DLOG_MAX_ARG = -1; - const int COMMANDLINE_DLOG_MIN_ARG = 0; - - const char* COMMANDLINE_FORWARD_NAME = "forward"; - const char* COMMANDLINE_FORWARD_DESC[] = { - "forward socket connections", - "forward spec is : tcp:" - }; - const int COMMANDLINE_FORWARD_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_FORWARD_DESC, char*); - const char* COMMANDLINE_FORWARD_ARG_DESC = " "; - const int COMMANDLINE_FORWARD_MAX_ARG = 2; - const int COMMANDLINE_FORWARD_MIN_ARG = 2; - - const char* COMMANDLINE_PUSH_NAME = "push"; - const char* COMMANDLINE_PUSH_DESC[] = { - "copy file/dir to device", - "(--with-utf8 means to create the remote file with utf-8 character encoding)" - }; - const int COMMANDLINE_PUSH_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_PUSH_DESC, char*); - const char* COMMANDLINE_PUSH_ARG_DESC = " [--with-utf8]"; - const int COMMANDLINE_PUSH_MAX_ARG = -1; - const int COMMANDLINE_PUSH_MIN_ARG = 2; - - const char* COMMANDLINE_PULL_NAME = "pull"; - const char* COMMANDLINE_PULL_DESC[] = { - "copy file/dir from device" - }; - const int COMMANDLINE_PULL_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_PULL_DESC, char*); - const char* COMMANDLINE_PULL_ARG_DESC = " []"; - const int COMMANDLINE_PULL_MAX_ARG = -2; - const int COMMANDLINE_PULL_MIN_ARG = 1; - - const char* COMMANDLINE_SHELL_NAME = "shell"; - const char* COMMANDLINE_SHELL_DESC[] = { - "run remote shell interactively" - }; - const int COMMANDLINE_SHELL_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_SHELL_DESC, char*); - const char* COMMANDLINE_SHELL_ARG_DESC = "[command]"; - const int COMMANDLINE_SHELL_MAX_ARG = -1; - const int COMMANDLINE_SHELL_MIN_ARG = 0; - - const char* COMMANDLINE_INSTALL_NAME = "install"; - const char* COMMANDLINE_INSTALL_DESC[] = { - "push package file and install it" - }; - const int COMMANDLINE_INSTALL_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_INSTALL_DESC, char*); - const char* COMMANDLINE_INSTALL_ARG_DESC = ""; - const int COMMANDLINE_INSTALL_MAX_ARG = 1; - const int COMMANDLINE_INSTALL_MIN_ARG = 1; - - const char* COMMANDLINE_UNINSTALL_NAME = "uninstall"; - const char* COMMANDLINE_UNINSTALL_DESC[] = { - "uninstall an app from the device" - }; - const int COMMANDLINE_UNINSTALL_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_UNINSTALL_DESC, char*); - const char* COMMANDLINE_UNINSTALL_ARG_DESC = ""; - const int COMMANDLINE_UNINSTALL_MAX_ARG = 1; - const int COMMANDLINE_UNINSTALL_MIN_ARG = 1; - - const char* COMMANDLINE_FORKSERVER_NAME = "fork-server"; - const int COMMANDLINE_FORKSERVER_MAX_ARG = 1; - const int COMMANDLINE_FORKSERVER_MIN_ARG = 1; - - const char* COMMANDLINE_SERIAL_SHORT_OPT = "s"; - const char* COMMANDLINE_SERIAL_LONG_OPT = "serial"; - const char* COMMANDLINE_SERIAL_DESC[] = { - "direct command to the USB device or emulator with the given serial number." - }; - const int COMMANDLINE_SERIAL_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_SERIAL_DESC, char*); - const char* COMMANDLINE_SERIAL_ARG_DESC = ""; - const int COMMANDLINE_SERIAL_HAS_ARG = 1; - - const char* COMMANDLINE_DEVICE_SHORT_OPT = "d"; - const char* COMMANDLINE_DEVICE_LONG_OPT = "device"; - const char* COMMANDLINE_DEVICE_DESC[] = { - "direct command to the only connected USB device.", - "return an error if more than one USB device is present." - }; - const int COMMANDLINE_DEVICE_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_DEVICE_DESC, char*); - const int COMMANDLINE_DEVICE_HAS_ARG = 0; - - const char* COMMANDLINE_EMULATOR_SHORT_OPT = "e"; - const char* COMMANDLINE_EMULATOR_LONG_OPT = "emulator"; - const char* COMMANDLINE_EMULATOR_DESC[] = { - "direct command to the only running emulator.", - "return an error if more than one emulator is running." - }; - const int COMMANDLINE_EMULATOR_DESC_SIZE = GET_ARRAY_SIZE(COMMANDLINE_EMULATOR_DESC, char*); - const int COMMANDLINE_EMULATOR_HAS_ARG = 0; - - const char* COMMANDLINE_ERROR_ARG_MISSING = "argument %s is missing for command %s"; diff --git a/src/sdb_constants.h b/src/sdb_constants.h deleted file mode 100644 index 110a138..0000000 --- a/src/sdb_constants.h +++ /dev/null @@ -1,211 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef SDB_CONSTANTS_H_ -#define SDB_CONSTANTS_H_ - -#define GET_ARRAY_SIZE(parameter, type) sizeof(parameter)/sizeof(type) - -enum host_type { - host, - transport -}; - -typedef enum host_type HOST_TYPE; - - const char* HELP_APPEND_STR; - - const char* EMPTY_STRING; - const char* DIR_APP_TMP; - const char *DIR_APP; - const char* QUOTE_CHAR; - - const char* PREFIX_HOST; - const char* PREFIX_HOST_USB; - const char* PREFIX_HOST_LOCAL; - const char* PREFIX_HOST_SERIAL; - - const char* PREFIX_TRANSPORT_ANY; - const char* PREFIX_TRANSPORT_USB; - const char* PREFIX_TRANSPORT_LOCAL; - const char* PREFIX_TRANSPORT_SERIAL; - - const char* COMMANDLINE_MSG_FULL_CMD; - - const char* COMMANDLINE_OPROFILE_NAME; - const int COMMANDLINE_OPROFILE_MAX_ARG; - const int COMMANDLINE_OPROFILE_MIN_ARG; - - const char* COMMANDLINE_DA_NAME; - const int COMMANDLINE_DA_MAX_ARG; - const int COMMANDLINE_DA_MIN_ARG; - - const char* COMMANDLINE_LAUNCH_NAME; - const int COMMANDLINE_LAUNCH_MAX_ARG; - const int COMMANDLINE_LAUNCH_MIN_ARG; - - const char* COMMANDLINE_DEVICES_NAME; - extern const char* COMMANDLINE_DEVICES_DESC[]; - const int COMMANDLINE_DEVICES_DESC_SIZE; - const int COMMANDLINE_DEVICES_MAX_ARG; - const int COMMANDLINE_DEVICES_MIN_ARG; - - const char* COMMANDLINE_DISCONNECT_NAME; - extern const char* COMMANDLINE_DISCONNECT_DESC[]; - const int COMMANDLINE_DISCONNECT_DESC_SIZE; - const char* COMMANDLINE_DISCONNECT_ARG_DESC; - const int COMMANDLINE_DISCONNECT_MAX_ARG; - const int COMMANDLINE_DISCONNECT_MIN_ARG; - - const char* COMMANDLINE_CONNECT_NAME; - extern const char* COMMANDLINE_CONNECT_DESC[]; - const int COMMANDLINE_CONNECT_DESC_SIZE; - const char* COMMANDLINE_CONNECT_ARG_DESC; - const int COMMANDLINE_CONNECT_MAX_ARG; - const int COMMANDLINE_CONNECT_MIN_ARG; - - const char* COMMANDLINE_GSERIAL_NAME; - extern const char* COMMANDLINE_GSERIAL_DESC[]; - const int COMMANDLINE_GSERIAL_DESC_SIZE; - const int COMMANDLINE_GSERIAL_MAX_ARG; - const int COMMANDLINE_GSERIAL_MIN_ARG; - - const char* COMMANDLINE_GSTATE_NAME; - extern const char* COMMANDLINE_GSTATE_DESC[]; - const int COMMANDLINE_GSTATE_DESC_SIZE; - const int COMMANDLINE_GSTATE_MAX_ARG; - const int COMMANDLINE_GSTATE_MIN_ARG; - - const char* COMMANDLINE_ROOT_NAME; - extern const char* COMMANDLINE_ROOT_DESC[]; - const int COMMANDLINE_ROOT_DESC_SIZE; - const char* COMMANDLINE_ROOT_ARG_DESC; - const int COMMANDLINE_ROOT_MAX_ARG; - const int COMMANDLINE_ROOT_MIN_ARG; - - const char* COMMANDLINE_SWINDOW_NAME; - extern const char* COMMANDLINE_SWINDOW_DESC[]; - const int COMMANDLINE_SWINDOW_DESC_SIZE; - const int COMMANDLINE_SWINDOW_MAX_ARG; - const int COMMANDLINE_SWINDOW_MIN_ARG; - - const char* COMMANDLINE_SSERVER_NAME; - extern const char* COMMANDLINE_SSERVER_DESC[]; - const int COMMANDLINE_SSERVER_DESC_SIZE; - const int COMMANDLINE_SSERVER_MAX_ARG; - const int COMMANDLINE_SSERVER_MIN_ARG; - - const char* COMMANDLINE_KSERVER_NAME; - extern const char* COMMANDLINE_KSERVER_DESC[]; - const int COMMANDLINE_KSERVER_DESC_SIZE; - const int COMMANDLINE_KSERVER_MAX_ARG; - const int COMMANDLINE_KSERVER_MIN_ARG; - - const char* COMMANDLINE_HELP_NAME; - extern const char* COMMANDLINE_HELP_DESC[]; - const int COMMANDLINE_HELP_DESC_SIZE; - - const char* COMMANDLINE_VERSION_NAME; - extern const char* COMMANDLINE_VERSION_DESC[]; - const int COMMANDLINE_VERSION_DESC_SIZE; - const int COMMANDLINE_VERSION_MAX_ARG; - const int COMMANDLINE_VERSION_MIN_ARG; - - const char* COMMANDLINE_FORWARD_NAME; - extern const char* COMMANDLINE_FORWARD_DESC[]; - const int COMMANDLINE_FORWARD_DESC_SIZE; - const char* COMMANDLINE_FORWARD_ARG_DESC; - const int COMMANDLINE_FORWARD_MAX_ARG; - const int COMMANDLINE_FORWARD_MIN_ARG; - - const char* COMMANDLINE_DLOG_NAME; - extern const char* COMMANDLINE_DLOG_DESC[]; - const int COMMANDLINE_DLOG_DESC_SIZE; - const char* COMMANDLINE_DLOG_ARG_DESC; - const int COMMANDLINE_DLOG_MAX_ARG; - const int COMMANDLINE_DLOG_MIN_ARG; - - const char* COMMANDLINE_PUSH_NAME; - extern const char* COMMANDLINE_PUSH_DESC[]; - const int COMMANDLINE_PUSH_DESC_SIZE; - const char* COMMANDLINE_PUSH_ARG_DESC; - const int COMMANDLINE_PUSH_MAX_ARG; - const int COMMANDLINE_PUSH_MIN_ARG; - - const char* COMMANDLINE_PULL_NAME; - extern const char* COMMANDLINE_PULL_DESC[]; - const int COMMANDLINE_PULL_DESC_SIZE; - const char* COMMANDLINE_PULL_ARG_DESC; - const int COMMANDLINE_PULL_MAX_ARG; - const int COMMANDLINE_PULL_MIN_ARG; - - const char* COMMANDLINE_SHELL_NAME; - extern const char* COMMANDLINE_SHELL_DESC[]; - const int COMMANDLINE_SHELL_DESC_SIZE; - const char* COMMANDLINE_SHELL_ARG_DESC; - const int COMMANDLINE_SHELL_MAX_ARG; - const int COMMANDLINE_SHELL_MIN_ARG; - - const char* COMMANDLINE_INSTALL_NAME; - extern const char* COMMANDLINE_INSTALL_DESC[]; - const int COMMANDLINE_INSTALL_DESC_SIZE; - const char* COMMANDLINE_INSTALL_ARG_DESC; - const int COMMANDLINE_INSTALL_MAX_ARG; - const int COMMANDLINE_INSTALL_MIN_ARG; - - const char* COMMANDLINE_UNINSTALL_NAME; - extern const char* COMMANDLINE_UNINSTALL_DESC[]; - const int COMMANDLINE_UNINSTALL_DESC_SIZE; - const char* COMMANDLINE_UNINSTALL_ARG_DESC; - const int COMMANDLINE_UNINSTALL_MAX_ARG; - const int COMMANDLINE_UNINSTALL_MIN_ARG; - - const char* COMMANDLINE_FORKSERVER_NAME; - const int COMMANDLINE_FORKSERVER_MAX_ARG; - const int COMMANDLINE_FORKSERVER_MIN_ARG; - - const char* COMMANDLINE_SERIAL_SHORT_OPT; - const char* COMMANDLINE_SERIAL_LONG_OPT; - extern const char* COMMANDLINE_SERIAL_DESC[]; - const int COMMANDLINE_SERIAL_DESC_SIZE; - const char* COMMANDLINE_SERIAL_ARG_DESC; - const int COMMANDLINE_SERIAL_HAS_ARG; - - const char* COMMANDLINE_DEVICE_SHORT_OPT; - const char* COMMANDLINE_DEVICE_LONG_OPT; - extern const char* COMMANDLINE_DEVICE_DESC[]; - const int COMMANDLINE_DEVICE_DESC_SIZE; - const int COMMANDLINE_DEVICE_HAS_ARG; - - const char* COMMANDLINE_EMULATOR_SHORT_OPT; - const char* COMMANDLINE_EMULATOR_LONG_OPT; - extern const char* COMMANDLINE_EMULATOR_DESC[]; - const int COMMANDLINE_EMULATOR_DESC_SIZE; - const int COMMANDLINE_EMULATOR_HAS_ARG; - - const char* COMMANDLINE_ERROR_ARG_MISSING; - -#endif /* SDB_CONSTANTS_H_*/ diff --git a/src/sdb_model.c b/src/sdb_model.c deleted file mode 100644 index 8b6162c..0000000 --- a/src/sdb_model.c +++ /dev/null @@ -1,176 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#include -#include -#include -#include "utils.h" -#include "fdevent.h" -#include "sdb_model.h" -#include "linkedlist.h" -#include "sdb.h" - -const COMMAND NULL_COMMAND = { - NULL, - 0, - NULL, - NULL, - null_function, - -1, - 0 -}; - -//Create a command. A user should free cmdptr manually. -void create_command(COMMAND** cmdptr, const char* name, const char** desc, int desc_size, const char* argdesc, - int (*Func)(int, char**, void**), int maxargs, int minargs) { - *cmdptr = (COMMAND*)malloc(sizeof(COMMAND)); - (*cmdptr)->name = name; - (*cmdptr)->desc = desc; - (*cmdptr)->desc_size = desc_size; - (*cmdptr)->argdesc = argdesc; - (*cmdptr)->Func = Func; - (*cmdptr)->maxargs = maxargs; - (*cmdptr)->minargs = minargs; -} - -//Create a command. A user should free optptr manually. -void create_option(OPTION** optptr, const char* longopt, const char* shortopt, const char** desc, - int desc_size, const char* argdesc, int hasarg) { - *optptr = (OPTION*)malloc(sizeof(OPTION)); - (*optptr)->longopt = longopt; - (*optptr)->shortopt = shortopt; - (*optptr)->desc = desc; - (*optptr)->desc_size = desc_size; - (*optptr)->argdesc = argdesc; - (*optptr)->hasarg = hasarg; -} - -COMMAND* get_command(LIST_NODE* cmd_list, char* name) { - - LIST_NODE* curptr = cmd_list; - - while(curptr != NULL) { - COMMAND* curcmd = (COMMAND*)(curptr->data); - - if(!strcmp(curcmd->name, name)) { - return curcmd; - } - curptr = curptr->next_ptr; - } - return (COMMAND*)&NULL_COMMAND; -} - -OPTION* get_option(LIST_NODE* opt_list, char* name, int longname) { - - LIST_NODE* curptr = opt_list; - - while(curptr != NULL) { - OPTION* curopt = (OPTION*)(curptr->data); - if(longname) { - if(!strcmp(curopt->longopt, name)) { - return curopt; - } - } - else if(!strcmp(curopt->shortopt, name)) { - return curopt; - } - curptr = curptr->next_ptr; - } - return NULL; -} - -INPUT_OPTION* get_inputopt(LIST_NODE* inputopt_list, char* shortname) { - LIST_NODE* curptr = inputopt_list; - - while(curptr != NULL) { - INPUT_OPTION* cur_inputopt = (INPUT_OPTION*)(curptr->data); - - if(!strcmp(cur_inputopt->option->shortopt, shortname)) { - return cur_inputopt; - } - - curptr = curptr->next_ptr; - } - - return NULL; -} - -int parse_opt(int argc, char** argv, LIST_NODE* opt_list, LIST_NODE** result_list) { - - D("Parsing options.\n"); - int pass_arg = 0; - while(argc > 0) { - if(argv[0][0] == '-') { - int local_pass_arg = 1; - int longname = 0; - char* name = NULL; - //long name - if(strlen(argv[0]) > 1 && argv[0][1] == '-') { - longname = 1; - name = argv[0] + 2; - } - else { - name = argv[0] + 1; - } - D("Parse option: %s with longname %d\n", name, longname); - OPTION* option = get_option(opt_list, name, longname); - if(option == NULL) { - fprintf(stderr, "unrecognized option: %s\n", name); - return -1; - } - - char* value = NULL; - if(option->hasarg) { - if(argc > 1 && argv[1][0] != '-') { - value = argv[1]; - local_pass_arg++; - } - else { - fprintf(stderr, "option: %s should have a argument\n", name); - return -1; - } - } - D("Option %s with value %s", name, value); - INPUT_OPTION* input = (INPUT_OPTION*)malloc(sizeof(INPUT_OPTION)); - input->option = option; - input->value = value; - append(result_list, input); - - argv = argv + local_pass_arg; - argc = argc - local_pass_arg; - pass_arg = pass_arg + local_pass_arg; - } - else { - return pass_arg; - } - } - return pass_arg; -} - -int null_function (int argc, char** argv, void** extraarg) { - fprintf(stderr, "unsupported command: %s\n", argv[0]); - return -1; -} diff --git a/src/sdb_model.h b/src/sdb_model.h deleted file mode 100644 index 60c889a..0000000 --- a/src/sdb_model.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -* SDB - Smart Development Bridge -* -* Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved. -* -* Contact: -* Ho Namkoong -* Yoonki Park -* -* 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. -* -* Contributors: -* - S-Core Co., Ltd -* -*/ - -#ifndef SDB_MODEL_H_ -#define SDB_MODEL_H_ - -#include "linkedlist.h" -#include "sdb_constants.h" - -#define TRACE_TAG TRACE_SDB - -struct command { - const char* name; - //hidden command does not have a description. - int desc_size; - const char** desc; - const char* argdesc; - int (*Func)(int, char**, void**); - // -1 means no max limit. - int maxargs; - int minargs; -}; -typedef struct command COMMAND; - -struct option { - const char* longopt; - const char* shortopt; - //hidden option does not have a description. - const char** desc; - int desc_size; - const char* argdesc; - int hasarg; -}; -typedef struct option OPTION; - -struct input_option { - char* value; - OPTION* option; -}; -typedef struct input_option INPUT_OPTION; - -const COMMAND NULL_COMMAND; - -int null_function (int argc, char** argv, void** extraarg); -void create_command(COMMAND** cmdptr, const char* name, const char** desc, int desc_size, const char* argdesc, - int (*Func)(int, char**, void**), int maxargs, int minargs); - -void create_option(OPTION** optptr, const char* longopt, const char* shortopt, const char** desc, int desc_size, const char* argdesc, int hasarg); -COMMAND* get_command(LIST_NODE* cmd_list, char* name); -OPTION* get_option(LIST_NODE* opt_list, char* name, int longname); -INPUT_OPTION* get_inputopt(LIST_NODE* inputopt_list, char* shortname); -int parse_opt(int argc, char** argv, LIST_NODE* opt_list, LIST_NODE** result_list); - -#endif /* SDB_MODEL_H_ */ diff --git a/src/sdb_usb.c b/src/sdb_usb.c deleted file mode 100755 index 0d63c9f..0000000 --- a/src/sdb_usb.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include "utils.h" -#include "fdevent.h" -#include "libusb/usb.h" - -#define TRACE_TAG TRACE_USB -#include "sdb.h" -#include "strutils.h" -#include "sdb_usb.h" - -SDB_MUTEX_DEFINE( usb_lock ); - -struct usb_handle -{ - usb_handle *prev; - usb_handle *next; - - usb_dev_handle *usb_handle; - char unique_node_path[PATH_MAX+1]; - unsigned char end_point[2]; // 0:in, 1:out - int interface; - unsigned zero_mask; - -}; - -static usb_handle handle_list = { - .prev = &handle_list, - .next = &handle_list, -}; - -int is_device_registered(const char *unique_node_path) -{ - usb_handle *usb; - int r = 0; - sdb_mutex_lock(&usb_lock); - for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ - if(!strcmp(usb->unique_node_path, unique_node_path)) { - // set mark flag to indicate this device is still alive - usb->zero_mask = 1; - r = 1; - break; - } - } - sdb_mutex_unlock(&usb_lock); - return r; -} - -void register_device(struct usb_device *dev) -{ - usb_dev_handle *udev; - int ret, i; - - char usb_path[PATH_MAX+1]; - - snprintf(usb_path, sizeof(usb_path), "/dev/bus/usb/%s/%s", dev->bus->dirname, dev->filename); - - if (is_device_registered(usb_path)) { - //D("skip to register device: %s\n", usb_path); - return; - } - - if (!dev->config) { - D("couldn't retrieve descriptors\n"); - return; - } - - struct usb_interface_descriptor *altsetting = NULL; - ret = 0; - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - struct usb_config_descriptor *config = &dev->config[i]; - int interface_index = 0; - for (interface_index = 0; interface_index < config->bNumInterfaces; interface_index++) { - struct usb_interface *interface = &config->interface[interface_index]; - int altsetting_index = 0; - for (altsetting_index = 0; altsetting_index < interface->num_altsetting; altsetting_index++) { - altsetting = &interface->altsetting[altsetting_index]; - - if (is_sdb_interface(altsetting->bInterfaceClass, altsetting->bInterfaceSubClass, altsetting->bInterfaceProtocol)) { - ret = 1; - break; - } - - } - } - } - if (ret == 0) { - //D("fail to get sdb interface descriptor\n"); - return; - } - if (altsetting->bNumEndpoints !=2) { - D("the number of endpoint should be two\n"); - return; - } - - usb_handle* usb = NULL; - usb = calloc(1, sizeof(usb_handle)); - - if (usb == NULL) { - return; - } - - int sdb_configuration = 2; - const struct usb_endpoint_descriptor *ep1 = &altsetting->endpoint[0]; - const struct usb_endpoint_descriptor *ep2 = &altsetting->endpoint[1]; - - usb->interface = altsetting->bInterfaceNumber; - // find out which endpoint is in or out - if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { - - usb->end_point[0] = ep1->bEndpointAddress; - usb->end_point[1] = ep2->bEndpointAddress; - } else { - usb->end_point[0] = ep2->bEndpointAddress; - usb->end_point[1] = ep1->bEndpointAddress; - } - - if (altsetting->bInterfaceProtocol == 0x01) { - usb->zero_mask = ep1->wMaxPacketSize - 1; - } - - udev = usb_open(dev); - if (udev == NULL) { - D("unable to open %s\n", usb_path); - free(usb); - return; - } - usb->usb_handle = udev; - char serial[256]; - - if (dev->descriptor.iSerialNumber) { - ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, serial, sizeof(serial)); - if (ret < 1) { - D("error to get serial name: %d(%s)\n", ret, strerror(errno)); - strcpy(serial, "unknown"); - } - } - - s_strncpy(usb->unique_node_path, usb_path, sizeof(usb->unique_node_path)); - - /* - // detach any kernel driver - if ((ret = libusb_kernel_driver_active(dev_handle, 0))) { - if ((ret = libusb_detach_kernel_driver(dev_handle, 0))) { - D ("detach!\n"); - } - }*/ - - ret = usb_reset(udev); - ret = usb_set_configuration(udev, sdb_configuration); - // claim the device - ret = usb_claim_interface(udev, usb->interface); - D("claim %d: %d(%s)\n", ret, errno, strerror(errno)); - - sdb_mutex_lock(&usb_lock); - usb->next = &handle_list; - usb->prev = handle_list.prev; - usb->prev->next = usb; - usb->next->prev = usb; - - D("-register new device (in: %04x, out: %04x) from %s\n",usb->end_point[0], usb->end_point[1], usb_path); - - register_usb_transport(usb, serial, 1); - sdb_mutex_unlock(&usb_lock); -} - - -void do_lsusb() -{ - struct usb_bus *bus; - - usb_init(); - - usb_find_busses(); - usb_find_devices(); - - for (bus = usb_busses; bus; bus = bus->next) { - struct usb_device *dev; - - for (dev = bus->devices; dev; dev = dev->next) { - register_device(dev); - } - - } - //D("finished loop\n"); -} - - -void* usb_poll_thread(void* sleep_msec) -{ - D("created usb detecting thread\n"); - int mseconds = (int) sleep_msec; - - while (1) { - do_lsusb(); - //kick_disconnected_devices(); - sdb_sleep_ms(mseconds); - } - return NULL; -} - - -void sdb_usb_init(void) -{ - sdb_thread_t tid; - - if(sdb_thread_create(&tid, usb_poll_thread, (void*)1000)){ - fatal_errno("cannot create input thread"); - } -} - -void sdb_usb_cleanup() -{ - close_usb_devices(); -} - -int sdb_usb_write(usb_handle *h, const void *_data, int len) -{ - char *data = (char*) _data; - int n = 0; - int need_zero = 0; - - D("+sdb_usb_write\n"); - - 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 > MAX_READ_WRITE) ? MAX_READ_WRITE : len; - - n = usb_bulk_write(h->usb_handle, h->end_point[1], data, xfer, 0); - if(n != xfer) { - D("fail to usb write: n = %d, errno = %d (%s)\n", n, errno, strerror(errno)); - return -1; - } - - len -= xfer; - data += xfer; - } - - if(need_zero){ - n = usb_bulk_write(h->usb_handle, h->end_point[1], (char *)_data, 0, 0); - if (n < 0) { - D("fail to to usb write for 0 len from %p\n", h->usb_handle); - } - return n; - } - D("-usb_write\n"); - return 0; -} - -int sdb_usb_read(usb_handle *h, void *_data, int len) -{ - char *data = (char*) _data; - int n; - - D("+sdb_usb_read\n"); - while(len > 0) { - int xfer = (len > MAX_READ_WRITE) ? MAX_READ_WRITE : len; - - n = usb_bulk_read(h->usb_handle, h->end_point[0], data, xfer, 0); - - if(n != xfer) { - if((errno == ETIMEDOUT)) { - D("usb bulk read timeout\n"); - if(n > 0){ - data += n; - len -= n; - } - continue; - } - D("fail to usb read: n = %d, errno = %d (%s)\n", n, errno, strerror(errno)); - return -1; - } - - len -= xfer; - data += xfer; - } - - D("-sdb_usb_read\n"); - return 0; -} - -void sdb_usb_kick(usb_handle *h) -{ - D("+kicking\n"); - D("-kicking\n"); -} - -int sdb_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; - - usb_release_interface(h->usb_handle, h->interface); - usb_close(h->usb_handle); - sdb_mutex_unlock(&usb_lock); - - if (h != NULL) { - free(h); - h = NULL; - } - D("-usb close\n"); - return 0; -} - -int is_sdb_interface(int usb_class, int usb_subclass, int usb_protocol) -{ - if (usb_class == SDB_INTERFACE_CLASS && usb_subclass == SDB_INTERFACE_SUBCLASS - && usb_protocol == SDB_INTERFACE_PROTOCOL) { - return 1; - } - - return 0; -} - -#ifdef _USE_UDEV_ - -#include -#include -#include -#include -#include - -int udev_notify(void) -{ - struct udev *udev; - struct udev_enumerate *enumerate; - struct udev_list_entry *devices, *dev_list_entry; - struct udev_device *dev; - - struct udev_monitor *mon; - int fd; - - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - printf("Can't create udev\n"); - exit(1); - } - - /* This section sets up a monitor which will report events when - devices attached to the system change. Events include "add", - "remove", "change", "online", and "offline". - - This section sets up and starts the monitoring. Events are - polled for (and delivered) later in the file. - - It is important that the monitor be set up before the call to - udev_enumerate_scan_devices() so that events (and devices) are - not missed. For example, if enumeration happened first, there - would be no event generated for a device which was attached after - enumeration but before monitoring began. - - Note that a filter is added so that we only get events for - "hidraw" devices. */ - - /* Set up a monitor to monitor hidraw devices */ - mon = udev_monitor_new_from_netlink(udev, "udev"); - udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_device"); - udev_monitor_enable_receiving(mon); - /* Get the file descriptor (fd) for the monitor. - This fd will get passed to select() */ - fd = udev_monitor_get_fd(mon); - - - /* Create a list of the devices in the 'hidraw' subsystem. */ - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(enumerate, "usb"); - udev_enumerate_scan_devices(enumerate); - - - devices = udev_enumerate_get_list_entry(enumerate); - /* For each item enumerated, print out its information. - udev_list_entry_foreach is a macro which expands to - a loop. The loop will be executed for each member in - devices, setting dev_list_entry to a list entry - which contains the device's path in /sys. */ - udev_list_entry_foreach(dev_list_entry, devices) { - const char *path; - - /* Get the filename of the /sys entry for the device - and create a udev_device object (dev) representing it */ - path = udev_list_entry_get_name(dev_list_entry); - dev = udev_device_new_from_syspath(udev, path); - - /* usb_device_get_devnode() returns the path to the device node - itself in /dev. */ - printf("Device Node Path: %s\n", udev_device_get_devnode(dev)); - - /* The device pointed to by dev contains information about - the hidraw device. In order to get information about the - USB device, get the parent device with the - subsystem/devtype pair of "usb"/"usb_device". This will - be several levels up the tree, but the function will find - it.*/ - /*dev = udev_device_get_parent_with_subsystem_devtype( - dev, - "usb", - "usb_device"); - if (!dev) { - printf("Unable to find parent usb device."); - exit(1); - }*/ - - /* From here, we can call get_sysattr_value() for each file - in the device's /sys entry. The strings passed into these - functions (idProduct, idVendor, serial, etc.) correspond - directly to the files in the /sys directory which - represents the USB device. Note that USB strings are - Unicode, UCS2 encoded, but the strings returned from - udev_device_get_sysattr_value() are UTF-8 encoded. */ - printf(" VID/PID: %s %s\n", - udev_device_get_sysattr_value(dev,"idVendor"), - udev_device_get_sysattr_value(dev, "idProduct")); - printf(" %s\n %s\n", - udev_device_get_sysattr_value(dev,"manufacturer"), - udev_device_get_sysattr_value(dev,"product")); - printf(" serial: %s\n", - udev_device_get_sysattr_value(dev, "serial")); - udev_device_unref(dev); - } - /* Free the enumerator object */ - udev_enumerate_unref(enumerate); - - /* Begin polling for udev events. Events occur when devices - attached to the system are added, removed, or change state. - udev_monitor_receive_device() will return a device - object representing the device which changed and what type of - change occured. - - The select() system call is used to ensure that the call to - udev_monitor_receive_device() will not block. - - The monitor was set up earler in this file, and monitoring is - already underway. - - This section will run continuously, calling usleep() at the end - of each pass. This is to demonstrate how to use a udev_monitor - in a non-blocking way. */ - while (1) { - /* Set up the call to select(). In this case, select() will - only operate on a single file descriptor, the one - associated with our udev_monitor. Note that the timeval - object is set to 0, which will cause select() to not - block. */ - fd_set fds; - struct timeval tv; - int ret; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - tv.tv_sec = 0; - tv.tv_usec = 0; - - ret = select(fd+1, &fds, NULL, NULL, &tv); - - /* Check if our file descriptor has received data. */ - if (ret > 0 && FD_ISSET(fd, &fds)) { - printf("\nselect() says there should be data\n"); - - /* Make the call to receive the device. - select() ensured that this will not block. */ - dev = udev_monitor_receive_device(mon); - if (dev) { - printf("Got Device\n"); - printf(" Node: %s\n", udev_device_get_devnode(dev)); - printf(" Subsystem: %s\n", udev_device_get_subsystem(dev)); - printf(" Devtype: %s\n", udev_device_get_devtype(dev)); - - printf(" Action: %s\n", udev_device_get_action(dev)); - udev_device_unref(dev); - } - else { - printf("No Device from receive_device(). An error occured.\n"); - } - } - usleep(250*1000); - printf("."); - fflush(stdout); - } - - - udev_unref(udev); - - return 0; -} - -#endif diff --git a/src/sdb_usb.h b/src/sdb_usb.h deleted file mode 100755 index 045926d..0000000 --- a/src/sdb_usb.h +++ /dev/null @@ -1,38 +0,0 @@ - -#ifndef __USB_H -#define __USB_H - - -#include - -// should bo implements for each os type -typedef struct usb_handle usb_handle; -typedef struct sdb_usb_dev_handle sdb_usb_dev_handle; - -void do_lsusb(void); - -void sdb_usb_init(); -void sdb_usb_cleanup(); -int sdb_usb_write(usb_handle *h, const void *data, int len); -int sdb_usb_read(usb_handle *h, void *data, int len); -int sdb_usb_close(usb_handle *h); -void sdb_usb_kick(usb_handle *h); - -int is_sdb_interface(int usb_class, int usb_subclass, int usb_protocol); -int is_device_registered(const char *node_path); -void* usb_poll_thread(void* sleep_msec); -void kick_disconnected_devices(); - -#define SDB_INTERFACE_CLASS 0xff -#define SDB_INTERFACE_SUBCLASS 0x20 -#define SDB_INTERFACE_PROTOCOL 0x02 - -/* - * Linux usbfs has a limit of one page size for synchronous bulk read/write. - * 4096 is the most portable maximum we can do for now. - * Linux usbfs has a limit of 16KB for the URB interface. We use this now - * to get better performance for USB 2.0 devices. - */ -#define MAX_READ_WRITE (16 * 1024) - -#endif // __USB_H 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 + (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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(name_len), + reinterpret_cast(buffer), + static_cast(*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(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(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(str_len), + reinterpret_cast(buffer), + static_cast(*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, + ¶m, 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(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(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 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 + 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 +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(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 +#pragma warning(disable: 4702) +#pragma warning(disable: 4201) +#include +#include +#include +#include +#include +#include +#pragma warning(default: 4201) +#pragma warning(disable: 4200) +#include +#include + +#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(buffer), + bytes_to_transfer, + &transferred, + sdb_io_completion->overlapped()) : + WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(), + endpoint_id(), + reinterpret_cast(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(buffer), + bytes_to_transfer, + &transferred, + &overlapped) : + WinUsb_WritePipe(parent_winusb_interface()->winusb_handle(), + endpoint_id(), + reinterpret_cast(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(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(&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(&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(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(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(ser_num); + try { + ser_num = + reinterpret_cast(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(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(str_len), + reinterpret_cast(buffer), + static_cast(*buffer_char_size), + NULL, NULL)) { + // Zero-terminate output string. + reinterpret_cast(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(buffer)[str_len] = L'\0'; + } + + if (ser_num != &initial_ser_num) + delete[] reinterpret_cast(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(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 +#pragma warning(disable: 4702) +#pragma warning(disable: 4201) +#include +#include +#include +#include +#include +#include +#pragma warning(default: 4201) +#pragma warning(disable: 4200) +#include + +#include "resource.h" + +using namespace ATL; diff --git a/src/services.c b/src/services.c index 0b82a68..555c91d 100755 --- a/src/services.c +++ b/src/services.c @@ -19,18 +19,23 @@ #include #include #include -#include "utils.h" -#include "fdevent.h" + +#include "sysdeps.h" #define TRACE_TAG TRACE_SERVICES #include "sdb.h" #include "file_sync_service.h" -# ifndef OS_WINDOWS +#if SDB_HOST +# ifndef HAVE_WINSOCK # include # include # include # endif +#else +# include "android_reboot.h" +# include +#endif typedef struct stinfo stinfo; @@ -49,8 +54,239 @@ void *service_bootstrap_func(void *x) 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; @@ -80,6 +316,150 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) 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; @@ -92,11 +472,84 @@ int service_to_fd(const char *name) 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); @@ -104,6 +557,7 @@ int service_to_fd(const char *name) return ret; } +#if SDB_HOST struct state_info { transport_type transport; char* serial; @@ -130,7 +584,9 @@ static void wait_for_state(int fd, void* cookie) 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")) { @@ -164,4 +620,4 @@ asocket* host_service_to_socket(const char* name, const char *serial) } 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 +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#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 ANDROID_SOCKET_NAMESPACE_ABSTRACT, + * ANDROID_SOCKET_NAMESPACE_RESERVED, or + * ANDROID_SOCKET_NAMESPACE_FILESYSTEM. 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 +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK + +int socket_local_client(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -1; +} + +#else /* !HAVE_WINSOCK */ + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK + +int socket_local_server(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -1; +} + +#else /* !HAVE_WINSOCK */ + +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include + +#define LISTEN_BACKLOG 4 +#define LOOPBACK_UP 1 +#define LOOPBACK_DOWN 0 + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#include +#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 index b4e27e8..c621f47 100644 --- a/src/sockets.c +++ b/src/sockets.c @@ -20,9 +20,8 @@ #include #include #include -#include "utils.h" -#include "fdevent.h" -#include "sdb_constants.h" + +#include "sysdeps.h" #define TRACE_TAG TRACE_SOCKETS #include "sdb.h" @@ -413,15 +412,35 @@ 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; @@ -435,6 +454,7 @@ static asocket *create_host_service_socket(const char *name, const char* serial) 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 @@ -619,9 +639,11 @@ char *skip_host_serial(char *service) { 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); @@ -663,10 +685,11 @@ static int smart_socket_enqueue(asocket *s, apacket *p) D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4)); +#if SDB_HOST service = (char *)p->data + 4; - if(!strncmp(service, PREFIX_HOST_SERIAL, strlen(PREFIX_HOST_SERIAL))) { + if(!strncmp(service, "host-serial:", strlen("host-serial:"))) { char* serial_end; - service += strlen(PREFIX_HOST_SERIAL); + service += strlen("host-serial:"); // serial number should follow "host:" and could be a host:port string. serial_end = skip_host_serial(service); @@ -675,15 +698,15 @@ static int smart_socket_enqueue(asocket *s, apacket *p) serial = service; service = serial_end + 1; } + } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) { ttype = kTransportUsb; - } else if (!strncmp(service, PREFIX_HOST_USB, strlen(PREFIX_HOST_USB))) { - service += strlen(PREFIX_HOST_USB); - } else if (!strncmp(service, PREFIX_HOST_LOCAL, strlen(PREFIX_HOST_LOCAL))) { + service += strlen("host-usb:"); + } else if (!strncmp(service, "host-local:", strlen("host-local:"))) { ttype = kTransportLocal; - service += strlen(PREFIX_HOST_LOCAL); - } else if (!strncmp(service, PREFIX_HOST, strlen(PREFIX_HOST))) { + service += strlen("host-local:"); + } else if (!strncmp(service, "host:", strlen("host:"))) { ttype = kTransportAny; - service += strlen(PREFIX_HOST); + service += strlen("host:"); } else { service = NULL; } @@ -739,6 +762,18 @@ static int smart_socket_enqueue(asocket *s, apacket *p) 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 diff --git a/src/sockets.dia b/src/sockets.dia new file mode 100644 index 0000000..c626f20 Binary files /dev/null and b/src/sockets.dia 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 +#include +#include + +#ifdef HAVE_WINSOCK +#include +typedef int socklen_t; +#elif HAVE_SYS_SOCKET_H +#include +#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/strutils.c b/src/strutils.c index 2823e12..40fee05 100644 --- a/src/strutils.c +++ b/src/strutils.c @@ -1,8 +1,9 @@ #include #include #include +#include "sysdeps.h" #include "strutils.h" -#include "utils.h" + #ifndef PATH_MAX #define PATH_MAX 4096 #endif 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ +extern void* load_file(const char* pathname, unsigned* psize); + +/* normally provided by */ +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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#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 + +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 +# 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 + +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 + +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 index a082ad4..4e7ff0b 100755 --- a/src/transport.c +++ b/src/transport.c @@ -19,8 +19,9 @@ #include #include #include -#include "fdevent.h" -#include "utils.h" + +#include "sysdeps.h" + #define TRACE_TAG TRACE_TRANSPORT #include "sdb.h" @@ -390,6 +391,7 @@ 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]; @@ -530,6 +532,12 @@ void update_transports(void) tracker = next; } } +#else +void update_transports(void) +{ + // nothing to do on the device side +} +#endif // SDB_HOST typedef struct tmsg tmsg; struct tmsg @@ -582,9 +590,9 @@ transport_write_action(int fd, struct tmsg* m) return 0; } - -static sdb_cond_t cond;// = PTHREAD_COND_INITIALIZER; - +#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; @@ -615,9 +623,9 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) sdb_mutex_lock(&transport_lock); t->next->prev = t->prev; t->prev->next = t->next; - - sdb_cond_broadcast(&cond); - +#ifndef _WIN32 + pthread_cond_broadcast(&cond); +#endif sdb_mutex_unlock(&transport_lock); run_transport_disconnects(t); @@ -672,9 +680,9 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) t->prev = transport_list.prev; t->next->prev = t; t->prev->next = t; - - sdb_cond_broadcast(&cond); - +#ifndef _WIN32 + pthread_cond_broadcast(&cond); +#endif sdb_mutex_unlock(&transport_lock); t->disconnects.next = t->disconnects.prev = &t->disconnects; @@ -864,6 +872,7 @@ retry: return result; } +#if SDB_HOST static const char *statename(atransport *t) { switch(t->connection_state){ @@ -960,6 +969,7 @@ void close_usb_devices() } 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) { @@ -974,18 +984,20 @@ void register_socket_transport(int s, const char *serial, int port, int local, c 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. @@ -994,10 +1006,11 @@ void register_socket_transport(int s, const char *serial, int port, int local, c t->device_name = strdup(device_name); } } - +#endif register_transport(t); } +#if SDB_HOST atransport *find_transport(const char *serial) { atransport *t; @@ -1050,7 +1063,9 @@ void unregister_all_tcp_transports() sdb_mutex_unlock(&transport_lock); } -static int get_connected_device_count(transport_type type) +#endif + +int get_connected_device_count(transport_type type) /* tizen specific */ { int cnt = 0; atransport *t; @@ -1059,7 +1074,6 @@ static int get_connected_device_count(transport_type type) if (type == t->type) cnt++; } - sdb_mutex_unlock(&transport_lock); D("connected device count:%d\n",cnt); return cnt; @@ -1081,9 +1095,9 @@ void register_usb_transport(usb_handle *usb, const char *serial, unsigned writea */ sdb_mutex_lock(&transport_lock); register_transport(t); - sdb_cond_init(&cond, NULL); - sdb_cond_wait(&cond, &transport_lock); - +#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)); diff --git a/src/transport_local.c b/src/transport_local.c index 8fca32d..f1e7dc0 100644 --- a/src/transport_local.c +++ b/src/transport_local.c @@ -18,11 +18,11 @@ #include #include #include -#include "fdevent.h" -#include "utils.h" + +#include "sysdeps.h" #include -#ifndef OS_WINDOWS +#ifndef HAVE_WIN32_IPC #include #include #include @@ -46,6 +46,7 @@ static inline void fix_endians(apacket *p) #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. @@ -55,6 +56,7 @@ static inline void fix_endians(apacket *p) 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) { @@ -115,7 +117,15 @@ int local_connect_arbitrary_ports(int console_port, int sdb_port, const char *de char buf[64]; int fd = -1; - fd = socket_loopback_client(sdb_port, SOCK_STREAM); +#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); @@ -128,10 +138,11 @@ int local_connect_arbitrary_ports(int console_port, int sdb_port, const char *de return -1; } +#if SDB_HOST /* tizen specific */ int get_devicename_from_shdmem(int port, char *device_name) { char *vms = NULL; -#ifndef OS_WINDOWS +#ifndef HAVE_WIN32_IPC int shm_id; void *shared_memory = (void *)0; @@ -193,8 +204,11 @@ int get_devicename_from_shdmem(int port, char *device_name) return 0; } -static void *register_local_connections(void *x) +#endif + +static void *client_socket_thread(void *x) { +#if SDB_HOST int port = DEFAULT_SDB_LOCAL_TRANSPORT_PORT; int count = SDB_LOCAL_TRANSPORT_MAX; @@ -203,22 +217,189 @@ static void *register_local_connections(void *x) /* 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 ) { + 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; } -/** - * register local connections +/* 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 *); - func = register_local_connections; + 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", @@ -229,21 +410,23 @@ void local_init(int port) static void remote_kick(atransport *t) { int fd = t->sfd; - int nn; - t->sfd = -1; sdb_shutdown(fd); sdb_close(fd); - 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; +#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 ); } - - sdb_mutex_unlock( &local_transports_lock ); +#endif } static void remote_close(atransport *t) @@ -252,6 +435,7 @@ static void remote_close(atransport *t) } +#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) { @@ -291,6 +475,7 @@ int get_available_local_transport_index() sdb_mutex_unlock( &local_transports_lock ); return result; } +#endif int init_socket_transport(atransport *t, int s, int sdb_port, int local) { @@ -306,6 +491,7 @@ int init_socket_transport(atransport *t, int s, int sdb_port, int local) t->type = kTransportLocal; t->sdb_port = 0; +#if SDB_HOST if (HOST && local) { sdb_mutex_lock( &local_transports_lock ); { @@ -328,6 +514,6 @@ int init_socket_transport(atransport *t, int s, int sdb_port, int local) } sdb_mutex_unlock( &local_transports_lock ); } - +#endif return fail; } diff --git a/src/transport_usb.c b/src/transport_usb.c index d809086..e4de29a 100644 --- a/src/transport_usb.c +++ b/src/transport_usb.c @@ -18,11 +18,15 @@ #include #include -#include "fdevent.h" -#include "utils.h" +#include + #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) @@ -48,7 +52,7 @@ unsigned host_to_le32(unsigned n) static int remote_read(apacket *p, atransport *t) { - if(sdb_usb_read(t->usb, &p->msg, sizeof(amessage))){ + if(usb_read(t->usb, &p->msg, sizeof(amessage))){ D("remote usb: read terminated (message)\n"); return -1; } @@ -61,7 +65,7 @@ static int remote_read(apacket *p, atransport *t) } if(p->msg.data_length) { - if(sdb_usb_read(t->usb, p->data, p->msg.data_length)){ + if(usb_read(t->usb, p->data, p->msg.data_length)){ D("remote usb: terminated (data)\n"); return -1; } @@ -81,12 +85,12 @@ static int remote_write(apacket *p, atransport *t) fix_endians(p); - if(sdb_usb_write(t->usb, &p->msg, sizeof(amessage))) { + 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(sdb_usb_write(t->usb, &p->data, size)) { + if(usb_write(t->usb, &p->data, size)) { D("remote usb: 2 - write terminated\n"); return -1; } @@ -96,13 +100,13 @@ static int remote_write(apacket *p, atransport *t) static void remote_close(atransport *t) { - sdb_usb_close(t->usb); + usb_close(t->usb); t->usb = 0; } static void remote_kick(atransport *t) { - sdb_usb_kick(t->usb); + usb_kick(t->usb); } void init_usb_transport(atransport *t, usb_handle *h, int state) @@ -117,5 +121,28 @@ void init_usb_transport(atransport *t, usb_handle *h, int 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..ab847bf --- /dev/null +++ b/src/usb_libusb.c @@ -0,0 +1,655 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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..158c686 --- /dev/null +++ b/src/usb_linux.c @@ -0,0 +1,715 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include +#else +#include +#endif +#include + +#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 j; + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + result /= 2; + for (j = 1; j < result; j++) { + serial[j - 1] = __le16_to_cpu(buffer[j]); + } + serial[j - 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 +#include +#include +#include + +#include +#include +#include +#include + +#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..683dadd --- /dev/null +++ b/src/usb_osx.c @@ -0,0 +1,538 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#include + +#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, + ¬ificationIterators[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..217aa8a --- /dev/null +++ b/src/usb_vendors.c @@ -0,0 +1,268 @@ +/* + * 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 + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include +# include +#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); + fclose(f); + exit(2); + } + + vendorIds[vendorIdCount++] = (int)value; + + /* make sure we don't go beyond the array */ + if (vendorIdCount == VENDOR_COUNT_MAX) { + break; + } + } + fclose(f); + } + } +} + +/* 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 +#include +#include +#include +#include +#include + +#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 index 28d16a3..963e9bc 100644 --- a/src/utils.c +++ b/src/utils.c @@ -14,294 +14,114 @@ * limitations under the License. */ +#include #include #include -#include -#include #include "utils.h" -#include "utils_backend.h" - -#include "fdevent.h" -#include "sdb_constants.h" -#include "sdb.h" - -#if defined(OS_WINDOWS) -const struct utils_os_backend* utils_backend = &utils_windows_backend; -#elif defined(OS_DARWIN) -const struct utils_os_backend* utils_backend = &utils_unix_backend; -#elif defined(OS_LINUX) -const struct utils_os_backend* utils_backend = &utils_unix_backend; -#else -#error "unsupported OS" -#endif - -int is_directory(char* path) { - struct stat st; - - if(stat(path, &st)) { - fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno)); - return -1; - } - - if(S_ISDIR(st.st_mode)) { - return 1; - } - - return 0; -} - -int mkdirs(char *name) +#define STRING_MAXLEN 1024 +char* +buff_addc (char* buff, char* buffEnd, int c) { - int ret; - char *x = name + 1; - - for(;;) { - x = sdb_dirstart(x); - if(x == 0) return 1; - *x = 0; - ret = sdb_mkdir(name, 0775); - if(ret < 0 && (errno != EEXIST || is_directory(name) != 1)) { - *x = OS_PATH_SEPARATOR; - return ret; - } - *x = OS_PATH_SEPARATOR; - x++; - } - return 1; -} - -void append_separator(char* result_path, char* path) { - - int path_len = strlen(path); - if(path_len > 0 && (path)[path_len -1] != '/') { - snprintf(result_path, PATH_MAX, "%s/",path); - } - else { - snprintf(result_path, PATH_MAX, "%s",path); - } -} + int avail = buffEnd - buff; -void append_file(char* result_path, char* dir, char* append_dir) { - char* tmp_append; + if (avail <= 0) /* already in overflow mode */ + return buff; - int len = strlen(append_dir); - if(len > 0) { - if(append_dir[0] == '/') { - tmp_append = append_dir + 1; - } - else { - tmp_append = append_dir; - } - } - else { - tmp_append = (char*)EMPTY_STRING; + if (avail == 1) { /* overflowing, the last byte is reserved for zero */ + buff[0] = 0; + return buff + 1; } - int dir_len = strlen(dir); - if(dir_len > 0 && dir[dir_len -1] != '/') { - snprintf(result_path, PATH_MAX, "%s/%s",dir, tmp_append); - } - else { - snprintf(result_path, PATH_MAX, "%s%s",dir, tmp_append); - } + buff[0] = (char) c; /* add char and terminating zero */ + buff[1] = 0; + return buff + 1; } -char* get_filename(char* full_name) { - char *name = sdb_dirstop(full_name); - if(name == 0) { - name = full_name; - } else { - name++; - } - - return name; -} - -void append_args(char *dest, int argc, const char** argv, size_t n) { - int full_cmd_len = strlen(dest); - int i; - for(i = 1; i < argc; i++) { - int arglen = strlen(argv[i]); - int size_checker = n - full_cmd_len - arglen - 1; - if(size_checker <0) { - break; - } - - strncat(dest, " ", n - full_cmd_len); - ++full_cmd_len; - strncat(dest, argv[i], n - full_cmd_len); - full_cmd_len += arglen; - } -} - -long long NOW() -{ - struct timeval tv; - gettimeofday(&tv, 0); - return ((long long) tv.tv_usec) + - 1000000LL * ((long long) tv.tv_sec); -} - -/** duplicate string and quote all \ " ( ) chars + space character. */ -void dup_quote(char* result_string, const char *source, int max_len) +char* +buff_adds (char* buff, char* buffEnd, const char* s) { - int i = 0; - for (; i < max_len-1; i++) { - if(*source == '\0') { - break; - } - char* quoted = (char*)QUOTE_CHAR; - while(*quoted != '\0') { - if(*source == *quoted++) { - i++; - *result_string++ = '\\'; - break; - } - } - *result_string++ = *source; - source++; - } - - if( i != max_len) { - *result_string = '\0'; - } - else { - *(--result_string) = '\0'; - } -} - -/**************************************************/ -/*** OS dependent helpers ***/ -/**************************************************/ -int launch_server(int server_port) { - return utils_backend->launch_server(server_port); -} - -void start_logging(void) { - return utils_backend->start_logging(); -} + int slen = strlen(s); -char* ansi_to_utf8(const char *str) { - return utils_backend->ansi_to_utf8(str); + return buff_addb(buff, buffEnd, s, slen); } -int sdb_open(const char* path, int options) { - return utils_backend->sdb_open(path, options); -} - -int sdb_open_mode(const char* pathname, int options, int mode) { - return utils_backend->sdb_open_mode(pathname, options, mode); -} - -int unix_open(const char* path, int options, ...) { - return utils_backend->unix_open(path, options); -} - -int sdb_creat(const char* path, int mode) { - return utils_backend->sdb_creat(path, mode); -} - -int sdb_read(int fd, void* buf, size_t len) { - return utils_backend->sdb_read(fd, buf, len); -} - -int sdb_write(int fd, const void* buf, size_t len) { - return utils_backend->sdb_write(fd, buf, len); -} - -int sdb_lseek(int fd, int pos, int where) { - return utils_backend->sdb_lseek(fd, pos, where); -} - -int sdb_shutdown(int fd) { - return utils_backend->sdb_shutdown(fd); -} - -int sdb_close(int fd) { - return utils_backend->sdb_close(fd); -} - -int sdb_unlink(const char* path) { - return utils_backend->sdb_unlink(path); -} - -int sdb_mkdir(const char* path, int mode) { - return utils_backend->sdb_mkdir(path, mode); -} - -void close_on_exec(int fd) { - return utils_backend->close_on_exec(fd); -} - -int sdb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) { - return utils_backend->sdb_socket_accept(serverfd, addr, addrlen); -} +char* +buff_addb (char* buff, char* buffEnd, const void* data, int len) +{ + int avail = (buffEnd - buff); -int sdb_socketpair(int sv[2]) { - return utils_backend->sdb_socketpair(sv); -} + if (avail <= 0 || len <= 0) /* already overflowing */ + return buff; -void sdb_sleep_ms(int mseconds) { - return utils_backend->sdb_sleep_ms(mseconds); -} + if (len > avail) + len = avail; -char* sdb_dirstart(const char* path) { - return utils_backend->sdb_dirstart(path); -} + memcpy(buff, data, len); -char* sdb_dirstop(const char* path) { - return utils_backend->sdb_dirstop(path); -} + buff += len; -int sdb_socket_setbufsize(int fd, int bufsize) { - return utils_backend->sdb_socket_setbufsize(fd, bufsize); -} + /* ensure there is a terminating zero */ + if (buff >= buffEnd) { /* overflow */ + buff[-1] = 0; + } else + buff[0] = 0; -void disable_tcp_nagle(int fd) { - return utils_backend->disable_tcp_nagle(fd); + return buff; } -int sdb_thread_create(sdb_thread_t *pthread, sdb_thread_func_t start, void* arg) { - return utils_backend->sdb_thread_create(pthread, start, arg); -} +char* +buff_add (char* buff, char* buffEnd, const char* format, ... ) +{ + int avail; -int sdb_mutex_lock(sdb_mutex_t *mutex) { - return utils_backend->sdb_mutex_lock(mutex); -} + avail = (buffEnd - buff); -int sdb_mutex_unlock(sdb_mutex_t *mutex) { - return utils_backend->sdb_mutex_unlock(mutex); -} + if (avail > 0) { + va_list args; + int nn; -int sdb_cond_init(sdb_cond_t *cond, const void *unused) { - return utils_backend->sdb_cond_init(cond, unused); -} + va_start(args, format); + nn = vsnprintf( buff, avail, format, args); + va_end(args); -int sdb_cond_wait(sdb_cond_t *cond, sdb_mutex_t *mutex) { - return utils_backend->sdb_cond_wait(cond, mutex); -} + 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; + } -int sdb_cond_broadcast(sdb_cond_t *cond) { - return utils_backend->sdb_cond_broadcast(cond); -} + buff += nn; -void sdb_sysdeps_init(void) { - return utils_backend->sdb_sysdeps_init(); + /* ensure that there is a terminating zero */ + if (buff >= buffEnd) + buff[-1] = 0; + else + buff[0] = 0; + } + return buff; } -int socket_loopback_client(int port, int type) { - return utils_backend->socket_loopback_client(port, type); -} +char *str_trim(const char* string) +{ + const char* s = string; + const char* e = string + (strlen(string) - 1); + char* ret; -int socket_network_client(const char *host, int port, int type) { - return utils_backend->socket_network_client(host, port, type); -} + while(*s == ' ' || *s == '\t') { // ltrim + s++; + } + while(*e == ' ' || *e == '\t') { // rtrim + e--; + } -int socket_loopback_server(int port, int type) { - return utils_backend->socket_loopback_server(port, type); -} + ret = strdup(s); + ret[e - s + 1] = 0; -int socket_inaddr_any_server(int port, int type) { - return utils_backend->socket_inaddr_any_server(port, type); + return ret; } diff --git a/src/utils.h b/src/utils.h index 42ec7ae..e675548 100644 --- a/src/utils.h +++ b/src/utils.h @@ -17,124 +17,55 @@ #ifndef _SDB_UTILS_H #define _SDB_UTILS_H -// TODO: move to c file -#if defined(OS_WINDOWS) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pthread_win32.h" +/* bounded buffer functions */ -#else -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -//append strings with white space at most n+1 character. (last char is \0) -void append_args(char *dest, int argc, const char** argv, size_t n); -//get file name from full path. For example, /usr/opt/app -> app, app -> app -char* get_filename(char* full_name); -//append separator to the end of path. -//If return value is 1, it allocates new memory and appends separator at the end of path. Therefore, users should free memory. -void append_separator(char* result_path, char* path); -//returns 1 if path is directory, returns 0 if path is file, returns -1 if error occurred. -int is_directory(char* path); -int mkdirs(char *name); -void append_file(char* result_path, char* dir, char* append_dir); -long long NOW(); -void dup_quote(char* result_string, const char *source, int max_len); - -// OS dependent helpers - -#if defined(OS_WINDOWS) -#define OS_PATH_SEPARATOR '\\' -#define OS_PATH_SEPARATOR_STR "\\" - -#define lstat stat /* no symlinks on Win32 */ -#define S_ISLNK(m) 0 /* no symlinks on Win32 */ - -extern int unix_read(int fd, void* buf, size_t len); - -#else -#define OS_PATH_SEPARATOR '/' -#define OS_PATH_SEPARATOR_STR "/" - -#define unix_read sdb_read -#define SDB_MUTEX_DEFINE(m) sdb_mutex_t m = PTHREAD_MUTEX_INITIALIZER -#endif -// thread helpers -typedef pthread_t sdb_thread_t; -typedef pthread_mutex_t sdb_mutex_t; -#define sdb_cond_t pthread_cond_t - -typedef void* (*sdb_thread_func_t)( void* arg ); +/* 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); + */ -void sdb_sysdeps_init(void); -int sdb_thread_create(sdb_thread_t *pthread, sdb_thread_func_t start, void* arg); -int sdb_mutex_lock(sdb_mutex_t *mutex); -int sdb_mutex_unlock(sdb_mutex_t *mutex); -int sdb_cond_init(sdb_cond_t *cond, const void *unused); -int sdb_cond_wait(sdb_cond_t *cond, sdb_mutex_t *mutex); -int sdb_cond_broadcast(sdb_cond_t *cond); +/* 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); -#define SDB_MUTEX(x) sdb_mutex_t x; +/* tries to add a string to the buffer */ +char* buff_adds (char* buff, char* buffEnd, const char* s); -SDB_MUTEX(dns_lock) -SDB_MUTEX(socket_list_lock) -SDB_MUTEX(transport_lock) -SDB_MUTEX(local_transports_lock) -SDB_MUTEX(usb_lock) -SDB_MUTEX(D_lock) +/* 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); -int launch_server(int server_port); -void start_logging(void); -char* ansi_to_utf8(const char *str); -int sdb_open(const char* path, int options); -int sdb_open_mode(const char* path, int options, int mode); -int unix_open(const char* path, int options, ...); -int sdb_creat(const char* path, int mode); -int sdb_read(int fd, void* buf, size_t len); -int sdb_write(int fd, const void* buf, size_t len); -int sdb_lseek(int fd, int pos, int where); -int sdb_shutdown(int fd); -int sdb_close(int fd); -int sdb_unlink(const char* path); -int sdb_mkdir(const char* path, int mode); -void close_on_exec(int fd); -int sdb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen); -int sdb_socketpair(int sv[2]); -void sdb_sleep_ms(int mseconds); -char* sdb_dirstart(const char* path); -char* sdb_dirstop(const char* path); -int sdb_socket_setbufsize(int fd, int bufsize); -void disable_tcp_nagle(int fd); +/* tries to add a formatted string to a bounded buffer */ +char* buff_add (char* buff, char* buffEnd, const char* format, ... ); -// sockets -int socket_loopback_client(int port, int type); -int socket_network_client(const char *host, int port, int type); -int socket_loopback_server(int port, int type); -int socket_inaddr_any_server(int port, int type); +/* 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) -#define DEVICEMAP_SEPARATOR ":" -#define DEVICENAME_MAX 256 -#define VMS_PATH OS_PATH_SEPARATOR_STR "vms" OS_PATH_SEPARATOR_STR -#define DEFAULT_DEVICENAME "" +char *str_trim(const char* string); #endif /* _SDB_UTILS_H */ diff --git a/src/utils_backend.h b/src/utils_backend.h deleted file mode 100644 index 39d0295..0000000 --- a/src/utils_backend.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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_BACKEND_UTILS_H -#define _SDB_BACKEND_UTILS_H - -#if defined(OS_WINDOWS) - -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 BIP_BUFFER_SIZE 4096 -#define WIN32_FH_BASE 100 -#define WIN32_MAX_FHS 128 - -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; - -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; - -#define MAX_LOOPER_HANDLES WIN32_MAX_FHS - -typedef struct EventLooperRec_ -{ - EventHook hooks; - HANDLE htab[ MAX_LOOPER_HANDLES ]; - int htab_count; - -} EventLooperRec; - -int _fh_to_int( FH f ); -FH _fh_from_int( int fd ); - -#else -#endif // end of unix - -struct utils_os_backend { - // human-readable name - const char *name; - - int (*launch_server)(int server_port); - void (*start_logging)(void); - char* (*ansi_to_utf8)(const char *str); - int (*sdb_open)(const char* path, int options); - int (*sdb_open_mode)(const char* path, int options, int mode); - int (*unix_open)(const char* path, int options, ...); - int (*sdb_creat)(const char* path, int mode); - int (*sdb_read)(int fd, void* buf, size_t len); - int (*sdb_write)(int fd, const void* buf, size_t len); - int (*sdb_lseek)(int fd, int pos, int where); - int (*sdb_shutdown)(int fd); - int (*sdb_close)(int fd); - int (*sdb_unlink)(const char* path); - int (*sdb_mkdir)(const char* path, int mode); - void (*close_on_exec)(int fd); - int (*sdb_socket_accept)(int serverfd, struct sockaddr* addr, socklen_t *addrlen); - int (*sdb_socketpair)(int sv[2]); - void (*sdb_sleep_ms)(int mseconds); - char* (*sdb_dirstart)(const char* path); - char* (*sdb_dirstop)(const char* path); - int (*sdb_socket_setbufsize)(int fd, int bufsize); - void (*disable_tcp_nagle)(int fd); - // simple implementation of pthread - int (*sdb_thread_create)(sdb_thread_t *pthread, sdb_thread_func_t start, void* arg); - int (*sdb_mutex_lock)(sdb_mutex_t *mutex); - int (*sdb_mutex_unlock)(sdb_mutex_t *mutex); - - /* - * Copyright (C) 2009 Andrzej K. Haczewski - * Implement simple condition variable for Windows threads, based on ACE - * See original implementation: http://github.com/git - */ - int (*sdb_cond_init)(sdb_cond_t *cond, const void *unused); - int (*sdb_cond_wait)(sdb_cond_t *cond, sdb_mutex_t *mutex); - int (*sdb_cond_broadcast)(sdb_cond_t *cond); - void (*sdb_sysdeps_init)(void); - // helpers for sockets - int (*socket_loopback_client)(int port, int type); - int (*socket_network_client)(const char *host, int port, int type); - int (*socket_loopback_server)(int port, int type); - int (*socket_inaddr_any_server)(int port, int type); - -}; - -extern const struct utils_os_backend* utils_backend; -extern const struct utils_os_backend utils_unix_backend; -extern const struct utils_os_backend utils_windows_backend; - -#endif /* _SDB_BACKEND_UTILS_H */ diff --git a/src/utils_unix.c b/src/utils_unix.c deleted file mode 100644 index c9227a1..0000000 --- a/src/utils_unix.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * 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. - */ - -#if defined(OS_DARWIN) -#import -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utils.h" -#include "fdevent.h" - -#define TRACE_TAG TRACE_SYSDEPS -#include "sdb.h" -#include "utils_backend.h" - -#define LISTEN_BACKLOG 4 - -static void _close_on_exec(int fd); - -#if defined(OS_DARWIN) - -void _get_sdb_path(char *s, size_t max_len) -{ - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - CFDictionaryRef dict; - dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); - CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, max_len, kCFStringEncodingUTF8); -} - -#elif defined(OS_LINUX) - -static void _get_sdb_path(char *exe, size_t max_len) -{ - char proc[64]; - snprintf(proc, sizeof proc, "/proc/%d/exe", getpid()); - int err = readlink(proc, exe, max_len - 1); - if(err > 0) { - exe[err] = '\0'; - } else { - exe[0] = '\0'; - } -} -#endif - -static int _launch_server(int server_port) -{ - 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_sdb_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(); - } - return 0; -} - - -static void _start_logging(void) -{ - const char* p = getenv("SDB_TRACE"); - if (p == NULL) { - return; - } - 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()); -} - -static 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; -} - -static 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 int _sdb_open( const char* pathname, int options ) -{ - int fd = open( pathname, options ); - if (fd < 0) - return -1; - _close_on_exec( fd ); - return fd; -} - -static int _sdb_open_mode( const char* pathname, int options, int mode ) -{ - return open( pathname, options, mode ); -} - - -static int _sdb_creat(const char* path, int mode) -{ - int fd = creat(path, mode); - - if ( fd < 0 ) - return -1; - - _close_on_exec(fd); - return fd; -} - -static int _sdb_read(int fd, void* buf, size_t len) -{ - return read(fd, buf, len); -} - -static int _sdb_write(int fd, const void* buf, size_t len) -{ - return write(fd, buf, len); -} - -static int _sdb_lseek(int fd, int pos, int where) -{ - return lseek(fd, pos, where); -} - -static int _sdb_shutdown(int fd) -{ - return shutdown(fd, SHUT_RDWR); -} - -static int _sdb_close(int fd) -{ - return close(fd); -} - -static void _close_on_exec(int fd) -{ - fcntl( fd, F_SETFD, FD_CLOEXEC ); -} - -static int _sdb_unlink(const char* path) -{ - return unlink(path); -} - -static int _sdb_mkdir(const char* path, int mode) -{ - return mkdir(path, mode); -} - -static 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; -} - -static int _unix_socketpair( int d, int type, int protocol, int sv[2] ) -{ - return socketpair( d, type, protocol, sv ); -} - -static 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; -} - -static void _sdb_sleep_ms( int mseconds ) -{ - usleep( mseconds*1000 ); -} - -static char* _sdb_dirstart(const char* path) -{ - return strchr(path, '/'); -} - -static char* _sdb_dirstop(const char* path) -{ - return strrchr(path, '/'); -} - -static int _sdb_socket_setbufsize( int fd, int bufsize ) -{ - int opt = bufsize; - return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)); -} - -static void _disable_tcp_nagle(int fd) -{ - int on = 1; - setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) ); -} - -static 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); // don't need to call join - - return pthread_create( pthread, &attr, start, arg ); -} - -static int _sdb_mutex_lock(sdb_mutex_t *mutex) { - return pthread_mutex_lock(mutex); -} - -static int _sdb_mutex_unlock(sdb_mutex_t *mutex) { - return pthread_mutex_unlock(mutex); -} - -static int _sdb_cond_init(sdb_cond_t *cond, const void *unused) { - return pthread_cond_init(cond, unused); -} - -static int _sdb_cond_wait(sdb_cond_t *cond, sdb_mutex_t *mutex) { - return pthread_cond_wait(cond, mutex); -} - -static int _sdb_cond_broadcast(sdb_cond_t *cond) { - return pthread_cond_broadcast(cond); -} - -static void _sdb_sysdeps_init(void) -{ -} - -static 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) { - sdb_close(s); - return -1; - } - - return s; - -} - -static 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) { - sdb_close(s); - return -1; - } - - return s; - -} - -static int _socket_loopback_server(int port, int type) -{ - struct sockaddr_in addr; - int s, n; - int cnt_max = 30; - - 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; -} - -static 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) { - 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; -} - -const struct utils_os_backend utils_unix_backend = { - .name = "unix utils", - .launch_server = _launch_server, - .start_logging = _start_logging, - .ansi_to_utf8 = _ansi_to_utf8, - .sdb_open = _sdb_open, - .sdb_open_mode = _sdb_open_mode, - .unix_open = _unix_open, - .sdb_creat = _sdb_creat, - .sdb_read = _sdb_read, - .sdb_write = _sdb_write, - .sdb_lseek = _sdb_lseek, - .sdb_shutdown = _sdb_shutdown, - .sdb_close = _sdb_close, - .close_on_exec = _close_on_exec, - .sdb_unlink = _sdb_unlink, - .sdb_mkdir = _sdb_mkdir, - .sdb_socket_accept = _sdb_socket_accept, - .sdb_socketpair = _sdb_socketpair, - .sdb_sleep_ms = _sdb_sleep_ms, - .sdb_dirstart = _sdb_dirstart, - .sdb_dirstop = _sdb_dirstop, - .sdb_socket_setbufsize = _sdb_socket_setbufsize, - .disable_tcp_nagle = _disable_tcp_nagle, - // simple pthread implementations - .sdb_thread_create = _sdb_thread_create, - .sdb_mutex_lock = _sdb_mutex_lock, - .sdb_mutex_unlock = _sdb_mutex_unlock, - .sdb_cond_init = _sdb_cond_init, - .sdb_cond_wait = _sdb_cond_wait, - .sdb_cond_broadcast = _sdb_cond_broadcast, - .sdb_sysdeps_init = _sdb_sysdeps_init, - .socket_loopback_client = _socket_loopback_client, - .socket_network_client = _socket_network_client, - .socket_loopback_server = _socket_loopback_server, - .socket_inaddr_any_server = _socket_inaddr_any_server -}; diff --git a/src/utils_windows.c b/src/utils_windows.c deleted file mode 100644 index 5b66145..0000000 --- a/src/utils_windows.c +++ /dev/null @@ -1,1642 +0,0 @@ -/* - * 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 -#include -#include -#include -#include "utils.h" -#include "fdevent.h" -#include "fdevent_backend.h" - -#define TRACE_TAG TRACE_SYSDEPS -#include "sdb.h" -#include "utils_backend.h" - -/* - * from: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx - */ -static int _launch_server(int server_port) { - HANDLE g_hChildStd_OUT_Rd = NULL; - HANDLE g_hChildStd_OUT_Wr = NULL; - SECURITY_ATTRIBUTES saAttr; - - // Set the bInheritHandle flag so pipe handles are inherited. - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - // Create a pipe for the child process's STDOUT. - if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { - fprintf(stderr, "failed to StdoutRd CreatePipe: %ld\n", GetLastError()); - return -1; - } - - // Ensure the read handle to the pipe for STDOUT is not inherited. - if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { - fprintf(stderr, "failed to set Stdout SetHandleInformation: %ld\n", GetLastError()); - return -1; - } - - // Create a child process that uses the previously created pipes for STDIN and STDOUT. - { - PROCESS_INFORMATION piProcInfo; - STARTUPINFO siStartInfo; - BOOL bSuccess = FALSE; - char module_path[MAX_PATH]; - TCHAR szCmdline[] = TEXT("sdb fork-server server"); - - // Set up members of the PROCESS_INFORMATION structure. - ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); - - // Set up members of the STARTUPINFO structure. - // This structure specifies the STDIN and STDOUT handles for redirection. - ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); - siStartInfo.cb = sizeof(STARTUPINFO); - siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); - siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; - siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - siStartInfo.dwFlags |= STARTF_USESTDHANDLES; - - // Get current module path - GetModuleFileName(NULL, module_path, sizeof(module_path)); - - // Create the child process. - bSuccess = CreateProcess(module_path, szCmdline, // command line - NULL, // process security attributes - NULL, // primary thread security attributes - TRUE, // handles are inherited - DETACHED_PROCESS, // creation flags - NULL, // use parent's environment - NULL, // use parent's current directory - &siStartInfo, // STARTUPINFO pointer - &piProcInfo); // receives PROCESS_INFORMATION - - // If an error occurs, exit the application. - if (!bSuccess) { - fprintf(stderr, "fail to create sdb server process: %ld\n", GetLastError()); - CloseHandle(g_hChildStd_OUT_Wr); - CloseHandle(g_hChildStd_OUT_Rd); - return -1; - } - else { - CloseHandle(g_hChildStd_OUT_Wr); - // Close handles to the child process and its primary thread. - // Some applications might keep these handles to monitor the status - // of the child process, for example. - - CloseHandle(piProcInfo.hProcess); - CloseHandle(piProcInfo.hThread); - - //Read "OK\n" stdout message from sdb server - DWORD dwRead; - char chBuf[3]; - - bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, sizeof(chBuf), &dwRead, NULL); - CloseHandle(g_hChildStd_OUT_Rd); - - if( ! bSuccess || dwRead == 0 ) { - fprintf(stderr, "fail to read ok message from sdb server: %ld\n", GetLastError()); - return -1; - } - if (dwRead != 3 || chBuf[0] != 'O' || chBuf[1] != 'K' || chBuf[2] != '\n') { - fprintf(stderr, "fail to get OK\\n from sdb server: [%s]\n", chBuf); - return -1; - } - } - } - return 0; -} - -static void _start_logging(void) -{ - const char* p = getenv("SDB_TRACE"); - if (p == NULL) { - return; - } - - 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()); -} - - -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) - -/**************************************************************************/ -/**************************************************************************/ -/***** *****/ -/***** common file descriptor handling *****/ -/***** *****/ -/**************************************************************************/ -/**************************************************************************/ - -static sdb_mutex_t _win32_lock; -static FHRec _win32_fhs[WIN32_MAX_FHS]; -static int _win32_fh_count; - -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; -} - -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 *****/ -/***** *****/ -/**************************************************************************/ -/**************************************************************************/ - -static 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); -} - -static int _sdb_open_mode(const char* path, int options, int mode) { - return sdb_open(path, options); -} - -static 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); - } -} - -/* ignore mode on Win32 */ -static 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); -} - -static int _sdb_read(int fd, void* buf, size_t len) { - FH f = _fh_from_int(fd); - - if (f == NULL) { - return -1; - } - - return f->clazz->_fh_read(f, buf, len); -} - -static int _sdb_write(int fd, const void* buf, size_t len) { - FH f = _fh_from_int(fd); - - if (f == NULL) { - return -1; - } - - return f->clazz->_fh_write(f, buf, len); -} - -static 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); -} - -static 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; -} - -static 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; -} - -static 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; -} - -static int _sdb_mkdir(const char* path, int mode) { - return _mkdir(path); -} - -static __inline__ int unix_close(int fd) { - return close(fd); -} - -int unix_read(int fd, void* buf, size_t len) { - return read(fd, buf, len); -} - -int unix_write(int fd, const void* buf, size_t len) { - return write(fd, buf, len); -} - -static void _close_on_exec(int fd) { - /* nothing really */ -} - -static void _sdb_sleep_ms(int mseconds) { - Sleep(mseconds); -} - -static int _sdb_socket_setbufsize(int fd, int bufsize) { - int opt = bufsize; - return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*) &opt, sizeof(opt)); -} - -static 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 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; -} - -/**************************************************************************/ -/**************************************************************************/ -/***** *****/ -/***** 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 - -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; - } -} - -static 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 - -static 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); -} - -static 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); -} - -static 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); -} - -static 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); -} - -static 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 -# 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 - - -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 }; - -static 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. *****/ -/***** *****/ -/**************************************************************************/ -/**************************************************************************/ - - -/** 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; -} - -static void _sdb_sysdeps_init(void) { - //re define mutex variable & initialized -#undef SDB_MUTEX -#define SDB_MUTEX(x) InitializeCriticalSection( & x ); - SDB_MUTEX(dns_lock) - SDB_MUTEX(socket_list_lock) - SDB_MUTEX(transport_lock) - SDB_MUTEX(local_transports_lock) - SDB_MUTEX(usb_lock) - SDB_MUTEX(D_lock) - SDB_MUTEX(_win32_lock); -} - -typedef void (*win_thread_func_t)(void* arg); - -static 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 int _pthread_mutex_lock(pthread_mutex_t *mutex) { - // Request ownership of the critical section. - EnterCriticalSection(mutex); - - return 0; -} - -static int _pthread_mutex_unlock(pthread_mutex_t* mutex) { - // Release ownership of the critical section. - LeaveCriticalSection(mutex); - - return 0; -} - -static int _pthread_cond_init(pthread_cond_t *cond, const void *unused) -{ - cond->waiters = 0; - cond->was_broadcast = 0; - InitializeCriticalSection(&cond->waiters_lock); - - cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL); - if (!cond->sema) { - //die("CreateSemaphore() failed"); - } - - cond->continue_broadcast = CreateEvent(NULL, /* security */ - FALSE, /* auto-reset */ - FALSE, /* not signaled */ - NULL); /* name */ - if (!cond->continue_broadcast) { - //die("CreateEvent() failed"); - } - - return 0; -} - -static int _pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex) { - int last_waiter; - - EnterCriticalSection(&cond->waiters_lock); - cond->waiters++; - LeaveCriticalSection(&cond->waiters_lock); - - /* - * Unlock external mutex and wait for signal. - * NOTE: we've held mutex locked long enough to increment - * waiters count above, so there's no problem with - * leaving mutex unlocked before we wait on semaphore. - */ - LeaveCriticalSection(mutex); - - /* let's wait - ignore return value */ - WaitForSingleObject(cond->sema, INFINITE); - - /* - * Decrease waiters count. If we are the last waiter, then we must - * notify the broadcasting thread that it can continue. - * But if we continued due to cond_signal, we do not have to do that - * because the signaling thread knows that only one waiter continued. - */ - EnterCriticalSection(&cond->waiters_lock); - cond->waiters--; - last_waiter = cond->was_broadcast && cond->waiters == 0; - LeaveCriticalSection(&cond->waiters_lock); - - if (last_waiter) { - /* - * cond_broadcast was issued while mutex was held. This means - * that all other waiters have continued, but are contending - * for the mutex at the end of this function because the - * broadcasting thread did not leave cond_broadcast, yet. - * (This is so that it can be sure that each waiter has - * consumed exactly one slice of the semaphor.) - * The last waiter must tell the broadcasting thread that it - * can go on. - */ - SetEvent(cond->continue_broadcast); - /* - * Now we go on to contend with all other waiters for - * the mutex. Auf in den Kampf! - */ - } - /* lock external mutex again */ - EnterCriticalSection(mutex); - - return 0; -} - -static int _pthread_cond_broadcast(pthread_cond_t *cond) -{ - EnterCriticalSection(&cond->waiters_lock); - - if ((cond->was_broadcast = cond->waiters > 0)) { - /* wake up all waiters */ - ReleaseSemaphore(cond->sema, cond->waiters, NULL); - LeaveCriticalSection(&cond->waiters_lock); - /* - * At this point all waiters continue. Each one takes its - * slice of the semaphor. Now it's our turn to wait: Since - * the external mutex is held, no thread can leave cond_wait, - * yet. For this reason, we can be sure that no thread gets - * a chance to eat *more* than one slice. OTOH, it means - * that the last waiter must send us a wake-up. - */ - WaitForSingleObject(cond->continue_broadcast, INFINITE); - /* - * Since the external mutex is held, no thread can enter - * cond_wait, and, hence, it is safe to reset this flag - * without cond->waiters_lock held. - */ - cond->was_broadcast = 0; - } else { - LeaveCriticalSection(&cond->waiters_lock); - } - return 0; -} - -static 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; -} -const struct utils_os_backend utils_windows_backend = { - .name = "windows utils", - .launch_server = _launch_server, - .start_logging = _start_logging, - .ansi_to_utf8 = _ansi_to_utf8, - .sdb_open = _sdb_open, - .sdb_open_mode = _sdb_open_mode, - .unix_open = _unix_open, - .sdb_creat = _sdb_creat, - .sdb_read = _sdb_read, - .sdb_write = _sdb_write, - .sdb_lseek = _sdb_lseek, - .sdb_shutdown = _sdb_shutdown, - .sdb_close = _sdb_close, - .close_on_exec = _close_on_exec, - .sdb_unlink = _sdb_unlink, - .sdb_mkdir = _sdb_mkdir, - .sdb_socket_accept = _sdb_socket_accept, - .sdb_socketpair = _sdb_socketpair, - .sdb_sleep_ms = _sdb_sleep_ms, - .sdb_dirstart = _sdb_dirstart, - .sdb_dirstop = _sdb_dirstop, - .sdb_socket_setbufsize = _sdb_socket_setbufsize, - .disable_tcp_nagle = _disable_tcp_nagle, -// simple pthread implementations - .sdb_thread_create = _sdb_thread_create, - .sdb_mutex_lock = _pthread_mutex_lock, - .sdb_mutex_unlock = _pthread_mutex_unlock, - .sdb_cond_wait = _pthread_cond_wait, - .sdb_cond_init = _pthread_cond_init, - .sdb_cond_broadcast = _pthread_cond_broadcast, - .sdb_sysdeps_init = _sdb_sysdeps_init, - .socket_loopback_client = _socket_loopback_client, - .socket_network_client = _socket_network_client, - .socket_loopback_server = _socket_loopback_server, - .socket_inaddr_any_server = _socket_inaddr_any_server -};