分类目录归档:Android

zmq-4.3.4、czmq-4.2.1、zyre-2.0.1及libsodium-1.0.18的编译

之前写过一次zmq的编译问题的文章,当时编译的是zmq-4.3.2,但是由于这次要将zmq移植到Android一体机,发生了一些奇怪的问题:一体机客户端zmq程序成功的send了数据,但是运行在云端的server毫无反应,一体机中用到的zmq是最新的4.3.4,所以决定将PC版的也升级看看问题是否还存在(4.3.2->4.3.4),本来没报太大希望的,结果将云端server的4.3.2升级到4.3.4后,Android的zmq客户端居然成功连上了,奇葩。

以下是最新4.3.4的编译过程:

1、编译libsodium-1.0.18

msvc已经配置好,直接打开解决方案:

libsodium-1.0.18\builds\msvc\vs2019\libsodium.sln

编译结果在bin目录下:

libsodium-1.0.18\bin

2、编译zeromq-4.3.4

没有现有msvc解决方案,得自己通过cmake指令生成。

  • 为避免生成带版本号的库文件,需要在CMakeLists.txt的1315行将POSTFIX的代码全部注释掉:
    set_target_properties(
      libzmq
      PROPERTIES PUBLIC_HEADER "${public_headers}"
                 #RELEASE_POSTFIX "${MSVC_TOOLSET}-mt-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}"
                 #RELWITHDEBINFO_POSTFIX "${MSVC_TOOLSET}-mt-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}"
                 #MINSIZEREL_POSTFIX "${MSVC_TOOLSET}-mt-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}"
                 #DEBUG_POSTFIX "${MSVC_TOOLSET}-mt-gd-${ZMQ_VERSION_MAJOR}_${ZMQ_VERSION_MINOR}_${ZMQ_VERSION_PATCH}"
                 RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
                 COMPILE_DEFINITIONS "DLL_EXPORT"
                 OUTPUT_NAME "lib${ZMQ_OUTPUT_BASENAME}")
  • 这个版本编译静态库会有一堆错误,我们没用静态库,所以没详细研究为什么,直接把静态库编译选项关了:
option(BUILD_STATIC "Whether or not to build the static archive" OFF)
  • 由于后面用到了zyre,而zyre默认为zmq使用了sodium加密,会直接去链接zmq的相关函数,如果zmq库不使用sodium,zyre会编译出错,所以需要将sodium激活,即,制作一个FindSodium.cmake文件,内容为:
########################################################################
# CMake module for finding SODIUM
#
# The following variables will be defined:
#
#  SODIUM_FOUND
#  SODIUM_INCLUDE_DIRS
#  SODIUM_LIBRARIES
#

find_path(SODIUM_INCLUDE_DIRS
  NAMES sodium.h
  PATHS ${PROJECT_SOURCE_DIR}/../libsodium-1.0.18/src/libsodium/include
  NO_DEFAULT_PATH)
find_library(SODIUM_LIBRARIES
  NAMES libsodium
  PATHS ${PROJECT_SOURCE_DIR}/../libsodium-1.0.18/bin/x64/Release/v142/dynamic
  NO_DEFAULT_PATH)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SODIUM DEFAULT_MSG SODIUM_LIBRARIES SODIUM_INCLUDE_DIRS)

一旦找到sodium,zmq会自动激活CURVE模式,将自动使用sodium加密算法。

  • 在zeromq-4.3.4目录中打开cmd,执行如下指令:
cmake -H. -Bbuild -G"Visual Studio 16 2019" -Ax64
cmake --build build --config release
cmake --build build --config debug

经过以上步骤,libzmq会顺利生成到lib及bin目录下,不会有任何编译错误。

3、编译czmq-4.2.1

  • czmq依赖libzmq,所以需要修改Findlibzmq.cmake文件,让其找到上面编译出来的libzmq:在if (NOT MSVC)加一段else就行了:
