按键,触摸屏流程分析

按键触摸屏流程分析:
WindowManagerService类的构造函数
WindowManagerService()
  mQueue = new KeyQ();
因为 WindowManagerService.java (frameworks\base\services\java\com\android\server)中有:    
private class KeyQ extends KeyInputQueue
KeyQ 是抽象类  KeyInputQueue 的实现,所以 new KeyQ类的时候实际上在 KeyInputQueue 类中创建了
一个线程  InputDeviceReader 专门用来冲设备读取按键事件,代码:
Thread mThread = new Thread("InputDeviceReader") {
  public void run()
  {
        在循环中调用:readEvent(ev);
    ...
    send = preprocessEvent(di, ev);
        实际调用的是 KeyQ 类的 preprocessEvent 函数
    ...
    int keycode = rotateKeyCodeLocked(ev.keycode);
      int[] map = mKeyRotationMap;
      for (int i=0; i<N; i+=2)
      {
        if (map[i] == keyCode)
          return map[i+1];
      } //
    addLocked(di, curTime, ev.flags,RawInputEvent.CLASS_KEYBOARD,newKeyEvent(di, di.mDownTime, curTime, down,keycode, 0, scancode,...));
      QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
  }
}

readEvent() 实际上调用的是 com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)中的:
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,jobject event)
  bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,&flags, &value, &when);
调用的是 EventHub.cpp (frameworks\base\libs\ui)中的:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
        int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
        int32_t* outValue, nsecs_t* outWhen)
在函数中调用了读设备操作:res = read(mFDs[i].fd, &iev, sizeof(iev));


在构造函数 WindowManagerService()调用 new KeyQ() 以后接着调用了:
  mInputThread = new InputDispatcherThread();       
  ...     
  mInputThread.start();
来启动一个线程  InputDispatcherThread
run()
  process();
    QueuedEvent ev = mQueue.getEvent(...)
因为WindowManagerService类中: final KeyQ mQueue;
所以实际上 InputDispatcherThread 线程实际上从  KeyQ 的事件队列中读取按键事件。
switch (ev.classType)
  case RawInputEvent.CLASS_KEYBOARD:
    ...
    dispatchKey((KeyEvent)ev.event, 0, 0);
    mQueue.recycleEvent(ev);
    break;
  case RawInputEvent.CLASS_TOUCHSCREEN:
    //Log.i(TAG, "Read next event " + ev);
    dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
    break;


setRotationUnchecked
littonton 触摸屏幕驱动修改记录
修改
内核代码/drivers/input/touchscreen/wm9713_touch.c
static int codec_ts_evt_add(codec_ts_t *ts, u16 pressure, u16 x, u16 y)
{
    ...
    input_report_abs(ts->idev, ABS_PRESSURE, pressure & 0xfff);
        //针对android加上的
    input_report_key(ts->idev, BTN_TOUCH,1);
    return 0;
}

static void codec_ts_evt_release(codec_ts_t *ts)
{
    input_report_abs(ts->idev, ABS_PRESSURE, 0);
    //针对android加上的
    input_report_abs(ts->idev,BTN_TOUCH,0);
    wm9713_event_ack(EVENT_TYPE_PDN);
}

static int ts_init(struct platform_device *pdev)
{
    ...
    //针对android加上的
    __set_bit(EV_KEY, codec_ts_input->evbit);
    __set_bit(BTN_TOUCH, codec_ts_input->keybit);
    __set_bit(EV_SYN,codec_ts_input->evbit);
    //针对android加上的 end
    ...
}
input_report_key() 报按键
input_report_abs() 报绝对坐标 函数分析:
Input.h (kernel\include\linux):
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
  input_event(dev, EV_ABS, code, value)
调用的是 Input.c (kernel\drivers\input)中的函数
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
    unsigned long flags;
                检测是否为支持的事件
    if (is_event_supported(type, dev->evbit, EV_MAX)) {
                //锁中断
        spin_lock_irqsave(&dev->event_lock, flags);
        add_input_randomness(type, code, value);
        input_handle_event(dev, type, code, value);
        spin_unlock_irqrestore(&dev->event_lock, flags);
    }
}
is_event_supported() 函数检测当前事件类型,输入子系统支持的事件类型在文件
kernel/include/linux/input.h 中定义:
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f

