【linux驱动分析】之dm9000驱动分析(一):dm9000原理及硬件分析
【linux驱动分析】之dm9000驱动分析(二):定义在板文件中的资源和设备以及几个宏
【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析
【linux驱动分析】之dm9000驱动分析(四):net_device结构体
【linux驱动分析】之dm9000驱动分析(五):另外几个重要的结构体
【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现
【linux驱动分析】之dm9000驱动分析(七):dm9000的卸载挂起和恢复以及打开和停止
硬件平台:友善之臂Tiny6410核心板 + DM9000EP
软件平台:linux-2.6.38
交叉编译器:Friendly ARM提供的arm-linux-gcc 4.5.1
一、源代码(mach-mini6410.c)
1 /* Ethernet */ 2 #ifdef CONFIG_DM9000 3 #define S3C64XX_PA_DM9000 (0x18000000) 4 #define S3C64XX_SZ_DM9000 SZ_1M 5 #define S3C64XX_VA_DM9000 S3C_ADDR(0x03b00300) 6 7 static struct resource dm9000_resources[] = { 8 [0] = { 9 .start = S3C64XX_PA_DM9000, 10 .end = S3C64XX_PA_DM9000 + 3, 11 .flags = IORESOURCE_MEM, 12 }, 13 [1] = { 14 .start = S3C64XX_PA_DM9000 + 4, 15 .end = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1, 16 .flags = IORESOURCE_MEM, 17 }, 18 [2] = { 19 .start = IRQ_EINT(7), 20 .end = IRQ_EINT(7), 21 .flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH, 22 }, 23 }; 24 25 static struct dm9000_plat_data dm9000_setup = { 26 .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY, 27 .dev_addr = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 }, 28 }; 29 30 static struct platform_device s3c_device_dm9000 = { 31 .name = "dm9000", 32 .id = 0, 33 .num_resources = ARRAY_SIZE(dm9000_resources), 34 .resource = dm9000_resources, 35 .dev = { 36 .platform_data = &dm9000_setup, 37 } 38 }; 39 40 static int __init dm9000_set_mac(char *str) { 41 unsigned char addr[6]; 42 unsigned int val; 43 int idx = 0; 44 char *p = str, *end; 45 46 while (*p && idx < 6) { 47 val = simple_strtoul(p, &end, 16); 48 if (end <= p) { 49 /* convert failed */ 50 break; 51 } else { 52 addr[idx++] = val; 53 p = end; 54 if (*p == ':'|| *p == '-') { 55 p++; 56 } else { 57 break; 58 } 59 } 60 } 61 62 if (idx == 6) { 63 printk("Setup ethernet address to %pM\n", addr); 64 memcpy(dm9000_setup.param_addr, addr, 6); 65 } 66 67 return 1; 68 } 69 70 __setup("ethmac=", dm9000_set_mac); 71 #endif 72 73 static struct map_desc mini6410_iodesc[] = { 74 { 75 /* LCD support */ 76 .virtual = (unsigned long)S3C_VA_LCD, 77 .pfn = __phys_to_pfn(S3C_PA_FB), 78 .length = SZ_16K, 79 .type = MT_DEVICE, 80 }, 81 #ifdef CONFIG_DM9000 /*这里的定义不知道是做什么用的*/ 82 { 83 .virtual = (u32)S3C64XX_VA_DM9000, 84 .pfn = __phys_to_pfn(S3C64XX_PA_DM9000), 85 .length = S3C64XX_SZ_DM9000, 86 .type = MT_DEVICE, 87 }, 88 #endif 89 };
DM9000的设备会在
platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));
里统一注册。
二、下面分析一下上面代码中红色的宏或者函数
1、ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof( (arr)[0] ) + __must_be_array(arr) )
它是定义在include/linux/kernel.h中的一个宏,用来计算数组中元素的个数。
__must_be_array是编译器相关的,用来防止传入的参数不是数组,比如说传入了指针,这样的话可能回编译不通过(猜测)。
2、simple_strtoul
simple_strtoul是定义在lib/vsprintf.c中的函数,它的作用是把一个字符串转换为unsigned long型的整数,并返回。/** * simple_strtoul - convert a string to an unsigned long * @cp: The start of the string * @endp: A pointer to the end of the parsed string will be placed here * @base: The number base to use */ unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
其中的endp参数存放解析后的字符串地址,base参数,是要转换的进制数。
vsprintf.c里还定义了其他好多字符串处理的函数,具体用到时去查。
3、__setup
它是定义在include/linux/init.h中的一个宏:
#define __setup(str, fn) \ __setup_param(str, fn, fn, 0)
关于__setup的更多分析见《__setup宏的作用》其中:str是关键字,fn是关联处理函数。__setup只是告诉内核在启动时输入串中含有str时,内核要去执行fn。Str必须以“=”符结束以使parse_args更方便解析。紧随“=”后的任何文本都会作为输入传给fn。
例如本例中的: __setup ( " ethmac= " , dm9000_set_mac);