토요일, 8월 05, 2023

리눅스(linux)에서 리모콘 설정 2/3

2. lirc가 수신에서 전달까지 모든 과정을 처리하는 방법

1번 포스트(이하 그냥 "1번"으로 씀)에서 적었듯 리모콘 IR 신호 처리는 lirc에서 kernel로 많이 옮겨간 것으로 보인다. 하지만 lirc에서 지원되지 않던 기종이 새롭게 kernel에 추가되는 것이 아니고 아직까지는 기존 lirc의 드라이버와 키맵 파일들이 하나씩 kernel에 포함(porting)되고 있는 것이 대부분이어서, 이 글을 쓰는 현재, kernel이 지원하는 리모콘 IR 수신기는 모두 lirc를 이용하여 설정할 수 있다고 보아도 무방할 듯.
다만 kernel이 지원하는 IR 수신기는 kernel을 사용하지 않고 대신 lirc를 사용하더라도 licr 드라이버와 장치 파일 설정에 있어 추가적인, 더 간편한 선택지를 제공한다. 이 두 가지 경우를 나누어 적는다.

kernel이 지원하지 않는 IR 수신기의 경우

[설정 환경]
  •   PC
  •     - 내 오래된 조립 PC, 내장 IR 수신기는 없다.
  •   OS & lirc
  •     - Gentoo linux, kernel 5.15.68 & lirc 0.10.2
  •   IR 신호 수신기
  •     - USB 타입 dvico Fusion IR 수신기
  •   IR 신호 송신기(리모콘)
  •     - dvico Fusion remote

“1번”에서 kernel 드라이버와 키맵 파일이 하던 일을 lirc가 제공하는 드라이버와 키맵 파일로 대체하는 것이기 때문에, 설정의 핵심은 내 리모콘 송수신기에 맞는 lirc 드라이버와 키맵 파일을 찾는 것이다.

    2-1. lirc 드라이버와 키맵 파일 찾기

    lirc 웹사이트에서 지원하는 드라이버 목록과 리모콘의 키맵 파일 목록을 확인할 수 있다. 다행히 dvico 리모콘은 lirc 지원 목록에 들어 있는데 드라이버는 "dvico", 키맵 파일은 “fusionHDTV.lircd.conf”이다. 드라이버는 lirc 패키지에 포함되어 Gentoo의 경우 /usr/lib64/lirc/plugins/ 디렉토리에 설치되지만(Arch/Artix의 경우 /usr/lib/lirc/plugins/), 키맵 파일은 패키지에 포함되어 있지 않기 때문에 위 lirc 사이트에서 내 리모콘에 맞는 키맵 파일을 다운로드하여 /etc/lirc/lircd.conf.d/ 디렉토리에 복사한다. root 권한이 필요하다. 

    문제는 lirc에서 드라이버와 키맵 파일을 제공하지 않는 경우인데, 이 경우 IR 수신기가 IR 신호를 수신할 수 있다면 원칙적으로--뭔가 불길하다-- irrecord라는 도구(프로그램)를 이용해서 키맵 파일을 만들 수는 있다고 한다. 하지만 엄청난(?) 삽질이 될 것이 뻔한데다 리모콘 송수신기 가격이 그렇게 높지 않기 때문에(5,000~10,000원) 차라리 lirc가 지원하는 송수신기를 하나 사는게 속 편할 것이다.

    2-2. IR 수신기 장치파일 찾기

    lirc 드라이버와 키맵 파일을 찾았다면, 다음은 /dev 디렉토리 아래 IR 수신기에 해당하는 장치파일을 찾아야 한다. 우선 수신기를 usb에 꽂은 후 kernel이 어떻게 수신기를 인식했는지 dmesg로 확인해 본다. 방금 꽂았으니 마지막 부분만 보면 된다.