if (NOT MSVC)
    include(FindPkgConfig)
    pkg_check_modules(PC_LIBZMQ "libzmq")
    if (PC_LIBZMQ_FOUND)
        # add CFLAGS from pkg-config file, e.g. draft api.
        add_definitions(${PC_LIBZMQ_CFLAGS} ${PC_LIBZMQ_CFLAGS_OTHER})
        # some libraries install the headers is a subdirectory of the include dir
        # returned by pkg-config, so use a wildcard match to improve chances of finding
        # headers and SOs.
        set(PC_LIBZMQ_INCLUDE_HINTS ${PC_LIBZMQ_INCLUDE_DIRS} ${PC_LIBZMQ_INCLUDE_DIRS}/*)
        set(PC_LIBZMQ_LIBRARY_HINTS ${PC_LIBZMQ_LIBRARY_DIRS} ${PC_LIBZMQ_LIBRARY_DIRS}/*)
    endif(PC_LIBZMQ_FOUND)
else()
    set(PC_LIBZMQ_INCLUDE_DIRS ../zeromq-4.3.4/include)
    set(PC_LIBZMQ_LIBRARY_DIRS ../zeromq-4.3.4/build/lib/Release)
    set(PC_LIBZMQ_INCLUDE_HINTS ${PC_LIBZMQ_INCLUDE_DIRS} ${PC_LIBZMQ_INCLUDE_DIRS}/*)
    set(PC_LIBZMQ_LIBRARY_HINTS ${PC_LIBZMQ_LIBRARY_DIRS} ${PC_LIBZMQ_LIBRARY_DIRS}/*)
endif (NOT MSVC)

message("######## ${PC_LIBZMQ_INCLUDE_HINTS}")
message("######## ${PC_LIBZMQ_LIBRARY_HINTS}")
  • 由于静态库编译会有一堆错误,没有详细研究过,也不需要用静态库,所以把他关了:
option(CZMQ_BUILD_STATIC "Whether or not to build the static archive" OFF)
  • 执行指令生成配置、生成库文件:
cmake -H. -Bbuild -G"Visual Studio 16 2019" -Ax64
cmake --build build --config release
cmake --build build --config debug

4、编译zyre-2.0.1

由于zyre同时依赖zmq、czmq,所以:

  • 将czmq中的Findlibzmq.cmake拷贝直接覆盖zyre下的Findlibzmq.cmake文件;
  • 修改Findczmq.cmake,方式与修改Findlibzmq.cmake一样
    然后执行指令生成配置、生成库文件:
cmake -H. -Bbuild -G"Visual Studio 16 2019" -Ax64
cmake --build build --config release
cmake --build build --config debug

5、czmq版本号问题

在这个版本中,czmq彻底删除了msvc的配置,所以没有version.rc或resource.rc文件,我从上一个版本找到了用过的resource.rc和resource.h文件,拷贝到src目录,改一下版本号,在CMakeList.txt中加进去就可以了:

resource.h:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by resource.rc

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

resource.rc:

// 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

/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

/////////////////////////////////////////////////////////////////////////////
//
// Version
//

VS_VERSION_INFO VERSIONINFO
 FILEVERSION 4,2,1,0
 PRODUCTVERSION 4,2,1,0
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x40004L
 FILETYPE 0x7L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "CompanyName", "The AUTHORS"
            VALUE "FileDescription", "The high-level C binding for 0MQ"
            VALUE "FileVersion", "4.2.1.0"
            VALUE "InternalName", "CZMQ"
            VALUE "LegalCopyright", "Copyright (c) the Authors"
            VALUE "OriginalFilename", "libczmq.dll"
            VALUE "ProductName", "CZMQ"
            VALUE "ProductVersion", "4.2.1.0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

#endif    // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//

/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

CMakeList.txt第415行,添加对rc文件的引用

      add_library(czmq SHARED ${czmq_sources} ${CMAKE_CURRENT_SOURCE_DIR}/src/resource.rc)

Android内核编译过程全解

之前编译过锤子的坚果手机内核,摸索了很长时间,遗憾的是没有把一些填坑的细节记录下来,免不了有些细节还得摸索一遍。这次要编译三星的Galaxy Note 5内核,加上已经有了一次成功经历,所以想把它系统化的记录下来,供自己或其他需要的人参考。

我这里主要讲内核的编译,至于关联到的一些其他工具的安装或配置,这里就不展开了,碰到这样的问题请移步问问Google。

1.  获取CPU信息

要为某手机编译内核,首先要了解手机所用的CPU,不同厂商生产的CPU,对应的linux内核是不一样的。

查看CPU信息的一种方法是利用adb,“adb shell cat /roc/cpuinfo”可以得到cpu架构和生产厂商。另外,利用“adb shell cat /proc/version”还可以得到手机中正在使用的内核信息。以下是我的Galaxy note 5的信息:将手机与PC通过usb线相连,首先查看adb是否已经可以访问、然后获取cpu信息、最后获取linux内核信息:

C:\Users\yuanhui>adb devices
List of devices attached
0715e7e408981f38 device

C:\Users\yuanhui>adb shell cat /proc/cpuinfo
Processor : AArch64 Processor rev 2 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
Features : fp asimd aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: AArch64
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 2

Hardware : SAMSUNG Exynos7420

C:\Users\yuanhui>adb shell cat /proc/version
Linux version 3.10.61-6137732 (dpi@SWDC3312) (gcc version 4.9 20140514 (prerelea
se) (GCC) ) #1 SMP PREEMPT Fri Feb 5 13:33:23 KST 2016
另外,通过官网也可以获得比较详细的硬件信息及参数:

QQ截图20160401115714

2. 下载源代码

得到CPU信息后,就可以到Android网站去下载linux内核源码了,下载地址说明:

http://source.android.com/source/building-kernels.html#figuring-out-which-kernel-to-build

这里需要特别注意,三星的源码树有两个,一个是kernel/exynos,一个是kernel/samsung,Galaxy note 5 用的是exynos芯片,所以一定要下载exynos的源码。

QQ截图20160401115941

所以,git命令为:

$ git clone https://android.googlesource.com/kernel/exynos

这里稍微注意一下,由于国内googlesource.com被墙了,只能通过VPN才能下载。

我为了方便,所有下载及编译都是在Ubuntu里完成的:

hyh@ubuntu:~$ git clone https://android.googlesource.com/kernel/exynos
Cloning into ‘exynos’…
remote: Sending approximately 733.06 MiB …
remote: Counting objects: 9, done
remote: Finding sources: 100% (9/9)
Receiving objects: 100% (3159494/3159494), 733.07 MiB | 631.00 KiB/s, done.
remote: Total 3159494 (delta 2631328), reused 3159494 (delta 2631328)
Resolving deltas: 100% (2631328/2631328), done.
Checking connectivity… done.
hyh@ubuntu:~$

这样,在我的home下就生成了一个exynos目录,源码就在这个文件夹里。

为了后面使用方便,把目录改成了linux-kernel-exynos。

到里面看看都有些什么分支:

hyh@ubuntu:~$ cd linux-kernel-exynos/
hyh@ubuntu:~/linux-kernel-exynos$ git branch -r
origin/HEAD -> origin/master
origin/android-exynos-3.4
origin/android-exynos-koi-3.10-marshmallow-mr1-wear-release
origin/android-exynos-manta-3.4-adf
origin/android-exynos-manta-3.4-jb-mr1
origin/android-exynos-manta-3.4-jb-mr1-fr
origin/android-exynos-manta-3.4-jb-mr1.1
origin/android-exynos-manta-3.4-jb-mr2
origin/android-exynos-manta-3.4-kitkat-mr0
origin/android-exynos-manta-3.4-kitkat-mr1
origin/android-exynos-manta-3.4-kitkat-mr2
origin/android-exynos-manta-3.4-lollipop-mr1
origin/android-exynos-manta-3.4-lollipop-release
origin/master
hyh@ubuntu:~/linux-kernel-exynos$

之前看到手机用的是3.10的内核,那就把3.10的分支checkout出来:

hyh@ubuntu:~/linux-kernel-exynos$ git checkout origin/android-exynos-koi-3.10-marshmallow-mr1-wear-release
Checking out files: 100% (45351/45351), done.
Note: checking out ‘origin/android-exynos-koi-3.10-marshmallow-mr1-wear-release’.

You are in ‘detached HEAD’ state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b <new-branch-name>

HEAD is now at 0f9bded… Merge “Fix that wrong eint information is displayed” into android-exynos-koi-3.10
hyh@ubuntu:~/linux-kernel-exynos$

这时候才能看到目录下有了源代码(之前目录里面实际有个800多兆的.git文件夹,隐藏了,看不到而已)。

3. 配置/定制内核

通常情况下,各个厂商都会针对自己的手机做大量定制,如果编译内核时选择生成相应的配置文件,则我们可以从手机中直接得到。能拿到这个内核配置文件编译内核就会顺利很多。

C:\Users\yuanhui>adb pull /proc/config.gz

大部分情况都能拿到这个config文件,然后解压出其中的.config文件放到linux-kernel-exynos目录下,直接调用make ARCH=arm menuconfig即可定制内核。

但是很不幸,三星的这款手机没有此文件:

C:\Users\yuanhui>adb pull /proc/config.gz
remote object ‘/proc/config.gz’ does not exist

好吧,没有也没关系,直接make ARCH=arm menuconfig,通常情况也是能顺利编译的……

我这里为了把CP210x的驱动编译进去,从Silicon官网下载了Android内核编译CP210x驱动的文档,照着把CP210x驱动编译选项勾选好,保存退出,在linux-kernel-exynos目录下生成了一个.config的文件:

hyh@ubuntu:~/linux-kernel-exynos$ make ARCH=arm menuconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/lxdialog/checklist.o
HOSTCC scripts/kconfig/lxdialog/inputbox.o
HOSTCC scripts/kconfig/lxdialog/menubox.o
HOSTCC scripts/kconfig/lxdialog/textbox.o
HOSTCC scripts/kconfig/lxdialog/util.o
HOSTCC scripts/kconfig/lxdialog/yesno.o
HOSTCC scripts/kconfig/mconf.o
SHIPPED scripts/kconfig/zconf.tab.c
SHIPPED scripts/kconfig/zconf.lex.c
SHIPPED scripts/kconfig/zconf.hash.c
HOSTCC scripts/kconfig/zconf.tab.o
In file included from scripts/kconfig/zconf.tab.c:2503:0:
scripts/kconfig/menu.c: In function ‘get_symbol_str’:
scripts/kconfig/menu.c:567:18: warning: ‘jump’ may be used uninitialized in this function [-Wmaybe-uninitialized]
jump->offset = r->len – 1;
^
scripts/kconfig/menu.c:528:19: note: ‘jump’ was declared here
struct jump_key *jump;
^
HOSTLD scripts/kconfig/mconf
scripts/kconfig/mconf Kconfig
#
# using defaults found in /boot/config-4.2.0-23-generic
#
/boot/config-4.2.0-23-generic:928:warning: symbol value ‘m’ invalid for BRIDGE_NETFILTER
/boot/config-4.2.0-23-generic:2668:warning: symbol value ‘m’ invalid for STMMAC_PLATFORM
/boot/config-4.2.0-23-generic:3834:warning: symbol value ‘m’ invalid for GPIO_UCB1400
/boot/config-4.2.0-23-generic:4336:warning: symbol value ‘m’ invalid for MFD_WM8994
/boot/config-4.2.0-23-generic:4343:warning: symbol value ‘m’ invalid for REGULATOR_88PM8607
/boot/config-4.2.0-23-generic:4365:warning: symbol value ‘m’ invalid for REGULATOR_LP872X
/boot/config-4.2.0-23-generic:4367:warning: symbol value ‘m’ invalid for REGULATOR_LP8788
/boot/config-4.2.0-23-generic:4410:warning: symbol value ‘m’ invalid for REGULATOR_TWL4030
/boot/config-4.2.0-23-generic:5453:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_REALTEK
/boot/config-4.2.0-23-generic:5454:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_ANALOG
/boot/config-4.2.0-23-generic:5455:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_SIGMATEL
/boot/config-4.2.0-23-generic:5456:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_VIA
/boot/config-4.2.0-23-generic:5457:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_HDMI
/boot/config-4.2.0-23-generic:5458:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CIRRUS
/boot/config-4.2.0-23-generic:5459:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CONEXANT
/boot/config-4.2.0-23-generic:5460:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CA0110
/boot/config-4.2.0-23-generic:5461:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CA0132
/boot/config-4.2.0-23-generic:5463:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_CMEDIA
/boot/config-4.2.0-23-generic:5464:warning: symbol value ‘m’ invalid for SND_HDA_CODEC_SI3054
/boot/config-4.2.0-23-generic:5465:warning: symbol value ‘m’ invalid for SND_HDA_GENERIC
/boot/config-4.2.0-23-generic:6539:warning: symbol value ‘m’ invalid for COMEDI_PCI_DRIVERS
/boot/config-4.2.0-23-generic:6594:warning: symbol value ‘m’ invalid for COMEDI_PCMCIA_DRIVERS
/boot/config-4.2.0-23-generic:6602:warning: symbol value ‘m’ invalid for COMEDI_USB_DRIVERS
/boot/config-4.2.0-23-generic:7036:warning: symbol value ‘m’ invalid for LP8788_ADC
/boot/config-4.2.0-23-generic:8176:warning: symbol value ‘m’ invalid for KVM
configuration written to .config

*** End of the configuration.
*** Execute ‘make’ to start the build or try ‘make help’.

hyh@ubuntu:~/linux-kernel-exynos$

 

4. 编译内核

 

不同的CPU架构,就得选择不同架构的工具链。我们在刚开始的时候通过adb shell cat /proc/cpuinfo已经得到了CPU的家规信息,为aarch64,所以我们要选择的aarch64工具链来编译,Google Source 网站上同时提供了各个版本的编译工具:

https://android.googlesource.com/platform/prebuilts/

我们选择aarch64 gcc 4.9的版本来编译:

hyh@ubuntu:~$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9
Cloning into ‘aarch64-linux-android-4.9’…
remote: Sending approximately 180.15 MiB …
remote: Counting objects: 108, done
remote: Finding sources: 100% (108/108)
remote: Total 1641 (delta 990), reused 1641 (delta 990)
Receiving objects: 100% (1641/1641), 180.18 MiB | 620.00 KiB/s, done.
Resolving deltas: 100% (990/990), done.
Checking connectivity… done.
hyh@ubuntu:~$

把这个路径加入到$PATH中,以便编译时省去冗长的路径:

hyh@ubuntu:~/linux-kernel-exynos$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
hyh@ubuntu:~/linux-kernel-exynos$ export PATH=$PATH:~/aarch64-linux-android-4.9/bin
hyh@ubuntu:~/linux-kernel-exynos$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/hyh/aarch64-linux-android-4.9/bin

可以看到,用于编译内核的编译器路径已经添加到了$PATH中。

下面开始编译:
hyh@ubuntu:~/linux-kernel-exynos$ make ARCH=arm CROSS_COMPILE=arm-linux-androideabi- zImage
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
make[1]: ‘include/generated/mach-types.h’ is up to date.
CC kernel/bounds.s
GEN include/generated/bounds.h
CC arch/arm/kernel/asm-offsets.s
In file included from include/linux/scatterlist.h:10:0,
from include/linux/dma-mapping.h:9,
from arch/arm/kernel/asm-offsets.c:15:
/home/hyh/linux-kernel-exynos/arch/arm/include/asm/io.h:30:28: fatal error: mach/exynos-ss.h: No such file or directory
#include <mach/exynos-ss.h>
^
compilation terminated.
/home/hyh/linux-kernel-exynos/./Kbuild:81: recipe for target ‘arch/arm/kernel/asm-offsets.s’ failed
make[1]: *** [arch/arm/kernel/asm-offsets.s] Error 1
Makefile:836: recipe for target ‘prepare0’ failed
make: *** [prepare0] Error 2
hyh@ubuntu:~/linux-kernel-exynos$

出错了,改个编译工具,网上下载了一个arm-eabi-4.8,放到hyh目录下,在$PATH中加入目录/home/hyh/arm-eabi-4.8/bin,编译。这次好点,编译了大部分代码,但是在编译驱动时出错了:

hyh@ubuntu:~/linux-kernel-exynos$ make ARCH=arm CROSS_COMPILE=arm-eabi- zImage
CHK include/linux/version.h
CHK include/generated/utsrelease.h
make[1]: ‘include/generated/mach-types.h’ is up to date.
CALL scripts/checksyscalls.sh
CHK include/generated/compile.h
CC drivers/mfd/ezx-pcap.o
drivers/mfd/ezx-pcap.c: In function ‘pcap_isr_work’:
drivers/mfd/ezx-pcap.c:205:2: error: implicit declaration of function ‘irq_to_gpio’ [-Werror=implicit-function-declaration]
} while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
^
cc1: some warnings being treated as errors
scripts/Makefile.build:305: recipe for target ‘drivers/mfd/ezx-pcap.o’ failed
make[2]: *** [drivers/mfd/ezx-pcap.o] Error 1
scripts/Makefile.build:441: recipe for target ‘drivers/mfd’ failed
make[1]: *** [drivers/mfd] Error 2
Makefile:945: recipe for target ‘drivers’ failed
make: *** [drivers] Error 2
hyh@ubuntu:~/linux-kernel-samsung$

发现ezx-pcap.c文件中有一段如下代码:

static void pcap_isr_work(struct work_struct *work)
{
struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
struct pcap_platform_data *pdata = pcap->spi->dev.platform_data;
u32 msr, isr, int_sel, service;
int irq;

do {
ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);

/* We can’t service/ack irqs that are assigned to port 2 */
if (!(pdata->config & PCAP_SECOND_PORT)) {
ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
isr &= ~int_sel;
}

ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);

