极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 19863|回复: 0

用pcDuino的otg做鼠标键盘

[复制链接]
发表于 2013-12-20 10:18:32 | 显示全部楼层 |阅读模式
                                                                                                有人说现代社会,计算机锁住了人类的双手,为什么呢?因为基本上一个人一天上班8小时手是不断的在键盘鼠标上面工作的。我们能不能让电脑更智能一些,例如电脑放弃人类的双手 ,更智能的工作,这里pcDuino跟你提过一个建议。让兼容arduino的各种传感器去采集信号,让pcDuino帮你实现鼠标键盘的各种工作。

               
       
               
       

第一步: 修改内核:

                                                                                vim vim linux-sunxi/drivers/usb/gadget/hid.c #在里面添加几个结构体和几行代码
static LIST_HEAD(hidg_func_list);
/* hid descriptor for a keyboard */
static struct hidg_func_descriptor pcduino_keyboard_date = {
    .subclass       = 0, /* No subclass */
    .protocol       = 1, /* Keyboard */
    .report_length      = 8,
    .report_desc_length = 63,
    .report_desc        = {
        0×05, 0×01, /* USAGE_PAGE (Generic Desktop)           */
        0×09, 0×06, /* USAGE (Keyboard)                       */
        0xa1, 0×01, /* COLLECTION (Application)               */
        0×05, 0×07, /*   USAGE_PAGE (Keyboard)                */
        0×19, 0xe0, /*   USAGE_MINIMUM (Keyboard LeftControl) */
        0×29, 0xe7, /*   USAGE_MAXIMUM (Keyboard Right GUI)   */
        0×15, 0×00, /*   LOGICAL_MINIMUM (0)                  */
        0×25, 0×01, /*   LOGICAL_MAXIMUM (1)                  */
        0×75, 0×01, /*   REPORT_SIZE (1)                      */
        0×95, 0×08, /*   REPORT_COUNT (8)                     */
        0×81, 0×02, /*   INPUT (Data,Var,Abs)                 */
        0×95, 0×01, /*   REPORT_COUNT (1)                     */
        0×75, 0×08, /*   REPORT_SIZE (8)                      */
        0×81, 0×03, /*   INPUT (Cnst,Var,Abs)                 */
        0×95, 0×05, /*   REPORT_COUNT (5)                     */
        0×75, 0×01, /*   REPORT_SIZE (1)                      */
        0×05, 0×08, /*   USAGE_PAGE (LEDs)                    */
        0×19, 0×01, /*   USAGE_MINIMUM (Num Lock)             */
        0×29, 0×05, /*   USAGE_MAXIMUM (Kana)                 */
        0×91, 0×02, /*   OUTPUT (Data,Var,Abs)                */
        0×95, 0×01, /*   REPORT_COUNT (1)                     */
        0×75, 0×03, /*   REPORT_SIZE (3)                      */
        0×91, 0×03, /*   OUTPUT (Cnst,Var,Abs)                */
        0×95, 0×06, /*   REPORT_COUNT (6)                     */
        0×75, 0×08, /*   REPORT_SIZE (8)                      */
        0×15, 0×00, /*   LOGICAL_MINIMUM (0)                  */
        0×25, 0×65, /*   LOGICAL_MAXIMUM (101)                */
        0×05, 0×07, /*   USAGE_PAGE (Keyboard)                */
        0×19, 0×00, /*   USAGE_MINIMUM (Reserved)             */
        0×29, 0×65, /*   USAGE_MAXIMUM (Keyboard Application) */
        0×81, 0×00, /*   INPUT (Data,Ary,Abs)                 */
        0xc0        /* END_COLLECTION                         */
    }
};
static struct platform_device pcduino_hid_keyboard = {
    .name           = “hidg”,
    .id         = 0,
    .num_resources      = 0,
    .resource       = 0,
    .dev.platform_data  = &pcduino_keyboard_date,
};
/* hid descriptor for a mouse */
static struct hidg_func_descriptor pcduino_mouse_data = {
.subclass = 0, //No SubClass
.protocol = 2, //Mouse
.report_length = 4,
.report_desc_length = 52,
.report_desc = {
0×05, 0×01,  //Usage Page(Generic Desktop Controls)
0×09, 0×02,  //Usage (Mouse)
0xa1, 0×01,  //Collction (Application)
0×09, 0×01,  //Usage (pointer)
0xa1, 0×00,  //Collction (Physical)
0×05, 0×09,  //Usage Page (Button)
0×19, 0×01,  //Usage Minimum(1)
0×29, 0×05,  //Usage Maximum(5)
0×15, 0×00,  //Logical Minimum(1)
0×25, 0×01,  //Logical Maximum(1)
0×95, 0×05,  //Report Count(5)
0×75, 0×01,  //Report Size(1)
0×81, 0×02,  //Input(Data,Variable,Absolute,BitField)
0×95, 0×01,  //Report Count(1)
0×75, 0×03,  //Report Size(3)
0×81, 0×01,  //Input(Constant,Array,Absolute,BitField)
0×05, 0×01,  //Usage Page(Generic Desktop Controls)
0×09, 0×30,  //Usage(x)
0×09, 0×31,  //Usage(y)
0×09, 0×38,  //Usage(Wheel)
0×15, 0×81,  //Logical Minimum(-127)
0×25, 0x7F,  //Logical Maximum(127)
0×75, 0×08,  //Report Size(8)
0×95, 0×03,  //Report Count(3)
0×81, 0×06,  //Input(Data,Variable,Relative,BitField)
0xc0,  //End Collection
0xc0  //End Collection
}
};