$ sudo dmesg ... [ 9157.309761] usb 3-3: new low-speed USB device number 2 using xhci_hcd [ 9157.440613] usb 3-3: New USB device found, idVendor=0fe9, idProduct=9010, bcdDevice= 1.00 [ 9157.440621] usb 3-3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [ 9157.440624] usb 3-3: Product: DVICO USB HID Remocon V1.00 [ 9157.440627] usb 3-3: Manufacturer: DVICO [ 9157.443563] hid-generic 0003:0FE9:9010.0006: hiddev97,hidraw5: USB HID v1.10 Device [DVICO DVICO USB HID Remocon V1.00] on usb-0000:00:14.0-3/input0

    dvico IR 수신기의 장치파일을 찾는데 도움이 될만한 정보(usb-0000:00:14.0-3/input0)가 보이기는 하는데 이것만으로는 부족하다. 그리고 커널이 dvico IR 수신기를 리모콘이 아닌 usb 키보드나 마우스 같은 범용 HID 장치(hid-generic, hiddev97, hidraw5)로 인식한다는 사실도 알 수 있다. 따라서 커널이 리모콘 수신장치를 발견했을 때 생성하는 /sys/class/rc/rcN(N은 일련번호) 디렉토리도 생성되지 않고, ir-keytable을 실행해 봐도 장치를 발견할 수 없다는 메시지만 출력된다.

    참고로 usb 타입 IR 수신기라 하더라도 usbmisc장치로 인식되는 dvico 수신기와는 다르게, kernel이 바로 IR 리모콘 수신기로 인식하는 기종도 있다. 아래 dmesg 출력은  lirc 웹페이지에서 퍼왔는데, MS Media Center에서 사용되는 eHome IR 송수신기는 리모콘 수신기로 인식된다는 사실을 보여 준다. 이런 경우라면 lirc를 사용하는 방법은 물론 앞에서 적은 “1번" 방법과 다음 “3번" 방법도 시도해 볼 수 있다.

usb 3-2: Product: eHome Infrared Transceiver Registered IR keymap rc-rc6-mce input: Media Center Ed. eHome Infrared Remote Transceiver (0609:031d)       as /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/rc/rc0/input16 rc0: Media Center Ed. eHome Infrared Remote Transceiver (0609:031d)       as /devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/rc/rc0 input: MCE IR Keyboard/Mouse (mceusb) as /devices/virtual/input/input17 rc rc0: lirc_dev: driver ir-lirc-codec (mceusb) registered at minor = 0

    다시 본론으로 돌아와서, dmesg 정보만으로 장치파일을 파악할 수 없으니 udev를 이용해 장치파일을 찾아 본다. udevadm을 모니터 모드로 실행 시키고 IR 수신기를 usb 포트에서 뺐다가 1~2초 후 다시 끼우면, 다음과 같이 커널이 발생시킨 uevent와 sysfs 경로를 볼 수 있다.

$ udevadm monitor -k monitor will print the received events for: KERNEL - the kernel uevent KERNEL[102.477441] remove   /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/usbmisc/hiddev1 (usbmisc) KERNEL[102.477484] remove   /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:0FE9:9010.0006/hidraw/hidraw5 (hidraw) KERNEL[102.477505] unbind   /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:0FE9:9010.0006 (hid) KERNEL[102.477531] remove   /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:0FE9:9010.0006 (hid) KERNEL[102.477959] unbind   /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0 (usb) KERNEL[102.477989] remove   /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0 (usb) KERNEL[102.478022] unbind   /devices/pci0000:00/0000:00:14.0/usb3/3-3 (usb) KERNEL[102.478048] remove   /devices/pci0000:00/0000:00:14.0/usb3/3-3 (usb) KERNEL[110.320227] add      /devices/pci0000:00/0000:00:14.0/usb3/3-3 (usb) KERNEL[110.320893] add      /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0 (usb) KERNEL[110.323035] add      /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:0FE9:9010.0007 (hid) KERNEL[110.323181] add      /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/usbmisc/hiddev1 (usbmisc) KERNEL[110.323205] add      /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:0FE9:9010.0007/hidraw/hidraw5 (hidraw) KERNEL[110.323227] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/0003:0FE9:9010.0007 (hid) KERNEL[110.323247] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0 (usb) KERNEL[110.323285] bind     /devices/pci0000:00/0000:00:14.0/usb3/3-3 (usb) ^C
    단지 usb IR 수신기 하나를 뺐다 꽂았을 뿐이지만 커널은 4개의 장치를 제거하였다가 다시 추가하는 uevent를 발생시킨다. 그 이유는 usb IR 수신기가 IR 수신기로만 이루어 진게 아니기 때문이다. 수신된 신호를 usb bus를 통해서 전달하는 인터페이스 장치가 있어야 하고, 커널이 발생시킨 4개 uevent 중 2개(마지막 괄호 안 내용에 주목)는 이러한 usb 장치와 관련된 것이다. 우리가 찾는 IR 수신기 장치는 맨 말단의--그래서 커널이 가장 먼저 제거 ueven를 발생시키는, 그리고 추가 시에는 가장 늦게 uevent를 발생시키는--“hiddev1”와 “hidraw5”일 것이다. 이 두개 장치 중 우리가 선택할 장치는 hiddev1이다. hiddev 장치는  HID parser에 의해 해석이 된 신호를 송수신하는 반면,hidraw는 parse되지 않은 말 그대로 날(raw) 것 그대로의 정보를 송수신한다. 