local_irq_disable();
service = isr & ~msr;
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
if (service & 1)
generic_handle_irq(irq);
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
} while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
}

定位到申明的位置:<linux/gpio.h>,在gpio.h文件中有这样一段申明:

static inline int irq_to_gpio(unsigned irq)
{
/* irq can never have been returned from gpio_to_irq() */
WARN_ON(1);
return -EINVAL;
}

可见被加了static,外部无法访问。而且,看到注释,以及返回值,可见这个函数是无用的。分析在ezx-pcap.c文件中的这段代码,do while会执行一次这段代码,但irq_to_gpio()永远只会返回失败,所以一定只会执行一次,所以果断把 “do{” 和 “}while”  这两行代码屏蔽:

static void pcap_isr_work(struct work_struct *work)
{
struct pcap_chip *pcap = container_of(work, struct pcap_chip, isr_work);
struct pcap_platform_data *pdata = pcap->spi->dev.platform_data;
u32 msr, isr, int_sel, service;
int irq;

//do {
ezx_pcap_read(pcap, PCAP_REG_MSR, &msr);
ezx_pcap_read(pcap, PCAP_REG_ISR, &isr);

/* We can’t service/ack irqs that are assigned to port 2 */
if (!(pdata->config & PCAP_SECOND_PORT)) {
ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel);
isr &= ~int_sel;
}

