Ralink 측으로부터 샘플로 받은 One-Chip 기반의 USB-to-Wireless Dongle 인 RT3572 에 대한 포팅 및 테스트 방법을 소개한다.
RT3572 의 경우, Ralink 에서 아직 시장에 출시한 제품이 아니기 때문에 WLB5254USB 와 같이 리눅스 커널에서 기본적으로 지원되지 않는다.
따라서 Ralink 로 부터 디바이스 드라이버 코드를 받아 컴파일하여 현재 커널에 올리는 방법을 사용했다.
참고로 개발환경은 다음과 같다.
H/W | Saturn 6 |
S/W | GP1 (linux-2.6.26) |
타겟(DTV)에 올리기 전에, PC 기반의 윈도우, 리눅스 상에서 동작하는 것을 확인하였다. 윈도우용 드라이버는 http://www.ralinktech.com/ralink/Home/Support/Windows.html 에서 다운로드 할 수 있다.
디바이스 드라이버 컴파일하기
리눅스용 드라이버(2009_0226_RT3572_Linux_STA_v2.1.0.0.tar.bz2)의 경우, 공개되어 있지 않아, Ralink 측으로부터 받았다.
PC 기반으로 컴파일하기 위해서는 별다른 수정없이 컴파일하면 된다. 하지만, 타겟에 올리기 위해서는 Makefile 을 수정해야 한다.
#cd 2009_0226_RT3572_Linux_STA_v2.1.0.0 #vi Makefile ... RT28xx_MODE = STA TARGET = LINUX CHIPSET = 3572 ... PLATFORM = STAR ... ifeq ($(PLATFORM), STAR) LINUX_SRC = /vol/users/wjkim/work/gp/os/saturn6_linux/kernel/linux-2.6.26-saturn6 CROSS_COMPILE = /opt/crosstool/mipsel-gcc-4.1.2-uclibc-0.9.28.3-mips32/bin/mipsel-linux- endif ...
.dat 파일 만들기
RT3572 디바이스 드라이버에서는 RT2870STA.dat 파일이다. 이 파일은 profile 과 같은 역할을 한다. 설정 파일이라고 보면 되겠다. 이 파일은 open() 할때('ifconfig ra0 up' 실행 시) 디바이스 드라이버에서 읽어들인다.
따라서 이 파일이 없다면, 아래와 같은 에러가 발생한다.
#ifconfig ra0 up ifconfig: ioctl 0x8914 failed: Operation not permitted
.dat 파일의 경로는 /include/os/rt_linux.h 에서 지정할 수 있다.
... #ifdef RTMP_MAC_USB #define STA_PROFILE_PATH "./RT2870STA.dat" // rt3572sta.ko 파일과 같은 디렉토리에 있다고 가정한다 #define STA_DRIVER_VERSION "2.1.0.0" #ifdef MULTIPLE_CARD_SUPPORT #define CARD_INFO_PATH "/etc/Wireless/RT2870STA/RT2870STACard.dat" #endif // MULTIPLE_CARD_SUPPORT // #endif // RTMP_MAC_USB // ...
주석에서도 설명했듯이, 나중에 .ko 파일과 .dat 파일을 rootfs 에 넣을 때, 같은 디렉토리에 복사해야 한다.
이제는 RT2870STA.dat 파일을 살펴보자!
#The word of "Default" must not be removed Default CountryRegion=5 CountryRegionABand=7 CountryCode= ChannelGeography=1 SSID=LGEWEP NetworkType=Infra WirelessMode=5 Channel=0 AuthMode=WEPAUTO EncrypType=WEP DefaultKeyID=1 Key1Type=0 Key1Str=a1215a0824b0501b1009c0522d PSMode=CAM
위의 설정은 사내 무선 AP 망에 WEP 인증방식으로 접속할 경우를 기준으로 작성하였다. 자세한 설정 방법은 README_STA 파일을 참고하기 바란다.
'ifconfig ra0 up' 을 실행함으로서, 위의 설정으로 세팅이 되더라도, 나중에, 'iwpriv' 명령어를 이용해서 바꿀 수 있다.
문제점 발생
여기서 잠시 아주 중요한 이야기를 해야 겠다. 앞에서 설명한 Makefile 파일만 수정하면, 컴파일하여 .ko 파일을 생성하는 데는 아무런 문제가 없다. 하지만, 타겟에 올리고 모듈을 올리는 과정 또는 올리고 나서 'ifconfig ra0 up' 명령 시에 Segmentation Fault 가 발생하는 문제가 있었다. 증상은 다음과 같다.
#insmod rt3572sta.ko #lsmod Module Size Used by Tainted: P rt3572sta 502336 - ... #ifconfig ra0 up Segmentation fault
이때, 에러 로그는 다음과 같다.
<1>[ 68.485000] CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == c06762fc, ra == c06762f4 <4>[ 68.485000] Oops[#1]: <4>[ 68.485000] Cpu 0 <4>[ 68.485000] $ 0 : 00000000 10008c00 00000000 35720220 <4>[ 68.485000] $ 4 : 00000004 808bd800 85c40280 805cc6c4 <4>[ 68.485000] $ 8 : 867fbfe0 00008c00 00000000 86796000 <4>[ 68.485000] $12 : 807d0bc8 86784d08 3b9aca00 00000000 <4>[ 68.485000] $16 : 00000000 c0580000 c0580000 c067e00c <4>[ 68.485000] $20 : c0683800 00008914 85c3310c 867fbe10 <4>[ 68.485000] $24 : 807d0b58 807d0bc8 <4>[ 68.485000] $28 : 867fa000 867fbd00 00000000 c06762f4 <4>[ 68.485000] Hi : 00000000 <4>[ 68.485000] Lo : 3b9aca00 <4>[ 68.485000] epc : c06762fc RTUSBReadMACRegister+0x4c/0x78 [rt3572sta] Tainted: P <4>[ 68.485000] ra : c06762f4 RTUSBReadMACRegister+0x44/0x78 [rt3572sta] <4>[ 68.485000] Status: 10008c03 KERNEL EXL IE <4>[ 68.485000] Cause : 0080000c <4>[ 68.485000] BadVA : 00000000 <4>[ 68.485000] PrId : 0001906c (MIPS 4KEc) <4>[ 68.485000] Modules linked in: rt3572sta mdrv_ve(P) mdrv_vd(P) mdrv_ttx(P) mdrv_tsp(P) mdrv_spi(P) mdrv_scaler(P) mdrv_rld(P) mdrv_pwm(P) <4>[ 68.485000] Process ifconfig (pid: 943, threadinfo=867fa000, task=85c29160, tls=00000000) <4>[ 68.485000] Stack : 00000000 c0580000 c06613ec c06762b0 c0631ee0 00001000 867fbd20 00000004 <4>[ 68.485000] 35720220 00001002 c06349ec c0634998 ffffffea 0000540d 86c65400 86dfe500 <4>[ 68.485000] 7feef5e8 86c65410 35720220 004376c8 c0580000 867dcc00 00001043 00001002 <4>[ 68.485000] 00000000 c0663a70 000fa1a4 00000000 000000e1 85c278f0 8081c6a0 8045cb44 <4>[ 68.485000] c0580000 c0663bec 80795210 86dfe500 0000540d 8081c7a0 867dcc00 00000000 <4>[ 68.485000] ... <4>[ 68.485000] Call Trace: <4>[ 68.485000] [<c06762fc>] RTUSBReadMACRegister+0x4c/0x78 [rt3572sta] // 마지막 Segmentation fault 가 발생하는 지점이다 <4>[ 68.485000] [<c0634998>] rt28xx_init+0x68/0x3d4 [rt3572sta] <4>[ 68.485000] [<c0663a70>] rt28xx_open+0x4c/0xe4 [rt3572sta] <4>[ 68.485000] [<c0663bec>] MainVirtualIF_open+0xe4/0x10c [rt3572sta] <4>[ 68.485000] [<806052b8>] dev_open+0x7c/0x124 <4>[ 68.485000] [<80605ccc>] dev_change_flags+0x9c/0x1f8 <4>[ 68.485000] [<8064d59c>] devinet_ioctl+0x7ec/0x880 <4>[ 68.485000] [<8064fa2c>] inet_ioctl+0xc0/0xd8 <4>[ 68.485000] [<805f486c>] sock_ioctl+0x210/0x2c0 <4>[ 68.485000] [<80499410>] vfs_ioctl+0xc0/0xd8 <4>[ 68.485000] [<804994d0>] do_vfs_ioctl+0xa8/0x3e8 <4>[ 68.485000] [<80499860>] sys_ioctl+0x50/0x90 <4>[ 68.485000] [<8040bad0>] stack_done+0x20/0x3c <4>[ 68.485000] <4>[ 68.485000] <4>[ 68.485000] Code: afa00010 04400006 8fa30020 <ae030000> 8fbf002c 8fb00028 03e00008 27bd0030 2403ffff
Segmentation fault 가 발생하는 RTUSBReadMACRegister() 함수를 보면, #ifdef 구문이 있는 H/W 의존적인 코드들이 대부분이다. 문제의 원인은 Saturn6 H/W 스펙에 디바이스 드라이버 코드가 적절히 대응되지 못하는 것이라 짐작했다.
조금 뒷 이야기를 하자면, 나는 Makefile 에 지정되어 있는 Platform 을 일일이 바꿔가며 테스트를 했었다. 하지만, 결과는 동일했다.
결국 ralink 측 엔지니어의 도움을 받아 문제를 해결할 수 있었다.
문제점 해결
지금부터 잘 보기 바란다. 아래와 같이 config.mk 파일을 주석에 따라 수정한다.
#cd os/linux #vi config.mk ... WFLAGS := -DAGGREGATION_SUPPROT -DPIGGYBACK_SUPPORT -DWMM_SUPPORT -DLINUX -Wall -Wstrict-prototypes -Wno-trigraphs // -DDBG 삭제한다 ... Ifeq ($(RT28xx_MODE), STA) WFLAGS += -DCONFIG_STA_SUPPORT // -DDBG 삭제한다 ... ifeq ($(CHIPSET), 3572) WFLAGS +=-DRTMP_MAC_USB -DRTMP_USB_SUPPORT -DRT2870 -DRT30xx -DRT35xx -DRTMP_TIMER_TASK_SUPPORT -DRTMP_RF_RW_SUPPORT -DRTMP_EFUSE_SUPPORT -DST // ★☆ 아주 중요하다 ★☆ endif ... ifeq ($(PLATFORM), STAR) #CFLAGS := ... // 주석 처리한다 EXTRA_CFLAGS := $(WFLAGS) -I$(RT28xx_DIR)/include endif ...
문제의 핵심은 '-DST' 였다. 이것을 define 해 줌으로서, 현재의 Saturn6 H/W 스펙에 충족할 수 있었다.
이제 다시 컴파일 하여 테스트 보자.
/mnt/lg/lgapp/drivers/wireless_util # insmod rt3572sta.ko [ 81.807000] rtusb init --->, Jit=326 [ 81.829000] usbcore: registered new interface driver rt2870 /mnt/lg/lgapp/drivers/wireless_util # lsmod Module Size Used by Tainted: P rt3572sta 502848 - ... /mnt/lg/lgapp/drivers/wireless_util # ifconfig ra0 up [ 52.377000] 0x1300 = 00064300 /mnt/lg/lgapp/drivers/wireless_util # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) ra0 Link encap:Ethernet HWaddr 00:0C:43:32:41:2A UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1374 errors:1 dropped:0 overruns:0 frame:0 TX packets:87 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:240795 (235.1 KiB) TX bytes:6832 (6.6 KiB)
연결 설정하기
여기서는 사내 무선 AP 에 접속하여, IP 를 받아온 후에 외부와의 네트워킹이 가능한지 확인해보겠다.
앞서 'dat 파일 만들기' 에서 설명했듯이, 추가적인 별다른 설정이 필요없다. 단지 udhcpc 를 이용해서 IP 를 받아오기만 하면 된다.
udhcpc 에 대한 자세한 설정사항은 [“WLB5254USB 사용하기”] 를 참고하기 바란다.
#ifconfig ra0 up // 자동으로 RT2870STA.dat 파일을 읽어들임으로서, AP 에 WEP 인증을 사용하여 연결하는 설정이 완료되었다 #busybox udhcpc -i wlan0 -s simple.script renew ### adapter index 2 ### adapter hardware address 00:0c:43:32:41:2a udhcpc (v1.10.1) started ### vfork'ing and execle'ing ../../../user/simple.script ### entering raw listen mode ### opening raw socket on ifindex 2 ### got raw socket fd 23 ### attached filter to raw socket fd 23 ### bound to raw socket fd 23 ### adding option 0x35 ### adding option 0x3d ### adding option 0x3c ### adding option 0x39 Sending discover... ### Waiting on select... ### adding option 0x35 ### adding option 0x3d ### adding option 0x3c ### adding option 0x39 Sending discover... ### Waiting on select... ### Got valid DHCP packet ### adding option 0x35 ### adding option 0x3d ### adding option 0x3c ### adding option 0x32 ### adding option 0x36 Sending select for 10.177.188.37... ### Waiting on select... ### Got valid DHCP packet Lease of 10.177.188.37 obtained, lease time 691200 ### vfork'ing and execle'ing ../../../user/simple.script deleting routers route: ioctl 0x890c failed: No such process adding dns 156.147.135.180 adding dns 156.147.135.181 adding dns 165.244.106.110 ### entering none listen mode /mnt/lg/lgapp/drivers/wireless_util # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) ra0 Link encap:Ethernet HWaddr 00:0C:43:32:41:2A inet addr:10.177.188.37 Bcast:10.177.189.255 Mask:255.255.254.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:14310 errors:7 dropped:0 overruns:0 frame:0 TX packets:114 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2623959 (2.5 MiB) TX bytes:9910 (9.6 KiB) /mnt/lg/lgapp/drivers/wireless_util # telnet 211.196.153.28 Entering character mode Escape character is '^]'. login:
인증 방식에 따른 설정
각 인증 방식에 따라 각각 설정을 달리해주어야 한다. iwpriv 명령어를 사용해서 다음과 같이 스크립트 파일을 만들어서 손쉽게 설정할 수 있다.
2009_0302_rt2870_linux_sta_v2.1.0.0_releasenote_dpb_2009_0318.pdf 를 참고하기 바란다.
NONE
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Infra /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=OPEN /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=NONE /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP"
WPA
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Infra /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=WPAPSK /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=TKIP /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set Key1="0123456789" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set WPAPSK="abcdefgh" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP"
AES
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Infra /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=WPAPSK /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=AES /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set Key1="0123456789" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set WPAPSK="abcdefgh" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP"
WPA2-AES
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Infra /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=WPA2PSK /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=AES /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set WPAPSK="abcdefgh" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP"
WPA2-PSK
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Infra /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=WPA2PSK /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=TKIP /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set WPAPSK="abcdefgh" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="RT2880_AP"
Ad-Hoc
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Adhoc /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=OPEN /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=NONE /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="fattt"
Ad-Hoc-Tkip
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set NetworkType=Adhoc /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set AuthMode=WPANONE /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set EncrypType=TKIP /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="a" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set WPAPSK="abcdefgh" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 set SSID="a"
WPS 모드
WPS 를 사용하기 위해서는 os/linux/config.mk 파일을 아래와 같이 수정해야 한다.
... #ifdef WSC_INCLUDE # Support WSC function HAS_WSC=y #endif // WSC_INCLUDE // ...
PIN 방식
WiFi Dongle 의 PIN 번호를 알기 위해서는 다음과 같이 명령한다.
#./iwpriv ra0 stat ra0 stat: Tx success = 156 Tx success without retry = 156 Tx success after retry = 0 Tx fail to Rcv ACK after retry = 0 RTS Success Rcv CTS = 0 RTS Fail Rcv CTS = 0 Rx success = 513 Rx with CRC = 384 Rx drop due to out of resource = 0 Rx duplicate frame = 0 False CCA (one second) = 0 RSSI-A = 0 RSSI-B (if available) = 0 RSSI-C (if available) = 0 WPS Information(Driver Auto-Connect is Enabled): RT2860 Linux STA PinCode 32934823 // PIN 번호 WPS not used. WPS Profile Count = 0
나중에 AP 에 위의 PIN 번호를 입력하면 된다. 다음과 같이 명령한다.
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_conf_mode 1 /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_mode 1 /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_ssid "ASW" /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_start
실행 후에, AP 쪽에서 PIN 번호를 추가해주면 된다. 이후 'iwconfig' 명령어로 AP 에 접속이 되었는지 확인해본다.
PUSH 방식
#!/bin/sh /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_conf_mode 1 /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_mode 2 /mnt/lg/lgapp/drivers/wireless_util/iwpriv ra0 wsc_start
실행 후에, AP 의 'WPS' 버튼을 누르면, 일정한 시간 후에 접속이 된다.