static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
    int disposition = INPUT_IGNORE_EVENT;
    ...
    switch (type) {
    case EV_ABS:
        if (is_event_supported(code, dev->absbit, ABS_MAX)) {

            value = input_defuzz_abs_event(value,
                    dev->abs[code], dev->absfuzz[code]);

            if (dev->abs[code] != value) {
                dev->abs[code] = value;
                disposition = INPUT_PASS_TO_HANDLERS;
            }
        }
        break;
    ...
    if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
        dev->sync = 0;

    if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
        dev->event(dev, type, code, value);

    if (disposition & INPUT_PASS_TO_HANDLERS)
        input_pass_event(dev, type, code, value);
}
函数的code是
ABS_PRESSURE
BTN_TOUCH
static void input_pass_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
    struct input_handle *handle;

    rcu_read_lock();

    handle = rcu_dereference(dev->grab);
    if (handle)
        handle->handler->event(handle, type, code, value);
    else
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
            if (handle->open)
                handle->handler->event(handle,
                            type, code, value);
    rcu_read_unlock();
}


Apm-power.c (kernel\drivers\input):    error = input_register_handle(handle);
Evbug.c (kernel\drivers\input):    error = input_register_handle(handle);
Evdev.c (kernel\drivers\input):    error = input_register_handle(&evdev->handle);
Input.c (kernel\drivers\input): * input_register_handle - register a new input handle
Input.c (kernel\drivers\input):int input_register_handle(struct input_handle *handle)
Input.c (kernel\drivers\input):EXPORT_SYMBOL(input_register_handle);
Input.h (kernel\include\linux):int input_register_handle(struct input_handle *);
Joydev.c (kernel\drivers\input):    error = input_register_handle(&joydev->handle);
Keyboard.c (kernel\drivers\char):    error = input_register_handle(handle);
Keychord.c (kernel\drivers\input\misc):    ret = input_register_handle(handle);
Keyreset.c (kernel\drivers\input):    ret = input_register_handle(handle);
Mousedev.c (kernel\drivers\input):        error = input_register_handle(&mousedev->handle);
Rfkill-input.c (kernel\net\rfkill):    error = input_register_handle(handle);


==================================================================
Littleton 开发板相关驱动:
Littleton.c (kernel\arch\arm\mach-pxa)
static void __init littleton_init(void)
{
    /* initialize MFP configurations */
    pxa3xx_mfp_config(ARRAY_AND_SIZE(littleton_mfp_cfg));

    /*
     * Note: we depend bootloader set the correct
     * value to MSC register for SMC91x.
     */
    platform_device_register(&smc91x_device);

    littleton_init_lcd();
    littleton_init_keypad();
    littleton_init_nand();
}


Littleton.c (kernel\arch\arm\mach-pxa)
键盘初始化
#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
static unsigned int littleton_matrix_key_map[] = {
    /* KEY(row, col, key_code) */
    KEY(1, 3, KEY_0), KEY(0, 0, KEY_1), KEY(1, 0, KEY_2), KEY(2, 0, KEY_3),
    KEY(0, 1, KEY_4), KEY(1, 1, KEY_5), KEY(2, 1, KEY_6), KEY(0, 2, KEY_7),
    KEY(1, 2, KEY_8), KEY(2, 2, KEY_9),

    KEY(0, 3, KEY_KPASTERISK),     /* * */
    KEY(2, 3, KEY_KPDOT),         /* # */

    KEY(5, 4, KEY_ENTER),

    KEY(5, 0, KEY_UP),
    KEY(5, 1, KEY_DOWN),
    KEY(5, 2, KEY_LEFT),
    KEY(5, 3, KEY_RIGHT),
    KEY(3, 2, KEY_HOME),
    KEY(4, 1, KEY_END),
    KEY(3, 3, KEY_BACK),

    KEY(4, 0, KEY_SEND),
    KEY(4, 2, KEY_VOLUMEUP),
    KEY(4, 3, KEY_VOLUMEDOWN),

    KEY(3, 0, KEY_F22),    /* soft1 */
    KEY(3, 1, KEY_F23),    /* soft2 */
};

static struct pxa27x_keypad_platform_data littleton_keypad_info = {
    .matrix_key_rows    = 6,
    .matrix_key_cols    = 5,
    .matrix_key_map        = littleton_matrix_key_map,
    .matrix_key_map_size    = ARRAY_SIZE(littleton_matrix_key_map),

    .enable_rotary0        = 1,
    .rotary0_up_key        = KEY_UP,
    .rotary0_down_key    = KEY_DOWN,

    .debounce_interval    = 30,
};
static void __init littleton_init_keypad(void)
{
    pxa_set_keypad_info(&littleton_keypad_info);
}
#else
static inline void littleton_init_keypad(void) {}
#endif