ezx_pcap_write(pcap, PCAP_REG_MSR, isr | msr);
ezx_pcap_write(pcap, PCAP_REG_ISR, isr);

local_irq_disable();
service = isr & ~msr;
for (irq = pcap->irq_base; service; service >>= 1, irq++) {
if (service & 1)
generic_handle_irq(irq);
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
//} while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
}

再次编译,过了:

……

LD vmlinux.o
MODPOST vmlinux.o
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
LD init/built-in.o
LD .tmp_vmlinux1
KSYM .tmp_kallsyms1.S
AS .tmp_kallsyms1.o
LD .tmp_vmlinux2
KSYM .tmp_kallsyms2.S
AS .tmp_kallsyms2.o
LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy.gzip
AS arch/arm/boot/compressed/piggy.gzip.o
SHIPPED arch/arm/boot/compressed/lib1funcs.S
AS arch/arm/boot/compressed/lib1funcs.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
hyh@ubuntu:~/linux-kernel-exynos$

5. 后记

最后一步的编译经常会出一些具体的问题,比如某个包含文件找不到、某段代码编译出错,这种错误需要仔细分析错误提示,往往能定位到问题。源码过于庞大,而实际的应用千差万别,导致自定义后的某些模块之间出现问题。所以这样的问题只能是具体问题具体分析,没用的代码甚至可以屏蔽。这恰恰也是导致部分自定义代码无法编译通过的原因。

 

