android系统移植

通过Android系统移植,让它在目标系统上运行起来。Android系统由于用的是linux内核,因此内核移植和嵌入式linux内核移植差异不大,过程如下:

(1)移植boot-loader和linux2.6内核到目标平台上,让linux内核可以启动起来,基本的驱动允许正常。
此过程完全是嵌入式linux的开发,这里直接跳过。需要注意的是,由于android已经被linux官方开除,因此从
网站上(如http://www.kernel.org/)下载的最新linux内核源代码已经不包含android的专有驱动,因此建议
从google网上下下载Linux内核,android源代码浏览网站如下:
http://android.git.kernel.org/
从该网站上发现内核相关的包如下:
kernel/common.git 通用android内核项目
kernel/experimental.git 实验性内核项目
kernel/linux-2.6.git 这个是标准的Linux内核,没有android的驱动
kernel/lk.git 微内核项目
kernel/msm.git 这个是高通msm7xxx系列芯片所用内核
kernel/omap.git
kernel/tegra.git NVIDIA Tegra系列芯片所用内核
下载内核代码的方法如下:
git clone git://android.git.kernel.org/kernel/common.git
下载完后用git branch -a查看所有git分支,结果如下:
  android-2.6.27
  origin/HEAD
  origin/android-2.6.25
  origin/android-2.6.27
  origin/android-2.6.29
  origin/android-2.6.32
  origin/android-2.6.35
  origin/android-2.6.36
  origin/android-goldfish-2.6.27
  origin/android-goldfish-2.6.29
然后切换到最新分支git checkout origin/android-2.6.36

(2)修改内核配置文件,打开Android必须的驱动(日志和BINDER)如下:
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_LOGGER=y
此部分的代码在内核drivers/staging/android目录下。

(3)为了提高启动速度,采用ramdisk,将android文件系统的部分内容压缩到内核中。
首先打开内核驱动:
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="root"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
然后在android源代码编译出来的out/target/product/merlin/root目录复制到内核目录下。

(4)根据android文件系统的要求对nand flash进行重新分区,举例如下:
将nand flash分区以下8个分区
NTIM
OBM
U-boot
Kernel
System
UserData
Mass Storage
BBT

(5)根据分区表修改内核启动参数如下:
CONFIG_CMDLINE="ubi.mtd=4 ubi.mtd=5 ubi.mtd=6 root=ubi0_0 rootfstype=ubifs console=ttyS1,115200 uart_dma init=/init"
参数的意思是:载入的文件系统部分有3个分区,分别为nand flash的第4,5,6分区(从0编号),文件系统采用ubifs格式,控制台设备为ttyS1,波特率为115200
启动的第一个应用程序是/init

(6)确保控制台的设置和硬件保持一致,如:硬件上串口用的是UART1,则内核启动参数中设置有console=ttyS1,而且android的启动过程中设要设置正确,修改
部分位于android源代码system/core/init/init.c文件中,将
static char *console_name = "/dev/console";
修改成
static char *console_name = "/dev/ttyS1";

(7)修改android源代码system/core/rootdir目录下的init.rc文件,作如下修改(android默认yaffs2文件系统):
首先将android文件系统修改成可读写,将
    mount rootfs rootfs / ro remount
修改成
    mount rootfs rootfs / rw remount
然后修改挂载system和userdata部分的代码,将
    # Mount /system rw first to give the filesystem a chance to save a checkpoint
    mount yaffs2 mtd@system /system
    mount yaffs2 mtd@system /system ro remount

    # We chown/chmod /data again so because mount is run as root + defaults
    mount yaffs2 mtd@userdata /data nosuid nodev
    chown system system /data
    chmod 0771 /data
改成
    # Mount /system rw first to give the filesystem a chance to save a checkpoint
    mount ubifs ubi0_0 /system ro

    # We chown/chmod /data again so because mount is run as root + defaults
    mount ubifs ubi1_0 /data nosuid nodev
    chown system system /data
    chmod 0771 /data

(8)完成后编译内核,可以启动文件系统,控制台可用,但是没有显示启动log,而且不停的重启。

(9)系统不停的重启,因此控制台已经可用了,自然而然的想到看到logcat日志,一看,发现logcat设备居然没起来,配置文件里面都定义了
居然没起来,查看了下内核drivers/staging/android目录,没有.o文件,证明是没编译到,在看内核目录下的.config文件,发现居然没有了
logcat和binder的宏定义,配置文件里面有定义而.config文件中无定义,肯定是相关Kconfig文件的问题,通过分析drivers/staging目录下的
Kconfig文件发现是因为STAGING_EXCLUDE_BUILD宏默认是y,在配置文件中否定此宏即可,在配置文件中CONFIG_STAGING定义后加上即可,如下:
CONFIG_STAGING=y
# CONFIG_STAGING_EXCLUDE_BUILD is not set
修改后重新编译发现系统完成正常启动,启动过程中启动log也显示正常。
至此,android初步移植工作已经完成,当然,系统还有很多问题,需要下一步继续修改。
总结:android的移植按如下流程:
(1)android linux内核的普通驱动移植,让内核可以在目标平台上运行起来。
(2)正确挂载文件系统,确保内核启动参数和android源代码system/core/rootdir目录下的init.rc中的文件系统挂载正确。
(3)调试控制台,让内核启动参数中的console参数以及android源代码system/core/init/init.c中的console_name设置和硬件保持一致
(4)打开android相关的驱动(logger,binder等),串口输入logcat看logger驱动起来,没有的话调试logger驱动。
说明:ARM的内核配置文件定义在内核arch/arm/configs目录下。

haolele 发表于 2011-11-11 21:03:34

Android系统移植之按键移植

本帖最后由 haolele 于 2011-11-11 21:04 编辑 

Android系统移植之按键移植这一部分主要是移植android的键盘和按键
(1)Android使用标准的linux输入事件设备(/dev/input目录下)和驱动,按键定义在内核include/linux/input.h文件中,
按键定义形式如下:
#define KEY_ESC            1
#define KEY_1            2
#define KEY_2            3

(2)内核中(我的平台是arch/arm/mach-mmp/merlin.c文件)中按键的定义如下形式:
static struct gpio_keys_button btn_button_table[] = {
    = {
        .code            =    KEY_F1,
        .gpio            =    MFP_PIN_GPIO2,
        .active_low        =    1,        /* 0 for down 0, up 1; 1 for down 1, up 0 */
        .desc            =    "H_BTN button",
        .type            =    EV_KEY,
        /* .wakeup            = */
        .debounce_interval    =    10,        /* 10 msec jitter elimination */
    },
    = {
        .code            =    KEY_F2,
        .gpio            =    MFP_PIN_GPIO3,
        .active_low        =    1,        /* 0 for down 0, up 1; 1 for down 1, up 0 */
        .desc            =    "O_BTN button",
        .type            =    EV_KEY,
        /* .wakeup            = */
        .debounce_interval    =    10,        /* 10 msec jitter elimination */
    },
    = {
        .code            =    KEY_F4,
        .gpio            =    MFP_PIN_GPIO1,
        .active_low        =    1,        /* 0 for down 0, up 1; 1 for down 1, up 0 */
        .desc            =    "S_BTN button",
        .type            =    EV_KEY,
        /* .wakeup            = */
        .debounce_interval    =    10,        /* 10 msec jitter elimination */
    },
};
static struct gpio_keys_platform_data gpio_keys_data = {
    .buttons  = btn_button_table,
    .nbuttons = ARRAY_SIZE(btn_button_table),
};

static struct platform_device gpio_keys = {
    .name = "gpio-keys",
    .dev  = {
        .platform_data = &gpio_keys_data,
    },
    .id   = -1,
};
上面定义是将MFP_PIN_GPIO2这个GPIO口的按键映射到Linux的KEY_F1按键,MPF_PIN_GPIO3映射到KEY_F2,MFP_PIN_GPIO1映射到KEY_F4

(3)上面(2)步实现了从硬件GPIO口到内核标准按键的映射,但是android并没有直接使用映射后的键值,而且对其再进行了一次映射,从内核标准键值
到android所用键值的映射表定义在android文件系统的/system/usr/keylayout目录下。标准的映射文件为qwerty.kl,定义如下:
key 399   GRAVE
key 2     1
key 3     2
key 4     3
key 5     4
key 6     5
key 7     6
key 8     7
key 9     8
key 10    9
key 11    0
key 158   BACK              WAKE_DROPPED
key 230   SOFT_RIGHT        WAKE
key 60    SOFT_RIGHT        WAKE
key 107   ENDCALL           WAKE_DROPPED
key 62    ENDCALL           WAKE_DROPPED
key 229   MENU              WAKE_DROPPED
key 139   MENU              WAKE_DROPPED
key 59    MENU              WAKE_DROPPED
key 127   SEARCH            WAKE_DROPPED
key 217   SEARCH            WAKE_DROPPED
key 228   POUND
key 227   STAR
key 231   CALL              WAKE_DROPPED
key 61    CALL              WAKE_DROPPED
key 232   DPAD_CENTER       WAKE_DROPPED
key 108   DPAD_DOWN         WAKE_DROPPED
key 103   DPAD_UP           WAKE_DROPPED
key 102   HOME              WAKE
key 105   DPAD_LEFT         WAKE_DROPPED
key 106   DPAD_RIGHT        WAKE_DROPPED
key 115   VOLUME_UP
key 114   VOLUME_DOWN
key 116   POWER             WAKE
key 212   CAMERA

key 16    Q
key 17    W
key 18    E
key 19    R
key 20    T
key 21    Y
key 22    U
key 23    I
key 24    O
key 25    P
key 26    LEFT_BRACKET
key 27    RIGHT_BRACKET
key 43    BACKSLASH

key 30    A
key 31    S
key 32    D
key 33    F
key 34    G
key 35    H
key 36    J
key 37    K
key 38    L
key 39    SEMICOLON
key 40    APOSTROPHE
key 14    DEL
        
key 44    Z
key 45    X
key 46    C
key 47    V
key 48    B
key 49    N
key 50    M
key 51    COMMA
key 52    PERIOD
key 53    SLASH
key 28    ENTER
        
key 56    ALT_LEFT
key 100   ALT_RIGHT
key 42    SHIFT_LEFT
key 54    SHIFT_RIGHT
key 15    TAB
key 57    SPACE
key 150   EXPLORER
key 155   ENVELOPE        

key 12    MINUS
key 13    EQUALS
key 215   AT

(4)android对底层按键的处理方法
android按键的处理是Window Manager负责,主要的映射转换实现在android源代码frameworks/base/libs/ui/EventHub.cpp
此文件处理来自底层的所有输入事件,并根据来源对事件进行分类处理,对于按键事件,处理过程如下:
(a)记录驱动名称为
(b)获取环境变量ANDROID_ROOT为系统路径(默认是/system,定义在android源代码/system/core/rootdir/init.rc文件中)
(c)查找路径为"系统路径/usr/keylayout/驱动名称.kl"的按键映射文件,如果不存在则默认用路径为"系统路径/usr/keylayout/qwerty.kl"
这个默认的按键映射文件,映射完成后再把经映射得到的android按键码值发给上层应用程序。
所以我们可以在内核中定义多个按键设备,然后为每个设备设定不同的按键映射文件,不定义则会默认用qwerty.kl

(5)举例
上面(2)步我们在内核中声明了一个名为"gpio-keys"的按键设备,此设备定义在内核drivers/input/keyboard/gpio_keys.c文件中
然后我们在内核启动过程中注册此设备:  platform_device_register(&gpio_keys);
然后我们可以自己定义一个名为gpio-keys.kl的android按键映射文件,此文件的定义可以参考querty.kl的内容,比如说我们想将MPF_PIN_GPIO3
对应的按键作android中的MENU键用,首先我们在内核中将MPF_PIN_GPIO3映射到KEY_F2,在内核include/linux/input.h中查找KEY_F2发现
#define KEY_F2            60
参照KEY_F2的值我们在gpio-keys.kl中加入如下映射即可
key 60    MENU              WAKE
其它按键也照此添加,完成后将按键表放置到/system/usr/keylayout目录下即可。
补充:
(1)android按键设备的映射关系可以在logcat开机日志中找的到(查找EventHub即可)
(2)android按键设备由Window Manager负责,Window Manager从按键驱动读取内核按键码,然后将内核按键码转换成android按键码,转换完成
后Window Manager会将内核按键码和android按键码一起发给应用程序来使用,这一点一定要注意。Android系统开发小知识-在android产品开 发中添加新的编译模块 Android开发中用户内容定义在vendor目录下,而用户产品的内容都定义在vendor/<company_name> /<board_name>目录下
如果需要添加新的内容,可以在该目录下新建子目录,同时修改AndroidBoard.mk文件即可。比如说要添加一个按键映射文件:
(1)在vendor/<company_name>/<board_name>目录下建立一个keymaps子目录
(2)将我们需要的按键映射文件gpio-keys.kl和power-button.kl复制到keymaps目录下
(3)在keymaps目录下新建一个Mdroid.mk文件,内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

file := $(TARGET_OUT_KEYLAYOUT)/gpio-keys.kl
ALL_PREBUILT += $(file)
$(file): $(LOCAL_PATH)/gpio-keys.kl | $(ACP)
    $(transform-prebuilt-to-target)

file := $(TARGET_OUT_KEYLAYOUT)/power-button.kl
ALL_PREBUILT += $(file)
$(file): $(LOCAL_PATH)/power-button.kl | $(ACP)
    $(transform-prebuilt-to-target)
(4)在vendor/<company_name>/<board_name>目录下的AndroidBoard.mk添加如下内容:
include $(LOCAL_PATH)/keymaps/Mdroid.mk

haolele 发表于 2011-11-11 21:04:44

Android系统移植之按键字符表

本帖最后由 haolele 于 2011-11-11 21:05 编辑 

Android系统移植之按键字符表

上节讲android的Window Manager将内核按键码通过按键映射表转换成android按键码,
这节讲的是android按键码向android字符的转换,转换也是通过Window Manager来完成的
(1)原始按键字符表,我们知道一个按键是可以显示多个字符的,决定显示字符的是CAPS(大小写),FN,NUNMBER等按键
举例如下:
                                           
                                                        
# keycode       display number  base    caps    fn      caps_fn
                                                        
A               'A'     '2'     'a'     'A'     '#'     0x00
B               'B'     '2'     'b'     'B'     '<'     0x00
C               'C'     '2'     'c'     'C'     '9'     0x00E7
D               'D'     '3'     'd'     'D'     '5'     0x00
E               'E'     '3'     'e'     'E'     '2'     0x0301
F               'F'     '3'     'f'     'F'     '6'     0x00A5
G               'G'     '4'     'g'     'G'     '-'     '_'
H               'H'     '4'     'h'     'H'     '['     '{'
I               'I'     '4'     'i'     'I'     '$'     0x0302
J               'J'     '5'     'j'     'J'     ']'     '}'
K               'K'     '5'     'k'     'K'     '"'     '~'
L               'L'     '5'     'l'     'L'     '''     '`'
M               'M'     '6'     'm'     'M'     '!'     0x00
N               'N'     '6'     'n'     'N'     '>'     0x0303
O               'O'     '6'     'o'     'O'     '('     0x00
P               'P'     '7'     'p'     'P'     ')'     0x00
Q               'Q'     '7'     'q'     'Q'     '*'     0x0300
R               'R'     '7'     'r'     'R'     '3'     0x20AC
S               'S'     '7'     's'     'S'     '4'     0x00DF
T               'T'     '8'     't'     'T'     '+'     0x00A3
U               'U'     '8'     'u'     'U'     '&'     0x0308
V               'V'     '8'     'v'     'V'     '='     '^'
W               'W'     '9'     'w'     'W'     '1'     0x00
X               'X'     '9'     'x'     'X'     '8'     0xEF00
Y               'Y'     '9'     'y'     'Y'     '%'     0x00A1
Z               'Z'     '9'     'z'     'Z'     '7'     0x00
                                                        
# on pc keyboards
COMMA           ','     ','     ','     ';'     ';'     '|'
PERIOD          '.'     '.'     '.'     ':'     ':'     0x2026
AT              '@'     '0'     '@'     '0'     '0'     0x2022
SLASH           '/'     '/'     '/'     '?'     '?'     '\'
                                                        
SPACE           0x20    0x20    0x20    0x20    0xEF01  0xEF01
ENTER         0xa     0xa     0xa     0xa     0xa     0xa
                                                        
TAB             0x9     0x9     0x9     0x9     0x9     0x9
0               '0'     '0'     '0'     ')'     ')'     ')'
1               '1'     '1'     '1'     '!'     '!'     '!'
2               '2'     '2'     '2'     '@'     '@'     '@'
3               '3'     '3'     '3'     '#'     '#'     '#'
4               '4'     '4'     '4'     '$'     '$'     '$'
5               '5'     '5'     '5'     '%'     '%'     '%'
6               '6'     '6'     '6'     '^'     '^'     '^'
7               '7'     '7'     '7'     '&'     '&'     '&'
8               '8'     '8'     '8'     '*'     '*'     '*'
9               '9'     '9'     '9'     '('     '('     '('
                                                        
GRAVE           '`'     '`'     '`'     '~'     '`'     '~'
MINUS           '-'     '-'     '-'     '_'     '-'     '_'
EQUALS          '='     '='     '='     '+'     '='     '+'
LEFT_BRACKET    '['     '['     '['     '{'     '['     '{'
RIGHT_BRACKET   ']'     ']'     ']'     '}'     ']'     '}'
BACKSLASH       '\'     '\'     '\'     '|'     '\'     '|'
SEMICOLON       ';'     ';'     ';'     ':'     ';'     ':'
APOSTROPHE      '''     '''     '''     '"'     '''     '"'
STAR            '*'     '*'     '*'     '*'     '*'     '*'
POUND           '#'     '#'     '#'     '#'     '#'     '#'
PLUS            '+'     '+'     '+'     '+'     '+'     '+'

(2)android为了减少载入时间,并没有使用原始按键表文件,而是将其转换成二进制文件
转换的工具源代码在android源代码build/tools/kcm目录下,android在编译过程中会
首先编译转换工具,然后利用转换工具将android源代码sdk/emulator/keymaps目录下
的qwerty.kcm和qwerty2.kcm文件分别转换成qwerty.kcm.bin和qwerty2.kcm.bin
转换后的二进制文件复制到out/target/product/<board_name>/system/usr/keychars
目录下,也就是目标平台的/system/usr/keychars目录中。

(3)Window Manager对按键的处理在android源代码frameworks/base/libs/ui/EventHub.cpp文件中
Window Manager从内核接收到一个按键输入事件后会首先调用按键映射表将内核按键码映射成android按键码(这部分上节已讲),然后会
将android按键码转换成字符,具体过程如下:
(a)设置系统系统属性hw.keyboards.设备号.devname的值为设备名
以上节的gpio-keys设备为例,会设置系统属性hw.keyboards.65539.devname的值为gpio-keys
(b)载入按键字符表,首先载入/system/usr/keychars目录下的设备名.kcm.bin文件(此例即gpio-keys.kcm.bin文件),如果载入失败
则载入该目录下的querty.kcm.bin.
(c)利用载入的按键字符表将android按键转换成按键字符发给上层应用程序。

(4)一般情况下一个控制按键是不需要作按键字符表的,系统会调用默认的去处理,但是如果要开发一个全功能键盘(包含了字母和数字),那可能就需要
自己作一个专用的按键字符表了。android系统开发小问题-启动过程中android字符没有显示出来 android目标平台可以正常启动,但是启动过程中的android字符没有显示出来,这个是linux内核配置的问题
打开内核framebuffer控制台即可。
(1)make menuconifg后选择Device Drivers->Graphics support->Console display driver support->Framebuffer Console support
然后打开相关的几个配置选项即可。
(2)直接修改内核配置文件,如下:
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y
CONFIG_FONT_6x11=y
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_SUN8x16 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_10x18 is not set
(3)android启动过程中的android字符显示在源代码的system/core/init.c中,如下:
    if( load_565rle_image(INIT_IMAGE_FILE) ) {
    fd = open("/dev/tty0", O_WRONLY);
    if (fd >= 0) {
        const char *msg;
            msg = "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"  // console is 40 cols x 30 lines
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "\n"
        "             A N D R O I D ";
        write(fd, msg, strlen(msg));
        close(fd);
    }
    } 

haolele 发表于 2011-11-11 21:06:22

Android系统开发之触摸屏tslib移植(内核)和原理分析

本帖最后由 haolele 于 2011-11-11 21:07 编辑 

Android系统开发之触摸屏tslib移植(内核)和原理分析

首先了解一下tslib的运行原理,tslib的运行分成两部分
(1)校验
在LCD固定坐标位置依次显示出5个坐标让用户触摸,把LCD坐标和用户触摸时驱动屏驱动底层的坐标总共5组值保存起来
运行tslib库的算法对其进行运算,得出校准用7个值

(2)校准
每次触摸屏驱动读取到硬件坐标时应用校准用的7个值对该坐标进行一次运算,然后将运算后的坐标作为正常坐标即可。
按照上面的原理,
(1)我们先修改内核部分,我的平台用的触摸屏幕驱动是tsc2007,驱动文件为内核/drivers/input/touchscreen
目录下的tsc2007.c和ts_linear.c
其中,ts_linear.c中定义的是校准模块,该模块在proc文件系统中建立了7个文件,用来存放校准用的7个点,7的点的默认值
为1,0,0,0,1,0,1,对应的目标平台文件系统的位置为/proc/sys/dev/ts_device目录下a0,a1,a2,a3,a4,a5,a6等7个文件
此模块中还定义了一个校准函数ts_linear_scale,此函数的主要内容是读取a0,a1,a2,a3,a4,a5,a6等7个文件中的值作为7个
校准值与传入的触摸平坐标值进行运算,返回运算结果。
ts_linear_scale函数定义如下:
int ts_linear_scale(int *x, int *y, int swap_xy)
{
    int xtemp, ytemp;

    xtemp = *x;
    ytemp = *y;

    if (cal.a == 0)
        return -EINVAL;

    *x = (cal.a + cal.a * xtemp + cal.a * ytemp) / cal.a;
    *y = (cal.a + cal.a * xtemp + cal.a * ytemp) / cal.a;

    if (swap_xy) {
        int tmp = *x;
        *x = *y;
        *y = tmp;
    }
    return 0;
}ts2007.c为触摸屏驱,与其他驱动不同的地方是在取得硬件坐标值发送之前先调用了ts_linear_scale函数对坐标值进行了校准
            if (x > 0 && y > 0)
            {
                ts_linear_scale(&x, &y, invert);
                input_report_abs(input, ABS_X, x);
                input_report_abs(input, ABS_Y, y);
                input_report_abs(input, ABS_PRESSURE, 255);
                input_report_abs(input, ABS_TOOL_WIDTH, 1);
                input_report_key(input, BTN_TOUCH, 1);
                input_sync(input);
            }

(2)在android源代码/system/core/rootdir/init.rc文件中添加tslib相关的宏定义如下:
# touchscreen parameters
    export TSLIB_FBDEVICE /dev/graphics/fb0
    export TSLIB_CALIBFILE /data/etc/pointercal
    export TSLIB_CONFFILE  /system/etc/ts.conf
    export TSLIB_TRIGGERDEV /dev/input/event0
    export TSLIB_TSDEVICE /dev/input/event1

(2)移植tslib库到android系统,比较麻烦,看下一节的内容。

(3)校验程序完成后会将生成的7个校准值写入到环境变量TSLIB_CALIBFILE对应的路径/data/etc/pointercal文件中

(4)校验完后将pointercal文件中的7个值分别写入到/proc/sys/dev/ts_device目录下a0,a1,a2,a3,a4,a5,a6文件即可。

(5)开机启动的时候我们编写一个应用程序,首先判断环境变量TSLIB_CALIBFILE对应的路径/data/etc/pointercal文件是否存在,如果
文件存在而且非空,则将该文件中的7个值取出来分别写入到/proc/sys/dev/ts_device目录下a0,a1,a2,a3,a4,a5,a6文件

(6)为了确保未校验前触摸屏可用,我们将一次校验后得出的7个坐标值作为初始值,修改到内核ts_linear.c文件中。下面是源代码:
ts_linear.c文件
/*
*  Touchscreen Linear Scale Adaptor
*
*  Copyright (C) 2009 Marvell Corporation

*  Author: Mark F. Brown <[email protected]>
*  Based on tslib 1.0 plugin linear.c by Russel King
*
* This library is licensed under GPL.
*
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/sysctl.h>
#include <asm/system.h>

/*
* sysctl-tuning infrastructure.
*/
static struct ts_calibration {
/* Linear scaling and offset parameters for x,y (can include rotation) */
    int a;
} cal;

static ctl_table ts_proc_calibration_table[] = {
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a0",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a1",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a2",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a3",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a4",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a5",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "a6",
     .data = &cal.a,
     .maxlen = sizeof(int),
     .mode = 0666,
     .proc_handler = &proc_dointvec,
     },

    {.ctl_name = 0}
};

static ctl_table ts_proc_root[] = {
    {
     .ctl_name = CTL_UNNUMBERED,
     .procname = "ts_device",
     .mode = 0555,
     .child = ts_proc_calibration_table,
     },
    {.ctl_name = 0}
};

static ctl_table ts_dev_root[] = {
    {
     .ctl_name = CTL_DEV,
     .procname = "dev",
     .mode = 0555,
     .child = ts_proc_root,
     },
    {.ctl_name = 0}
};

static struct ctl_table_header *ts_sysctl_header;

int ts_linear_scale(int *x, int *y, int swap_xy)
{
    int xtemp, ytemp;

    xtemp = *x;
    ytemp = *y;

    if (cal.a == 0)
        return -EINVAL;

    *x = (cal.a + cal.a * xtemp + cal.a * ytemp) / cal.a;
    *y = (cal.a + cal.a * xtemp + cal.a * ytemp) / cal.a;

    if (swap_xy) {
        int tmp = *x;
        *x = *y;
        *y = tmp;
    }
    return 0;
}

EXPORT_SYMBOL(ts_linear_scale);

static int __init ts_linear_init(void)
{
    ts_sysctl_header = register_sysctl_table(ts_dev_root);
    /* Use default values that leave ts numbers unchanged after transform */
    cal.a = 1;
    cal.a = 0;
    cal.a = 0;
    cal.a = 0;
    cal.a = 1;
    cal.a = 0;
    cal.a = 1;
    return 0;
}

static void __exit ts_linear_cleanup(void)
{
    unregister_sysctl_table(ts_sysctl_header);
}

module_init(ts_linear_init);
module_exit(ts_linear_cleanup);

MODULE_DESCRIPTION("touch screen linear scaling driver");
MODULE_LICENSE("GPL");



ts2007.c文件
/*
*  linux/drivers/input/touchscreen/tsc2007.c
*
*  touch screen driver for tsc2007
*
*  Copyright (C) 2006, Marvell Corporation
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License version 2 as
*  published by the Free Software Foundation.
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/freezer.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <mach/gpio.h>

#include <linux/sysctl.h>
#include <asm/system.h>

extern int ts_linear_scale(int *x, int *y, int swap_xy);

/* Use MAV filter */
#define TSC_CMD_SETUP 0xb0

/* Use 12-bit */
#define TSC_CMD_X 0xc0
#define TSC_CMD_PLATEX 0x80
#define TSC_CMD_Y 0xd0
#define TSC_CMD_PLATEY 0x90

#define TSC_X_MAX 4096
#define TSC_Y_MAX 4096
#define TSC_X_MIN 0
#define TSC_Y_MIN 0

/* delay time for compute x, y, computed as us */

#define DEBUG
#ifdef DEBUG
#define TS_DEBUG(fmt,args...) printk(KERN_DEBUG fmt, ##args )
#else
#define TS_DEBUG(fmt,args...)
#endif
static int x_min=TSC_X_MIN;
static int y_min=TSC_Y_MIN;
static int x_max=TSC_X_MAX;
static int y_max=TSC_Y_MAX;
static int invert = 0;
static int debounce_time  = 150;
static int init_debounce = true;
static int delay_time = 1;

enum tsc2007_status {
    PEN_UP,
    PEN_DOWN,
};

struct _tsc2007 {
    struct input_dev *dev;
    int x;        /* X sample values */
    int y;        /* Y sample values */

    int status;
    struct work_struct irq_work;
    struct i2c_client *client;
    unsigned long last_touch;
};
struct _tsc2007 *g_tsc2007;

/* update abs params when min and max coordinate values are set */
int tsc2007_proc_minmax(struct ctl_table *table, int write, struct file *filp,
                     void __user *buffer, size_t *lenp, loff_t *ppos)
{
    struct _tsc2007 *tsc2007= g_tsc2007;
    struct input_dev *input = tsc2007->dev;

    /* update value */
    int ret = proc_dointvec(table, write, filp, buffer, lenp, ppos);

    /* updated abs params */
    if (input) {
        TS_DEBUG(KERN_DEBUG "update x_min %d x_max %d"
            " y_min %d y_max %d\n", x_min, x_max,
            y_min, y_max); 
        input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0);
        input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0);
    }
    return ret;
}

static ctl_table tsc2007_proc_table[] = {
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "x-max",
        .data        = &x_max,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &tsc2007_proc_minmax,
    },
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "y-max",
        .data        = &y_max,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &tsc2007_proc_minmax,
    },
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "x-min",
        .data        = &x_min,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &tsc2007_proc_minmax,
    },
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "y-min",
        .data        = &y_min,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &tsc2007_proc_minmax,
    },
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "invert_xy",
        .data        = &invert,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &proc_dointvec,
    },
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "debounce_time",
        .data        = &debounce_time,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &proc_dointvec,
    },
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "delay_time",
        .data        = &delay_time,
        .maxlen        = sizeof(int),
        .mode        = 0666,
        .proc_handler    = &proc_dointvec,
    },
    { .ctl_name = 0 }
};

static ctl_table tsc2007_proc_root[] = {
    {
        .ctl_name    = CTL_UNNUMBERED,
        .procname    = "ts_device",
        .mode        = 0555,
        .child        = tsc2007_proc_table,
    },
    { .ctl_name = 0 }
};

static ctl_table tsc2007_proc_dev_root[] = {
    {
        .ctl_name    = CTL_DEV,
        .procname    = "dev",
        .mode        = 0555,
        .child        = tsc2007_proc_root,
    },
    { .ctl_name = 0 }
};

static struct ctl_table_header *sysctl_header;

static int __init init_sysctl(void)
{
    sysctl_header = register_sysctl_table(tsc2007_proc_dev_root);
    return 0;
}

static void __exit cleanup_sysctl(void)
{
    unregister_sysctl_table(sysctl_header);
}

static int tsc2007_measure(struct i2c_client *client, int *x, int * y)
{
    u8 x_buf = {0, 0};
    u8 y_buf = {0, 0};

    i2c_smbus_write_byte(client, TSC_CMD_PLATEX);
    msleep_interruptible(delay_time);

    i2c_smbus_write_byte(client, TSC_CMD_X);
    i2c_master_recv(client, x_buf, 2);
    *x = (x_buf<<4) | (x_buf >>4);

    i2c_smbus_write_byte(client, TSC_CMD_PLATEY);
    msleep_interruptible(delay_time);

    i2c_smbus_write_byte(client, TSC_CMD_Y);
    i2c_master_recv(client, y_buf, 2);
    *y = (y_buf<<4) | (y_buf >>4);
    *y = 4096 - *y; //added by allen
    printk("\ntouchscreen x = 0x%x, y = 0x%x\n",*x,*y);
    return 0;
}

static void tsc2007_irq_work(struct work_struct *work)
{
    struct _tsc2007 *tsc2007= g_tsc2007;
    struct i2c_client *client = tsc2007-> client;
    struct input_dev *input = tsc2007->dev;

    int x = -1, y = -1, is_valid = 0;
    int tmp_x = 0, tmp_y = 0;

    int gpio = irq_to_gpio(client->irq);


    /* Ignore if PEN_DOWN */
    if(PEN_UP == tsc2007->status){

        if (gpio_request(gpio, "tsc2007 touch detect")) {
            printk(KERN_ERR "Request GPIO failed, gpio: %X\n", gpio);
            return;
        }
        gpio_direction_input(gpio);    
        
        while(0 == gpio_get_value(gpio)){

                        if ((jiffies_to_msecs(
                                ((long)jiffies - (long)tsc2007->last_touch)) <
                 debounce_time &&
                tsc2007->status == PEN_DOWN) ||
                init_debounce)
                        {
                init_debounce = false;
                                tsc2007_measure(client, &tmp_x, &tmp_y);
                                TS_DEBUG(KERN_DEBUG
                "dropping pen touch %lu %lu (%u)\n",
                                jiffies, tsc2007->last_touch,
                                jiffies_to_msecs(
                (long)jiffies - (long)tsc2007->last_touch));
                                schedule();
                continue;
                        }


            /* continue report x, y */
            if (x > 0 && y > 0)
            {
                ts_linear_scale(&x, &y, invert);
                input_report_abs(input, ABS_X, x);
                input_report_abs(input, ABS_Y, y);
                input_report_abs(input, ABS_PRESSURE, 255);
                input_report_abs(input, ABS_TOOL_WIDTH, 1);
                input_report_key(input, BTN_TOUCH, 1);
                input_sync(input);
            }

            tsc2007->status = PEN_DOWN;
            tsc2007_measure(client, &x, &y);
            TS_DEBUG(KERN_DEBUG "pen down x=%d y=%d!\n", x, y);
            is_valid = 1;
            schedule();
        }

        if (is_valid)
        {
            /*consider PEN_UP */
            tsc2007->status = PEN_UP;
            input_report_abs(input, ABS_PRESSURE, 0);
            input_report_abs(input, ABS_TOOL_WIDTH, 1);
            input_report_key(input, BTN_TOUCH, 0);
            input_sync(input);
            tsc2007->last_touch = jiffies;
            TS_DEBUG(KERN_DEBUG "pen up!\n"); 
        }

        gpio_free(gpio);    
    }
}

static irqreturn_t tsc2007_interrupt(int irq, void *dev_id)
{    
    schedule_work(&g_tsc2007->irq_work);
    
    return IRQ_HANDLED;
}

static int __devinit tsc2007_probe(struct i2c_client *client, 
                const struct i2c_device_id *id)
{
    struct _tsc2007 *tsc2007;
    struct input_dev *input_dev;
    int ret;

    tsc2007 = kzalloc(sizeof(struct _tsc2007), GFP_KERNEL);
    input_dev = input_allocate_device();

    g_tsc2007 = tsc2007;

    if (!tsc2007 || !input_dev) {
        ret = -ENOMEM;
        goto fail1;
    }

    i2c_set_clientdata(client, tsc2007);

    tsc2007->dev = input_dev;

    input_dev->name = "tsc2007";
    input_dev->phys = "tsc2007/input0";

    //input_dev->id.bustype = BUS_HOST;
    input_dev->dev.parent = &client->dev;

    __set_bit(EV_KEY, input_dev->evbit);
    __set_bit(BTN_TOUCH, input_dev->keybit);

    __set_bit(EV_ABS, input_dev->evbit);
    __set_bit(ABS_PRESSURE, input_dev->evbit);
    __set_bit(ABS_X, input_dev->evbit);
    __set_bit(ABS_Y, input_dev->evbit);

    input_set_abs_params(input_dev, ABS_X, x_min, x_max, 0, 0);
    input_set_abs_params(input_dev, ABS_Y, y_min, y_max, 0, 0);
    input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);

    ret = request_irq(client->irq, tsc2007_interrupt, 
        IRQF_DISABLED | IRQF_TRIGGER_FALLING,
         "tsc2007 irq", NULL);
    if (ret){
        printk(KERN_ERR "tsc2007 request irq failed\n");
        goto fail2;
    }

    ret = input_register_device(tsc2007->dev);
    if (ret){
        printk(KERN_ERR "tsc2007 register device fail\n");
        goto fail2;
    }

    /*init */
    tsc2007->status = PEN_UP;
    tsc2007->client = client;
    tsc2007->last_touch = jiffies;

    INIT_WORK(&tsc2007->irq_work, tsc2007_irq_work);

    /* init tsc2007 */
    i2c_smbus_write_byte(client, TSC_CMD_SETUP);

    return 0;

fail2:
    free_irq(client->irq, client);
fail1:
    i2c_set_clientdata(client, NULL);
    input_free_device(input_dev);
    kfree(tsc2007);
    return ret;
}

static int __devexit tsc2007_remove(struct i2c_client *client)
{
    struct _tsc2007 *tsc2007 = i2c_get_clientdata(client);

    if(client->irq)
        free_irq(client->irq, client);
    
    i2c_set_clientdata(client, NULL);
    input_unregister_device(tsc2007->dev);
    kfree(tsc2007);

    return 0;
}

static struct i2c_device_id tsc2007_idtable[] = { 
    { "tsc2007", 0 }, 
    { } 
}; 

MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);

static struct i2c_driver tsc2007_driver = {
    .driver = {
        .name     = "tsc2007",
    },
    .id_table       = tsc2007_idtable,
    .probe        = tsc2007_probe,
    .remove        = __devexit_p(tsc2007_remove),
};

static int __init tsc2007_ts_init(void)
{
    init_sysctl();
    return i2c_add_driver(&tsc2007_driver);     
}

static void __exit tsc2007_ts_exit(void)
{
    cleanup_sysctl();
    i2c_del_driver(&tsc2007_driver);
}

module_init(tsc2007_ts_init);
module_exit(tsc2007_ts_exit);

MODULE_DESCRIPTION("tsc2007 touch screen driver");
MODULE_LICENSE("GPL");

haolele 发表于 2011-11-11 21:07:48

Android系统开发之tslib移植

本帖最后由 haolele 于 2011-11-11 21:08 编辑 

Android系统开发之tslib移植

(1)切换至tslib目录然后执行如下命令(以marvell平台为例)
./autogen.sh
echo "ac_cv_func_malloc_0_nonnull=yes" > arm-marvell-linux.cache
./configure --host=arm-marvell-linux-gnueabi --prefix=/work/svn/ts_build --cache-file=arm-marvell-linux.cache
上面三步仅仅是为了取得tslib目录下的config.h文件

(2)将tslib复制到android源代码vendor/<company_name>/<board_name>目录下

(3)修改vendor/<company_name>/<board_name>目录下的AndroidBoard.mk文件,加入如下内容
include $(LOCAL_PATH)/tslib/Mdroid.mk
一定要主义LOCAL_PATH这个宏的时效性

(4)在tslib目录下创建Mdroid.mk,内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

TS_PATH := $(LOCAL_PATH)

include $(TS_PATH)/src/Mdroid.mk
include $(TS_PATH)/plugins/Mdroid.mk
include $(TS_PATH)/tests/Mdroid.mk

include $(CLEAR_VARS)
file := $(TARGET_OUT_ETC)/ts.conf
$(file) : $(TS_PATH)/etc/ts.conf | $(ACP)
    $(transform-prebuilt-to-target)
ALL_PREBUILT += $(file)


(5)在tslib/src目录下创建Mdroid.mk,内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= ts_attach.c ts_close.c ts_config.c \
    ts_error.c ts_fd.c ts_load_module.c ts_open.c ts_parse_vars.c \
    ts_read.c ts_read_raw.c ts_option.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../

LOCAL_SHARED_LIBRARIES += libutils libcutils

LOCAL_SHARED_LIBRARIES += libdl
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libts

include $(BUILD_SHARED_LIBRARY)


(6)在tslib/plugins目录下创建Mdroid.mk,内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= input-raw.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../src

LOCAL_SHARED_LIBRARIES := libts
LOCAL_MODULE := input
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_SRC_FILES:= pthres.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../src

LOCAL_SHARED_LIBRARIES := libts
LOCAL_MODULE := pthres
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_SRC_FILES:= variance.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../src

LOCAL_SHARED_LIBRARIES := libts
LOCAL_MODULE := variance
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_SRC_FILES:= dejitter.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../src

LOCAL_SHARED_LIBRARIES := libts
LOCAL_MODULE := dejitter
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_SRC_FILES:= linear.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../src

LOCAL_SHARED_LIBRARIES := libts
LOCAL_MODULE := linear
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)


(7)在tslib/tests目录下创建Mdroid.mk,内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= ts_calibrate.c fbutils.c testutils.c font_8x8.c font_8x16.c

LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../src

LOCAL_SHARED_LIBRARIES := libts

LOCAL_SHARED_LIBRARIES += libutils libcutils

LOCAL_MODULE := tscalibrate

include $(BUILD_EXECUTABLE)


(8)在tslib/config.h文件中加入如下定义:
#define TS_CONF  "/system/etc/ts.conf"
#define PLUGIN_DIR "/system/lib"
#define TS_POINTERCAL "/data/etc/pointercal"


(9)将下面路径文件
tslib/src/ts_open.c
tslib/tests/ts_calibrate.c
tslib/tests/fbutils.c
中的
#include <sys/fcntl.h>
修改成
#include <fcntl.h>

(10)将tslib/tests/ts_calibrate.c文件中
static int clearbuf(struct tsdev *ts)
修改为
static void clearbuf(struct tsdev *ts)

(11)修改tslib/etc/ts.conf内容如下:
module_raw input
module pthres pmin=1
module variance delta=30
module dejitter delta=100
module linear

(12)在android源代码init.rc中声明tslib相关的宏如下:
# touchscreen parameters
    export TSLIB_FBDEVICE /dev/graphics/fb0
    export TSLIB_CALIBFILE /data/etc/pointercal
    export TSLIB_CONFFILE  /system/etc/ts.conf
    export TSLIB_TRIGGERDEV /dev/input/event0
    export TSLIB_TSDEVICE /dev/input/event1

(13)重新编译后即可调用tscalibrate命令来校验触摸屏,校验后产生一个/data/etc/pointercal文件。

你可能感兴趣的:(android,struct,Module,input,Path,linux内核)