分类:
2009-05-27 14:04:02
文档说明:自威盛公司的VNT6656USB_Embedded_Module无线网卡到货,到今天已经有差不多将近3个星期了。这3个星期里,通过不断的发现问题和解决问题,从开发板厂商提供的linux2.4.18内核,到自己移植的linux2.6.13内核,再到CS8900A网卡驱动,USB Host驱动的移植修改,昨天终于成功的将VNT6656USB_Embedded_Module无线网卡Port到了FriendlyARM的 SBC2410开发板上,同时实现了两块开发板通过Ad Hoc方式组网完成单跳数据传输。这其中经历的风风雨雨,酸甜苦辣或许只有自己才知道。本文档主要记录了移植Linux2.6.13到SBC2410开发板和将VNT6656USB_Embedded_Module无线网卡Port到运行Linux2.6.13 Os的SBC2410开发板的全过程,同时也记录了我在完成这一工作时所遇到的困难和问题及一些感想。
DebugLife
2008.7.3
一.放弃Linux2.4.18内核,转向Linux2.6.13内核
拿到VNT6656USB无线网卡后,首先仔细查看了其DataSheet和文档说明,主要是下面一段话。
Kernel & Linux Supported
========================
This driver support linux kernel version 2.6.x/2.4.x and have been
tested under:
1. 2.6.11-1.1369 (Fredora core 4.0, up/smp)
2. 2.6.9-1.667 (Fredora core 3.0, up/smp)
3. 2.6.5-1.358 (Fredora core 2.0, up/smp)
4. 2.4.22-1.2115 (Fredor cor2 1.0, up/smp)
5. 2.4.20 (Redhat 9.0, up/smp)
也就是说该无线网卡同时支持Linux2.4.x内核和Linux2.6.x内核,心中一阵窃喜。买来的开发板配有Linux2.4.18稳定内核并包含其下所有模块的驱动程序,Linux2.4.18内核与2.4.20内核的开发时间差别不大,代码的修改量应该也不是很多,是不是可以将该 VNT6656USB无线网卡直接Port到该内核上呢?这样只要重新交叉编译VNT6656USB无线网卡的驱动即可。
立刻开始动手,参照威盛代理商提供的Porting_doc,修改Makefile中的CC,LD为交叉编译环境。
CC := arm-linux-gcc
LD := arm-linux-ld
基本上也就可以编译,但编译出现了很多错误。修改驱动源代码如下:
1.去掉80211mgr.h中除了第一个和最后一个的所有#progma pack(1),原因好像是结构体对齐方式内核不支持。
2.device.h -500,+501 将struct usb_ctrlrequest sUsbCtlRequest换成devrequest sUsbDevRequest(2.4.18内核中usb_ctrlrequest结构体的替代品)
3.usbpipe.c -245,246,247,248,249,+250,251,252,253,254,-259,+263,-314,315,316,317,318,+319,320,321,322,323,-327,+331(将 sUsbCtlRequest换成sUsbDevRequest)
4.main_usb.c -801(get_wireless_stats在2.4.18内核中没有定义,网上讨论说作用不大,去掉也无妨)
再次编译,成功通过生成vntwusb.o文件。
再交叉编译出utility文件夹下的wetctl可执行程序,将vntwusb.o和wetctl下载到SBC2410开发板上,然后执行插入模块insmod vntwusb.o,dmesg模块插入成功,再激动的将无线网卡插入USB Host,终端再次显示发现模块VNTUSB_Wireless_interface(模块名称,公司声明等信息), 一切看似都很正常。最后配置IP并激活网卡,输入命令: ifconfig eth1 192.168.245.1
灾难发生:SIOCSIFFLAGS: Cannot allocate memory
通过google,出现该问题的主要原因是内核不支持网卡。郁闷了两三天,仍然不能解决。于是,Email给代理商,寻求解决方法,但并不抱什么期望。印象中国内的代理商很多都是只会卖东西而丝毫不关心技术和售后的家伙,但这一次让我改变了看法,代理商很快回信说,他们已经我出现的问题提交给了公司技术部门,并将尽快给我答复。果然,两天后我又收到了邮件,回复说他们的驱动不支持linux2.4.20以下内核,让我更新内核试试。为什么 DataSheet上说适用于2.4.x内核,却仅支持2.4.20以上版本呢?又是厂商为了增加产品吸引力的一个小伎俩!迫于无奈,我开始了移植 Linux2.6.13到SBC2410开发板的艰辛历程。
二.移植Linux2.6.13内核到SBC2410开发板
为什么选用2.6.13内核而不选用无线网卡文档上说明的已经测试过的其他几个内核版本呢?主要原因是开发板厂商除了卖s3c2410开发板,还卖 s3c2440开发板,而s3c2440开发板配有的稳定内核是Linux2.6.13,s3c2410与s3c2440MCU的主要差别是在主頻上,其他方面差别不大,而且据经验,同一个开发板厂商类似的开发板驱动通常都是通用的或仅有少量改动。因此,可以拿Friendly s3c2440开发板配有的linux2.6.13移植到我们的s3c2410上,这样在驱动移植上可以避免不少麻烦。下文所述的移植过程也是在这一基础上进行的。
移植大致过程:
1.在Makefile中修改交叉编译环境,linux2.6.13的交叉编译器为gcc-3.4.1
2.配置内核(make menuconfig)
修改内核的System ARCH,从2440改为2410,然后根据我们对nand flash的分区,修改Boot Option选项为:noinitrd root=/dev/mtdblock/0 init=/linuxrc console=ttySAC0,115200;暂时先去掉cs8900a的驱动
3.修改arch/arm/mach-s3c2410/devs.c添加nand flash分区信息
/***********add here*************/
#i nclude
#i nclude
#i nclude
/***********end add*************/
...
/*****************************add here***************************/
static struct mtd_partition partition_info[] ={
{
name: "loader",
size: 0x00020000,
offset: 0,
}, {
name: "eboot",
size: 0x00020000,
offset: 0x00020000,
}, {
name: "param",
size: 0x00010000,
offset: 0x00040000,
}, {
name: "kernel",
size: 0x001c0000,
offset: 0x00050000,
}, {
name: "root",
size: 0x03e00000,
offset: 0x00220000,
}
};
struct s3c2410_nand_set nandset ={
nr_partitions: 5 ,
partitions: partition_info ,
};
struct s3c2410_platform_nand superlpplatform={
tacls:0,
twrph0:30,
twrph1:0,
sets: &nandset,
nr_sets: 1,
};
/********************************end add****************************/
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = 0xec,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
.dev = {
.platform_data = &superlpplatform //***********add here*****
}
};
必须保证linux kernel中flash nand的分区格式与我们实际的nand flash分区相同,否则内核将无法启动(感谢操虹提供的帮助)。我们的nand flash分区格式如下:(在vivi下可以通过part info查看,注意区分bon part与part命令,个人觉得用part对MTD分区更加容易理解一些)
4. 添加"&s3c_device_nand" to the __initdata in the arch/arm/mach-s3c2410/mach-smdk2410.c
5. 禁止ecc,修改the drivers/mtd/nand/s3c2410.c
for example:
/**** chip->eccmode = NAND_ECC_SOFT; **/
chip->eccmode = NAND_ECC_NONE;
6.重新编译内核,并下载到开发板即可运行。
7.附注:除了以上操作,我们还修改了vivi的默认分区格式( part reset可以将分区置为默认分区格式),同时为了在linux下配合minicom使用串口烧写,需要修改vivi中xmodem的参数,综合以上几点建议重新编译vivi.当然,不重新编译vivi也可以,只要你不怕每次烧过vivi后重设相关参数的麻烦即可。
主要修改3处:
(1)修改vivi/arch/s3c2410/smdk.c,设置默认mtd分区格式,免得重烧vivi后又需要重新为mtd分区。
mtd_partition_t default_mtd_partitions[] = {
.......
}
(2)修改vivi/arch/s3c2410/smdk.c,将xmodem协议的过期时延加大,以适应minicom
vivi_parameter_t default_vivi_parameters[] = { //这些是vivi的默认参数
.....
{“xmodem_initial_timeout”, 20000000, NULL},
{“xmodem_timeout”, 1500000, NULL},
.....
};
(3)修改vivi/arch/s3c2410/smdk.c,改变默认启动参数,免得每次重烧vivi后又需要修改内核启动参数
char linux_cmd[] = “noinitrd root=/dev/mtdblock/4 init=/linuxrc console=ttySAC0”;
三.移植cs8900a网卡驱动和usb host驱动到Linux2.6.13
本以为Friendly s3c2440开发板配备的2.6.13内核中的驱动程序可以不加修改用到Friendly s3c2410开发板上,实验证明这是错误的。下面是移植cs8900a和usb host驱动的过程。
1.cs8900a以太网控制器的驱动移植
(1)本来是需要将cs8900.c和cs8900.h两个文件拷贝到相应目录里的,但是这一点Friendly已经完成了。
(2)我们关键是要在arch/arm/mach-s3c2410/mach-smdk2410.c的static_struct map_desc smdk2410_iodesc[] __initdata中增加将设备物理地址映射到虚拟地址上的映射。
{vSMDK2410_ETH_IO,pSMDK2410_ETH_IO,SZ_1M,MT_DEVICE},
//下面三个是什么映射我也不知道,还是加上吧:)
{0xe0000000,0x08000000,0x00100000,MT_DEVICE},
{0xe0100000,0x10000000,0x00100000,MT_DEVICE},
{(unsigned long)S3C24XX_VA_IIS,S3C2410_PA_IIS,S3C24XX_SZ_ISS,MT_DEVICE},
别忘了包含头文件
#include
(3)修改cs8900.c
#ifdef CONFIG_ARCH_SMDK2410
#i nclude “arm/arch/sbc2440v3-map.h” //added here
#i nclude
#endif
这样,再重新编译内核时,选择cs8900模块,将以太网卡编入内核,启动后ifconfig eth0 ....就可以正常工作了。
2.usb host驱动的移植
编译内核时,已经将usb host编入,且选择scsi disk,期望能够支持U盘,不论何时,插入U盘,usb无线网卡等,都显示如下错误:
usb 1-1: device deor read/64, error -110
参考了这篇牛帖http://blog.chinaunix.net/u2/70800/showart_1071031.html
我按照如下方法来做。
(1)在arch/arm/mach-s3c2410.c中添加
/**********add by shengfeng**********/
/* Add by lili for USB setup
*
*/
static unsigned int power_state[2];
static void
usb_simtec_powercontrol(int port, int to)
{
pr_debug("usb_simtec_powercontrol(%d,%d)\n", port, to);
power_state[port] = to;
if (power_state[0] && power_state[1])
s3c2410_gpio_setpin(S3C2410_GPB4, 0);
else
s3c2410_gpio_setpin(S3C2410_GPB4, 1);
}
static irqreturn_t
usb_simtec_ocirq(int irq, void *pw, struct pt_regs *regs)
{
struct s3c2410_hcd_info *info = (struct s3c2410_hcd_info *)pw;
if (s3c2410_gpio_getpin(S3C2410_GPG10) == 0) {
pr_debug("usb_simtec: over-current irq (oc detected)\n");
s3c2410_usb_report_oc(info, 3);
} else {
pr_debug("usb_simtec: over-current irq (oc cleared)\n");
s3c2410_usb_report_oc(info, 0);
}
return IRQ_HANDLED;
}
static void usb_simtec_enableoc(struct s3c2410_hcd_info *info, int on)
{
int ret;
if (on) {
ret = request_irq(IRQ_USBOC, usb_simtec_ocirq, SA_INTERRUPT,
"USB Over-current", info);
if (ret != 0) {
printk(KERN_ERR "failed to request usb oc irq\n");
}
set_irq_type(IRQ_USBOC, IRQT_BOTHEDGE);
} else {
free_irq(IRQ_USBOC, info);
}
}
static struct s3c2410_hcd_info usb_simtec_info = {
.port[0] = {
.flags = S3C_HCDFLG_USED
},
.port[1] = {
.flags = S3C_HCDFLG_USED
},
.power_control = usb_simtec_powercontrol,
.enable_oc = usb_simtec_enableoc,
};
/*****************end****************/
**********add by shengfeng**********/
void __init sbc2410_init(void)
{
set_s3c2410ts_info(&sbc2410_ts_cfg);
set_s3c2410udc_info(&sbc2410_udc_cfg);
set_s3c2410fb_info(&sbc2410_lcdcfg);
s3c2410_gpio_cfgpin(S3C2410_GPH0, S3C2410_GPH0_OUTP);
s3c2410_gpio_pullup(S3C2410_GPH0, 0);
s3c2410_gpio_setpin(S3C2410_GPH0, 0);
printk("USB Power Control, (c) 2004 Simtec Electronics\n");
s3c_device_usb.dev.platform_data = &usb_simtec_info; //important
s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPB4, 1);
}
/*****************end****************/
(2)别忘了在arch/arm/mach-s3c2410.c中包含头文件
/**********add by shengfeng**********/
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
/*****************end****************/
/**********add by shengfeng**********/
#i nclude "usb-simtec.h"
#i nclude
#i nclude
#i nclude
#i nclude
#i nclude
/*****************end****************/
(3)修改arch/arm/devs.c
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource
.dev = {
.platform_data = &superlpplatform //***********add here*****
}
};
(4)修改driver/usb/host/ohci-s3c2410.c以改变USB时钟频率。
static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd)
{
struct s3c2410_hcd_info *info = dev->dev.platform_data;
dev_dbg(&dev->dev, "s3c2410_start_hc:\n");
clk_enable(clk);
if (info != NULL) {
info->hcd = hcd;
info->report_oc = s3c2410_hcd_oc;
if (info->enable_oc != NULL) {
(info->enable_oc)(info, 1);
}
}
/****************add shengfeng********************/
unsigned long upllvalue = (0x78<<12)|(0x02<<4)|(0x03);
while (upllvalue != __raw_readl(S3C2410_UPLLCON))
{
__raw_writel(upllvalue, S3C2410_UPLLCON);
mdelay(1);
}
/********************************************************/
}
这样,USB Host+U盘支持就已经驱动起来了。
四.移植VNT6656USB_EMBEDDED_Module无线网卡到Linux2.6.13
有了上面的工作,这里就变得非常简单了。同样是修改Makefile中的关于交叉编译环境的部分,不同的是源代码驱动使用威盛原厂的只要很少修改,就可以编译通过。
源代码修改如下:
(1)注释掉main_usb.c中所有的DEVICE_PARAM(.....)
(2)注释掉ioctl.c line:298
pList->sBSSIDList[ii].wBeaconInterval = pBSS->wBeaconInterval;
否则gcc会出错。
修改的就是这么一点。
然后将编译好的vntwusb.ko,myconfig.wlan, wetctl通过minicom的zmodem协议发送到开发板上.
依次执行:
insmod vntwusb.ko
插入usb wireless card
ifconfig eth1 192.168.245.1 //可以省略
./myconfig.wlan
完全正确,扫描结束,没有发现网络,并建立了一个ad hoc网络
在另一块开发板上执行相同操作
扫描结束,发现网络,加入ad hoc网络成功。
然后双方互ping同ip,ftp传送文件也没有问题。至此,历时半个多月从内核移植到无线网卡的移植全部完成。
附: myconfig.wlan脚本
#! /bin/sh
# VIA Networking Wireless LAN USB device network setup
#
###################### Wireless Settings ######################################
# Assigned available device interface such as eth0 or eth1.
DEVICE=eth1
# wireless settings
DESIRED_SSID="ARM_AP" # ["xxx"] Service Set ID for used(Max 32)
BSSTYPE=adhoc # [adhoc|infra] adhoc or infrastructure mode
CHANNEL=6 # [1..14] default startup channel
PSMODE=no # [yes|no] to enable power saving
AUTHTYPE=open # [open|share] authentication mode
# WEP enable/disable
WEP_ENABLE=yes # [yes|no] to enable WEP
WEP_DEFKEY=0 # [0..3] Key Index for use
# WEP HEX key settings, only WEP_ENABLE=yes, key will take effect.
# [no|"xx:xx:xx:xx:xx"]: no key or 40bit(10-hex)/104(26-hex)wep hex key
WEP_KEY0="11:22:33:44:55" # 104 bit wep key
WEP_KEY1=no
WEP_KEY2=no
WEP_KEY3=no
###################### Settings End ###########################################
#
# Don't modify the following setup !
# The following use "Wireless Settings" to configure wirless network.
#
###################### Begin ###########################################
cnt=`/sbin/lsmod | grep -c vntwusb`
if [ "$cnt" = "0" ]; then
echo "insmod 'vntwusb'"
/sbin/insmod vntwusb.o
fi
# Open device
/sbin/ifconfig $DEVICE 192.168.245.1
#/sbin/ifconfig $DEVICE up
if [ "$DESIRED_SSID" = "" ] ; then
DESIRED_SSID="any"
fi
# Set your wep keys, if present
if [ "$WEP_ENABLE" = "yes" ] ; then
./wetctl $DEVICE privacy \
$WEP_DEFKEY \
key0 $WEP_KEY0 \
key1 $WEP_KEY1 \
key2 $WEP_KEY2 \
key3 $WEP_KEY3
else
./wetctl $DEVICE privacy off
fi
# Scan for existing networks
echo "Scanning channels"
./wetctl $DEVICE scan
# Join for existing networks
./wetctl $DEVICE join \
$BSSTYPE \
$DESIRED_SSID \
$CHANNEL \
$PSMODE \
$AUTHTYPE
echo "Joining with " \
"Desired SSID=$DESIRED_SSID," \
"Network Type=$BSSTYPE"