=========================================
大板子添加了lifeng触摸屏驱动
linux-2.6.25/drivers/input/touchscreen/bbk_lifeng_ts.c
需要修改的配置文件
linux-2.6.25/drivers-r8/input/touchscreen/Kconfig
在 if INPUT_TOUCHSCREEN 后面加上下面内容:
config TOUCHSCREEN_LIFENG
        tristate "LIFENG based touchscreen"
    depends on INPUT_EVDEV
    default y
    help
      Say Y here if you want to enable lifeng touch driver
修改 linux-2.6.25/drivers-r8/input/touchscreen/Makefine 文件,添加:
obj-$(CONFIG_TOUCHSCREEN_LIFENG)       += bbk_lifeng_ts.o
让起编译  linux-2.6.25/drivers/input/touchscreen/bbk_lifeng_ts.c 文件


文件 linux-2.6.25/arch/arm/mach-pxa/littleton.c 中需要修改的地方:
static unsigned int littleton_matrix_key_map[] = {
    /* KEY(row, col, key_code) */
    //KEY(1, 3, KEY_0), KEY(0, 0, KEY_1), KEY(1, 0, KEY_2), KEY(2, 0, KEY_3),
    //KEY(0, 1, KEY_4), KEY(1, 1, KEY_5), KEY(2, 1, KEY_6), KEY(0, 2, KEY_7),
    //KEY(1, 2, KEY_8), KEY(2, 2, KEY_9),
    KEY(0, 2, KEY_1), KEY(0, 3, KEY_2), KEY(0, 4, KEY_3), KEY(1, 2, KEY_4),
    KEY(1, 3, KEY_5), KEY(1, 4, KEY_6), KEY(2, 2, KEY_7), KEY(2, 3, KEY_8),
    KEY(2, 4, KEY_9), KEY(0, 1, KEY_0),

    KEY(0, 0, KEY_MENU),            /*添加的*/

        //KEY(0, 3, KEY_KPASTERISK),     /* * */
    //KEY(2, 3, KEY_KPDOT),         /* # */

    KEY(1, 0, KEY_KPASTERISK),     /* * */
    KEY(1, 1, KEY_KPDOT),         /* # */
}

static struct pxa27x_keypad_platform_data littleton_keypad_info = {
    .matrix_key_rows    = 5, /* 由 6 改为 5 */
    .matrix_key_cols    = 4, /* 由 5 改为 4 */
    ...
};
//添加
static struct platform_device lifeng_ts_device = {
    .name         = "lifeng-ts",
    .id         = -1,
};

static struct platform_device *devices[] __initdata = {
    &smc91x_device,
    &lifeng_ts_device,//添加
    &micco_ts_device,
    &micco_bl_device,
    &micco_kp_bl_device,
    &pxa3xx_device_imm,
    &micco_charger_device,
};
========================================


一篇介绍linux输入子系统非常好的文章
linux输入子系统
http://hi.baidu.com/licett/blog/item/bf0102f934ebcd5c252df2b3.html

KeyEvent newKeyEvent(InputDevice device, long downTime,long eventTime, boolean down, int keycode, int repeatCount,int scancode, int flags)
 