Android SDK DDMS通过Wifi接收Android日志

将Android手机与PC通过USB连接,我们就可以在Android Device Monitor中看到Android手机的实时日志,这个对程序的调试非常有帮助。但是一般来讲手机只有一个usb口,如果usb被别的设备占用,我们就没法获取日志了。

实际上,Android Device Monitor是通过手机端的ADB服务与PC通讯来显示日志的。adb非常流弊,指令丰富,功能齐全,是黑进手机的必备工具。这里就用到了adb的网络功能。要想拿到日志,必须在手机端启动adb的tcp服务,然后在PC端通过adb与手机的adb服务连接,这样我们就可以通过网络获取日志了,而不是usb有线方式获取日志。

具体步骤如下:

step 1. 将手机通过usb连接到PC机

step 2. 通过adb的tcpip命令启动一个tcp监听服务

step 3. 通过adb的connect指令从PC端连接Android手机

step 4. 启动Dalvik Debug Monitor,就会看到日志从网络上发动到了本机

 

具体操作如下:

D:\Android>adb tcpip 8630
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
restarting in TCP mode port: 8630

D:\Android>adb connect 192.168.3.42:8630
unable to connect to 192.168.3.42:8630

 

 

D:\Android>adb devices
List of devices attached
90a16a93 device
D:\Android>adb connect 192.168.3.42:8630
unable to connect to 192.168.3.42:8630

