SSD_HID KEYBOARD


1. 环境搭建

1.1. 修改kernel配置

添加配置:make menuconfig

  1. usb Gadget 框架配置 -> Device Drivers -> USB support -> Device Drivers -> USB support -> USB Gadget Support

    输出模块:usb-common.ko udc-core.ko

  2. udc 驱动配置:该模块为硬件ip相关模块,视具体情况进行配置 -> Device Drivers -> USB support -> USB Gadget Support -> USB Peripheral Controller -> Sstar USB 2.0 Device Controller

    输出模块:udc-msb250x.ko

  3. gadget hid -> Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers -> Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers -> USB HID Gadget

    输出模块:libcomposite.ko usb_f_hid.ko g_hid.ko

注意:

  1. 当配置选项为 y (可在kernel目录查看.config)时,代表该模块编译为builtin模式,默认编译进kernel内核,此时需将该Image替换烧录,通常生成为uImage.xz(目录arch/arm/boot/)文件。

  2. 当配置选项为 m 时,代表该模块编译为 module模式,在kernel/modules目录会生成相应 xxx.ko,此时需要将相应的文件在linux启动之后进行加载: insmod xxx.ko。

  3. 模块之前具有相应依赖关系,配置与加载都需要有先后顺序。


1.2. driver端修改

drivers/sstar/usb/gadget/udc/usb20/src/msb250x_gadget.c中的修改

修改msb250x_gadget_match_ep函数:

struct usb_ep* msb250x_gadget_match_ep(struct usb_gadget *g, struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *ep_comp) {
        ......

        switch (usb_endpoint_type(desc))
    {
        //case USB_ENDPOINT_XFER_INT:
            //MSB250X_INTR_EP(dev, ep);
            //break;
        }

        return ep;
}
注意:如果同时支持键盘和鼠标的话,就需要注释上面的case部分;如果只支持鼠标或键盘时,上面的case不需要注释

1.3. 板端的驱动加载顺序

#insmod /config/modules/4.9.84/ehci-hcd.ko //与host控制器相关,不需要加载
insmod /customer/usb-common.ko
insmod /customer/udc-core.ko
insmod /customer/libcomposite.ko
insmod /customer/udc-msb250x.ko
insmod /customer/usb_f_hid.ko out_enable=0  //说明:out_enable=0关闭out端点,防止部分平台因端点数量不够导致驱动无法正常运行。                                        
insmod /customer/g_hid.ko hid_mode='composite'   //说明:hid_mode='composite'同时支持keyboard和mouse。若只需要键盘,配置hid_mode='key'。若只需要鼠标,配置hid_mode='mouse'。
echo 4 > /proc/sys/kernel/printk

insmod /config/modules/4.9.84/cifs.ko
insmod /config/modules/4.9.84/nls_utf8.ko
insmod /config/modules/4.9.84/grace.ko
insmod /config/modules/4.9.84/sunrpc.ko
insmod /config/modules/4.9.84/lockd.ko
insmod /config/modules/4.9.84/nfs.ko
insmod /config/modules/4.9.84/nfsv2.ko
insmod /config/modules/4.9.84/mmc_core.ko
insmod /config/modules/4.9.84/mmc_block.ko
insmod /config/modules/4.9.84/kdrv_sdmmc.ko
insmod /config/modules/4.9.84/fat.ko
insmod /config/modules/4.9.84/msdos.ko
insmod /config/modules/4.9.84/vfat.ko
insmod /config/modules/4.9.84/ntfs.ko
insmod /config/modules/4.9.84/sd_mod.ko
insmod /config/modules/4.9.84/ms_notify.ko
# kernel_mod_list
insmod /config/modules/4.9.84/mhal.ko
# misc_mod_list
insmod /config/modules/4.9.84/mi_common.ko
insmod /config/modules/4.9.84/mi_sys.ko cmdQBufSize=768 logBufSize=256 
insmod /config/modules/4.9.84/mi_sensor.ko
insmod /config/modules/4.9.84/mi_gfx.ko
insmod /config/modules/4.9.84/mi_rgn.ko
insmod /config/modules/4.9.84/mi_ai.ko
insmod /config/modules/4.9.84/mi_vpe.ko
insmod /config/modules/4.9.84/mi_pspi.ko
insmod /config/modules/4.9.84/mi_shadow.ko
insmod /config/modules/4.9.84/mi_gyro.ko
insmod /config/modules/4.9.84/mi_ao.ko
insmod /config/modules/4.9.84/mi_panel.ko
insmod /config/modules/4.9.84/mi_disp.ko
insmod /config/modules/4.9.84/mi_vif.ko
insmod /config/modules/4.9.84/mi_venc.ko
insmod /config/modules/4.9.84/mi_divp.ko
insmod /config/modules/4.9.84/mi_vdisp.ko
# mi module
major=`cat /proc/devices | busybox awk "\\$2==\""mi_poll"\" {print \\$1}"`
busybox mknod /dev/mi_poll c $major 0
insmod /config/modules/4.9.84/fbdev.ko