mThread.start();
Thread mThread = new Thread("InputDeviceReader")
readEvent(ev);
readEvent
android_server_KeyInputQueue_readEvent
hub->getEvent(&deviceId, &type, &scancode, &keycode,
 
 
ViewRoot()
  mWindow = new W(this, context);
    static class W extends IWindow.Stub
    public void dispatchKey(KeyEvent event)
      public void dispatchKey(KeyEvent event)
      Message msg = obtainMessage(DISPATCH_KEY);
      sendMessageAtTime(msg, event.getEventTime());
      
      
  ViewRoot.java (frameworks\base\core\java\android\view)    
      public void handleMessage(Message msg)
        case DISPATCH_KEY:
          deliverKeyEvent((KeyEvent)msg.obj, true);
          如果是输入框
          if (mLastWasImTarget)
          {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null && mView != null) {
                int seq = enqueuePendingEvent(event, sendDone);
                if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME:



AC 97 AUDIO + TOUCHPANEL CODEC
WM9713 - AC 97 AUDIO TOUCHPANEL CODEC - Wolfson Microelectronics plc
WM9713,它的主要功能其实就是A/D转换和D/A转换,播放时把数字信号转换成模拟信号,录音时把模拟信号转换成数字信号。因为触摸屏也用到了A/D转换,为了重用这个功能,所以WM9713集成了音频处理和触摸屏处理两部分功能。
WM9713支持AC97标准,通过64个寄存器对它进行控制,这个在WM9713的datasheet里有详细的描述。
WM9713与PXA300之间通过同步串号SSP通信。PXA300在硬件上对AC97有支持,它提供了FIFO,所以音频数据可以通过DMA方式读写。
它旁边是WM9713音频芯片,主要功能是A/D转换和D/A转换,播放时把数字信号转换成模拟信号,录音时把模拟信号转换成数字信号。因为触摸屏也用到了A/D转换,为了重用这个功能,所以WM9713集成了音频处理和触摸屏处理两部分功能;
三星智能机i900 音频和触摸使用的是 wolfson  WM9713


9713触摸问题
我现在用9713采样触摸屏的点,用的是polling的模式,一次采样5个点!但是发现如果触摸笔不离开屏(不停的采样),会降低系统的性能!所以想改用 DMA的模式(Continuous mode),尝试了几次没有成功(点击第一次之后就没反应了),请问有没有使用过9713 DMA模式采样的兄弟们,给点提示!谢谢!环境是:wince6,pxa310, wm9713和4-wire触摸屏。bsp是Mavell的.
文档说,dma模式可以节约cpu干预而损失的时间,比polling方式少大概一半时间(我的实验结果),其他都是一样的

Audio:1个AC97,采用wolf公司的WM9713,1个II2S,采用wolf公司的WM8580A;

     
WM8580 介绍
http://www.wolfsonmicro.cn/products/WM8580/
WM8580是一款带有S/PDIF收发器的多路音频编码解码器(CODEC),特别适用于DVD以及需要有环绕立体声处理的家用高保真音响、汽车和其它视听设备等应用。
该器件集成了一个内置的立体声24位多比特模数转换器(ADC),支持16~32位字长的数字音频输出及8kHz~192kHz的采样率。
此外,该器件还包含三个立体声24位多比特数模转换器,每个都带有各自的超采样数字内插滤波器,并支持16~32位字长的数字音频输入及8kHz~192kHz的采样率。每路DAC通道都有独立的数字音量和静音控制。
两个独立的音频数据接口支持I2S、左对齐、右对齐及DSP数字音频格式。每个音频接口都可以工作在主时钟模式或从时钟模式。
S/PDIF收发器可与IEC-60958-3兼容,支持32k/s~192k/s的帧频。它带有四个多路复用输入端及一个输出端。它还有内置的状态和错误监控功能,其结果可以通过串行接口或者GPO接脚进行传输。同时,还提供S/PDIF通道块配置功能。
该器件带有两个锁相环(PLL),可以对它们进行单独配置,以生成两个系统时钟作为内部或外部使用。
对该器件的控制和设置是通过一个两线制或三线制(可与SPI兼容)的串行接口来实现的。串行接口为所有功能的应用提供了渠道,这些功能包括通道选择、音量控制、静音、去/加重、S/PDIF控制/状态,以及电源管理工具。另外,该器件的硬件管理模式还可以通过选择管脚来激活或禁止器件的功能。
WM8580是采用48引脚TQFP封装,现可供货。

WM9713
http://www.wolfsonmicro.cn/products/WM9713
带有触摸屏控制器的声音和音频编码解码器
WM9713L是一款高度集成的输入/输出器件,专为移动计算和通信而设计。
    该芯片采用了双编解码器运行的架构,通过AC连接接口支持高保真(Hi-Fi)立体声编解码器功能,同时还通过一个PCM型同步串行端口(SSP)额外支持声音编码解码器功能。该芯片还提供了一个第三辅助数字模拟转换器,用于采用与主编码解码器不同的采样率,支持产生监控音调(supervisory tones)或铃声等。
    该器件能够直接连接到一个4线或5线触摸屏、单声道或立体声麦克风、立体声耳机以及立体声扬声器,从而降低了系统元器件总数。与耳机、扬声器以及听筒的无电容连接,可节省成本和印刷电路板面积。另外,还提供了多个模拟输入和输出引脚,与无线通讯设备模拟连接无缝集成在一起。
    通过一个符合AC-97标准的单独的AC-Link接口,可以连接和控制所有的芯片功能。可以直接输入24.576MHz的主时钟,或者由板上的锁相环从一个13MHz (或其他频率)时钟从内部产生,该锁相环支持从2.048Mhz 到78.6Mhz的大范围输入时钟。
WM9713L运行的电源电压范围为1.8V-3.6V;芯片上的任何部分都可以通过软件控制实现关断来降低功耗。该器件采用了小型的无引线7×7mm封装,是掌上和便携系统应用的理想方案。


音频处理介绍(Linux手机) 李先静
http://blog.csdn.net/absurd/archive/2007/11/07/1872258.aspx
浅析linux下键盘设备工作和注册流程[转]
http://qgjie456.blog.163.com/blog/static/35451367200810361449524/
linux设备驱动之控制台驱动
http://blog.chinaunix.net/u2/79779/showart_1211518.html



========================


KeyInputQueue.java (frameworks\base\services\java\com\android\server):
的线程  Thread mThread = new Thread("InputDeviceReader") 本地调用:
readEvent(ev);读取按键。readEvent 调用的是文件:
com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)中的函数:
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
                                          jobject event)