D:\Android>adb tcpip 9999
restarting in TCP mode port: 9999

D:\Android>adb connect 192.168.3.42:9999
connected to 192.168.3.42:9999

 

参考:http://stackoverflow.com/questions/2604727/how-can-i-connect-to-android-with-adb-over-tcp

 

在命令模式下编译Android NDK 的 *.so 库

为Android编译c++写的库文件(*.so文件)有很多种方式,实际上就是有很多种不同的工具可以选择:Eclipse+ADT、Android Studio,本质上还是调用android-ndk下的build-ndk(.bat)指令来编译arm版本的、针对Android操作系统的so文件。

我们在用Eclipse编译so文件时基本上就是建一个Android工程,为其定义一个编译配置(配置好的Android ndk路径、src路径、workspace路径等等),然后为其增加Application.mk和Android.mk两个文件,Eclipse就会自动调用Application.mk和Android.mk make文件去编译、生成so文件了。

我一直都在用Eclipse配置、编译so库,但说实话,Eclipse不但配置繁琐、容易出错,Eclipse对工程的管理也是非常不灵活的:我要是有多个不相关的so库需要编译,每次打开时都会全部加载(也许是我用的不好吧),编译时又得指定要编译的库单独编译;如果某个库的路径变了,Eclipse得重新配置,否则一大堆错误,真正要编译的项目却淹没其中。

