A. 【Camera专题】Camera驱动源码全解析_下
1、手把手撸一份驱动 到 点亮 Camera
2、Camera dtsi 完全解析
3、Camera驱动源码全解析上
4、Camera驱动源码全解析下
上篇文章分析了C文件函数的实现,本文继续分析h文件的配置信息。
推荐文章:
MIPI CSI2学习(一):说一说MIPI CSI2
高通camera驱动分析
参照sensor规格书或者咨询fae,配置:
2.1 sensor帧的输出和关闭
sensor以流的方式 输出帧.
2.2 开启sensor 端的group 功能
开启sensor 端的group 功能,将曝光(line),gain等打包,保证在同一帧进去生效
2.3 sensor嵌入式数据
2.4 sensor初始化相关寄存器
2.5 sensor分辨率相关寄存器
以上的sensor寄存器配置一般有fae厂商提供,驱动工程师尽可能的掌握相关寄存器代表的含义。
如控制宽高、帧率、曝光等等寄存器
我们只用到16位,因此
sensor_id_reg_addr = 0x300b,
sensor_id = 0x0D42,
曝光时间以行长为单位; PCLK以Hz为单位;
行长以周期数为单位,帧长以行长数为单位;其中周期数就是频率
T 周期以ms为单位;
f 频率以Hz为单位;
f = 1 / T;
可以参考这篇文章:
camera曝光和帧率_songqiangzmt的博客
比如这里又3个寄存器,每个寄存器是8bit:
max_linecount = 0x ff ff -8
暂时没弄清楚
这里指的是暗电流值,
一般来说 raw8 都是 16, raw10为 16x4=64, raw12 =16x4x4
传感器可以流式传输许多不同的 数据类型(DT) 。
该数据被包装在不同的流中。 在一个流中,可以有一个或多个不同的DT。 一种 虚拟通道(VC) 分配给每个流。 DT和VC的组合应为唯一,并分配了一个通道ID(CID)。
有关如何指定CID的要求/限制。 当前的MIPI CSI_Rx支持四个VC,每个VC最多可以有四个CID,如下表所示。
传感器可能具有内置的pattern generator。 通过设置专用寄存器,传感器可以将生成的图案输出。
当出现图像异常时,可以使用此功能看看sensor本身输出是否有问题。
binning_factor主要是用来控制拍照亮度跟预览亮度一致的,当然也可以解决预览噪点过大的问题!
3A算法里,曝光时间 snap_exp_time *= (float)(binning_multiplier);
3A源码
赋值源码
关键日志:
CSI :Camera Serial Interface 定义了一个位于处理器和摄像模组之间的高速串行接口
为使CSI_Tx(传感器)和CRI_Rx(设备)正常工作,需要一段时间它们之间需要同步。
此时间在此处设置为计时器时钟滴答数。 它必须介于公式计算的MIN和MAX值之间
MIN [Settle count * T(Timer clock)] > T(HS_SETTLE)_MIN
MAX [Settle count * T(Timer clock)] < T(HS-PREPARE)+T(HS_ZERO) - 4*T(Timer clock)
settle_cnt(即稳定计数)– 必须根据传感器输出特性配置该值,以确保传感器的 PHY
发送器与 MSM 的 PHY 接收器无障碍同步。
对于 28 nm 以及更小的 MSM 芯片,使用以下公式计算稳定计数:
settle_cnt = T(HS_SETTLE)_avg /T(TIMER_CLK),
其中 T(HS_SETTLE)_avg = (T(HS_SETTLE)_min + T(HS_SETTLE)_max) / 2,如传
感器数据表所指示
如果sensor可以直接流式传输HDR帧,该函数才有用。
这里的 rolloff compensations = Lens Shading Correction (LSC)
有些sensor可以自己内部做lsc补偿。rolloff_config就是用了配置sensor的这些信息。
注意:如果你使用了sensor LSC补偿,平台端 lsc补偿就要关闭,否则双倍补偿,可能会造成图片失真。
typedef enum {
SENSOR_DELAY_EXPOSURE, /* delay for exposure /
SENSOR_DELAY_ANALOG_SENSOR_GAIN, / delay for sensor analog gain /
SENSOR_DELAY_DIGITAL_SENSOR_GAIN, / delay for sensor digital gain /
SENSOR_DELAY_ISP_GAIN, / delay for sensor ISP (error) gain*/
SENSOR_DELAY_MAX,
} sensor_delay_type_t;
SENSOR_DELAY_EXPOSURE – Sets the exposure of frame N at frame N + delay
SENSOR_DELAY_ANALOG_SENSOR_GAIN – Sets the analog gain register at frame N + delay
SENSOR_DELAY_DIGITAL_SENSOR_GAIN – Sets the digital gain register at frame N + delay
SENSOR_DELAY_ISP_GAIN – Passes the isp digital gain to the isp mole at frame N + delay
如果出现ae闪烁问题,可以尝试修改延迟,让gain和expose同步。
This is the readout time (in nanoseconds) of the sensor’s analog-to-digital converter. Usually it is
the minimum line time when the sensor is running at the maximum pixel clock.
NOTE: This is the sensor mole’s own information. Refer to the sensor vendor for more information
noise_coeff 小波里用来定义噪声的模板
噪声系数模型: N(x) = sqrt(Sx + O)
这些参数一般由tunning团队修改。
关于角度
注意:
如果 <MountAngle>360</MountAngle>; 这个值配置成360度,那么以dtsi配置的角度为准。
源码:
关于帧率
https://www.cnblogs.com/ZHJEE/p/10351155.html
继续当一名咸鱼( ̄︶ ̄)!
B. android镓嬫満閮芥湁鍝浜泂ensor
1.Sensor Type
閲嶅姏镒熷簲/锷犻熷害浼犳劅鍣 (G-Sensor)
鍏夋劅搴 (Light-Sensor)
娓╁害镒熷簲
鏂瑰悜镒熷簲
纾佸満銆
涓磋繎镐
2.濡备綍瀹炵幇Sensor缂栫▼
a.銮峰彇绯荤粺链嶅姟锛圫ENSOR_SERVICE)杩斿洖涓涓猄ensorManager 瀵硅薄
sensormanager = (SensorManager)getSystemSeriver(SENSOR_SERVICE);
b.阃氲繃SensorManager瀵硅薄銮峰彇鐩稿簲镄凷ensor绫诲瀷镄勫硅薄
sensorObject = sensormanager.getDefaultSensor(sensor Type);
c.澹版槑涓涓猄ensorEventListener 瀵硅薄鐢ㄤ簬渚﹀惉Sensor 浜嬩欢锛屽苟閲嶈浇onSensorChanged鏂规硶
SensorEventListener sensorListener = new SensorEventListener(){
};
d.娉ㄥ唽鐩稿簲镄凷ensorService
sensormanager.registerListener(sensorListener, sensorObject, Sensor TYPE);
e.阌姣佺浉搴旂殑SensorService
sensormanager.unregisterListener(sensorListener, sensorObject);
f: SensorListener 鎺ュ彛鏄浼犳劅鍣ㄥ簲鐢ㄧ▼搴忕殑涓蹇冦傚畠鍖呮嫭涓や釜蹇呴渶鏂规硶锛
銆銆 onSensorChanged(int sensor,float values[]) 鏂规硶鍦ㄤ紶镒熷櫒鍊兼洿鏀规椂璋幂敤銆
璇ユ柟娉曞彧瀵瑰弹姝ゅ簲鐢ㄧ▼搴忕洃瑙嗙殑浼犳劅鍣ㄨ皟鐢(镟村氩唴瀹硅佷笅鏂)銆傝ユ柟娉旷殑鍙傛暟鍖呮嫭锛氢竴涓鏁存暟锛屾寚绀烘洿鏀圭殑浼犳劅鍣;涓涓娴镣瑰兼暟缁勶纴琛ㄧず浼犳劅鍣ㄦ暟鎹链韬銆傛湁浜涗紶镒熷櫒鍙鎻愪緵涓涓鏁版嵁鍊硷纴鍙︿竴浜涘垯鎻愪緵涓変釜娴镣瑰笺傛柟钖戝拰锷犻熻〃浼犳劅鍣ㄩ兘鎻愪緵涓変釜鏁版嵁鍊笺
銆銆 褰扑紶镒熷櫒镄勫嗳纭镐ф洿鏀规椂锛屽皢璋幂敤 onAccuracyChanged(int sensor,int accuracy) 鏂规硶銆傚弬鏁板寘𨰾涓や釜鏁存暟锛氢竴涓琛ㄧず浼犳劅鍣锛屽彟涓涓琛ㄧず璇ヤ紶镒熷櫒鏂扮殑鍑嗙‘鍊笺
3.鍏充簬G-Sensor
Android 锷犻熷害浼犳劅鍣ㄧ殑绫诲瀷鏄 Sensor.TYPE_ACCELEROMETER
阃氲繃 android.hardware.SensorEvent 杩斿洖锷犻熷害浼犳劅鍣ㄥ笺
锷犻熷害浼犳劅鍣ㄨ繑锲炲肩殑鍗曚綅鏄锷犻熷害镄勫崟浣 m/s^2(绫虫疮浜屾℃柟绉)锛屾湁涓変釜鏂瑰悜镄勫煎垎鍒鏄
銆銆values[0]: x-axis 鏂瑰悜锷犻熷害
銆銆values[1]: y-axis 鏂瑰悜锷犻熷害
銆銆values[2]: z-axis 鏂瑰悜锷犻熷害
銆銆鍏朵腑x,y,z鏂瑰悜镄勫畾涔夋槸浠ユ按骞虫斁缃鍦ㄧ殑镓嬫満镄勫彸涓嬭剼涓哄弬镦х郴鍧愭爣铡熺偣
銆銆x 鏂瑰悜灏辨槸镓嬫満镄勬按骞虫柟钖戯纴鍙充负姝
銆銆y 鏂瑰悜灏辨槸镓嬫満镄勬按骞冲瀭鐩存柟钖戯纴鍓崭负姝
銆銆y 鏂瑰悜灏辨槸镓嬫満镄勭┖闂村瀭鐩存柟钖戯纴澶╃┖镄勬柟钖戜负姝o纴鍦扮悆镄勬柟钖戜负璐
闇瑕佹敞镒忕殑鏄锛岀敱浜庡湴鐞冨浐链夌殑閲嶅姏锷犻熷害g (鍊间负9.8 m/s^2)锛
銆銆锲犳ょ幇瀹炰腑瀹为檯锷犻熷害鍊煎簲璇ユ槸 z鏂瑰悜杩斿洖鍊 - 9.8 m/s^2.
銆銆姣斿备綘浠 2 m/s^2 镄勫姞阃熷害灏嗘坠链烘姏璧凤纴杩欐椂z鏂瑰悜镄勮繑锲炲煎簲璇ユ槸 11.8 m/s^2.
銆銆鍙崭箣鑻ヤ互镓嬫満浠2 m/s^2 镄勫姞阃熷害鍧犺惤锛屽垯z鏂瑰悜镄勮繑锲炲煎簲璇ユ槸 7.8 m/s^2.
銆銆x,y鏂瑰悜鍒欐病链変笂杩伴檺鍒躲
C. android如何实现陀螺仪 sensor 在 android 吗
千锋扣丁学堂Android开发为您解答:
sensors.h中还定义了其他各种sensor。要实现的就是这两个:
#define SENSOR_TYPE_MAGNETIC_FIELD 2
#define SENSOR_TYPE_ORIENTATION 3
在/hardware/sensors/sensors.cpp 中添加对MAGNETIC_FIELD和ORIENTATION 的支持
[cpp] view plain
//加入需要的宏定义
#define ID_BASE SENSORS_HANDLE_BASE
#define ID_ACCELERATION (ID_BASE+0)
#define ID_MAGNETIC_FIELD (ID_BASE+1)
#define ID_ORIENTATION (ID_BASE+2)
#define S_HANDLE_ACCELEROMETER (1<<ID_ACCELERATION)
#define S_HANDLE_MAGNETIC_FIELD (1<<ID_MAGNETIC_FIELD)
#define S_HANDLE_ORIENTATION (1<<ID_ORIENTATION)
#define SENSORS_NUM 4
#define SUPPORTED_SENSORS ((1<<NUM_SENSORS)-1)
//在 sensor_t sensors_list[] 中添加两个sensor的信息,
//这些只是一些Sensor的信息,应用程序可以获取到。
#ifdef MAGNETIC_FIELD
{
name : "XXX 3-axis Magnetic field sensor",
vendor : "XXX company",
version : 1,
handle : S_HANDLE_MAGNETIC_FIELD,
type : SENSOR_TYPE_MAGNETIC_FIELD,
maxRange : 600.0f,//最大范围
resolution : 30.0f,//最小分辨率
power : 6.7f,//这个不太懂
},
#endif
#ifdef ORIENTATION
{
name: "XXX Orientation sensor",
vendor: "XXX company",
version: 1,
handle: S_HANDLE_ORIENTATION,
type: SENSOR_TYPE_ORIENTATION,
maxRange: 360,
resolution: 0.1,
power: 20,
},
#endif
//定义一个结构来保存orientation的信息
static struct orientation{
float azimuth;
float pitch;
float roll;
}orientation;
//在 control__open_data_source()函数中打开设备
static native_handle_t*
control__open_data_source(struct sensors_control_device_t *dev)
{
SensorControl* ctl = (void*)dev;
native_handle_t* handle;
int fd_m = open (MAGNETIC_DATA_DEVICE, O_RDONLY);
LOGD ("Open Magnetic Data source: %d, %d/n", fd_m, errno);
if (fd_m>= 0)
{
dev->fd[ID_MAGNETIC_FIELD] = p(fd_m);
}
return handle;
}
//实现数据的打开和关闭函数
static int
data__data_open(struct sensors_data_device_t *dev, native_handle_t* handle)
{
struct sensors_data_context_t *dev;
dev = (struct sensors_data_context_t *)device;
for(int i=0 ;i<SENSORS_NUM; i++)
{
dev->fd[i] = p(handle->data[i]);
}
native_handle_close(handle);
native_handle_delete(handle);
return 0;
}
static int
data__data_close(struct sensors_data_device_t *dev)
{
struct sensors_data_context_t *dev;
dev = (struct sensors_data_context_t *)device;
for(int i=0 ;i<SENSORS_NUM; i++)
{
if (dev->fd[i] >= 0)
{
close(dev->fd[i]);
}
dev->fd[i] = -1;
}
return 0;
}
//最关键的poll函数
static int
data__poll(struct sensors_data_device_t *dev, sensors_data_t* values)
{
SensorData* data = (void*)dev;
int fd = data->events_fd;
//判断设备是否打开
if(dev->fd[ID_MAGNETIC_FIELD] < 0)
{
LOGD("In %s dev[%d] is not open!/n",__FUNCTION__ ,ID_MAGNETIC_FIELD);
return -1;
}
pollfd pfd[SENSORS_NUM] =
{
//省略其他sensor代码
{
fd: dev->fd[ID_MAGNETIC_FIELD],
events: POLLIN,
revents: 0
},
//省略其他sensor代码
};
int err = poll (pfd, SENSORS_NUM, s_timeout);
unsigned int mask = SUPPORTED_SENSORS;
static unsigned int poll_flag=0;
if(poll_flag==0)
{
poll_flag = mask;
}
//省略其他sensor
if(poll_flag&(1<<ID_MAGNETIC_FIELD))
{
if((pfd[ID_MAGNETIC_FIELD].revents&POLLIN) == POLLIN)
{
char rawData[6];
err = read (dev->fd[ID_MAGNETIC_FIELD], &rawData, sizeof(rawData));
if(err<0)
{
LOGE("read magnetic field ret:%d errno:%d/n", err, errno);
return err;
}
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
data->time = timespec_to_ns(&t);
data->sensor = SENSOR_TYPE_MAGNETIC_FIELD;
data->magnetic.status = SENSOR_STATUS_ACCURACY_HIGH;
//上报的数据单位要转换成 uTesla
data->magnetic.x = ( (rawData[1] << 8 ) | rawData[0])/ MAGNETIC_CONVERT;
data->magnetic.y = ( (rawData[3] << 8 ) | rawData[2])/ MAGNETIC_CONVERT;
data->magnetic.z = ( (rawData[5] << 8 ) | rawData[4])/ MAGNETIC_CONVERT;
//把陀螺仪需要的数据计算出来,用atan2(),头文件要加上#include <math.h>
float azimuth = atan2( (float)(data->magnetic.x ),(float)(data->magnetic.y) );
if(azimuth<0)
{
azimuth = 360 - fabs(azimuth*180/PI);
}
else
{
azimuth = azimuth*180/PI;
}
orientation.azimuth = 360-azimuth;
//rotation around the X axis.+180~-180 degree
orientation.pitch = atan2( (float)(data->magnetic.y ),(float)(data->magnetic.z)
)*180/PI;
//rotation around the Y axis +90~-90 degree
float roll = atan2( (float)(data->magnetic.x ),(float)(data->magnetic.z) )
*180/PI;
if (roll > 90)
{
roll = -(180.0-roll);
}
else if (roll < -90)
{
roll = 180 + roll;
}
orientation.roll = roll;
}
return S_HANDLE_MAGNETIC_FIELD;
}
if(poll_flag&(1<<ID_MAGNETIC_FIELD))
{
//数据已经计算好了直接上报就行
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
data->time = timespec_to_ns(&t);
data->sensor = SENSOR_TYPE_ORIENTATION;
data->orientation.azimuth = orientation.azimuth;
data->orientation.pitch = orientation.pitch;
data->orientation.roll = orientation.roll;
poll_flag &= ~(1<<ID_ORIENTATION);
return S_HANDLE_ORIENTATION;
}
}
D. android如何实现陀螺仪 sensor 在 android 吗
设备中的三自由度Orientation
Sensor就是一个可以识别设备相对于地面,绕x、y、z轴转动角度的感应器(自己的理解,不够严谨)。智能手机,平板电脑有了它,可以实现很多好玩的应用,比如说指南针等。
我们可以用一个磁场感应器(magnetic sensor)来实现。
磁场感应器是用来测量磁场感应强度的。一个3轴的磁sensor
IC可以得到当前环境下X、Y和Z方向上的磁场感应强度,对于Android中间层来说就是读取该感应器测量到的这3个值。当需要时,上报给上层应用程序。磁感应强度的单位是T(特斯拉)或者是Gs(高斯),1T等于10000Gs。
先来看看android定义的坐标系,在/hardware/libhardware/include/hardware/sensors.h中有个图。
求z和x的反正切可得到此值。
sensors.h中还定义了其他各种sensor。要实现的就是这两个:
#define SENSOR_TYPE_MAGNETIC_FIELD 2
#define SENSOR_TYPE_ORIENTATION 3
在/hardware/sensors/sensors.cpp 中添加对MAGNETIC_FIELD和ORIENTATION 的支持
E. android 非上层程序怎么调用sensor
Android上层应用apk到G-sensor driver的大致流程: Android HAL层,即硬件抽象层,是Google响应厂家“希望不公开源码”的要求推出的新概念 1,源代码和目标位置 源代码: /hardware/libhardware目录,该目录的目录结构如下: /hardware/libhardware/hardware.c编译成libhardware.so,目标位置为/system/lib目录 /hardware/libhardware/include/hardware目录下包含如下头文件: hardware.h 通用硬件模块头文件 bit.h bit模块头文件 gralloc.h gralloc模块头文件 qemud.h qemud模块头文件 sensors.h 传感器模块头文件 /hardware/libhardware/moles目录下定义了很多硬件模块 这些硬件模块都编译成xxx.xxx.so,目标位置为/system/lib/hw目录 2,Android对于Sensor的API定义在 hardware/libhardware/include/hardware/sensor.h中,要求在sensor.so提供以下8个API函数 [控制方面] int (*open_data_source)(struct sensors_control_device_t *dev); int (*activate)(struct sensors_control_device_t *dev, int handle, int enabled); int (*set_delay)(struct sensors_control_device_t *dev, int32_t ms); int (*wake)(struct sensors_control_device_t *dev); [数据方面] int (*data_open)(struct sensors_data_device_t *dev, int fd); int (*data_close)(struct sensors_data_device_t *dev); int (*poll)(struct sensors_data_device_t *dev, sensors_data_t* data); [模块方面] int (*get_sensors_list)(struct sensors_mole_t* mole, struct sensor_t const** list); 在java层Sensor的状态控制由SensorService来负责,它的java代码和JNI代码分别位于: frameworks/base/services/java/com/Android/server/SensorService.java frameworks/base/services/jni/com_Android_server_SensorService.cpp 在Java层Sensor的数据控制由SensorManager来负责,它的java代码和JNI代码分别位于: frameworks/base/core/java/Android/hardware/SensorManager.java frameworks/base/core/jni/Android_hardware_SensorManager.cpp Android framework中与sensor通信的是sensorService.java和sensorManager.java。 sensorService.java的具体通信是通过JNI调用sensorService.cpp中的方法实现的。 sensorManager.java的具体通信是通过JNI调用sensorManager.cpp中的方法实现的。 sensorService.cpp和sensorManger.cpp通过hardware.c与sensor.so通信。其中sensorService.cpp实现对sensor的状态控制,sensorManger.cpp实现对sensor的数据控制。 sensor.so通过ioctl控制sensor driver的状态,通过打开sensor driver对应的设备文件读取G-sensor采集的数据。 Android SDK提供了4个类来于sensor通信,分别为 sensor,sensorEvent,sensorEventListener,sensorManager。其中 sensorEventListener用来在sensorManager中注册需要监听的sensor类型。 sensorManager.java提供registrater(),unregistrater()接口供sensorEventListener使用。 sensorManager.java不断轮询从sensor.so中取数据。取到数据后送给负责监听此类型sensor的 sensorEventListener.java。sensorEventListener.java通过在sensorManager.java中注册可以监听特定类型的sensor传来的数据。 系统启动时执行systemProcess,会启动sensorService.java,在sensorService.java的构造函数中调用JNI方法_sensor_control_init()。 sensorService.cpp中相应的方法Android_int()会被执行。该函数会调用hardware.c中的方法hw_get_mole()此函数又通过调用load()函数在system/lib/hw下查找sensor.so 查找时会根据harware.c中定义好的sensor.*.so的扩展名的顺序查找,找到第一个匹配的时候即停止,并将该sensor.so中定义好的一个全局变量HAL_MODULE_INFO_SYM带回。该变量包含的一个 重要信息是它的一个成员结构变量中包含的一个函数指针open,该指针所指函数会对一个device结构变量赋值,从而带出sensorService.cpp和sensorManager.cpp与sensor通信所需要的全部信息。 device结构变量有两种变体分别供sensorService.cpp和sensorManaer.cpp使用。其中主要是一些函数指针指向与sensor通信的函数。 sensorService.cpp和sensorManager.cpp在得到HAL_MODULE_INFO_SYM结构后都会调用 sensors.h的inline函数open()通过HAL_MODULE_INFO_SYM的open函数指针将所需的device信息取回。 系统在启动activityManager.java时,它会启动sensorManager.java,它也会调用hardware.c中的方法hw_get_mole()带回HAL_MODULE_INFO_SYM。
F. android gsensor 休眠震动唤醒功能如何实现
一、唤醒源
设备休眠后,通过触发唤醒源使设备恢复正常工作模式。设备唤醒源有多种,对于Android设备常见的就有PowerKey、来电唤醒、Alarm唤醒等。
唤醒源的实现处于内核空间,本文重点讨论下PowerKey作为唤醒源的具体实现。
二、PowerKey唤醒源
PowerKey唤醒设备的原理,本质其实就是中断。
PowerKey连接到CPU的一个输入(Input)引脚(Pin)上,该Pin运行在中断模式上。一旦PowerKey按下,引发Pin中断;而该中断具有唤醒CPU的功能,于是设备得以唤醒。
三、PowerKey对应的Pin
Configuration
和PowerKey相连的Pin的具体配置位于板级dts文件中,比如如下配置:
arch/arm/boot/dts/xxxxx.dts
power-key {
/** 是CPU的哪个Pin */
gpios = <&gpio0 GPIO_A5 GPIO_ACTIVE_LOW>;
/** Key code */
linux,code = <116>;
/** 起个名字 */
label = "power";
/** 该Pin具有wakeup的功能 */
gpio-key,wakeup;
};
着重说下linux,code =
<116>,116怎么来的?
对于键盘,每一个按键都有唯一的编码,在Linux中,编码值位于:
input.h (kernelincludeuapilinux)
/*
* Keys and buttons
*/
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_BACKSPACE 14
#define KEY_TAB 15
#define KEY_POWER 116 /* SC System Power Down */
可知,PowerKey的编码也在该文件中,且编码值为116;一旦按下PowerKey,该值作为键值传到input_event结构体的code成员变量中:
input.h (kernelincludeuapilinux)
/*
* The event structure itself
*/
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
之后我们会写个Linux应用程序读取code值。