# misc_mod_list_late
insmod /config/modules/4.9.84/media.ko
# insmod /config/modules/4.9.84/videodev.ko
# insmod /config/modules/4.9.84/v4l2-common.ko
# insmod /config/modules/4.9.84/usb-common.ko
# insmod /config/modules/4.9.84/usbcore.ko
# insmod /config/modules/4.9.84/ehci-hcd.ko
# insmod /config/modules/4.9.84/scsi_mod.ko
# insmod /config/modules/4.9.84/usb-storage.ko
# insmod /config/modules/4.9.84/videobuf2-core.ko
# insmod /config/modules/4.9.84/videobuf2-v4l2.ko
# insmod /config/modules/4.9.84/videobuf2-memops.ko
# insmod /config/modules/4.9.84/videobuf2-vmalloc.ko
# insmod /config/modules/4.9.84/uvcvideo.ko

insmod /customer/hid/usb-common.ko
insmod /config/modules/4.9.84/usbcore.ko
insmod /config/modules/4.9.84/scsi_mod.ko
insmod /customer/hid/udc-core.ko
insmod /customer/hid/libcomposite.ko
insmod /customer/hid/udc-msb250x.ko
insmod /customer/hid/usb_f_hid.ko out_enable=0
insmod /customer/hid/g_hid.ko hid_mode='key'

# kernel_mod_list_late
insmod /config/modules/4.9.84/gc1054_dual_MIPI.ko chmap=1
insmod /config/modules/4.9.84/gc1054_MIPI.ko chmap=2
mdev -s
mkdir /var/tmp
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/customer/lib:/lib:/config/lib

重烧kernel并正确加载驱动后,会生成两个设备:/dev/hidg0和/dev/hidg1,其中/dev/hidg0代表键盘,/dev/hidg1代表鼠标。


2. USB Key board协议及过程

  1. USB Key board需每次发送8Byte

  2. 如果是发送GBK 中文,第0个字节设为0x04

  3. 如果是发送特殊字符,如 !感叹号或大写字母A,需要Shift配合,第0个字节设为0x02

  4. 普通字符的发送,第0个字节设为0x00

  5. 发送过程,如下2个例子:

    1. 发送字母A

      memset(report, 0, 8);
      report[0] = 0x02;
      report[2] = 0x04; //键盘上的‘A’, 协议规定键值为0x04
      write(key_fd, report, 8);
      DELAY_TIME();
      
      memset(report, 0, 8);
      report[0] = 0x02;
      report[2] = 0x00;
      write(key_fd, report, 8);
      DELAY_TIME();
      
      memset(report, 0, 8);
      report[0] = 0x00;
      report[2] = 0x00;
      write(key_fd, report, 8);
      DELAY_TIME();
      
    2. 发送中文GBK:会, ‘会’的GBK十进制值为48097

      static unsigned char gKeyNumValue[10] = 
      {
              /* 定义 0 ~ 9 对应的键值 */
              0x62, /*0*/
              0x59, /*1*/
              0x5A, /*2*/
              0x5B, /*3*/
              0x5C, /*4*/
              0x5D, /*5*/
              0x5E, /*6*/
              0x5F, /*7*/
              0x60, /*8*/
              0x61, /*9*/
      };
      
      while(1)  //循环调用如下依次发送4/8/0/9/7
      {
          memset(report, 0, 8);
          report[0] = 0x04;
          report[2] = 0x00;
          write(key_fd, report, 8);
          DELAY_TIME();
          memset(report, 0, 8);
          report[0] = 0x04;
          report[2] = gKeyNumValue[buf[i]-'0'];
          write(key_fd, report, 8);
          DELAY_TIME();
      }
      
      //End of one gbk sending, it should send the following
      memset(report, 0, 8);
      report[0] = 0x04;
      report[2] = 0x00;
      write(key_fd, report, 8);
      DELAY_TIME();
      
      memset(report, 0, 8);
      report[0] = 0x00;
      report[2] = 0x00;
      write(key_fd, report, 8);
      DELAY_TIME();
      