既然Eclipse也不过是调用了Android-ndk的指令去结合Application.mk和Android.mk文件实现编译,为什么不能从命令模式直接调用android-ndk命令结合Application.mk和Android.mk文件来编译呢?这样每个项目各自不会纠缠在一起,干净利落,岂不美哉?

下面拿一个项目做个测试。

step 1: 建立一个目录,名称为:PerceptionNeuronPrj

step 2: 将项目的源码拷贝进去

这里是PerceptionNeuronSDK目录,可以看到此SDK的所有源代码都放在了PerceptionNeuronSDK目录下的src下(文件太多,打印此目录树时暂时移走了),对外的头文件直接放在PerceptionNeuronSDK目录下;

step 3: 加入依赖的第三方库或源码

与PerceptionNeuronSDK目录同级的是Eigen-3.2.2和InhouseLibs,即PerceptionNeuronSDK依赖的第三方库或源代码;

step 4: 创建用于编译PerceptionNeuronSDK的NDK配置文件

在PerceptionNeuronSDK目录下新建一个目录,这里命名为build_Android,同时在build_Android下新建jni目录、libs目录;

step 5: 为PerceptionNeuronSDK增加Application.mk及Android.mk配置文件

这里一定要小心,不能再Android.mk里通过脚本加载第三方的源码进去,而应该在PerceptionNeuronSDK的src目录中,在使用到诸如Eigen的地方通过#include引用!

附1:Application.mk文件内容

APP_ABI           := all
#APP_ABI          := armeabi armeabi-v7a x86

APP_OPTIM         := release
APP_PLATFORM      := android-8
#APP_BUILD_SCRIPT := Android.mk

# GNU STL implements most C++11 features. Use either gnustl_static or gnustl_shared
# Without this your C++ code will not be able to access headers like <thread>, <mutex>
#APP_STL      := stlport_static
#APP_CPPFLAGS := -std=c++11 -frtti -fexceptions 
APP_STL       := gnustl_static
APP_CPPFLAGS  := -std=gnu++11 -pthread -frtti -fexceptions -DNDEBUG #-NDEBUG -mfpu=neon -fomit-frame-pointer -DULM_BLOCKED -msse3 -mfpmath=sse

附2: Android.mk文件内容

# 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.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE   := PNLib

# 锟斤拷锟斤拷锟皆硷拷锟斤拷源锟侥硷拷目录锟斤拷源锟侥硷拷锟斤拷缀锟斤拷
MY_FILES_PATH  :=  $(LOCAL_PATH)/../../src

#$(warning $(MY_FILES_PATH))

