首页
chatGPT
关于
友链
其它
统计
更多
壁纸
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,382 阅读
2
修改Linux Kernel defconfig的标准方法
6,376 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
5,957 阅读
4
使用git生成patch和应用patch
3,444 阅读
5
c语言的__attribute__
3,165 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
网址
adtxl
累计撰写
365
篇文章
累计收到
14
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
工具
软件工具
Bug
COMPANY
页面
chatGPT
关于
友链
其它
统计
壁纸
留言
搜索到
131
篇与
的结果
2024-06-12
Arm64体系架构-MPIDR_EL1寄存器
背景在Arm64多核处理器中, 各核间的关系可能不同. 比如1个16 core的cpu, 每4个core划分为1个cluster,共享L2 cache. 当我们需要从core 0将任务调度出来时,如果优先选择core 1~3, 那么性能明显时优于其他core的.那么操作系统怎么知道core之间这样的拓扑信息呢? Arm提供了MPIDR_EL1 寄存器. 每个core都有一个该寄存器。字段说明a.该寄存器为只读寄存器b.AFF3 & AFF2 都为ClusterID(从软件角度理解为不同CPU组的ID),AFF1 为CPUID,AFF0 为多线程核的线程ID(指的是是否支持超线程的id)MPIDR_EL1U, bit [30]0表示多核处理, 1表示单核处理MT, bit [24]0表示没有使用单核超线程, 1表示使用了单核超线程。其他的affinity,则表示了各核之间的亲和性。以一个8核2 cluster 非超线程cpu为例, core0的mpidr_el1的affinity为(0,0,0,0),core1为(0,0,0,1),以次类推, core7则为(0,0,1,3)。Arm规范要求了每个core的(Aff3,Aff2,Aff1,Aff0)编码必须唯一。不支持超线程的cpu, Aff0表示核id这样通过树形结构的编码,OS可以从该寄存器中获取各core之间的关系。kernel 应用// kernel表示每个core的拓扑结构,每个core对应一个该结构 struct cpu_topology { int thread_id; int core_id; int package_id; int llc_id; cpumask_t thread_sibling; cpumask_t core_sibling; cpumask_t llc_sibling; }; void store_cpu_topology(unsigned int cpuid) { struct cpu_topology *cpuid_topo = &cpu_topology[cpuid]; // 读取MPIDR_EL1 u64 mpidr = read_cpuid_mpidr(); /* Create cpu topology mapping based on MPIDR. */ // 判断芯片是否支持超线程 if (mpidr & MPIDR_MT_BITMASK) { /* Multiprocessor system : Multi-threads per core */ // 在支持超线程的cpu, Aff0表示一个core内的超线程id cpuid_topo->thread_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 1); // package_id即cluster id cpuid_topo->package_id = MPIDR_AFFINITY_LEVEL(mpidr, 2) | MPIDR_AFFINITY_LEVEL(mpidr, 3) << 8; } else { /* Multiprocessor system : Single-thread per core */ cpuid_topo->thread_id = -1; // 不支持超线程的cpu, Aff0表示核id cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); cpuid_topo->package_id = MPIDR_AFFINITY_LEVEL(mpidr, 1) | MPIDR_AFFINITY_LEVEL(mpidr, 2) << 8 | MPIDR_AFFINITY_LEVEL(mpidr, 3) << 16; } ... ... }MPIDR_EL1在devicetree中的体现配置DTS时,需要设置MPIDR_EL1的值到CPU node中的reg property,以ArmV8 64bit系统为例:当#address-cell property为2时,需要设置MPIDR_EL1[39:32]到第一个reg cell的reg[7:0]、MPIDR_EL1[23:0]到第二个reg celll的reg[23:0]; 当#address-cellproperty为1时,需要设置MPIDR_EL1[23:0]到reg[23:0];reg的其他位设置位0。Linux启动过程中MPIDR_EL1的相关逻辑 a.内核中定义了cpu的逻辑映射变量如下,该变量保存MPIDR_EL1寄存器中亲和值。 /* * Logical CPU mapping. */ extern u64 __cpu_logical_map[NR_CPUS]; #define cpu_logical_map(cpu) __cpu_logical_map[cpu] b.cpu0(boot cpu/primary cpu)获取mpidr_el1亲和值的方式与其他cpu(secondary cpu) 获取方式有所不同。 void __init smp_setup_processor_id(void) { /*启动该过程时只有boot cpu即cpu0在执行,其他cpu还未启动 通过read_cpuid_mpidr获取的MPIDR_EL1值即为当前执行的CPU0 的亲和值*/ u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; /*将获取到的cpu0的亲和值保存在cpu_logical_map(0)*/ cpu_logical_map(0) = mpidr; /* * clear __my_cpu_offset on boot CPU to avoid hang caused by * using percpu variable early, for example, lockdep will * access percpu variable inside lock_release */ set_my_cpu_offset(0); pr_info("Booting Linux on physical CPU 0x%lx\n", (unsigned long)mpidr); }
2024年06月12日
62 阅读
0 评论
0 点赞
2024-05-11
ARM WFI和WFE指令
转载自https://zhuanlan.zhihu.com/p/6860633741.基本介绍ARM有两条和低功耗相关的指令: WFI和WFE。WFI全称是Wait For Interrupt,WFE全称是Wait For Event。这两条指令统称为WFx,可以让ARM核进入low-power standby模式,由ARM架构定义,由ARM core实现,使用时不需要带任何参数。ARM架构并没有规定“low-power standby state”的具体形式,可以由ARM core自定义实现,根据ARM的建议,一般可以实现为standby(关闭clock、保持供电)、dormant、shutdown等。但有个原则,不能造成内存一致性的问题。以Cortex-A57 ARM core为例,它把WFI和WFE实现为“put the core in a low-power state by disabling the clocks in the core while keeping the core powered up”,即我们通常所说的standby模式,保持供电,关闭clock。接下来了解一下ARM处理器进入低功耗状态的机制。当一个处理器核的工作负载不高时,可以通过降压降频(DVFS)的方式来让处理器运行在较低频率,从而降低芯片功耗。如果处理器没有工作负载,完全空闲下来,这时就需要让处理器进入更低的功耗模式。CPU进入空闲状态的大概顺序如下:应用处理器(Application Processor)判断是否具备进入空闲状态的条件,如果满足条件则进行一些准备工作;发消息给系统控制器(System control processor);等待全部操作执行完;执行WFI进入空闲状态;等待系统控制器做出下一步动作,或者关闭时钟,或者关闭电源等等。从WFI的名字可以看出,唤醒(wake up)处理器的一个机制就是发送中断给处理器(也有一些其他的机制)。GIC中断控制器接收到中断后,产生唤醒信号给系统控制器或者是电源管理模块(Power Management Unit),系统控制器或者PMU根据处理器核的状态以及系统状态,决定下一步的动作。WFE与WFI类似,只不过等待的是事件,也就是说系统可以通过发送事件来唤醒CPU核。WFE的一个典型应用场景是自旋锁spinlock。当多个核竞争同一个临界区资源时,只有一个核能获得权限,其它的核需要等待临界区资源被释放,也就是这些核“自旋”。当然操作系统也可以采取其它的处理方式,比如把时间片分给其它的应用程序。显然,应用处理器简单的“自旋”对于功耗不友好,一个解决办法就是让这些“自旋”的核进入低功耗模式,获得临界区资源的核在完成当前进程后,唤醒这些核去重新竞争临界区资源。这时的唤醒机制不宜采取中断方式,因为效率不高。ARM采用的是事件方式,即通过执行SEV(Send Event)指令向其它应用处理器发送唤醒的事件。WFI和WFE还有一种变体形式,WFIT和WFET,其中的T是超时(timeout)的意思,也就是说,唤醒机制多了一种超时的方式,没有中断或者事件产生,时间到了也会发出唤醒。2.不同点WFE状态的唤醒事件比WFI多了一个SEV指令(Send Event),也就是说WFE还会受控于一个1bit event register,此寄存器软件不可访问。此功能时为多核而准备的。WFE在event register为0,或为1时,其功能是不一样的。WFE的E就是指这个事件寄存器。进一步描述:假设两个核,分别为core1和core2,A、core1可以执行SEV指令更改event register,同时它可以通过TXEV硬连接到core2的RXEV更改core2的event register。 B、core1在执行WFE时,如果发现event register=1,它是不会进低功耗的,这里就是区别。 基于以上描述WFE是多核低功耗用的,举个例子:当core1和core2抢同一个资源A时,core1占据A,那core2就WFE进低功耗,等待core1的SEV发过来。SEV指令是一个用来改变Event Register的指令,有两个:SEV会修改所有PE上的寄存器;SEVL只修改本PE的寄存器值。3.使用场景1)WFIWFI一般用于cpu idle。在ARM64架构中,当CPU Idle时,会调用WFI指令,关掉CPU的Clock以便降低功耗。2)WFEWFE的典型使用场景是在spinlock中(可参考arch_spin_lock,对arm64来说,位于arm64/include/asm/spinlock.h中)。spinlock的功能,是在不同CPU core之间,保护共享资源。使用WFE的流程是:1. 资源空闲 2. Core1访问资源,acquire lock,获得资源 3. Core2访问资源,此时资源不空闲,执行WFE指令,让core进入low-power state 4. Core1释放资源,release lock,释放资源,同时执行SEV指令,唤醒Core2 5. Core2获得资源
2024年05月11日
151 阅读
0 评论
0 点赞
2024-01-09
Android sensor hal框架
1. 简介2. 驱动大部分传感器都会使用iio驱动框架,以light sensor为例3. sensor hal3.1 sensor hal 2.0接口sensor hal 2.0的几个接口说明,参考ISensors.halgetSensorsList()getSensorsList() generates (vec<SensorInfo> list);getSensorsList()函数用于获取当前设备的所有可用的sensors,返回值为SensorInfo,该结构体定义如下,struct SensorInfo { /** * handle that identifies this sensors. This handle is used to reference * this sensor throughout the HAL API. */ int32_t sensorHandle; /** * Name of this sensor. * All sensors of the same "type" must have a different "name". */ string name; /** vendor of the hardware part */ string vendor; /** * version of the hardware part + driver. The value of this field * must increase when the driver is updated in a way that changes the * output of this sensor. This is important for fused sensors when the * fusion algorithm is updated. */ int32_t version; /** this sensor's type. */ SensorType type; /** * type of this sensor as a string. * * When defining an OEM specific sensor or sensor manufacturer specific * sensor, use your reserve domain name as a prefix. * e.g. com.google.glass.onheaddetector * * For sensors of known type defined in SensorType (value < * SensorType::DEVICE_PRIVATE_BASE), this can be an empty string. */ string typeAsString; /** maximum range of this sensor's value in SI units */ float maxRange; /** smallest difference between two values reported by this sensor */ float resolution; /** rough estimate of this sensor's power consumption in mA */ float power; /** * this value depends on the reporting mode: * * continuous: minimum sample period allowed in microseconds * on-change : 0 * one-shot :-1 * special : 0, unless otherwise noted */ int32_t minDelay; /** * number of events reserved for this sensor in the batch mode FIFO. * If there is a dedicated FIFO for this sensor, then this is the * size of this FIFO. If the FIFO is shared with other sensors, * this is the size reserved for that sensor and it can be zero. */ uint32_t fifoReservedEventCount; /** * maximum number of events of this sensor that could be batched. * This is especially relevant when the FIFO is shared between * several sensors; this value is then set to the size of that FIFO. */ uint32_t fifoMaxEventCount; /** * permission required to see this sensor, register to it and receive data. * Set to "" if no permission is required. Some sensor types like the * heart rate monitor have a mandatory require_permission. * For sensors that always require a specific permission, like the heart * rate monitor, the android framework might overwrite this string * automatically. */ string requiredPermission; /** * This value is defined only for continuous mode and on-change sensors. * It is the delay between two sensor events corresponding to the lowest * frequency that this sensor supports. When lower frequencies are requested * through batch()/setDelay() the events will be generated at this frequency * instead. * It can be used by the framework or applications to estimate when the * batch FIFO may be full. * * NOTE: periodNs is in nanoseconds where as maxDelay/minDelay are in * microseconds. * * continuous, on-change: maximum sampling period allowed in * microseconds. * * one-shot, special : 0 */ int32_t maxDelay; /** Bitmask of SensorFlagBits */ bitfield<SensorFlagBits> flags; }; setOperationMode()setOperationMode设置模块的模式,有两种选择,SENSOR_HAL_NORMAL_MODE - Normal operation. Default state of the module.SENSOR_HAL_DATA_INJECTION_MODE - Loopback mode.Data is injected for the supported sensors by the sensor service in this mode.setOperationMode(OperationMode mode) generates (Result result);activate()activate()用来activate/de-activate一个sensor,在de-activate一个sensor后,尚未写入事件队列的现有传感器事件必须立即丢弃,以便后续的激活不会得到陈旧的传感器事件(即在后续激活之前生成的事件)。enabled设置为true激活一个传感器,设置为false停用一个传感器。activate(int32_t sensorHandle, bool enabled) generates (Result result);initialize()initialize()函数用于初始化hal的快速消息队列Fast Message Queues (FMQ)和回调函数。快速消息队列(FMQ)用于在框架和HAL之间发送数据。回调由HAL用于通知框架异步事件,例如动态传感器的连接。事件FMQ用于将传感器事件从HAL传输到框架。使用 eventQueueDescriptor 创建事件FMQ。只能将数据写入事件FMQ,不得从事件FMQ读取数据,因为框架是唯一的读取者。收到传感器事件后,HAL将传感器事件写入事件FMQ。一旦HAL完成将传感器事件写入事件FMQ,HAL必须通知框架可以读取和处理传感器事件。有两种方式实现:调用事件FMQ的 EventFlag::wake() 函数,使用 EventQueueFlagBits::READ_AND_PROCESS在事件FMQ的 writeBlocking() 函数中将写通知设置为 EventQueueFlagBits::READ_AND_PROCESS。// Initialize the Sensors HAL's Fast Message Queues (FMQ) and callback. // @param eventQueueDescriptor Fast Message Queue descriptor that is used to // create the Event FMQ which is where sensor events are written. The // descriptor is obtained from the framework's FMQ that is used to read // sensor events. // @param wakeLockDescriptor Fast Message Queue descriptor that is used to // create the Wake Lock FMQ which is where wake_lock events are read // from. The descriptor is obtained from the framework's FMQ that is // used to write wake_lock events. // @param sensorsCallback sensors callback that receives asynchronous data // from the Sensors HAL. // @return result OK on success; BAD_VALUE if descriptor is invalid (such // as null) @entry @callflow(next = {"getSensorsList"}) initialize(fmq_sync<Event> eventQueueDescriptor, fmq_sync<uint32_t> wakeLockDescriptor, ISensorsCallback sensorsCallback) generates (Result result);batch()设置传感器的参数,包括采样频率和最大报告延迟。此函数可在传感器处于激活状态时调用,此时不能导致任何传感器测量数据的丢失:从一个采样率过渡到另一个采样率不能导致事件丢失,也不能从高最大报告延迟过渡到低最大报告延迟导致事件丢失。/* @param sensorHandle 要更改的传感器句柄。 @param samplingPeriodNs 指定传感器样本周期,以纳秒为单位。 @param maxReportLatencyNs 在事件被采样到报告时间之前允许的延迟时间。 @return 成功时返回 OK,如果任何参数无效则返回 BAD_VALUE。 */ batch(int32_t sensorHandle, int64_t samplingPeriodNs, int64_t maxReportLatencyNs) generates ( Result result); flush()刷新将向指定传感器的“批处理模式”FIFO末尾添加一个 FLUSH_COMPLETE 元数据事件,并刷新FIFO。如果FIFO为空或传感器不支持批处理(FIFO大小为零),则返回 SUCCESS,并在事件流中添加一个简单的 FLUSH_COMPLETE 事件。这适用于除单次触发传感器之外的所有传感器。如果传感器是单次触发传感器,刷新必须返回 BAD_VALUE,并且不生成任何刷新完成的元数据。如果在调用 flush() 时传感器处于非活动状态,flush() 必须返回 BAD_VALUE。// Trigger a flush of internal FIFO. flush(int32_t sensorHandle) generates (Result result);injectSensorData()当设备处于 NORMAL 模式时,调用此函数将操作环境数据推送到设备。在此操作中,事件始终为 SensorType::AdditionalInfo 类型。当设备处于 DATA_INJECTION 模式时,此函数还用于注入传感器事件。无论 OperationMode 如何,注入的 SensorType::ADDITIONAL_INFO 类型事件不应路由回传感器事件队列。// Inject a single sensor event or push operation environment parameters to device. injectSensorData(Event event) generates (Result result);registerDirectChannel()使用提供的共享内存信息注册直接通道。在返回时,传感器硬件负责将内存内容重置为初始值(取决于内存格式设置)。// Register direct report channel. registerDirectChannel(SharedMemInfo mem) generates (Result result, int32_t channelHandle); SharedMemInfo结构体定义如下, /** * Shared memory information for a direct channel */ struct SharedMemInfo { SharedMemType type; // shared memory type SharedMemFormat format; uint32_t size; // size of the memory region, in bytes handle memoryHandle; // shared memory handle, it is interpreted // depending on type field, see SharedMemType. }; /** * Direct channel shared memory types. See struct SharedMemInfo. */ @export(name="direct_mem_type_t", value_prefix="SENSOR_DIRECT_MEM_TYPE_") enum SharedMemType : int32_t { // handle contains 1 fd (ashmem handle) and 0 int. ASHMEM = 1, // handle definition matches gralloc HAL. GRALLOC }; /** * Direct channel lock-free queue format, this defines how the shared memory is * interpreted by both sensor hardware and application. * * @see SharedMemInfo. */ @export(name="direct_format_t", value_prefix="SENSOR_DIRECT_FMT_") enum SharedMemFormat : int32_t { SENSORS_EVENT = 1, // shared memory is formated as an array of data // elements. See SensorsEventFormatOffset for details. // Upon return of channel registration call, the // shared memory space must be formated to all 0 by HAL. };unregisterDirectChannel()注销先前使用 registerDirectChannel 注册的直接通道,并移除在该直接通道中配置的所有活动传感器报告。// Unregister direct report channel. unregisterDirectChannel(int32_t channelHandle) generates (Result result);configDirectReport()此函数启动、修改速率或停止在特定直接通道中传感器的直接报告。// Configure direct sensor event report in direct channel. configDirectReport( int32_t sensorHandle, int32_t channelHandle, RateLevel rate ) generates ( Result result, int32_t reportToken); /** * Direct report rate level definition. Except for SENSOR_DIRECT_RATE_STOP, each * rate level covers the range (55%, 220%] * nominal report rate. For example, * if config direct report specify a rate level SENSOR_DIRECT_RATE_FAST, it is * legal for sensor hardware to report event at a rate greater than 110Hz, and * less or equal to 440Hz. Note that rate has to remain steady without variation * before new rate level is configured, i.e. if a sensor is configured to * SENSOR_DIRECT_RATE_FAST and starts to report event at 256Hz, it cannot * change rate to 128Hz after a few seconds of running even if 128Hz is also in * the legal range of SENSOR_DIRECT_RATE_FAST. Thus, it is recommended to * associate report rate with RateLvel statically for single sensor. */ @export(name="direct_rate_level_t", value_prefix="SENSOR_DIRECT_RATE_") enum RateLevel : int32_t { STOP, // stop NORMAL, // nominal 50Hz FAST, // nominal 200Hz VERY_FAST, // nominal 800Hz }; 3.2 具体hal实现可参考Android12源码中两个google的实现:device/generic/goldfish/sensors/device/google/trout/hal/sensors/2.0
2024年01月09日
196 阅读
0 评论
0 点赞
2024-01-02
此内容被密码保护
加密文章,请前往内页查看详情
2024年01月02日
21 阅读
0 评论
0 点赞
2023-12-29
此内容被密码保护
加密文章,请前往内页查看详情
2023年12月29日
14 阅读
0 评论
0 点赞
2023-12-27
此内容被密码保护
加密文章,请前往内页查看详情
2023年12月27日
18 阅读
0 评论
0 点赞
2023-12-21
此内容被密码保护
加密文章,请前往内页查看详情
2023年12月21日
14 阅读
0 评论
0 点赞
2023-12-14
常用的Android keyevent事件
keyevent1.keyevent事件有一张对应的表,可以直接发送对应的数字,也可以方式字符串,如下两个方法都能实现back键。打开cmd输入指令方法一:> adb shell input keyevent KEYCODE_BACK方法二:> adb shell input keyevent 42.常用的keyevent事件解锁(KEYCODE_NOTIFICATION): adb shell input keyevent 83向上(KEYCODE_DPAD_UP):adb shell input keyevent 19向下(KEYCODE_DPAD_DOWN):adb shell input keyevent 20向左(KEYCODE_DPAD_LEFT):adb shell input keyevent 21向右(KEYCODE_DPAD_RIGHT):adb shell input keyevent 22keyevent事件对应数字0 –> “KEYCODE_UNKNOWN” 1 –> “KEYCODE_MENU” 2 –> “KEYCODE_SOFT_RIGHT” 3 –> “KEYCODE_HOME” 4 –> “KEYCODE_BACK” 5 –> “KEYCODE_CALL” 6 –> “KEYCODE_ENDCALL” 7 –> “KEYCODE_0” 8 –> “KEYCODE_1” 9 –> “KEYCODE_2” 10 –> “KEYCODE_3” 11 –> “KEYCODE_4” 12 –> “KEYCODE_5” 13 –> “KEYCODE_6” 14 –> “KEYCODE_7” 15 –> “KEYCODE_8” 16 –> “KEYCODE_9” 17 –> “KEYCODE_STAR” 18 –> “KEYCODE_POUND” 19 –> “KEYCODE_DPAD_UP” 20 –> “KEYCODE_DPAD_DOWN” 21 –> “KEYCODE_DPAD_LEFT” 22 –> “KEYCODE_DPAD_RIGHT” 23 –> “KEYCODE_DPAD_CENTER” 24 –> “KEYCODE_VOLUME_UP” 25 –> “KEYCODE_VOLUME_DOWN” 26 –> “KEYCODE_POWER” 27 –> “KEYCODE_CAMERA” 28 –> “KEYCODE_CLEAR” 29 –> “KEYCODE_A” 30 –> “KEYCODE_B” 31 –> “KEYCODE_C” 32 –> “KEYCODE_D” 33 –> “KEYCODE_E” 34 –> “KEYCODE_F” 35 –> “KEYCODE_G” 36 –> “KEYCODE_H” 37 –> “KEYCODE_I” 38 –> “KEYCODE_J” 39 –> “KEYCODE_K” 40 –> “KEYCODE_L” 41 –> “KEYCODE_M” 42 –> “KEYCODE_N” 43 –> “KEYCODE_O” 44 –> “KEYCODE_P” 45 –> “KEYCODE_Q” 46 –> “KEYCODE_R” 47 –> “KEYCODE_S” 48 –> “KEYCODE_T” 49 –> “KEYCODE_U” 50 –> “KEYCODE_V” 51 –> “KEYCODE_W” 52 –> “KEYCODE_X” 53 –> “KEYCODE_Y” 54 –> “KEYCODE_Z” 55 –> “KEYCODE_COMMA” 56 –> “KEYCODE_PERIOD” 57 –> “KEYCODE_ALT_LEFT” 58 –> “KEYCODE_ALT_RIGHT” 59 –> “KEYCODE_SHIFT_LEFT” 60 –> “KEYCODE_SHIFT_RIGHT” 61 –> “KEYCODE_TAB” 62 –> “KEYCODE_SPACE” 63 –> “KEYCODE_SYM” 64 –> “KEYCODE_EXPLORER” 65 –> “KEYCODE_ENVELOPE” 66 –> “KEYCODE_ENTER” 67 –> “KEYCODE_DEL” 68 –> “KEYCODE_GRAVE” 69 –> “KEYCODE_MINUS” 70 –> “KEYCODE_EQUALS” 71 –> “KEYCODE_LEFT_BRACKET” 72 –> “KEYCODE_RIGHT_BRACKET” 73 –> “KEYCODE_BACKSLASH” 74 –> “KEYCODE_SEMICOLON” 75 –> “KEYCODE_APOSTROPHE” 76 –> “KEYCODE_SLASH” 77 –> “KEYCODE_AT” 78 –> “KEYCODE_NUM” 79 –> “KEYCODE_HEADSETHOOK” 80 –> “KEYCODE_FOCUS” 81 –> “KEYCODE_PLUS” 82 –> “KEYCODE_MENU” 83 –> “KEYCODE_NOTIFICATION” 84 –> “KEYCODE_SEARCH” 85 –> “TAG_LAST_KEYCODE”
2023年12月14日
100 阅读
0 评论
0 点赞
2023-11-24
此内容被密码保护
加密文章,请前往内页查看详情
2023年11月24日
15 阅读
0 评论
0 点赞
2023-11-23
此内容被密码保护
加密文章,请前往内页查看详情
2023年11月23日
17 阅读
0 评论
0 点赞
1
2
3
4
...
14