udev를 이용해 IR 수신기의 sysfs 경로를 파악하였다면 devtmpfs(/dev 디렉토리) 경로는 -p 옵션을 이용해 쉽게 알 수 있다.
$ udevadm info -p /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/usbmisc/hiddev1 P: /devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/usbmisc/hiddev1 M: hiddev1 R: 1 U: usbmisc D: c 180:97 N: usb/hiddev1 L: 0 S: usb/dvico_remote E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3:1.0/usbmisc/hiddev1 E: SUBSYSTEM=usbmisc E: DEVNAME=/dev/usb/hiddev1 E: MAJOR=180 E: MINOR=97 E: USEC_INITIALIZED=110333133 E: DEVLINKS=/dev/usb/dvico_remote
    찾았다! dvico IR 수신기의 장치파일(위에서 DEVNAME)은 “/dev/usb/hiddev1”이다.

    2-3. 1차 테스트

    드라이버와 장치파일을 알았으니 lirc 패키지에 포함된 mode2라는 프로그램을 이용하여 1차 테스트를 해본다. lirc daemon(lircd)은 아직 실행하지 않아도 된다.

$ sudo mode2 --driver dvico --device /dev/usb/dvico_remote Using driver dvico on device /dev/usb/dvico_remote Trying device: /dev/usb/dvico_remote Using device: /dev/usb/dvico_remote Running as regular user dks code: 0x0100090001000000 code: 0x46000100fe5e0000 code: 0x0100090001000000 code: 0x46000100fede0000 code: 0x0100090001000000 code: 0x46000100fe5f0000 code: 0x0100090001000000 code: 0x46000100fedf0000 ^C

    위와 같이 lirc 드라이버가 해독한 code를 볼 수 있으면 성공이다. 만약 실패라면 lirc 드라이버를 잘못 선택하였을 가능성이 높다. 😒

    [선택 사항]

    위에서 dvico IR 수신기는 hiddev1라는 이름으로 커널(udev)에 의해 명명되었는데, 이름의 마지막 숫자는 커널이 장치를 발견한 순서에 따라 달라질 수 있다. 실제로 내 경우에 dvico IR 수신기를 부팅 후에 꽂았을 때는 hiddev0은 마우스, hiddev1은 수신기이지만, 수신기를 꽂은 채로 부팅하면 hiddev0이 수신기, hiddev1가 마우스가 된다. 이러한 문제를 해결하기 위해 IR 수신기 장치파일이 만들어 질 때 이 장치파일에 대한 symbolic link가 만들어지도록 udev 규칙 파일을 하나 만든다. 아래는 내 udev 규칙 파일.