MY_FILES_SUFFIX := %.cpp %.c

# 递归遍历目录下的所有的文件
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))

# 获取相应的源文件
MY_ALL_FILES := $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*.*) ) 
MY_ALL_FILES := $(MY_ALL_FILES:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)
MY_SRC_LIST  := $(filter $(MY_FILES_SUFFIX),$(MY_ALL_FILES)) 
MY_SRC_LIST  := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)

# 去除字串的重复单词
define uniq =
  $(eval seen :=)
  $(foreach _,$1,$(if $(filter $_,${seen}),,$(eval seen += $_)))
  ${seen}
endef

# 递归遍历获取所有目录
MY_ALL_DIRS := $(dir $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*/) ) )
MY_ALL_DIRS := $(call uniq,$(MY_ALL_DIRS))

# 赋值给NDK编译系统
LOCAL_SRC_FILES  := $(MY_SRC_LIST)
LOCAL_C_INCLUDES := $(MY_ALL_DIRS)

# Add additional include directories
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../
#LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../Eigen-3.2.2   
#必须从Android.mk配置文件中拿掉对Eigen的直接包含,放到程序代码中用相对路径包含:
# #include "../../Eigen-3.2.2/Eigen"
# using namespace Eigen;

#$(warning $(LOCAL_SRC_FILES))
#$(warning $(LOCAL_C_INCLUDES))

# use log system in NDK
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)

step 6: 为了方便使用,增加一个build.bat文件

在里面加入如下脚本:

ndk-build NDK_APPLICATION_MK=./Application.mk

pause

至此,所需文件及配置均已建立完毕,在windows中通过 ‘tree /f’ 指令可以看到其中的目录结构如下所示:

 

 

1

双击build.bat或通过命令行加载build.bat文件,即可编译出结果:

D:\PerceptionNeuronPrj\PerceptionNeuronSDK\build_Android\jni>build.bat

D:\PerceptionNeuronPrj\PerceptionNeuronSDK\build_Android\jni>ndk-build NDK_APPLI
CATION_MK=./Application.mk
[arm64-v8a] Compile++      : PNLib <= AntiJointCompensation.cpp
[arm64-v8a] Compile++      : PNLib <= BVHPlayerWrapper.cpp
[arm64-v8a] Compile++      : PNLib <= BoneMapping.cpp
[arm64-v8a] Compile++      : PNLib <= BoneMass.cpp
[arm64-v8a] Compile++      : PNLib <= BoneTable.cpp
[arm64-v8a] Compile++      : PNLib <= BvhBinaryOutputPacker.cpp
[arm64-v8a] Compile++      : PNLib <= BvhDataConvert.cpp
In file included from D:/PerceptionNeuronPrj/PerceptionNeuronSDK/build_Android/j
ni/../../src/./AvatarManagement/../PluginMngr/Interface/IPluginActionRecog.h:3:0
,
                 from D:/PerceptionNeuronPrj/PerceptionNeuronSDK/build_Android/j
ni/../../src/./AvatarManagement/Avatar.h:13,
                 from D:/PerceptionNeuronPrj/PerceptionNeuronSDK/build_Android/j
ni/../../src/BvhDataConvert.cpp:9:
D:/PerceptionNeuronPrj/PerceptionNeuronSDK/build_Android/jni/../../src/./AvatarM
anagement/../PluginMngr/Interface/IPluginObject.h:55:69: warning: 'visibility' a
ttribute ignored [-Wattributes]
         PNLIB_PLUGIN_EXPORT typedef IPluginObject* (*GetPluginFunc)();
....................
.....
.                                                                 ^

 

编译速度非常慢,看项目大小和编译的架构多少,十几分钟几十分钟不等。

编译完成后,so文件自动放到libs下的相应架构目录下,目录结构如下:

QQ截图20160311143749

 

QQ截图20160311143406

step 7: 清除及重新编译

由于ndk-build.bat编译完项目后会缓存所有中间文件(*.obj及其他中间文件),再次运行ndk-build.bat只是简单的从缓冲拷贝so文件,所以需要增加一个build clean,以便清除缓冲:

附3:build_clean.bat

#自动到NDK_PROJECT_PATH目录下找jni目录,利用其中的Android.mk清除上次编译的结果
ndk-build NDK_PROJECT_PATH=../ clean

pause