HIDDEV 프로그래밍 인터페이스를 통해 이방 디지털 펜의 좌표 데이터 읽기
15305 단어 소프트웨어 프로그래밍 기술
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 명령을 다음과 같이 정리합니다.
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에 정의된 값입니다.