3. Code & Makefile

keyboard.cpp是完整的demo程序

3.1. App Demo

# include <stdio.h>
# include <unistd.h>
# include <stdlib.h>
# include <stdbool.h>
# include <poll.h>
# include <string.h>
# include <time.h>
# include <signal.h>
# include <fcntl.h>
# include <pthread.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/time.h>
# include <netinet/in.h>
# include <netinet/tcp.h>

//#include "common.h"

# define Key_DevName "/dev/hidg0"

static unsigned char gKeyNumValue[10] = 
{
0x62, /*0*/
0x59, /*1*/
0x5A, /*2*/
0x5B, /*3*/
0x5C, /*4*/
0x5D, /*5*/
0x5E, /*6*/
0x5F, /*7*/
0x60, /*8*/
0x61, /*9*/
};

static int key_fd = -1;

# ifdef DEBUG
static int idx = 0;
# endif

# define DELAY_TIME() //usleep(50000)
# define DELAY_WAIT_TIME() usleep(100*1000)
static bool Hid_Send_OneGBKEnglish(const unsigned char charactor)
{
int offset = 0;
static unsigned char charBase = 0x04; /* A or a */
unsigned char keyType = 0;
unsigned char report[8] = {0};

if(charactor >= 'a' && charactor <= 'z')
{
keyType = 0;
offset = charactor - 'a';
}
else if(charactor >= 'A' && charactor <= 'Z')
{
keyType = 0x02;
offset = charactor - 'A';
}
else
{
printf("[fun:%s - Line:%d]unable to support charactor:0x%02x\n", __FUNCTION__, __LINE__, charactor);
}

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = keyType;
report[2] = charBase + offset;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = keyType;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

DELAY_WAIT_TIME(); 
return true; 
}

static bool Hid_Send_OneGBKChinese(const unsigned char valueH, const unsigned char valueL)
{
int i;
unsigned char buf[16] = {0};
unsigned char report[8] = {0};
unsigned short value = (valueH<<8) | valueL;

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset((unsigned char *)buf, 0, 16);
sprintf((char *)buf, "%d", value);
printf("value:%d buf:%s\n", value, buf);

for(i=0; i<16; i++)
{
if(buf[i] == 0) { break; }

memset(report, 0, 8);
report[0] = 0x04;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = 0x04;
report[2] = gKeyNumValue[buf[i]-'0'];
write(key_fd, report, 8);
DELAY_TIME();
}

//End of one gbk sending, it should send the following
memset(report, 0, 8);
report[0] = 0x04;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

DELAY_WAIT_TIME(); 
return true;
}

static bool Hid_Send_Enter()
{
unsigned char report[8] = {0};

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x28;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

DELAY_WAIT_TIME(); 
return true; 
}

static bool Hid_Send_Space()
{
unsigned char report[8] = {0};

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x2C;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

DELAY_WAIT_TIME(); 
return true; 
}