static struct platform_device pcduino_hid_mouse = {
.name = “hidg”,
.id = 1,
.num_resources = 0,
.resource = 0,
.dev.platform_data = &pcduino_mouse_data,
};
static int __init hidg_init(void)
{
    int status;

    status = platform_device_register(&pcduino_hid_keyboard);
    if (status < 0) {
        printk(“hid keyboard  reg failed\n”);
        platform_device_unregister(&pcduino_hid_keyboard);
        return status;
    }
    status = platform_device_register(&pcduino_hid_mouse);
    if (status < 0) {
        printk(“hid mouse reg  failed\n”);
        platform_device_unregister(&pcduino_hid_mouse);
        return status;
    }


    status = platform_driver_probe(&hidg_plat_driver,
                hidg_plat_driver_probe);
    if (status < 0)
        return status;

    status = usb_composite_probe(&hidg_driver, hid_bind);
    if (status < 0)
        platform_driver_unregister(&hidg_plat_driver);

    return status;
}
module_init(hidg_init);

static void __exit hidg_cleanup(void)
{
    platform_driver_unregister(&hidg_plat_driver);
    platform_device_unregister(&pcduino_hid_keyboard);
    platform_device_unregister(&pcduino_hid_mouse);
    usb_composite_unregister(&hidg_driver);
}
结构体是需要添加的,后面两个函数里面要加上结构注册。这里说明一下,根据usb协议,每一个设备都要有描述符,两个结构体就是hid 鼠标键盘的描述符。下面配置内核。
Device Drivers  —>
         
  • USB support  —>
                       <M>   USB Gadget Support  —>
                                          <M>     SoftWinner SUN4I USB Peripheral Controller                              
                       < >     Dummy HCD (DEVELOPMENT)                        
                             USB Gadget Drivers                                
                       < >     Gadget Zero (DEVELOPMENT)                       
                       < >     Audio Gadget (EXPERIMENTAL)                     
                       <M>     Ethernet Gadget (with CDC Ethernet support)     
                      
  •        RNDIS support                                 
                       [ ]       Ethernet Emulation Model (EEM) support        
                       < >     Network Control Model (NCM) support            
                       < >     Gadget Filesystem (EXPERIMENTAL)               
                       < >     Function Filesystem (EXPERIMENTAL)              
                       < >     File-backed Storage Gadget (DEPRECATED)         
                       < >     Mass Storage Gadget                             
                       <M>     Serial Gadget (with CDC ACM and CDC OBEX support)
                       <M>     MIDI Gadget (EXPERIMENTAL)                     
                       <M>     Printer Gadget                                 
                       < >     CDC Composite Device (Ethernet and ACM)         
                       < >     CDC Composite Device (ACM and mass storage)      
                       < >     Multifunction Composite Gadget (EXPERIMENTAL)   
                       <M>     HID Gadget                                      
                       < >     EHCI Debug Device Gadget                        
                       < >     USB Webcam Gadget

    第二步:加载内核

                                                                                    关于怎么使用编译生成的内核,已经写过好多文章了。这里大致提一下。
    sudo mount  /dev/nanda  /mnt
    tar xvf pcduino_a10_hwpack_20131129.tar.xz
    cp  kernel/*  /mnt  -f
    cp  rootfs/lib/modules/3.4.29+  /lib/modules/
    vim /etc/modules #添加下面内容
    sw_usb_udc
    g_hid

    第三步:测试驱动

                                                                                    这里测试用到的是linux-sunxi/Documentation/usb里面的测试程序。
    1 /* hid_gadget_test */
      2
      3 #include <pthread.h>
      4 #include <string.h>
      5 #include <stdio.h>
      6 #include <ctype.h>
      7 #include <fcntl.h>
      8 #include <errno.h>
      9 #include <stdio.h>
    10 #include <stdlib.h>
    11 #include <unistd.h>
    12
    13 #define BUF_LEN 512
    14
    15 struct options {
    16     const char    *opt;
    17     unsigned char val;
    18 };
    19
    20 static struct options kmod[] = {
    21     {.opt = “–left-ctrl”,      .val = 0×01},
    22     {.opt = “–right-ctrl”,     .val = 0×10},
    23     {.opt = “–left-shift”,     .val = 0×02},
    24     {.opt = “–right-shift”,    .val = 0×20},
    25     {.opt = “–left-alt”,       .val = 0×04},
    26     {.opt = “–right-alt”,      .val = 0×40},
    27     {.opt = “–left-meta”,      .val = 0×08},
    28     {.opt = “–right-meta”,     .val = 0×80},
    29     {.opt = NULL}
    30 };
    31
    32 static struct options kval[] = {
    33     {.opt = “–return”, .val = 0×28},
    34     {.opt = “–esc”,    .val = 0×29},
    35     {.opt = “–bckspc”, .val = 0x2a},
    36     {.opt = “–tab”,    .val = 0x2b},
    37     {.opt = “–spacebar”,   .val = 0x2c},
    38     {.opt = “–caps-lock”,  .val = 0×39},
    39     {.opt = “–f1″,     .val = 0x3a},
    40     {.opt = “–f2″,     .val = 0x3b},
    41     {.opt = “–f3″,     .val = 0x3c},
    42     {.opt = “–f4″,     .val = 0x3d},
    43     {.opt = “–f5″,     .val = 0x3e},
    44     {.opt = “–f6″,     .val = 0x3f},
    45     {.opt = “–f7″,     .val = 0×40},
    46     {.opt = “–f8″,     .val = 0×41},
    47     {.opt = “–f9″,     .val = 0×42},
    48     {.opt = “–f10″,    .val = 0×43},
    49     {.opt = “–f11″,    .val = 0×44},
    50     {.opt = “–f12″,    .val = 0×45},
    51     {.opt = “–insert”, .val = 0×49},
    52     {.opt = “–home”,   .val = 0x4a},
    53     {.opt = “–pageup”, .val = 0x4b},
    54     {.opt = “–del”,    .val = 0x4c},
    55     {.opt = “–end”,    .val = 0x4d},
    56     {.opt = “–pagedown”,   .val = 0x4e},
    57     {.opt = “–right”,  .val = 0x4f},
    58     {.opt = “–left”,   .val = 0×50},
    59     {.opt = “–down”,   .val = 0×51},
    60     {.opt = “–kp-enter”,   .val = 0×58},
    61     {.opt = “–up”,     .val = 0×52},
    62     {.opt = “–num-lock”,   .val = 0×53},
    63     {.opt = NULL}
    64 };
    65
    66 int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
    67 {
    68     char *tok = strtok(buf, ” “);
    69     int key = 0;
    70     int i = 0;
    71
    72     for (; tok != NULL; tok = strtok(NULL, ” “)) {
    73
    74         if (strcmp(tok, “–quit”) == 0)
    75             return -1;
    76
    77         if (strcmp(tok, “–hold”) == 0) {
    78             *hold = 1;
    79             continue;
    80         }
    81
    82         if (key < 6) {
    83             for (i = 0; kval.opt != NULL; i++)
    84                 if (strcmp(tok, kval.opt) == 0) {
    85                     report[2 + key++] = kval.val;
    86                     break;
    87                 }
    88             if (kval.opt != NULL)
    89                 continue;
    90         }
    91
    92         if (key < 6)
    93             if (islower(tok[0])) {
    94                 report[2 + key++] = (tok[0] – (‘a’ – 0×04));
    95                 continue;
    96             }
    97
    98         for (i = 0; kmod.opt != NULL; i++)
    99             if (strcmp(tok, kmod.opt) == 0) {
    100                 report[0] = report[0] | kmod.val;
    101                 break;
    102             }
    103         if (kmod.opt != NULL)
    104             continue;
    105
    106         if (key < 6)
    107             fprintf(stderr, “unknown option: %s\n”, tok);
    108     }
    109     return 8;
    110 }
    111
    112 static struct options mmod[] = {
    113     {.opt = “–b1″, .val = 0×01},
    114     {.opt = “–b2″, .val = 0×02},
    115     {.opt = “–b3″, .val = 0×04},
    116     {.opt = NULL}
    117 };
    118
    119 int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
    120 {
    121     char *tok = strtok(buf, ” “);
    122     int mvt = 0;
    123     int i = 0;
    124     for (; tok != NULL; tok = strtok(NULL, ” “)) {
    125
    126         if (strcmp(tok, “–quit”) == 0)
    127             return -1;
    128
    129         if (strcmp(tok, “–hold”) == 0) {
    130             *hold = 1;
    131             continue;
    132         }
    133
    134         for (i = 0; mmod.opt != NULL; i++)
    135             if (strcmp(tok, mmod.opt) == 0) {
    136                 report[0] = report[0] | mmod.val;
    137                 break;
    138             }
    139         if (mmod.opt != NULL)
    140             continue;
    141
    142         if (!(tok[0] == ‘-’ && tok[1] == ‘-’) && mvt < 2) {
    143             errno = 0;
    144             report[1 + mvt++] = (char)strtol(tok, NULL, 0);
    145             if (errno != 0) {
    146                 fprintf(stderr, “Bad value:’%s’\n”, tok);
    147                 report[1 + mvt--] = 0;
    148             }
    149             continue;
    150         }
    151
    152         fprintf(stderr, “unknown option: %s\n”, tok);
    153     }
    154     return 3;
    155 }
    156
    157 static struct options jmod[] = {
    158     {.opt = “–b1″,     .val = 0×10},
    159     {.opt = “–b2″,     .val = 0×20},
    160     {.opt = “–b3″,     .val = 0×40},
    161     {.opt = “–b4″,     .val = 0×80},
    162     {.opt = “–hat1″,   .val = 0×00},
    163     {.opt = “–hat2″,   .val = 0×01},
    164     {.opt = “–hat3″,   .val = 0×02},
    165     {.opt = “–hat4″,   .val = 0×03},
    166     {.opt = “–hatneutral”, .val = 0×04},
    167     {.opt = NULL}
    168 };
    169
    170 int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
    171 {
    172     char *tok = strtok(buf, ” “);
    173     int mvt = 0;
    174     int i = 0;
    175
    176     *hold = 1;
    177
    178     /* set default hat position: neutral */
    179     report[3] = 0×04;
    180
    181     for (; tok != NULL; tok = strtok(NULL, ” “)) {
    182
    183         if (strcmp(tok, “–quit”) == 0)
    184             return -1;
    185
    186         for (i = 0; jmod.opt != NULL; i++)
    187             if (strcmp(tok, jmod.opt) == 0) {
    188                 report[3] = (report[3] & 0xF0) | jmod.val;
    189                 break;
    190             }
    191         if (jmod.opt != NULL)
    192             continue;
    193
    194         if (!(tok[0] == ‘-’ && tok[1] == ‘-’) && mvt < 3) {
    195             errno = 0;
    196             report[mvt++] = (char)strtol(tok, NULL, 0);
    197             if (errno != 0) {
    198                 fprintf(stderr, “Bad value:’%s’\n”, tok);
    199                 report[mvt--] = 0;
    200             }
    201             continue;
    202         }
    203
    204         fprintf(stderr, “unknown option: %s\n”, tok);
    205     }
    206     return 4;
    207 }
    208
    209 void print_options(char c)
    210 {
    211     int i = 0;
    212
    213     if (c == ‘k’) {
    214         printf(“    keyboard options:\n”
    215                “        –hold\n”);
    216         for (i = 0; kmod.opt != NULL; i++)
    217             printf(“\t\t%s\n”, kmod.opt);
    218         printf(“\n  keyboard values:\n”
    219                “        [a-z] or\n”);
    220         for (i = 0; kval.opt != NULL; i++)
    221             printf(“\t\t%-8s%s”, kval.opt, i % 2 ? “\n” : “”);
    222         printf(“\n”);
    223     } else if (c == ‘m’) {
    224         printf(“    mouse options:\n”
    225                “        –hold\n”);
    226         for (i = 0; mmod.opt != NULL; i++)
    227             printf(“\t\t%s\n”, mmod.opt);
    228         printf(“\n  mouse values:\n”
    229                ”        Two signed numbers\n”
    230                “–quit to close\n”);
    231     } else {
    232         printf(“    joystick options:\n”);
    233         for (i = 0; jmod.opt != NULL; i++)
    234             printf(“\t\t%s\n”, jmod.opt);
    235         printf(“\n  joystick values:\n”
    236                “        three signed numbers\n”
    237                “–quit to close\n”);
    238     }
    239 }
    240
    241 int main(int argc, const char *argv[])
    242 {
    243     const char *filename = NULL;
    244     int fd = 0;
    245     char buf[BUF_LEN];
    246     int cmd_len;
    247     char report[8];
    248     int to_send = 8;
    249     int hold = 0;
    250     fd_set rfds;
    251     int retval, i;
    252
    253     if (argc < 3) {
    254         fprintf(stderr, “Usage: %s devname mouse|keyboard|joystick\n”,
    255             argv[0]);
    256         return 1;
    257     }
    258
    259     if (argv[2][0] != ‘k’ && argv[2][0] != ‘m’ && argv[2][0] != ‘j’)
    260       return 2;
    261
    262     filename = argv[1];
    263
    264     if ((fd = open(filename, O_RDWR, 0666)) == -1) {
    265         perror(filename);
    266         return 3;
    267     }
    268
    269     print_options(argv[2][0]);
    270
    271     while (42) {
    272
    273         FD_ZERO(&rfds);
    274         FD_SET(STDIN_FILENO, &rfds);
    275         FD_SET(fd, &rfds);
    276
    277         retval = select(fd + 1, &rfds, NULL, NULL, NULL);
    278         if (retval == -1 && errno == EINTR)
    279             continue;
    280         if (retval < 0) {
    281             perror(“select()”);
    282             return 4;
    283         }
    284
    285         if (FD_ISSET(fd, &rfds)) {
    286             cmd_len = read(fd, buf, BUF_LEN – 1);
    287             printf(“recv report:”);
    288             for (i = 0; i < cmd_len; i++)
    289                 printf(” %02x”, buf);
    290             printf(“\n”);
    291         }
    292
    293         if (FD_ISSET(STDIN_FILENO, &rfds)) {
    294             memset(report, 0×0, sizeof(report));
    295             cmd_len = read(STDIN_FILENO, buf, BUF_LEN – 1);
    296
    297             if (cmd_len == 0)
    298                 break;
    299
    300             buf[cmd_len - 1] = ‘\0′;
    301             hold = 0;
    302
    303             memset(report, 0×0, sizeof(report));
    304             if (argv[2][0] == ‘k’)
    305                 to_send = keyboard_fill_report(report, buf, &hold);
    306             else if (argv[2][0] == ‘m’)
    307                 to_send = mouse_fill_report(report, buf, &hold);
    308             else
    309                 to_send = joystick_fill_report(report, buf, &hold);
    310
    311             if (to_send == -1)
    312                 break;
    313
    314             if (write(fd, report, to_send) != to_send) {
    315                 perror(filename);
    316                 return 5;
    317             }
    318             if (!hold) {
    319                 memset(report, 0×0, sizeof(report));
    320                 if (write(fd, report, to_send) != to_send) {
    321                     perror(filename);
    322                     return 6;
    323                 }
    324             }
    325         }
    326     }
    327
    328     close(fd);
    329     return 0;
    330 }
    编译:gcc  gadget_hid.c
    运行:
    root@ubuntu:/home/ubuntu# ./a.out /dev/hidg0  k   # 键盘
    keyboard options:
    –hold
    –left-ctrl
    –right-ctrl
    –left-shift
    –right-shift
    –left-alt
    –right-alt
    –left-meta
    –right-meta
    keyboard values:
    [a-z] or
    –return             –esc
    –bckspc             –tab
    –spacebar                 –caps-lock
    –f1                –f2
    –f3                –f4
    –f5                –f6
    –f7                –f8
    –f9                –f10
    –f11               –f12
    –insert              –home
    –pageup           –del
    –end              –pagedown
    –right               –left
    –down            –kp-enter
    –up               –num-lock
    根据提示输入: a  b  c  d  e  f
    你电脑上可以看到已经有显示了。
    root@ubuntu:/home/ubuntu# ./a.out /dev/hidg1 m  #鼠标
    mouse options:
    –hold
    –b1
    –b2
    –b3
    mouse values:
    Two signed numbers
    –quit to close
    说明一下
    –b1  10  10
    执行这个的时候,相当于鼠标左键。
    –b2  1
    执行这个的时候,相等于鼠标右键
    –b3  -10  100
    这个相当于移动鼠标。

                   
           
                           


                   
           
                           
    第四步:用joystick做鼠标:

                                                                                    这里代码太多了,今天写了一天,我就不贴,你可以到
    https://github.com/Pillar1989/arduino
    我git上面下载,直接运行output/test/usb
    joystick接的是pcDuino的A4和A5,由于里面有个delay(500)。所有移动的时候桌面有些卡顿。具体的演示,就看你自己的了。
                   
           
                           
    第五步:补充USB协议
                                                                                    下面是自己整理的鼠标键盘的通信格式,如果你有兴趣可以研究一下。
    鼠标发送给PC的数据每次4个字节
    BYTE1 BYTE2 BYTE3 BYTE4
    定义分别是:
    BYTE1 –
    |–bit7:   1   表示   Y   坐标的变化量超出-256   ~   255的范围,0表示没有溢出
    |–bit6:   1   表示   X   坐标的变化量超出-256   ~   255的范围,0表示没有溢出
    |–bit5:   Y   坐标变化的符号位,1表示负数,即鼠标向下移动
    |–bit4:   X   坐标变化的符号位,1表示负数,即鼠标向左移动
    |–bit3:     恒为1
    |–bit2:     1表示中键按下
    |–bit1:     1表示右键按下
    |–bit0:     1表示左键按下
    BYTE2 — X坐标变化量,与byte的bit4组成9位符号数,负数表示向左移,正数表右移。用补码表示变化量
    BYTE3 — Y坐标变化量,与byte的bit5组成9位符号数,负数表示向下移,正数表上移。用补码表示变化量
    BYTE4 — 滚轮变化。
    由于手上没有USB鼠标,对BYTE1的4-7位没有测试,对于BYTE2 BYTE3做个测试,BYTE1的4-7全为0的时候,BYTE2 BYTE3的正负表示鼠标移动方向
    键盘发送给PC的数据每次8个字节
    BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE6 BYTE7 BYTE8
    定义分别是:
    BYTE1 –
    |–bit0:   Left Control是否按下,按下为1
    |–bit1:   Left Shift  是否按下,按下为1
    |–bit2:   Left Alt    是否按下,按下为1
    |–bit3:   Left GUI    是否按下,按下为1
    |–bit4:   Right Control是否按下,按下为1
    |–bit5:   Right Shift 是否按下,按下为1
    |–bit6:   Right Alt   是否按下,按下为1
    |–bit7:   Right GUI   是否按下,按下为1
    BYTE2 — 暂不清楚,有的地方说是保留位
    BYTE3–BYTE8 — 这六个为普通按键
    键盘经过测试。
    例如:键盘发送一帧数据   02 00 0×04 0×05 00 00 00 00
    表示同时按下了Left Shift + ‘a’+‘b’三个键
    附件里面是usb协议中文版,喜欢折腾的可以看看。USB1.1协议中文版
                   
           
                           





                   
           
                           
                                                                                                                                 


  • 回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册

    本版积分规则 需要先绑定手机号

    Archiver|联系我们|极客工坊

    GMT+8, 2024-4-25 08:10 , Processed in 0.047375 second(s), 17 queries .

    Powered by Discuz! X3.4 Licensed

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表