Android jogball 驱动分析


1 简介
JogBall是首款Android手机-HTC Dream上附带的类似轨迹球Trackball的滑轮,通过Jogball用户可以轻松的实现网页的翻页、地图翻转等操作,这种在手机上增加轨迹球的方式可以取消传统的4维导航键,Jogball并非HTC公司的首例,早在HTC 3300(Dopod P800)就实现了这项功能,轨迹球操作方式的加入可以实现像电脑上鼠标一样轻松操控。现在jogball基本上算是android手机的标准配置了。

2 框图&架构

2.1 gpio_event子系统
Jogball驱动是由linux代码中的gpio_event子系统实现的。
由以下文件组成:
driver/input/misc/gpio_event.c
driver/input/misc/gpio_input.c
driver/input/misc/gpio_output.c
driver/input/misc/gpio_matrix.c
driver/input/misc/gpio_axis.c
gpio_event子系统是一个关于gpio的输入设备的通用代码,里面可以包含一般嵌入式设备中需要的键盘阵列和坐标有关的输入等。

2.2 Jogball架构
Jogball是归类于gpio_axis.c中的,是一个相对坐标系,判断电信号的高低来对坐标加或者减一定数值。具体细节在gpio_axis.c中
gpio_axis.c中的结构体初始化是在xxx-borad.c中的,基本架构就是这样,细节在下文叙述。

3 硬件描述

3.1 Jogball机械特性
Jogball是一个基于滚球滚动的输入设备,所以它机械特性也是比较重要,知道他的机械特性,便于我们理解驱动。
Jogball和以前的滚球鼠标比较相似,依靠滚轮的滚动来带动四周的圆柱体旋转。每个圆柱体端头都有一个磁铁,这个磁体铁从横断面来看就是由N极的扇形和S极的扇形来组成一个圆周。这样当磁铁旋转的时候,N极和S极切换,使霍尔IC产生高低电平。
注意:在滚球旋转时,两个相对方向只有一个方向可以转动,这是为了减化软件编写的一个机械特性。

3.2 Jogball的电气特性
Jogball最重要的电特性就是这个霍尔IC,
霍尔元件的工作原理:所谓霍尔效应,是指磁场作用于载流金属导体、半导体中的载流子时,产生横向电位差的物理现象。金属的霍尔效应是1879年被美 国物理学家霍尔发现的。当电流通过金属箔片时,若在垂直于电流的方向施加磁场,则金属箔片两侧面会出现横向电位差。半导体中的霍尔效应比金属箔片中更为明 显,而铁磁金属在居里温度以下将呈现极强的霍尔效应。
所以jogball是旋转磁轴引起磁场的变幻,来产生高低电压的。从而根据机械特性就不难理解jogball的数字输入过程。

3.3 Jogball的原理图
其实jogball只是机械结构,原理图是hall ic的,有兴趣的人在这里下载

4 数据结构
struct hero_axis_info {
        struct gpio_event_axis_info info; /*gpio_event_axis结构的信息*/
        uint16_t in_state;/*记录输入的高低电平信号*/
        uint16_t out_state;/*记录输出的高低电平信号*/
        uint16_t temp_state;/*临时存储高低电平*/
        uint16_t threshold;/*门限值,超过这个值上报给input子系统*/
};

struct gpio_event_axis_info {
        /* initialize to gpio_event_axis_func */
        struct gpio_event_info info; /*gpio_event信息*/
        uint8_t  count; /*gpio的数量*/
        uint8_t  type; /* EV_REL or EV_ABS */
        uint16_t code; /*输入键值类型*/
        uint16_t decoded_size; /*gpio 数组的大小*/
        uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); /*解析回调函数*/
        uint32_t *gpio; /*gpio端口数组*/
        uint32_t flags; /*调试标值*/
        uint32_t enable_emc_protect_delay; /*保护间隔时间*/
        uint16_t emc_gpio_state; /* 延时中用到gpio_state*/
        atomic_t emc_disable_irqnum;/*延时中用到的屏蔽的中断号*/
};

struct gpio_event_info {
        int (*func)(struct input_dev *input_dev,
        struct gpio_event_info *info,
        void **data, int func);
        /*给gpio_event子系统中实现注册到内核中的回调函数*/
        int (*event)(struct input_dev *input_dev,
        struct gpio_event_info *info,
        void **data, unsigned int type,
        unsigned int code, int value); /* out events */
};

struct gpio_event_platform_data {
        const char *name;/*名字*/
        struct gpio_event_info **info;/*gpio_event的结构指针*/
        size_t info_count;/*注册到axis中的info 结构体的数量*/
        int (*power)(const struct gpio_event_platform_data *pdata, bool on);/*电源管理回调函数*/
};

5 代码分析

5.1 通用代码
driver/input/misc/gpio_event.c
/*gpio_event基本数据结构*/
struct gpio_event {
        struct input_dev *input_dev;
        const struct gpio_event_platform_data *info;
        struct early_suspend early_suspend;
        void *state[0];
};