/**************************
\1. From ~ to ?
\2. From ` to /
**************************/
static bool Hid_Send_Symbol(unsigned char symbol)
{
unsigned char keyType;
unsigned char keyValue;
unsigned char report[8] = {0};

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
switch(symbol)
{
/*From 1 to / */
case '1':
keyType = 0x00;
keyValue = 0x1E; 
break;
case '2':
keyType = 0x00;
keyValue = 0x1F;
break;
case '3':
keyType = 0x00;
keyValue = 0x20; 
break;
case '4':
keyType = 0x00;
keyValue = 0x21;
break;
case '5':
keyType = 0x00;
keyValue = 0x22; 
break;
case '6':
keyType = 0x00;
keyValue = 0x23;
break;
case '7':
keyType = 0x00;
keyValue = 0x24; 
break;
case '8':
keyType = 0x00;
keyValue = 0x25;
break;
case '9':
keyType = 0x00;
keyValue = 0x26; 
break;
case '0':
keyType = 0x00;
keyValue = 0x27;
break;
case '-':
keyType = 0x00;
keyValue = 0x2D;
break;
case '=':
keyType = 0x00;
keyValue = 0x2E;
break;
case '[':
keyType = 0x00;
keyValue = 0x2F;
break;
case ']':
keyType = 0x00;
keyValue = 0x30;
break;
case '\\':
keyType = 0x00;
keyValue = 0x31;
break;
case ';':
keyType = 0x00;
keyValue = 0x33;
break;
case '\'':
keyType = 0x00;
keyValue = 0x34;
break;
case '`':
keyType = 0x00;
keyValue = 0x35;
break;
case ',':
keyType = 0x00;
keyValue = 0x36;
break;
case '.':
keyType = 0x00;
keyValue = 0x37;
break;
case '/':
keyType = 0x00;
keyValue = 0x38;
break; 
/*From ! to ?*/
case '!':
keyType = 0x02;
keyValue = 0x1E; 
break;
case '@':
keyType = 0x02;
keyValue = 0x1F;
break;
case '#':
keyType = 0x02;
keyValue = 0x20; 
break;
case '$':
keyType = 0x02;
keyValue = 0x21;
break;
case '%':
keyType = 0x02;
keyValue = 0x22; 
break;
case '^':
keyType = 0x02;
keyValue = 0x23;
break;
case '&':
keyType = 0x02;
keyValue = 0x24; 
break;
case '*':
keyType = 0x02;
keyValue = 0x25;
break;
case '(':
keyType = 0x02;
keyValue = 0x26; 
break;
case ')':
keyType = 0x02;
keyValue = 0x27;
break;
case '_':
keyType = 0x02;
keyValue = 0x2D;
break;
case '+':
keyType = 0x02;
keyValue = 0x2E;
break;
case '{':
keyType = 0x02;
keyValue = 0x2F;
break;
case '}':
keyType = 0x02;
keyValue = 0x30;
break;
case '|':
keyType = 0x02;
keyValue = 0x31;
break;
case ':':
keyType = 0x02;
keyValue = 0x33;
break;
case '"':
keyType = 0x02;
keyValue = 0x34;
break;
case '~':
keyType = 0x02;
keyValue = 0x35;
break;
case '<':
keyType = 0x02;
keyValue = 0x36;
break;
case '>':
keyType = 0x02;
keyValue = 0x37;
break;
case '?':
keyType = 0x02;
keyValue = 0x38;
break;

default:
printf("[fun:%s - Line:%d]unable to support symbol:0x%x\n", __FUNCTION__, __LINE__, symbol);
return false;
}

memset(report, 0, 8);
report[0] = keyType;
report[2] = keyValue;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = keyType;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

memset(report, 0, 8);
report[0] = 0x00;
report[2] = 0x00;
write(key_fd, report, 8);
DELAY_TIME();

DELAY_WAIT_TIME(); 
return true;
}

static bool Hidg_Send_Test()
{
unsigned char charactor;

sleep(5);

Hid_Send_OneGBKChinese(0xBB, 0xE1);
Hid_Send_OneGBKChinese(0xB5, 0xB1);
Hid_Send_Space();
Hid_Send_Enter();


charactor = 'a';
while(charactor <= 'z')
{
Hid_Send_OneGBKEnglish(charactor++);
Hid_Send_Space();
}
Hid_Send_Enter();
charactor = 'A';
while(charactor <= 'Z')
{
Hid_Send_OneGBKEnglish(charactor++);
Hid_Send_Space();
}
Hid_Send_Enter();


Hid_Send_Symbol('`');
Hid_Send_Space();
Hid_Send_Symbol('1');
Hid_Send_Space();
Hid_Send_Symbol('2');
Hid_Send_Space();
Hid_Send_Symbol('3');
Hid_Send_Space();
Hid_Send_Symbol('4');
Hid_Send_Space();
Hid_Send_Symbol('5');
Hid_Send_Space();
Hid_Send_Symbol('6');
Hid_Send_Space();
Hid_Send_Symbol('7');
Hid_Send_Space();
Hid_Send_Symbol('8');
Hid_Send_Space();
Hid_Send_Symbol('9');
Hid_Send_Space();
Hid_Send_Symbol('0');
Hid_Send_Space();
Hid_Send_Symbol('-');
Hid_Send_Space();
Hid_Send_Symbol('=');
Hid_Send_Space();
Hid_Send_Symbol('[');
Hid_Send_Space();
Hid_Send_Symbol(']');
Hid_Send_Space();
Hid_Send_Symbol(';');
Hid_Send_Space();
Hid_Send_Symbol('\'');
Hid_Send_Space();
Hid_Send_Symbol('\\');
Hid_Send_Space();
Hid_Send_Symbol(',');
Hid_Send_Space();
Hid_Send_Symbol('.');
Hid_Send_Space();
Hid_Send_Symbol('/');
Hid_Send_Space();
Hid_Send_Enter();

Hid_Send_Symbol('~');
Hid_Send_Space();
Hid_Send_Symbol('!');
Hid_Send_Space();
Hid_Send_Symbol('@');
Hid_Send_Space();
Hid_Send_Symbol('#');
Hid_Send_Space();
Hid_Send_Symbol('$');
Hid_Send_Space();
Hid_Send_Symbol('%');
Hid_Send_Space();
Hid_Send_Symbol('^');
Hid_Send_Space();
Hid_Send_Symbol('&');
Hid_Send_Space();
Hid_Send_Symbol('*');
Hid_Send_Space(); 
Hid_Send_Symbol('(');
Hid_Send_Space();
Hid_Send_Symbol(')');
Hid_Send_Space();
Hid_Send_Symbol('_');
Hid_Send_Space();
Hid_Send_Symbol('+');
Hid_Send_Space();
Hid_Send_Symbol('{');
Hid_Send_Space();
Hid_Send_Symbol('}');
Hid_Send_Space();
Hid_Send_Symbol(':');
Hid_Send_Space();
Hid_Send_Symbol('"');
Hid_Send_Space();
Hid_Send_Symbol('|');
Hid_Send_Space(); 
Hid_Send_Symbol('<');
Hid_Send_Space();
Hid_Send_Symbol('>');
Hid_Send_Space();
Hid_Send_Symbol('?');
Hid_Send_Space();
Hid_Send_Enter();

return true;

}

static bool Hidg_Dev_Init(char *key_path, char *mouse_path)
{ 
key_fd = open(key_path, O_RDWR, 0666);
if(key_fd < 0)
{
return false;
}


return true;
}

static void Hidg_Dev_DeInit(void)
{ 
close(key_fd);
}


int main(int argc, char **argv)
{
if(!Hidg_Dev_Init((char *)Key_DevName, (char *)Key_DevName))
{
printf("Hidg_Dev_Init err\n");
return -1;
}

Hidg_Send_Test();

return 0;
}

3.2. Makefile

all:

arm-linux-gnueabihf-sigmastar-9.1.0-gcc keyboard.cpp -o key -lpthread
arm-linux-gnueabihf-sigmastar-9.1.0-strip key

clean:

rm key

附件:Makefile demo.sh