android_server_KeyInputQueue_readEvent中有:
hub = new EventHub;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
            &flags, &value, &when);

hub->getEvent 调用的是
EventHub.cpp (frameworks\base\libs\ui) 文件中的函数:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
        int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
        int32_t* outValue, nsecs_t* outWhen)
读取按键。

class RefBase::weakref_impl : public RefBase::weakref_type



在系统启动后,android 会通过
static const char *device_path = "/dev/input";
bool EventHub::openPlatformInput(void)
  res = scan_dir(device_path);


通过下面的函数打开设备。
int EventHub::open_device(const char *deviceName)
{
  ...
  fd = open(deviceName, O_RDWR);
  ...
  mFDs[mFDCount].fd = fd;
  mFDs[mFDCount].events = POLLIN;
  ...
  ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);
  ...
  const char* root = getenv("ANDROID_ROOT");
  snprintf(keylayoutFilename, sizeof(keylayoutFilename),
                 "%s/usr/keylayout/%s.kl", root, tmpfn);
  ...
  device->layoutMap->load(keylayoutFilename);
  ...
}
打开设备的时候,如果 device->classes&CLASS_KEYBOARD 不等于 0 表明是键盘。
常用输入设备的定义有:
enum {
        CLASS_KEYBOARD      = 0x00000001, //键盘
        CLASS_ALPHAKEY      = 0x00000002, //
        CLASS_TOUCHSCREEN   = 0x00000004, //触摸屏
        CLASS_TRACKBALL     = 0x00000008  //轨迹球
    };
打开键盘设备的时候通过上面的  ioctl 获得设备名称,命令字 EVIOCGNAME 的定义在文件:
kernel/include/linux/input.h 中。
#define EVIOCGNAME(len)   _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
在内核键盘驱动文件 drivers/input/keyboard/pxa27x_keypad.c 中定义了设备名称:pxa27x-keypad
static struct platform_driver pxa27x_keypad_driver = {
    .probe        = pxa27x_keypad_probe,
    .remove        = __devexit_p(pxa27x_keypad_remove),
    .suspend    = pxa27x_keypad_suspend,
    .resume        = pxa27x_keypad_resume,
    .driver        = {
        .name    = "pxa27x-keypad",
        .owner    = THIS_MODULE,
    },
};
ANDROID_ROOT 为环境变量,在android的命令模式下通过 printenv 可以知道它为: system
所以 keylayoutFilename 为:/system/usr/keylayout/pxa27x-keypad.kl
pxa27x-keypad.kl 定义了按键映射,具体内容如下:
----------------------
# NUMERIC KEYS 3x4
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 83  POUND
key 55  STAR

# FUNCTIONAL KEYS
key 231  MENU        WAKE_DROPPED
key 192  BACK           WAKE_DROPPED
key 193  HOME       WAKE
key 107  DEL        WAKE
key 102  CALL        WAKE_DROPPED
key 158  ENDCALL     WAKE_DROPPED
key 28   DPAD_CENTER     WAKE
key 115  VOLUME_UP
key 114  VOLUME_DOWN
----------------------
如果没有定义键盘映射文件,那么默认使用系统的 /system/usr/keylayout/qwerty.kl
可以修改 /system/usr/keylayout/qwerty.kl 文件改变Android公司的按键映射。

device->layoutMap->load(keylayoutFilename) 调用的是文件:
KeyLayoutMap.cpp (frameworks\base\libs\ui)中的函数:
status_t KeyLayoutMap::load(const char* filename)通过解析 pxa27x-keypad.kl
把按键的映射关系保存在 :KeyedVector<int32_t,Key> m_keys; 中。
当获得按键事件以后调用:
status_t KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags)
由映射关系 KeyedVector<int32_t,Key> m_keys 把扫描码转换成andorid上层可以识别的按键。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值