$ cat /etc/udev/rules.d/81-dvico-remote.rules # Give dvico remote a permernant name ACTION=="add", SUBSYSTEM=="usbmisc", ATTRS{product}=="DVICO USB HID Remocon V1.00", SYMLINK+="usb/dvico_remote"
    udev 규칙 파일을 작성하는 방법에 대해서는 인터넷에서 쉽게 찾을 수 있으니 여기서 설명하지는 않겠지만, 혹시 이 글을 보는 사람 중에 udev 규칙 파일에 익숙하지 않은 사람을 위해 위 규칙 파일의 내용을 간단히 설명하면, 어떤 장치의 SUBSYSTEM이 usbmisc이고 그 장치와 연결된 부모(parent) 장치 중 하나가 product 속성으로 "DVICO USB HID Remocon V1.00" 값을 가지고 있다면 그 장치가 추가될 때 /dev/usb/dvico_remote라는 심볼릭 링크를 만들라는 내용이다. 다음 부팅--정확히는 위 udev 규칙 파일 실행--이후부터는  /dev/usb/hiddevN 대신 /dev/usb/dvico_remote라는 장치파일을 사용할 수 있다.

    2-4. lirc 설정

    lirc(lircd)의 설정 파일은 "/etc/lirc/lirc_options.conf" 이다. 이 파일을 에디터로 열어 driver와 device를  다음과 같이 위에서 파악한 값으로 수정한다. 다른 항목은 특별한 이유가 없으면 건드릴 필요 없다.

driver          = dvico device          = /dev/usb/dvico_remote

    이제 lirc daemon(lircd)을 실행하기 앞서, 위 "2-1"에서 다운받은 키맵 파일이 /etc/lirc/lircd.conf.d/ 디렉토리 아래 제대로 복사되어 있는지 다시 한번 확인한다. 그리고 lircd (재)실행!
    아래는 openrc 환경인 경우이고--재실행인 경우에는 당근 start 대신 restart--, systemd 사용자라면 systemctl을 이용해야 할 것이다.

$ sudo /etc/init.d/lircd start  * Starting lircd ...                                          [ ok ]

    2-5. 2차 테스트

    1차 테스트가 리모콘이 송신한 IR 신호를 lirc 드라이버가 lirc 스캔코드로 해독/변환할 수 있는가를 테스트한 것이었다면, 2차 테스트는 여기에 더해 키맵 파일이 lirc 스캔코드를 정확한 입력신호(keycode)로 변환하고 있는지 확인하는 테스트이다. 1차 테스트에서 mode2라는 프로그램을 사용하였는데, 2차 테스트에서는 irw라는 프로그램을 사용한다. irw 역시 lirc 패키지에 포함되어 있으므로 따로 설치할 필요가 없다. 리모콘의 버튼을 하나씩 눌러 정확한 keycode로 변환(mapping)되는지 확인해 본다. 만약 일부 버튼이 잘못 변환된다면 해당 키맵 파일을 수정하면 된다.

