usb로 들어오는 버튼 입력을 테스트 했다. LED를 켜는 출력과 비슷하게 진행된다. 역시 출력을 보는 작업이 제일 어려웠다.
입력 디바이스 할당: input_allocate_device();
urb 할당
usb_alloc_coherent
usb_alloc_urb
인터럽트로 urb 설정: usb_rcvintpipe, usb_fill_int_urb
input device 주소 변경?: input_set_drvdata
open, close 함수 오버로드
input_dev->open = xpad_open
input_dev->close = xpad_close
usb_submit_urb call back 함수 설정
input_dev->close에 usb_kill_urb로 urb를 해제하지 않으면 다시 입력 디이스를 열었을 때 사용할 수 없다.
xpad.c 드라이버는 인터럽트 실행될 때 완료 함수를 등록했다. 그 함수 안에있는 work queue가 input_report_key, input_sync로 입력 키를 이벤트로 OS에 전달한다.
버튼이 눌렸을 경우 이벤트가 0, 1 값으로 전달되어야 한다. urb로 들어오는 data 패킷 중 2번, 3번 주소에 mask를 씌워 EV_KEY 값 상태를 확인한다.
인터럽트 발생 확인.
urb data 값 수신.
해당하는 주소에 mask를 씌워 특정 이벤트를 OS에 전달.
리눅스 시스템 내에서 사용할 수 있는 이벤트가 이미 정해져 있어 사용자 맘대로 설정할 수 없다. 그 말은 조이스틱 이동을 마우스 이동 키로 할당하면 마우스 대신 조이스틱을 사용할 수 있단 말이다. 조이스틱을 눌렀을 때 어느 데이터가 켜지는지는 기기마다 다르기 때문에 몇 번 테스트를 해야 한다.
고유한 번호를 부여하기 위하여 define_ida, ida_simple_get, ida_simple_remove 형식으로 사용한다.
//사용할 구조체 선언.
struct usb_xpad{
struct input_dev *dev; /* input device interface */
...
int pad_nr; // order
};
...
static DEFINE_IDA(xpad_pad_seq);
static int xpad_led_probe(struct usb_xpad *xpad)
{
//ida 할당
//pad_nr을 계속 증가..
//led가 몇번에 연결되어 있는지 알 수 있음.
//0~0x8,000,000 - 1까지 증가
xpad->pad_nr = ida_simple_get(&xpad_pad_seq, 0, 0, GFP_KERNEL);
if (xpad->pad_nr < 0){
retval = xpad->pad_nr;
goto free_mem;
}
free_mem:
kfree(led);
return retval;
}
xbox 360 패드가 4개 램프를 가지고 있다. 플레이어에 따라 램프를 달리 켜줘야 하는데, xpad->pad_nr을 ida 번호를 부여하여 4로 나눈 나머지에 2를 더했다. 5번째 패드를 붙여도 불을 것 같지만 xpad_led5이런 식으로 추가될 듯 하다. 플레이어 1과 같은 램프가 켜질 듯 하다.
메모리가 부족하면 ida를 할당하지 못한다. xpad.c를 보면 다시 해제 하도록 했다.
led+숫자로 등록하여 usb_unregister로 led 디바이스를 해제하면 된다. 이제 xbox 컨트롤러를 입력으로 사용하는 방법으로 넘어가도 된다.
xbox 360 컨트롤러가 4개 LED를 가지고 있다. 총 4명 플레이어가 있을 경우 1 ~ 4까지 시계 방향으로 램프를 켜주게 된다. usb -> input 버튼 입력을 받아 들이기 전 간단해 보는 LED 제어를 해보기로 했다. 하..어렵다. 입력 받아들이는 것 보다 더 어려운 듯 하다.
cdev_led_register를 실행하면 /sys/class/led에 led 모듈이 등록된다.
pi@raspberrypi:/sys/class/leds $ dmesg | tail -20
[ 7044.723682] leds xpad_led: Setting an LED's brightness failed (-524)
[ 7044.723824] disconnected
[ 7344.482279] xpad is 56bd80be, xpad->udev is 7ea0c81d
[ 7344.482296] interface is 49be3431
[ 7344.482318] xbox360 1-1.1:1.0: interrupt in, out found. 2cedb0f6, 62b37858
[ 7344.482579] xbox360 1-1.1:1.0: usb xbox360 driver was registerd
[ 7344.482861] xpad is db7e959d, xpad->udev is 7ea0c81d
[ 7344.482875] interface is c0605ae3
[ 7344.482893] xbox360 1-1.1:1.1: interrupt in, out found. 6497d636, 4bf83f77
[ 7344.483394] xbox360 1-1.1:1.1: usb xbox360 driver was registerd
[ 7344.483545] usb 1-1.1: Led xpad_led renamed to xpad_led_1 due to name collision
[ 7344.483699] xpad is 2c597e01, xpad->udev is 7ea0c81d
[ 7344.483713] interface is dfef8df2
[ 7344.483730] xbox360 1-1.1:1.2: Could not find both interrupt-in and interrpt-out endpoints
[ 7344.483743] error -6
[ 7344.483824] xpad is 2c597e01, xpad->udev is 7ea0c81d
[ 7344.483837] interface is f1417f63
[ 7344.483852] xbox360 1-1.1:1.3: Could not find both interrupt-in and interrpt-out endpoints
[ 7344.483865] error -6
[ 7344.483985] usbcore: registered new interface driver xbox360
pi@raspberrypi:/sys/class/leds $ ls -tl
total 0
lrwxrwxrwx 1 root root 0 Jan 4 23:52 xpad_led -> ../../devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.1/leds/xpad_led
lrwxrwxrwx 1 root root 0 Jan 4 23:52 xpad_led_1 -> ../../devices/platform/scb/fd500000.pcie/pci0000
xbox 컨트롤러가 총 4개를 등록할 수 있는데, 인터럽트로 2개를 등록할 수 있다. 나머지 2개는 무엇인지 모르겠다. 암튼 xpad.c는 2번만 등록했다.
CDEV_LED로 등록을 하면 brightness_set 함수를 overload 할 수 있다. brightness_set -> xpad_led_set -> xpad_send_led_command으로 연결한다. xpad_send_led_command 안에서 LED 제어하는 패킷을 만들어 URB에 넣어 준다. urb->transfer_buffer_length를 잘 넣어줘야 URB 패킷을 전송한다.
인터럽트로 한번 등록하면 일정 주기로 계속 명령을 실행한다.
[ 340.057841] xpad is c97d9e68, xpad->udev is decb56d0
[ 340.057858] interface is d89e5572
[ 340.057871] cur_altsetting is 2
[ 340.057893] xbox360 1-1.1:1.0: interrupt in, out found. 00000000, 3b46194c
[ 340.058231] xbox360 1-1.1:1.0: usb xbox360 driver was registerd
[ 340.058253] dma output address 6ce62de4 with size 64, point to f2c4e403 was allocated
[ 340.058266] actual address is 6ce62de4
[ 340.058280] urb was allocated
[ 340.058292] xpad->endpoint_out->bInterval is 8
[ 340.058306] XPAD_OUT_LED_IDX: 2
[ 340.058318] XPAD_NUM_OUT_PACKETS: 3
[ 340.058440] xpad->led_command->data[0]:1,[1]:3,[2]:10, with 3 len
[ 340.058463] xpad_send_led_command: completed
[ 340.058828] usbcore: registered new interface driver xbox360
[ 359.127170] usbcore: deregistering interface driver xbox360
[ 359.127225] xpad address is c97d9e68, intf is d89e5572
[ 359.129187] xpad->led_command->data[0]:1,[1]:3,[2]:0, with 3 len
[ 359.129217] xbox360 1-1.1:1.0: xpad_send_led_command - usb_submit_urb failed with -ENOENT
[ 359.131553] xpad led was unregistered
[ 359.131575] dma output address 6ce62de4 with size 64 was freed
[ 359.131590] disconnected
집에서 놀고 있던 8년 전에 구매한 xbox 360 유선 usb 패드를 raspberry pi4에 등록하여 사용하고 싶다. 나온 지 오래되어 이미 xpad 리눅스 드라이버가 공개되어 있다. 그러나 usb, input을 한번에 사용하여 잘 이해되지 않는다. 남은 출장 기간 중 천천히 공부 하기로 했다.
usb를 등록하려면 vendor id, product id, 연결되면 실행되는 call back probe, disconnect 등 을 알아야 한다. 인터넷에 공개된 좋은 자료, 소스를 공부했다. 관련 코드는 여기에 저장했다.
usb-devices로 xbox 패드를 확인하면 (I가 interface 인듯하다) 0, 1, 2, 3인터페이스가 등록되지 않았다.
회사에 있는 좋은 프로그램으로 라즈베리 파이4를 구했다. 나는 시간만 썼다. 11월 말까지 진행할 계획인데, 시간이 지날수록 그 프로그램 목적 달성하기 어려워 보인다. 나는 얻은 바 있어 거래에 만족한다. 나중에 보고서를 작성하면 된다.
저가형 모터를 돌리려 라즈베리 파이를 사용함은 너무 비싸다. 단순 독립된 디바이스와 달리 IoT 디바이스로 라즈베리 만큼 좋은 장차가 없다. 아무리 싸게 만들어도 같은 기능을 가진 하드웨어를 라즈베리 파이보다 싸게 만들 수 없어 보인다. 맞춤형 하드웨어를 설계하는 순간 수 백만원 깨질 듯 하다. 이런 좋은 사양 디바이스를 그냥 쓰기 아까워 docker를 설치하여 mysql, ptyhon 기능을 추가했다.
특히 요즘 뜨는 python이 데이터를 최적화하여 처리한다. 배우기도 쉽다. 모듈도 다양하여 남이 작성하여 공개한 인터넷 코드를 보면 금방 적용할 수 있다. python을 사용하지 않고 gnu c로 같은 기능을 만든다면 손해가 엄청나다.
mysql 역시 데이터를 잘 처리한다. 파일에 데이터를 저장하나 조회하기 어렵고 python과 연계가 어렵다. mysql이 기본 라즈베리에 파이 OS에 설치되지 않아, docker로 금방 설치했다.
PLC가 주는 데이터를 socket으로 받았고, 메인 프로세스가 인터넷에서 받은 데이터와 비교하여 모터를 특정 각도로 움직이기로 했다. 중간에 IPC를 적용하여 억지로 넣은 코드도 좀 있다.
다음 그림과 같이 구성했다.
gnu c에서 python을 사용하려면 fork로 child process를 만들고 exec로 대체해야 한다. 잘못 사용하면 while(1){fork();} 이런 식으로 전개되는 수가 있다. 이 코드를 실행하는 순간 ssh가 먹통되고 아무 작업을 할 수 없다.