HIDDEV 프로그래밍 인터페이스를 통해 이방 디지털 펜의 좌표 데이터 읽기

이방 디지털 펜은 기본적으로 표준 USB HID 장치(Mouse)로 OS가 안드로이드인 태블릿PC에 접속할 때 USB Mouse 형식을 바탕으로 출력하는 필적 정밀도가 부족하다. 이를 hiddev 장치 유형으로 전환하고 관련 명령을 보내서 펜 출력의 원시 좌표 정보를 얻으면 정밀도가 크게 향상된다.
USB HID 프로토콜을 기반으로 일반적인 입력 장치 외에 사용자 정의 통신 기능을 가진 장치로도 사용할 수 있다.사실상 현재 Linux 핵에는 이 두 가지 HID 이벤트의 인터페이스, 즉 입력 서브시스템과hiddev 인터페이스가 제공되어 있다(구체적으로 Linux 핵 루트 디렉터리의 Documentation/hid/hiddev.txt 문서 참조).
hiddev 장치를 사용하기 위해서, 우리는 커널을 설정할 때 hiddev 옵션을 켜야 한다. 그러면 펜의 원시 좌표 정보를 읽을 수 있는 응용 프로그램 코드를 작성할 수 있다.Hiddev 드라이버는 문자형 드라이버로 접근 노드는 보통/dev/usb/hiddev [0 ~ 15] 이며, 프로그램에서 이 장치 노드를 열면 Hiddev API를 호출하여 Hiddev 장치와 통신할 수 있습니다.
hiddev API에는 read 및 ioctl 호출 인터페이스가 두 개 있습니다.hiddev 장치의 상태 변화를 가져오는 데만 사용되고 호스트와 장치 간의 데이터 교환은 ioctl 호출을 통해 이루어진다. 데이터를 쓸 때 ioctl로 전송되는 명령자는 HDIOCSREPORT이고 데이터를 읽을 때 HDIOCGREPORT로 전송되며 전송된 데이터는 리포트에 봉인되어 있으며 리포트마다 여러 개의 filed로 나뉘고 filed마다 여러 개의 usage가 있다.
디지털 펜에 접근할 때, 우리는 장치에 명령 데이터를 보내서 장치에 출력 모드 (원시 좌표 정보 출력) 를 전환하라고 알릴 뿐만 아니라, 데이터 펜이 출력한 원시 좌표 정보도 실시간으로 읽어야 한다.
1. 장치 켜기
int digitalpen_open(void)
{
  int  index;
  int  fd;
  char hid_dev_node[50];
  struct hiddev_devinfo dinfo;
 
  for(index = 0; index < 15; index ++) {
 
    sprintf(hid_dev_node, "/dev/usb/hiddev%d", index);
    fd = open(hid_dev_node, O_RDONLY);
 
    if(fd > 0) {
 
      memset(&dinfo, 0, sizeof(dinfo));
      ioctl(fd, HIDIOCGDEVINFO, &dinfo);
 
      if( (dinfo.vendor == 0x0e20) && (dinfo.product == 0x0101))
        break;
 
      close(fd);
      fd = -1;
 
    }
  }
 
  return fd;
}

열기 함수에서 USB에 대한 VID 및 PID 정보를 사용하여 열려 있는 장치가 디지털 펜인지 확인합니다(이하 명령을 통해 장치의 VID 및 PID를 찾을 수 있습니다).켜진 경우 디지털 펜 장치의 파일 설명자를 반환합니다.
hfhe@hfhe:~$ lsusb
Bus 003 Device 003: ID 0e20:0101 Pegasus Technologies Ltd. NoteTaker
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 008: ID 04b3:3107 IBM Corp. ThinkPad 800dpi Optical Travel Mouse
Bus 001 Device 006: ID 04f2:b217 Chicony Electronics Co., Ltd
Bus 001 Device 005: ID 0a5c:217f Broadcom Corp. Bluetooth Controller
Bus 001 Device 004: ID 147e:2016 Upek Biometric Touchchip/Touchstrip Fingerprint Sensor
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