/*注册给input的回调函数*/
static int gpio_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

/*调用下层给的回调函数来实现具体的注册*/
static int gpio_event_call_all_func(struct gpio_event *ip, int func)
/*电源管理*/
#ifdef CONFIG_HAS_EARLYSUSPEND
void gpio_event_suspend(struct early_suspend *h)

void gpio_event_resume(struct early_suspend *h)
#endif
/*注册*/
static int __init gpio_event_probe(struct platform_device *pdev)
/*注销*/
static int gpio_event_remove(struct platform_device *pdev)

static struct platform_driver gpio_event_driver
static int __devinit gpio_event_init(void)

static void __exit gpio_event_exit(void)
gpio_event.c:主要是gpio_event子系统的框架

5.2 soc代码
driver/input/misc/gpio_axis.c
struct gpio_axis_state {
struct input_dev *input_dev;/*input设备结构*/
struct gpio_event_axis_info *info; /*坐标系数据结构*/
uint32_t pos; /*位置*/
struct hrtimer emc_hrtimer_delay; /* 延时时间*/
atomic_t atomic_emc_hrtimer_is_run; /*延时的原子变量*/
};
/*上报input数据*/
static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
/*时间处理函数*/
static enum hrtimer_restart emc_progress_hrtimer_handler_func(struct hrtimer *timer)
/*中断处理函数*/
static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
/*给上层的注册回调函数*/
int gpio_event_axis_func(struct input_dev *input_dev, struct gpio_event_info *info, void **data, int func)

5.3 platform代码
例如:/arch/arm/match-msm/board-72×7.c
uint16_t hero_axis_map(struct gpio_event_axis_info *info, uint16_t in)
{
        struct hero_axis_info *ai = container_of(info, struct hero_axis_info, info);
        uint16_t out = ai->out_state;
 
if (nav_just_on) {
        if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1)
                goto ignore;
        nav_just_on = 0;
}
 
if((ai->in_state ^ in) & 1) /*检测在方向1上有没有数值变化*/
        out--;
if((ai->n_state ^ in) & 2) /*检测在方向1相对方向上有没有数值变化*/
        out++;
ai->;out_state = out;
ignore:
        ai->in_state = in;
if (ai->out_state - ai->temp_state == ai->threshold) {
        ai->temp_state++;
        ai->out_state = ai->temp_state;
} else if (ai->temp_state - ai-out_state == ai->threshold) {
        ai->temp_state--;
        ai->out_state = ai->temp_state;
} else if (abs(ai->out_state - ai->;temp_state) > ai->threshold)
        ai->temp_state = ai->out_state;
 
return ai->temp_state;
}
以上函数,是最终的数据解析函数,也就是把硬件中读出来的数据,转化为实际的位置变化的函数。此函数主要做了差错控制,增加
了程序的鲁棒性和可订制性。

static struct hero_axis_info hero_x_axis = {
        .threshold = 1,
                .info = {
                .info.func = gpio_event_axis_func,
                .count = ARRAY_SIZE(hero_x_axis_gpios),
                .type = EV_REL,
                .code = REL_X,
                .decoded_size = 1U << ARRAY_SIZE(hero_x_axis_gpios),
                .map = hero_axis_map,
                .gpio = hero_x_axis_gpios,
                .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */,
                .enable_emc_protect_delay = 1 * NSEC_PER_MSEC,
        }
};
X轴上的info结构体的初始化

static struct hero_axis_info hero_y_axis = {
        .threshold = 1,
                .info = {
                .info.func = gpio_event_axis_func,
                .count = ARRAY_SIZE(hero_y_axis_gpios),
                .type = EV_REL,
                .code = REL_Y,
                .decoded_size = 1U << ARRAY_SIZE(hero_y_axis_gpios),
                .map = hero_axis_map,
                .gpio = hero_y_axis_gpios,
                .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT  */,
                .enable_emc_protect_delay = 1 * NSEC_PER_MSEC,
        }
};
Y轴上的info结构体的初始化

6 备注
在android的framework的代码中识别jogball设备是识别成鼠标的特性的,不仅有坐标还是由按键的,但是驱动中并没有axis中并没
有按键,所以在注册input设备的时候要加入按键的键值。如下代码:
在drivers/input/misc/gpio_axis.c中的gpio_event_axis_func函数中
as->input_dev = input_dev;
as->info = ai;

input_set_capability(input_dev, ai->type, ai->;code);
if (ai->type == EV_ABS) {
        input_set_abs_params(input_dev, ai->code, 0,
        ai->decoded_size - 1, 0, 0);
}
input_set_capability(input_dev, ai->type, BT_MOUSE);
本文源码是基于hero手机kernel代码
kernel_hero/arch/arm/mach-msm/board-hero.c
下载地址 http://developer.htc.com/

相关内容