Let’s go!

  • linux usb driver xbox360(5)

    linux usb driver xbox360(5)

    드디어 마지막이다. xbox 패드를 마우스처럼 사용하고 싶어 이렇게 동작하도록 수정했다. 처음 연결하면 조이스틱으로 동작하고, 가운데 버튼을 누르면 마우스로 동작하도록 했다. 리눅스에서 조이스틱 쓸 일이 있나?? 초기값을 마우스로 바꿨어야 했다.

    input_device에 name을 설정하지 않고 등록하면 xserver로 이벤트를 전달할 수 없다. input_device에 name을 설정하지 않으면 evtest에 이름이 표시되지 않는다.

    xbox 스틱으로 들어오는 값을 그대로 사용하면 값이 너무 커서 커서가 화면 끝에서 끝까지 점프한다. 대략 2,000으로 나눴다. 바꿔 말하면 큰 값을 짧은 시간에 한번에 전달하기 위해 조이스틱을 사용한다.

    버튼을 누르면 rising edge, falling edge 두 번 동작한다. 비트 시프트로 특정 값을 만족하면 모드가 바뀌도록 했다. 인터럽트 등록할 때 rising, falling edge 필터링 할 수 있는데, urb로 등록하면 그런 게 없는 듯 하다. 짱구를 많이 굴렸다.

    xpad.c 드라이버를 따라 한 수준이지만 커스터 마이징을 해 보면서 무엇을 모르고 무엇을 학습했는지 알 수 있었다. 책만 보고서는 절대 이해하지 못했을 지식이다. 사용되는 함수들은 소스 코드를 보지 않는 한 절대 사용하지 못할 함수 들이다. 이미 내가 고민했던 내용을 누군가 해결하여 코드로 구현했다. 소스코드 검색을 자주 해야 시간을 줄일 수 있다.

    xpad.c에 보면 사용할 struct에 관심있는 주소를 모두 때려 넣었다. 구조가 복잡하고, 주소가 너무 많아 북마크처럼 주소를 관리해야 한다. 커널에서 내려오면 항상 메모리를 해제할 수 있도록 했다. 보면 볼수록 container_of 매크로로 위력을 실감한다. 부분을 알면 구조체 멤버에 무제한으로 접근할 수 있다. 와!! 이거 생각해낸 사람이 정말 대단하다.

    나도 이제 usb, character device에서 하산해도 될 듯 하다.

    https://github.com/now0930/xbox_driver

    참조 자료

  • linux usb driver xbox360(4)

    linux usb driver xbox360(4)

    input 테스트

    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에 전달.

    리눅스 시스템 내에서 사용할 수 있는 이벤트가 이미 정해져 있어 사용자 맘대로 설정할 수 없다. 그 말은 조이스틱 이동을 마우스 이동 키로 할당하면 마우스 대신 조이스틱을 사용할 수 있단 말이다. 조이스틱을 눌렀을 때 어느 데이터가 켜지는지는 기기마다 다르기 때문에 몇 번 테스트를 해야 한다.

    data 패킷 중 버튼 값 위치
    static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
                                       u16 cmd, unsigned char *data)
    {
            if (data[0] != 0x00)
                    return;
            /* buttons A,B,X,Y,TL,TR and MODE */
            input_report_key(dev, BTN_A,            data[3] & 0x10);
            input_report_key(dev, BTN_B,            data[3] & 0x20);
            input_sync(dev);
    }

    아날로그 버튼이라면 얼마나 눌렸는지 값을 전달할 수 있다. 콘솔에서 실행 가능한 evtest로 입력값을 확인할 수 있다.

    xpad.c를 직접 해 보면서 다음을 학습했다. The Linux Kernel 에는 너무 간단한 예가 있다. 내가 무엇을 알고 있고, 모르는 지를 정확하게 확인되지 않고는 위 문서를 제대로 이해할 수 없다. 적절한 삽질로 정확하게 이해할 수 있다.

    • usb 등록, 해제
    • urb packet 사용 방법
    • input device 등록, 해제
    • input device 사용

    참조 사이트

  • linux usb driver xbox360(3)

    IDA 사용하기

    고유한 번호를 부여하기 위하여 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 컨트롤러를 입력으로 사용하는 방법으로 넘어가도 된다.

  • linux usb driver xbox360(2)

    LED 제어하기

    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

    10번을 보내면 LED가 rotate 한다.

    –추가–

    • interrupt 등록, usb_submit_urb 완료 함수(xpad_irq_outfn) 등록.
    • set_brightness로 led 밝기 조정 함수에 등록.
    • xpad->urb_active == true일 경우 명령에 맞는 패킷 만들어 전송.
    • xpad_irq_outfn 안ㅔ서 xpad->urb_active = false로 수정. xpad->urb_active가 없으면 일정 주기로 계속 실행됨.

    참조 사이트.

  • linux usb driver xbox360(1)

    집에서 놀고 있던 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인터페이스가 등록되지 않았다.

    pi@raspberrypi:~/rasp/xbox_driver $ usb-devices 
    T:  Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=12  MxCh= 0
    D:  Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS= 8 #Cfgs=  1
    P:  Vendor=045e ProdID=028e Rev=01.14
    S:  Manufacturer=©Microsoft Corporation
    S:  Product=Controller
    S:  SerialNumber=0C59FC4
    C:  #Ifs= 4 Cfg#= 1 Atr=a0 MxPwr=500mA
    I:  If#=0x0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=5d Prot=01 Driver=(none)
    I:  If#=0x1 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=5d Prot=03 Driver=(none)
    I:  If#=0x2 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=5d Prot=02 Driver=(none)
    I:  If#=0x3 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=fd Prot=13 Driver=(none)

    usb_register_dev로 interface 0, 1을 등록했다.

    pi@raspberrypi:~/rasp/xbox_driver $ usb-devices 
    T:  Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=12  MxCh= 0
    D:  Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS= 8 #Cfgs=  1
    P:  Vendor=045e ProdID=028e Rev=01.14
    S:  Manufacturer=©Microsoft Corporation
    S:  Product=Controller
    S:  SerialNumber=0C59FC4
    C:  #Ifs= 4 Cfg#= 1 Atr=a0 MxPwr=500mA
    I:  If#=0x0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=5d Prot=01 Driver=xbox360
    I:  If#=0x1 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=5d Prot=03 Driver=xbox360
    I:  If#=0x2 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=5d Prot=02 Driver=(none)
    I:  If#=0x3 Alt= 0 #EPs= 0 Cls=ff(vend.) Sub=fd Prot=13 Driver=(none)
    
    pi@raspberrypi:~/rasp/xbox_driver $ dmesg 
    [48226.848081] xpad is 0c15d4e5, xpad->udev is 14758ec9
    [48226.848101] interface is 913f883f
    [48226.848126] xbox360 1-1.1:1.0: interrupt in, out found. 55429370, 0119bb5d
    [48226.848528] xbox360 1-1.1:1.0: usb xbox360 driver was registerd
    [48226.848710] xpad is a5c87389, xpad->udev is 14758ec9
    [48226.848726] interface is f687e098
    [48226.848746] xbox360 1-1.1:1.1: interrupt in, out found. efcdcd2e, 2fcb2b2f
    [48226.849000] xbox360 1-1.1:1.1: usb xbox360 driver was registerd
    [48226.849162] xpad is 5c5394a1, xpad->udev is 14758ec9
    [48226.849178] interface is 7fa06804
    [48226.849196] xbox360 1-1.1:1.2: Could not find both interrupt-in and interrpt-out endpoints
    [48226.849211] error -6
    [48226.849304] xpad is 5c5394a1, xpad->udev is 14758ec9
    [48226.849320] interface is c9ea30ca
    [48226.849337] xbox360 1-1.1:1.3: Could not find both interrupt-in and interrpt-out endpoints
    [48226.849352] error -6
    [48226.849490] usbcore: registered new interface driver xbox360

    참조 사이트.