2. 디지털 펜에 대한 구성 정보 얻기
데이터를 정식으로 읽기 전에 디지털 펜 장치의 설정 정보를 얻습니다.Google에서 Hiddev 디바이스 구성 정보를 자세히 설명한 문서가 없습니다. Kernel의 코드와 앞서 언급한 Hiddev만 볼 수 있습니다.txt 파일 - 이와 관련된 IOCTL 명령을 다음과 같이 정리합니다.
  • HIDIOCGNAME - 장치 이름 지정
  • HIDIOCGDEVINFO - 구조체 Hiddev 획득devinfo에서 기술한 장치 정보
  • HIDIOCGCOLLECTIONINFO - 획득: 구조체 Hiddevcollection_설명된 장치가 가지고 있는 집합 (응용 집합 포함) 은 호출할 때 index역으로 전송됩니다. 예를 들어 index가 마지막 집합을 초과하면 반환값은 -1입니다.
  • HIDIOCGREPORTINFO - 호출 시 구조체 Hiddev 를 기입해야 합니다report_info의 type과 id 필드입니다.ID가 HID인 경우REPORT_ID_FIRST는 첫 번째 보고서 정보를 전송하고 (HID REPORT ID NEXT |report id)는 다음 보고서 정보를 전송합니다.유형 값은 HIDREPORT_TYPE_MIN 및 HIDREPORT_TYPE_맥스실.
  • HIDIOCGFIELDINFO - 보고서의 도메인(filed) 정보를 반환합니다.호출할 때 보고서의 id와 type을 보내고field index를 입력해야 합니다. filed의 수는 호출된 HIDIOCGREPORTINFO의 복귀 구조체에서 얻을 수 있습니다.
  • HIDIOCGUCODE - 용도 코드로 되돌아오기, 호출할 때 보고서의 id와 type, 필드의 index와 용도의 인덱스(최대 용도 인덱스 값은 HIDIOCGFIELDINFO를 호출한 용도 구조체에서 획득)
  • 이를 통해 알 수 있듯이 HIDIOCGNAME와 HIDIOCGDEVINFO 호출을 제외하고는 모두 매거 과정이다.관련 구조체의 정의는 첨부되지 않습니다. 다음은 설정 정보를 얻을 수 있는 코드를 직접 드리겠습니다.
    int digitalpen_getconfiginfo(int fd)
    {
      char name[1024];
      int  index;
      int  report_type,report_id,field_index,usage_index;
     
      struct hiddev_devinfo dinfo;
      struct hiddev_collection_info cinfo;
      struct hiddev_report_info rinfo;
      struct hiddev_field_info finfo;
      struct hiddev_usage_ref uref;
     
      const char *collection_type_name[] = {"Physical", "Application", "Logical"};
      const char *report_type_name[] = {"Input", "Output", "Feature"};
     
      // get device name
      memset(name, 0, sizeof(name));
      if( ioctl(fd, HIDIOCGNAME(sizeof(name)), name) < 0) {;
        return -1;
      }
     
      // get device info
      memset(&dinfo, 0, sizeof(dinfo));
      if( ioctl(fd, HIDIOCGDEVINFO, &dinfo) < 0) {
        return -1;
      }
     
      printf("
    [Device Information]
    "); printf("device name = %s
    ", name); printf("vendor id = 0x%04x
    ", dinfo.vendor & 0xffff); printf("product id = 0x%04x
    ", dinfo.product & 0xffff); printf("version = 0x%04x
    ", dinfo.version & 0xffff); printf("num_applications = 0x%x
    ", dinfo.num_applications); // get collection info printf("
    [Collections Information]
    "); index = 0; while(1) { memset(&cinfo, 0, sizeof(cinfo)); cinfo.index = index; if(ioctl(fd, HIDIOCGCOLLECTIONINFO, &cinfo) < 0) break; index++; printf("index = %d
    ", cinfo.index); if(cinfo.type >= 0 && cinfo.type <= 2) printf("type = %s
    ", collection_type_name[cinfo.type]); else printf("type = %d
    ", cinfo.type); printf("usage = 0x%x
    ", cinfo.usage); printf("level = %d

    ", cinfo.level); } // get reports info printf("[Report Information]
    "); for(report_type = HID_REPORT_TYPE_MIN; report_type <= HID_REPORT_TYPE_MAX; report_type++) { for(report_id = HID_REPORT_ID_FIRST; ;report_id++) { memset(&rinfo, 0, sizeof(rinfo)); rinfo.report_type = report_type; rinfo.report_id = report_id; if(ioctl(fd, HIDIOCGREPORTINFO, &rinfo) < 0) break; printf("report_type = %s
    ", report_type_name[report_type - 1]); printf("report_id = %d
    ", report_id); printf("num_fields = %d
    ", rinfo.num_fields); // get field info printf("
    [field information]
    "); for(field_index = 0; field_index < rinfo.num_fields; field_index++) { memset(&finfo, 0, sizeof(finfo)); finfo.report_type = report_type; finfo.report_id = report_id; finfo.field_index = field_index; if(ioctl(fd, HIDIOCGFIELDINFO, &finfo) < 0) break; printf(" field_index = %d
    ", finfo.field_index); printf(" maxusage = %d
    ", finfo.maxusage); printf(" flags = %d
    ", finfo.flags); printf(" physical = %d
    ", finfo.physical); printf(" logical = %d
    ", finfo.logical); printf(" application = 0x%x
    ", finfo.application); printf(" unit_exponent = %d
    ", finfo.unit_exponent); printf(" unit = %d
    ", finfo.unit); printf(" logical_minimum = %d; logical_maximum = %d
    ", finfo.logical_minimum, finfo.logical_maximum); printf(" physical_minimum = %d; physical_maximum = %d
    ", finfo.physical_minimum, finfo.physical_maximum); // get usage info printf("
    [Usage code information]
    "); for(usage_index = 0; usage_index < finfo.maxusage; usage_index++) { memset(&uref, 0, sizeof(uref)); uref.report_type = report_type; uref.report_id = report_id; uref.field_index = field_index; uref.usage_index = usage_index; if(ioctl(fd, HIDIOCGUCODE, &uref) < 0) break; printf(" usage_index = %d, usage_code = 0x%x, value = %d
    ", uref.usage_index, uref.usage_code, uref.value); } } report_id = report_id | HID_REPORT_ID_NEXT; printf("
    "); } } }

    런타임 결과 출력:
    [Device Information]
    device name      = Pegasus Technologies Ltd. EN202 Ver 3.02
    vendor id        = 0x0e20
    product id       = 0x0101
    version          = 0x0303
    num_applications = 0x1
     
    [Collections Information]
    index = 0
    type  = Application
    usage = 0xffa00001
    level = 0
     
    [Report Information]
    report_type = Input
    report_id   = 256
    num_fields  = 1
     
        [field information]
        field_index   = 0
        maxusage      = 64
        flags         = 2
        physical      = 0
        logical       = 0
        application   = 0xffa00001
        unit_exponent = 0
        unit          = 0
        logical_minimum  = -128; logical_maximum  = 127
        physical_minimum = 0; physical_maximum = 0
     
            [Usage code information]
            usage_index = 0, usage_code = 0xffa00002, value = 0
            usage_index = 1, usage_code = 0xffa00002, value = 0
            usage_index = 2, usage_code = 0xffa00002, value = 0
            usage_index = 3, usage_code = 0xffa00002, value = 0
            usage_index = 4, usage_code = 0xffa00002, value = 0
            usage_index = 5, usage_code = 0xffa00002, value = 0
            usage_index = 6, usage_code = 0xffa00002, value = 0
            usage_index = 7, usage_code = 0xffa00002, value = 0
            usage_index = 8, usage_code = 0xffa00002, value = 0
            usage_index = 9, usage_code = 0xffa00002, value = 0
            usage_index = 10, usage_code = 0xffa00002, value = 0
            usage_index = 11, usage_code = 0xffa00002, value = 0
            usage_index = 12, usage_code = 0xffa00002, value = 0
            usage_index = 13, usage_code = 0xffa00002, value = 0
            usage_index = 14, usage_code = 0xffa00002, value = 0
            usage_index = 15, usage_code = 0xffa00002, value = 0
            usage_index = 16, usage_code = 0xffa00002, value = 0
            usage_index = 17, usage_code = 0xffa00002, value = 0
            usage_index = 18, usage_code = 0xffa00002, value = 0
            usage_index = 19, usage_code = 0xffa00002, value = 0
            usage_index = 20, usage_code = 0xffa00002, value = 0
            usage_index = 21, usage_code = 0xffa00002, value = 0
            usage_index = 22, usage_code = 0xffa00002, value = 0
            usage_index = 23, usage_code = 0xffa00002, value = 0
            usage_index = 24, usage_code = 0xffa00002, value = 0
            usage_index = 25, usage_code = 0xffa00002, value = 0
            usage_index = 26, usage_code = 0xffa00002, value = 0
            usage_index = 27, usage_code = 0xffa00002, value = 0
            usage_index = 28, usage_code = 0xffa00002, value = 0
            usage_index = 29, usage_code = 0xffa00002, value = 0
            usage_index = 30, usage_code = 0xffa00002, value = 0
            usage_index = 31, usage_code = 0xffa00002, value = 0
            usage_index = 32, usage_code = 0xffa00002, value = 0
            usage_index = 33, usage_code = 0xffa00002, value = 0
            usage_index = 34, usage_code = 0xffa00002, value = 0
            usage_index = 35, usage_code = 0xffa00002, value = 0
            usage_index = 36, usage_code = 0xffa00002, value = 0
            usage_index = 37, usage_code = 0xffa00002, value = 0
            usage_index = 38, usage_code = 0xffa00002, value = 0
            usage_index = 39, usage_code = 0xffa00002, value = 0
            usage_index = 40, usage_code = 0xffa00002, value = 0
            usage_index = 41, usage_code = 0xffa00002, value = 0
            usage_index = 42, usage_code = 0xffa00002, value = 0
            usage_index = 43, usage_code = 0xffa00002, value = 0
            usage_index = 44, usage_code = 0xffa00002, value = 0
            usage_index = 45, usage_code = 0xffa00002, value = 0
            usage_index = 46, usage_code = 0xffa00002, value = 0
            usage_index = 47, usage_code = 0xffa00002, value = 0
            usage_index = 48, usage_code = 0xffa00002, value = 0
            usage_index = 49, usage_code = 0xffa00002, value = 0
            usage_index = 50, usage_code = 0xffa00002, value = 0
            usage_index = 51, usage_code = 0xffa00002, value = 0
            usage_index = 52, usage_code = 0xffa00002, value = 0
            usage_index = 53, usage_code = 0xffa00002, value = 0
            usage_index = 54, usage_code = 0xffa00002, value = 0
            usage_index = 55, usage_code = 0xffa00002, value = 0
            usage_index = 56, usage_code = 0xffa00002, value = 0
            usage_index = 57, usage_code = 0xffa00002, value = 0
            usage_index = 58, usage_code = 0xffa00002, value = 0
            usage_index = 59, usage_code = 0xffa00002, value = 0
            usage_index = 60, usage_code = 0xffa00002, value = 0
            usage_index = 61, usage_code = 0xffa00002, value = 0
            usage_index = 62, usage_code = 0xffa00002, value = 0
            usage_index = 63, usage_code = 0xffa00002, value = 0
     
    report_type = Output
    report_id   = 256
    num_fields  = 1
     
        [field information]
        field_index   = 0
        maxusage      = 8
        flags         = 8
        physical      = 0
        logical       = 0
        application   = 0xffa00001
        unit_exponent = 0
        unit          = 0
        logical_minimum  = -128; logical_maximum  = 127
        physical_minimum = 0; physical_maximum = 255
     
            [Usage code information]
            usage_index = 0, usage_code = 0xffa00003, value = 0
            usage_index = 1, usage_code = 0xffa00003, value = 0
            usage_index = 2, usage_code = 0xffa00003, value = 0
            usage_index = 3, usage_code = 0xffa00003, value = 0
            usage_index = 4, usage_code = 0xffa00003, value = 0
            usage_index = 5, usage_code = 0xffa00003, value = 0
            usage_index = 6, usage_code = 0xffa00003, value = 0
            usage_index = 7, usage_code = 0xffa00003, value = 0

    3. 디지털 펜 장치에 데이터 쓰기
    hiddev 장치와 데이터 교환은 read와 write 두 함수를 통해 이루어지는 것이 아니라 IOCTL (명령어는 HIDIOCGREPORT와 HIDIOCSREPORT) 을 호출하여 전송된 데이터를 리포트에 봉하여 이루어진다.
    데이터를 쓰는 것은 비교적 실현하기 쉽다.hiddev에 따라.txt의 설명은 보고서를 보내는 동안 먼저 HIDIOCSUSAGE(IOCTL에 전송)를 호출하여 전송할 데이터를 작성하고 그 다음에 HIDIOCSREPORT를 호출하여 Linux에서 장치에 보고서를 심사하여 보내도록 하는 것이다.
    int hiddev_send_report(int fd, unsigned int report_id, unsigned int num_fields, unsigned int field_index, unsigned int usage_index, unsigned int usage_code, char *buf, unsigned int size)
    {
      unsigned int index;
      struct hiddev_usage_ref_multi urefm;
      struct hiddev_report_info rinfo;
     
      if(size > HID_MAX_MULTI_USAGES)
        return -1;
     
      memset(&urefm, 0, sizeof(urefm));
      urefm.uref.report_type = HID_REPORT_TYPE_OUTPUT;
      urefm.uref.report_id = report_id;
      urefm.uref.field_index = field_index;
      urefm.uref.usage_index = usage_index;
      urefm.uref.usage_code = usage_code;
     
      urefm.num_values = size;
      for(index = 0; index < size; index++)
        urefm.values[index] = (unsigned int)buf[index];
     
      if(ioctl(fd, HIDIOCSUSAGES, &urefm) < 0)
        return -2;
     
      memset(&rinfo, 0, sizeof(rinfo));
      rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
      rinfo.report_id = report_id;
      rinfo.num_fields = num_fields;
      if(ioctl(fd, HIDIOCSREPORT, &rinfo) < 0)
        return -3;
     
      return 0;
    }

    호출할 때, 2소절에서 얻은 일부 파라미터를 사용해야 하는데, 비교적 좋은 방법은 이 파라미터를 전사 구조체에 넣는 것이다.물론 그림의 편의를 위해 얻은 파라미터를 직접 입력할 수도 있다. 예를 들어 디지털 펜을 원시 좌표 출력 모드로 전환하려면 아래의 호출을 통해 실현할 수 있다.
    const char cmd_opmode_pen[]   = { 0x02, 0x04, 0x80, 0xB5, 0x01, 0x01, 0x00, 0x00};
     
    if(hiddev_send_report(fd, HID_REPORT_ID_FIRST, 1, 0, 0, 0xffa00003, cmd_opmode_pen, sizeof(cmd_opmode_pen)) < 0)
        printf("Fail to send report
    ");

    얻은 설정 정보를 통해 알 수 있듯이 보고 형식이 출력 모드일 때 리포트id 값은 256(매크로 HID)REPORT_ID_FIRST에 정의된 값입니다.

    좋은 웹페이지 즐겨찾기