$ irw 0001004600005efe 00 KEY_OK DVICO_MCE 0001004600005efe 01 KEY_OK DVICO_MCE 00010046000053fe 00 KEY_DOWN DVICO_MCE 00010046000053fe 01 KEY_DOWN DVICO_MCE 00010046000053fe 02 KEY_DOWN DVICO_MCE
    모든 리모콘 버튼이 정확하게 키 코드로 변환된다면 OK!  아니면 2-1부터 다시 하나씩 확인 😕

    2-6. kodi 설정

    kodi에서 lirc를 이용한 리모콘 신호 처리는 2단계에 걸쳐 이루어 진다. 1단계는 Lircmap.xml 파일을 통해 lirc가 인식한 버튼 이름(예: KEY_PLAY)을 kodi에서 사용하는 버튼 이름(예: play)로 변환하고, 2단계는 remote.xml 파일을 통해 kodi 버튼을 실행가능한 Action(예: Play)으로 맵핑(mapping) 한다.

    kodi의 기본 Lircmap.xml 파일은 /usr/share/kodi/system/ 디렉토리에서 찾을 수 있는데, 이를 열어보면 다음과 같이 리모콘 기종별로 lirc 버튼(keycode)을 kodi 버튼으로 맵핑해 놓은 것을 볼 수 있다. 
 <lircmap>         <remote device="mceusb">                 <play>KEY_PLAY</play>                 <pause>KEY_PAUSE</pause>                              ...         </remote>         <remote device="Microsoft_Xbox">                 <play>PLAY</play>                 <pause>PAUSE</pause>                              ...         </remote>                              ... </lircmap>
    위에서 device= 다음에 나오는 "mceusb"나 "Microsoft_Xbox"가 리모콘 기종(장치이름)에 해당한다. 만약 기본 Lircmap.xml 파일에 내 리모콘 기종이 없다면 userdata 디렉토리 아래 나만의 Lircmap.xml 파일을 만들어야 한다. 위 2-5 irw 실행 결과를 참고해서 기본 Lircmap.xml에서 제공하는 기종 중  keycode(lirc 버튼 이름)가 비슷한 기종을 골라 복사해서 수정하면 된다. "장치이름"은 2-1에서 다운 받은 키맵 파일에 적혀 있고,  irw 실행 결과(마지막 칼럼)에서도 알 수 있다. 아래는 내 Lircmap.xml의 일부.
<lircmap>     <remote device="DVICO_MCE">         <play>KEY_PLAYPAUSE</play>         <pause>KEY_PLAYPAUSE</pause>         <stop>KEY_STOP</stop>                          ...     </remote> </lircmap>
    2단계 변환을 담당하는 remote.xml 파일은 각 window(화면)별로 구분이 되어 있고, 같은 버튼이라도 window별로 수행되는 액션을 다르게 설정할 수 있다. 아래는 /usr/share/kodi/system/keymaps/ 디렉토리에 있는 kodi의 기본 remote.xml의 일부인데, left 키는 기본적으로(global) Left 액션을 수행하도록 되어 있지만, FullscreenVideo 화면에서는 StepBack 액션을 수행하도록 맵핑되어 있는 것을 볼 수 있다.
 <keymap>   <global>     <remote>       <left>Left</left>                ...     </remote>   </global>   <FullscreenVideo>     <remote>       <left>StepBack</left>                ...     </remote>   </FullscreenVideo>                ... </keymap>
    기본 remote.xml에서 설정되어 있는 액션을 바꾸고 싶다면 <userdata>/keymap/ 디렉토리 아래에 사용자 remote.xml 파일--사실 파일 확장자가 .xml이면 파일이름은 아무래도 상관없다--을  만들어 액션을 재정의(override)하면 된다. 예를 들어 FullscreenVideo 화면에서 left를 눌렀을 때 StepBack 액션 대신 SmallStepBack 액션을 수행하도록 하고 싶다면 다음과 같은 내용으로 xml 파일을 만든다. 재정의하려는 부분만 적으면 된다. 나머지는 기본 remote.xml의 맵핑이 적용될 것이다.
 <keymap>   <FullscreenVideo>     <remote>       <left>SmallStepBack</left>     </remote>   </FullscreenVideo> </keymap>

    2-7. 시스템 시작 시 lircd가 자동으로 시작하도록 설정

    openrc 사용자라면 다음과 같이 default runlevel에 등록. systemd 사용자라면 lirc service enable.

$ sudo rc-update add lircd default

kernel이 지원하는 IR 수신기의 경우

"1번"에서 언급했듯이 kernel은 리모콘 수신기 장치를 발견하면  sysfs에 이 리모콘 수신기를 나타내는 디렉토리(/sys/class/rc/rcN)를 만들고, 이 디렉토리 아래 서브 디렉토리와 파일을 통해서 장치 정보(modalias)를 노출/공유한다. 그리고 kernel(디바이스 드라이버가 kernel module인 경우 udev)은 user space(/dev)에 이 sysfs 장치에 대응하는 /dev/input/eventN(N은 일련번호) 장치 파일을 만들어, kernel이 해독한 keycode를 여기로 전송한다. 물론 "1번"에서 적은 것처럼 kernel이 IR 신호를 적절히 수신/해독할 수 있는 프로토콜과 device driver가 설정되어 있는 경우에 말이다. 

