성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Get Started with Milvus Vector DB i...
[정성태] cyberark/PipeViewer - A tool that...
[정성태] WinForms in a 64-Bit world – our st...
[정성태] 예제에서 SELECT_SQL도 내부적으로는 SqlCommand/...
[victor] SELECT_LINQ SELECT_SQL 같은 쿼리인...
[victor] 답변 갑사합니다. 예외(Exception)가 났습니다. ...
[정성태] 일단, 위의 방식대로 하면 예외(Exception) 없이 잘 동...
[정성태] Windows 10 (버전 1809)에 이런 기능이 ^^ 추가되...
[정성태] pde windbg extension ; https://lea...
[정성태] // GetEnumerator extensions for Ran...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Raspberry Pi Zero(OTG)를 다른 컴퓨터에 연결해 가상 마우스 + 키보드로 쓰는 방법 (두 번째 이야기)</h1> <p> 지난 글에서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Raspberry Pi Zero(OTG)를 다른 컴퓨터에 연결해 가상 마우스 + 키보드로 쓰는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11356'>http://www.sysnet.pe.kr/2/0/11356</a> </pre> <br /> Raspberry Pi Zero를 마우스 + 키보드 장치로 컴퓨터에 인식시키는 방법을 살펴봤는데요. 거기서 사용했던 방법은, 파이에서 2개의 USB 장치를 구현하는 것으로 했었습니다. 그런데, 1개의 USB 장치로 2개의 내부 HID 장치를 구현하는 것이 가능합니다. 이에 대한 내용을 다음의 글에서 자세하게 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Tutorial about USB HID Report Descriptors ; <a target='tab' href='http://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/'>http://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/</a> </pre> <br /> 위의 글을 "<a target='tab' href='http://www.sysnet.pe.kr/2/0/11356'>Raspberry Pi Zero(OTG)를 다른 컴퓨터에 연결해 가상 마우스 + 키보드로 쓰는 방법</a>" 장치에 적용해 보면, 마우스와 키보드에 대해 각각의 report descriptors를 이어붙인 다음 개별 장치에 대해 REPORT_ID를 부여하면 되는 것입니다.<br /> <a name='report_desc'></a> <br /> 다음은 이에 대한 구현입니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 05, 01, USAGE_PAGE (Generic Desktop) 09, 06, USAGE (Keyboard) a1, 01, COLLECTION (Application) <span style='color: blue; font-weight: bold'>85, 01, REPORT_ID (1)</span> 05, 07 USAGE_PAGE (Keyboard) 19, e0 USAGE_MINIMUM (Keyboard LeftControl) 29, e7 USAGE_MINIMUM (Keyboard Right GUI) 15, 00 LOGICAL_MINIMUM (0) 25, 01 LOGICAL_MAXIMUM (1) 75, 01 REPORT_SIZE (1) 95, 08 REPORT_COUNT (8) 81, 02 INPUT (Data, Var, Abs) 95, 01 REPORT_COUNT (1) 75, 08 REPORT_SIZE (8) 81, 03 INPUT (Cnst, Var, Abs) 95, 05 REPORT_COUNT (5) 75, 01 REPORT_SIZE (1) 05, 08 USAGE_PAGE (LEDs) 19, 01 USAGE_MINIMUM (Num Lock) 29, 05 USAGE_MAXIMUM (Kana) 91, 02 OUTPUT (Data, Var, Abs) 95, 01 REPORT_COUNT (1) 75, 03 REPORT_SIZE (3) 91, 03 OUTPUT (Cnst, Var, Abs) 95, 06 REPORT_COUNT (6) 75, 08 REPORT_SIZE (8) 15, 00 LOGICAL_MINIMUM (0) 25, 65 LOGICAL_MAXIMUM (101) 05, 07 USAGE_PAGE (Keyboard) 19, 00 USAGE_MINIMUM (Reserved (no event indicated)) 29, 65 USAGE_MAXIMUM (Keyboard Application) 81, 00 INPUT (Data, Ary, Abs) C0 END_COLLECTION 05, 01 USAGE_PAGE (Generic Desktop) 09, 02 USAGE (Mouse) a1, 01 COLLECTION (Application) 09, 01 USAGE (Pointer) a1, 00 COLLECTION (Physical) <span style='color: blue; font-weight: bold'>85, 02 REPORT_ID (2)</span> 05, 09 USAGE_PAGE (Button) 19, 01 USAGE_MINIMUM (Button 1) 29, 03 USAGE_MAXIMUM (Button 3) 15, 00 LOGICAL_MINIMUM (0) 25, 01 LOGICAL_MAXIMUM (1) 95, 03 REPORT_COUNT (3) 75, 01 REPORT_SIZE (1) 81, 02 INPUT (Data, Var, Abs) 95, 01 REPORT_COUNT (1) 75, 05 REPORT_SIZE (5) 81, 03 INPUT (Cnst, Var, Abs) 05, 01 USAGE_PAGE (Generic Desktop) 09, 30 USAGE (X) 09, 31 USAGE (Y) 15, 81 LOGICAL_MINIMUM (-127) 25, 7f LOGICAL_MAXIMUM (127) 75, 08 REPORT_SIZE (8) 95, 02 REPORT_COUNT (2) 81, 06 INPUT (Data, Var, Rel) C0 END_COLLECTION C0 END_COLLECTION </pre> <br /> 위와 같이 적용하면, 키보드와 마우스 장치로 write 해야 할 바이트의 구조가 "<a target='tab' href='http://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/'>Tutorial about USB HID Report Descriptors</a>" 글에 나온 대로 다음과 같이 바뀝니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct keyboard_report_t { <span style='color: blue; font-weight: bold'>uint8_t report_id;</span> uint8_t modifier; uint8_t reserved; uint8_t keycode[6]; } struct mouse_report_t { <span style='color: blue; font-weight: bold'>uint8_t report_id;</span> uint8_t buttons; int8_t x; int8_t y; } </pre> <br /> 하지만 실제로 위의 구조로 테스트해보면 키보드의 경우 write 함수 호출에서 실패하는 것을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int size = sizeof(keyboard_report_t); // size == 9 int result = write(fd, keyPress, size); // result == 8 if (result != size) { perror("failed to key press"); return; } </pre> <br /> 왠지 키보드에 전송하는 데이터는 8바이트를 넘어서는 안되는 것입니다. (혹시 이 부분에 대해 아시는 분은 덧글 부탁드립니다. ^^)<br /> <a name='keyboard_report'></a> <br /> 그래서, 키보드 데이터 중에서 keycode 6바이트를 다음과 같이 5바이트로 변경해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct keyboard_report_t { uint8_t report_id; uint8_t modifier; uint8_t reserved; <span style='color: blue; font-weight: bold'>uint8_t keycode[5];</span> } </pre> <br /> 그리고 이를 맞춰 주기 위해 report descriptors도 그 부분을 6에서 5로 바꿔야 합니다. 그래서 최종 report descriptors는 다음과 같이 구성됩니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 05, 01, USAGE_PAGE (Generic Desktop) 09, 06, USAGE (Keyboard) a1, 01, COLLECTION (Application) <span style='color: blue; font-weight: bold'>85, 01, REPORT_ID (1)</span> 05, 07 USAGE_PAGE (Keyboard) 19, e0 USAGE_MINIMUM (Keyboard LeftControl) 29, e7 USAGE_MINIMUM (Keyboard Right GUI) 15, 00 LOGICAL_MINIMUM (0) 25, 01 LOGICAL_MAXIMUM (1) 75, 01 REPORT_SIZE (1) 95, 08 REPORT_COUNT (8) 81, 02 INPUT (Data, Var, Abs) 95, 01 REPORT_COUNT (1) 75, 08 REPORT_SIZE (8) 81, 03 INPUT (Cnst, Var, Abs) 95, 05 REPORT_COUNT (5) 75, 01 REPORT_SIZE (1) 05, 08 USAGE_PAGE (LEDs) 19, 01 USAGE_MINIMUM (Num Lock) 29, 05 USAGE_MAXIMUM (Kana) 91, 02 OUTPUT (Data, Var, Abs) 95, 01 REPORT_COUNT (1) 75, 03 REPORT_SIZE (3) 91, 03 OUTPUT (Cnst, Var, Abs) <span style='color: blue; font-weight: bold'>95, 05 REPORT_COUNT (5)</span> 75, 08 REPORT_SIZE (8) 15, 00 LOGICAL_MINIMUM (0) 25, 65 LOGICAL_MAXIMUM (101) 05, 07 USAGE_PAGE (Keyboard) 19, 00 USAGE_MINIMUM (Reserved (no event indicated)) 29, 65 USAGE_MAXIMUM (Keyboard Application) 81, 00 INPUT (Data, Ary, Abs) C0 END_COLLECTION 05, 01 USAGE_PAGE (Generic Desktop) 09, 02 USAGE (Mouse) a1, 01 COLLECTION (Application) 09, 01 USAGE (Pointer) a1, 00 COLLECTION (Physical) <span style='color: blue; font-weight: bold'>85, 02 REPORT_ID (2)</span> 05, 09 USAGE_PAGE (Button) 19, 01 USAGE_MINIMUM (Button 1) 29, 03 USAGE_MAXIMUM (Button 3) 15, 00 LOGICAL_MINIMUM (0) 25, 01 LOGICAL_MAXIMUM (1) 95, 03 REPORT_COUNT (3) 75, 01 REPORT_SIZE (1) 81, 02 INPUT (Data, Var, Abs) 95, 01 REPORT_COUNT (1) 75, 05 REPORT_SIZE (5) 81, 03 INPUT (Cnst, Var, Abs) 05, 01 USAGE_PAGE (Generic Desktop) 09, 30 USAGE (X) 09, 31 USAGE (Y) 15, 81 LOGICAL_MINIMUM (-127) 25, 7f LOGICAL_MAXIMUM (127) 75, 08 REPORT_SIZE (8) 95, 02 REPORT_COUNT (2) 81, 06 INPUT (Data, Var, Rel) C0 END_COLLECTION C0 END_COLLECTION </pre> <br /> 이를 반영해 쉘 스크립트를 만들어 실행하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #!/bin/bash # From the README at https://github.com/girst/hardpass dtoverlay dwc2 modprobe dwc2 modprobe libcomposite cd /sys/kernel/config/usb_gadget/ mkdir -p g1 cd g1 echo 0x1d6b > idVendor # Linux Foundation echo 0x0104 > idProduct # Multifunction Composite Gadget echo 0x0100 > bcdDevice # v1.0.0 echo 0x0200 > bcdUSB # USB2 mkdir -p strings/0x409 echo "fedcba9876543210" > strings/0x409/serialnumber echo "girst" > strings/0x409/manufacturer echo "Hardpass" > strings/0x409/product N="usb0" mkdir -p functions/hid.$N echo 1 > functions/hid.usb0/protocol echo 1 > functions/hid.usb0/subclass echo 8 > functions/hid.usb0/report_length echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x85\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x05\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0\\x05\\x01\\x09\\x02\\xa1\\x01\\x09\\x01\\xa1\\x00\\x85\\x02\\x05\\x09\\x19\\x01\\x29\\x03\\x15\\x00\\x25\\x01\\x95\\x03\\x75\\x01\\x81\\x02\\x95\\x01\\x75\\x05\\x81\\x03\\x05\\x01\\x09\\x30\\x09\\x31\\x15\\x81\\x25\\x7f\\x75\\x08\\x95\\x02\\x81\\x06\\xC0\\xC0 > functions/hid.usb0/report_desc C=1 mkdir -p configs/c.$C/strings/0x409 echo "Config $C: ECM network" > configs/c.$C/strings/0x409/configuration echo 250 > configs/c.$C/MaxPower ln -s functions/hid.usb0 configs/c.$C/ ls /sys/class/udc > UDC </pre> <br /> 다음과 같이 2개의 HID 구성 요소("HID Keyboard Device", "HID-compliant mouse")를 내장한 1개의 "USB Input Device"가 생성됩니다.<br /> <br /> <img alt='rd_1usb_2hid_1.png' src='/SysWebRes/bbs/rd_1usb_2hid_1.png' /><br /> <br /> 참고로, <a target='tab' href='http://www.sysnet.pe.kr/2/0/11356'>기존 실습</a>을 한 경우 다음과 같이 서비스를 제거한 다음,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ sudo systemctl disable create-dual-usb $ sudo rm /etc/systemd/system/create-dual-usb.service $ sudo systemctl daemon-reload </pre> <br /> 위의 USB 장치 등록 쉘 스크립트를 dual2.sh로 저장 후,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ cd /share $ sudo nano dual2.sh $ chmod +x ./dual2.sh </pre> <br /> /etc/systemd/system/ 디렉터리에 create-dual2-usb.service 파일을 만들어,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ cd /etc/systemd/system/ $ sudo nano create-dual2-usb.service </pre> <br /> 내용을 채운 다음,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Unit] Description=Create Mouse/Keyboard USB gadgets [Service] Type=oneshot ExecStart=/share/dual2.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target </pre> <br /> 다시 서비스로 등록해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ sudo systemctl daemon-reload $ sudo systemctl enable create-dual2-usb $ sudo reboot </pre> <br /> <hr style='width: 50%' /><br /> <br /> 장치가 인식되었으면 이제 driver 측의 코드도 변경해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Linux USB HID gadget driver ; <a target='tab' href='https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt'>https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt</a> </pre> <br /> 위의 코드에서 mouse와 keyboard의 첫 번째 바이트를 해당하는 report id로 채워야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold) { char *tok = strtok(buf, " "); int key = 0; int i = 0; <span style='color: blue; font-weight: bold'>report[0] = 1;</span> for (; tok != NULL; tok = strtok(NULL, " ")) { if (strcmp(tok, "--quit") == 0) return -1; if (strcmp(tok, "--hold") == 0) { *hold = 1; continue; } if (<span style='color: blue; font-weight: bold'>key < 5</span>) { for (i = 0; kval[i].opt != NULL; i++) if (strcmp(tok, kval[i].opt) == 0) { <span style='color: blue; font-weight: bold'>report[3 + key++] = kval[i].val;</span> break; } if (kval[i].opt != NULL) continue; } if (<span style='color: blue; font-weight: bold'>key < 5</span>) if (islower(tok[0])) { <span style='color: blue; font-weight: bold'>report[3 + key++] = (tok[0] - ('a' - 0x04));</span> continue; } for (i = 0; kmod[i].opt != NULL; i++) if (strcmp(tok, kmod[i].opt) == 0) { <span style='color: blue; font-weight: bold'>report[1] = report[1] | kmod[i].val;</span> break; } if (kmod[i].opt != NULL) continue; if (<span style='color: blue; font-weight: bold'>key < 5</span>) fprintf(stderr, "unknown option: %s\n", tok); } return 8; } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold) { char *tok = strtok(buf, " "); int mvt = 0; int i = 0; <span style='color: blue; font-weight: bold'>report[0] = 2;</span> for (; tok != NULL; tok = strtok(NULL, " ")) { if (strcmp(tok, "--quit") == 0) return -1; if (strcmp(tok, "--hold") == 0) { *hold = 1; continue; } for (i = 0; mmod[i].opt != NULL; i++) if (strcmp(tok, mmod[i].opt) == 0) { <span style='color: blue; font-weight: bold'>report[1] = report[1]</span> | mmod[i].val; break; } if (mmod[i].opt != NULL) continue; if (!(tok[0] == '-' && tok[1] == '-') && mvt < 2) { errno = 0; <span style='color: blue; font-weight: bold'>report[2 + mvt++]</span> = (char)strtol(tok, NULL, 0); if (errno != 0) { fprintf(stderr, "Bad value:'%s'\n", tok); <span style='color: blue; font-weight: bold'>report[2 + mvt--]</span> = 0; } continue; } fprintf(stderr, "unknown option: %s\n", tok); } <span style='color: blue; font-weight: bold'>return 4;</span> } </pre> <br /> 이후, 테스트는 <a target='tab' href='http://www.sysnet.pe.kr/2/0/11356'>이전 글과 동일하게 수행</a>하면 됩니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5599
(왼쪽의 숫자를 입력해야 합니다.)