성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
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>
첨부파일
스팸 방지용 인증 번호
2141
(왼쪽의 숫자를 입력해야 합니다.)