그런데 kernel(udev)이 리모콘 수신기 장치를 발견했을 때 만드는 장치 파일은 이 뿐이 아니다. kernel(udev)은 /dev/lircN(N은 일련번호) 파일을 만들어 해독(decode)되지 않은 IR 수신정보 원본을 여기로 전송한다. 이 수신정보 원본 전송은 kernel이 직접 수신정보를 해독하느냐 여부와 상관없이 kernel이 IR 수신기를 리모콘 수신기로 인식하고 /sys/class/rc/rcN 장치로 IR 신호를 수신할 수 있으면 자동적으로 이루어 진다. 앞에서 보았던 lirc 프로토콜이 이때 사용된다.

말이 길어 졌지만, 요점은 IR 수신기를 kernel이 인식한다면 위 2-2에서처럼 장치파일을 찾느라 삽질할 필요 없이 /dev/lircN 장치파일을 사용할 수 있다는 것이다. 대부분의 경우 리모콘 수신장치를 2개 이상 설치해서 사용하는 경우가 드무니 그 장치 파일 이름은 십중팔구 /dev/lirc0이 될 것이다. 한가지 더 희소식은 lirc는 /dev/lircN으로부터 IR 신호를 읽어 오는데 "default"라는 전용 드라이버를 제공한다는 점이다.

[설정 환경]

 "1번"의 설정 환경에 lirc  버전 0.10.2을 사용한다.

    2-1. lirc 장치 파일 확인

    ir-keytable로 lirc 장치 파일을 확인한다. 역시나 /dev/lirc0이고 lirc 프로토콜이 활성화 되어 있음을 알 수 있다.

$ ir-keytable
Found /sys/class/rc/rc0/ with:
Name: ITE8708 CIR transceiver
   Driver: ite-cir
   Default keymap: rc-rc6-mce
   Input device: /dev/input/event4
   LIRC device: /dev/lirc0
   Supported kernel protocols: lirc rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp imon rc-mm 
   Enabled kernel protocols: lirc 
   Extra capabilities: <access denied>

    2-2. 1차 테스트

    mode2를 이용해서 kernel이 /dev/lirc0로 IR 신호를 잘 보내고 있는지 확인해 본다. 아래와 같은 결과를 볼 수 있으면 OK! 
$ sudo mode2 --device /dev/lirc0 --driver default Using driver default on device /dev/lirc0 Trying device: /dev/lirc0 Using device: /dev/lirc0 Running as regular user dks pulse 8955 space 4512 pulse 529 space 1709 pulse 546 ... timeout 125477 ^C

    위의 kernel이 지원하지 않는 IR 수신기의 mode2 실행 결과와 조금 다르기는 하지만 정상이다.

    2-3. 키맵 파일 찾기

    lirc 사이트에서 내 리모콘에 맞는 키맵 파일을 다운로드하여 /etc/lirc/lircd.conf.d/ 디렉토리에 복사한다.

    2-4. lirc 설정

    /etc/lirc/lirc_options.conf 파일을 에디터로 열어 driver와 device를 다음과 같이 수정한다.

driver          = default device          = /dev/lirc0

    2-5. 2차 테스트

    lirc daemon(lircd)을 실행한 후 irw로 2차 테스트를 해본다. 위의 kernel이 지원하지 않는 IR 수신기의 irw 실행 결과와 같이 리모콘 버튼을 누를 때마다 최종 keycode로 변환된 결과가 나와야 한다. 만약 아무 반응이 없다면 키맵 파일이 부정확할 가능성이 크다. 이 때는 lirc 웹사이트에서 다른 키맵 파일을 찾아보던지, irrecord를 이용해서 직접 키맵 파일을 만들어야 한다.

    나머지 kodi 설정과 lirc 자동 시작 설정은 위 kernel이 지원하지 않는 IR 수신기의 경우 설정과 동일하다.  

0 Comment: