分类: LINUX
2010-09-14 10:56:46
2.1 net_open 与 net_close
net_open 函数主要完成的工作有:(这段 net_open 函数的概要内容总结来源于网络,网址: )
A .获取私有数据指针存放于 lp
B .启动设备总线控制功能和启动存储器
C .调用 request_irq() 请求中断并注册 net_interrupt 为中断服务程序;
D .写中断号存于设备中 write_irq()
E .如果无法申请中断号,则返回错误
F .如果支持 DMA 则通过以下函数初始化 DMA :
_get_dma_pages();
get_order();
dma_page_eq();
G .申请 DMA , requeset_dma()
H .初始化设备结构中关于 DMA 的参数,并使能 DMA ;
I .设置以太网地址, writereg()
J .检测链路,从而确定连接媒体类型
K .配置链路:
a.10B_T:detect_tp()
b.AUI:detect_aui()
c.10B_2:detect_bnu()
d.AUTO: 从头检测自动配置
L .输出信息
M .启动链路串行接收和发送功能
N .初始化 lp 相关参数
O .配置 DMA , set_dma_cfg()
P .配置芯片相关寄存器
Q .配置 DMA 缓冲 dma_bufcfg()
R .使能芯片中断;
S .启动网络传输队列, netif_start_queue()
这部分内容都与 cs8900 芯片具体操作相关,相对来说和比较简单,下面直接给出 net_open 与 net_close 的相关注解
static int net_open(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
int result = 0;
int i;
int ret;
......// 省略一些信息
/* FIXME: Cirrus' release had this: */
writereg(dev, PP_BusCTL, readreg(dev,PP_BusCTL)|ENABLE_IRQ);// 使能 cs8900 中断
write_irq(dev, lp->chip_type, dev->irq);// 该函数选择 cs8900 芯片内部的中断线,
二、
net_open
、
net_close
和
net_interrupt
2.1
net_open
与
net_close
net_open
函数主要完成的工作有:(这段
net_open
函数的概要内容总结来源于网络,网址:
)
A
.获取私有数据指针存放于
lp
B
.启动设备总线控制功能和启动存储器
C
.调用
request_irq()
请求中断并注册
net_interrupt
为中断服务程序;
D
.写中断号存于设备中
write_irq()
E
.如果无法申请中断号,则返回错误
F
.如果支持
DMA
则通过以下函数初始化
DMA
:
_get_dma_pages();
get_order();
dma_page_eq();
G
.申请
DMA
,
requeset_dma()
H
.初始化设备结构中关于
DMA
的参数,并使能
DMA
;
I
.设置以太网地址,
writereg()
J
.检测链路,从而确定连接媒体类型
K
.配置链路:
a.10B_T:detect_tp()
b.AUI:detect_aui()
c.10B_2:detect_bnu()
d.AUTO:
从头检测自动配置
L
.输出信息
M
.启动链路串行接收和发送功能
N
.初始化
lp
相关参数
O
.配置
DMA
,
set_dma_cfg()
P
.配置芯片相关寄存器
Q
.配置
DMA
缓冲
dma_bufcfg()
R
.使能芯片中断;
S
.启动网络传输队列,
netif_start_queue()
这部分内容都与
cs8900
芯片具体操作相关,相对来说和比较简单,下面直接给出
net_open
与
net_close
的相关注解
static int net_open(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
int result = 0;
int i;
int ret;
......//
省略一些信息
/* FIXME: Cirrus' release had this: */
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ ); //
使能
cs8900
中断
write_irq(dev, lp->chip_type, dev->irq); //
该函数选择
cs8900
芯片内部的中断线,
//
见本文件中的
write_irq
实现
//++++++++++++++++++++++
这段代码为自己添加,内核原版中没有
#if defined(CONFIG_ARCH_S3C2410)
set_irq_type(dev->irq, IRQT_RISING);
//
该函数在
kernel\irq\chip
实现,
//
可选择的中断类型有
include\linux\interrupt.h
中定义,此处设置为上升沿触发中断
#endif
//++++++++++++++++++++++
ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); //
注册中断
if (ret) {
if (net_debug)
printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq);
goto bad_out;
}
……
/* set the Ethernet address *///
将
MAC
地址设置到
cs8900
的
Individual Address
寄存器
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
/* while we're testing the interface, leave interrupts disabled */
writereg(dev, PP_BusCTL, MEMORY_ON);
//
使
cd8900
工作到
memory
模式,
//
如果
dev->mem_start
域为
0
,
//
将关闭该模式
//
以下代码为选择
cs8900
的物理传输媒体的类型
/* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
//
由于没有
eeprom
,
lp->adapter_cnf
在
cs89x0_probe1
中未设置,此值为
0.
if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) && (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
lp->linectl = LOW_RX_SQUELCH;
else
lp->linectl = 0;
/* check to make sure that they have the "right" hardware available */
switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T: result = lp->adapter_cnf & A_CNF_10B_T; break;
case A_CNF_MEDIA_AUI:
result = lp->adapter_cnf & A_CNF_AUI; break;
case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break;
default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);
}
#if defined(CONFIG_ARCH_PNX0105) || defined(CONFIG_ARCH_S3C2410)//+++++++++
result = A_CNF_10B_T;
//
上面由于
lp->adapter_cnf=0
,导致
result=0
,
//
这里额外设置
该值可以根据需要实际情况设置,可设置的值可在
//
cs89x0.h
中找到
当然这里也可以设置
lp->adapter_cnf
成想要的值
#endif
if (!result) {//result==0
时执行此段代码
printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name);
release_irq:
......
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
free_irq(dev->irq, dev);
ret = -EAGAIN;
goto bad_out;
}
/* set the hardware to the configured choice */
switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {//lp->adapter_cnf & A_CNF_MEDIA_TYPE==0,
//
不符合任何
case
情况,将执行
default
,但未实现
default
case A_CNF_MEDIA_10B_T:
result = detect_tp(dev);
//detect_tp
探测物理传输媒体类型是
RJ-45H,
//
还是
RJ-45F
if (result==DETECTED_NONE) {
printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
}
break;
case A_CNF_MEDIA_AUI:
result = detect_aui(dev);//detect_tp
探测物理传输媒体类型是否为
AUI
型
if (result==DETECTED_NONE) {
printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */
}
break;
case A_CNF_MEDIA_10B_2:
result = detect_bnc(dev);
//detect_tp
探测物理传输媒体类型是否为
BNC
型
if (result==DETECTED_NONE) {
printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
}
break;
case A_CNF_MEDIA_AUTO:
writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
if (lp->adapter_cnf & A_CNF_10B_T)
if ((result = detect_tp(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_AUI)
if ((result = detect_aui(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_10B_2)
if ((result = detect_bnc(dev)) != DETECTED_NONE)
break;
printk(KERN_ERR "%s: no media detected\n", dev->name);
goto release_irq;
}
switch(result) {
//
上面将
result
赋成了
A_CNF_10B_T
,该值为
1
,刚好等于
//
DETECTED_RJ45H
所以也可以在上面的
result
中直接赋成
//
DETECTED_RJ45H
或者其他类型的接口
case DETECTED_NONE:
printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name);
goto release_irq;
case DETECTED_RJ45H:
printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
break;
case DETECTED_RJ45F:
printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
break;
case DETECTED_AUI:
printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name);
break;
case DETECTED_BNC:
printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name);
break;
}
/* Turn on both receive and transmit operations */
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
/* Receive only error free packets addressed to this card */
lp->rx_mode = 0;//
确定接收模式,
0
表示接收广播
,
非
0
表示全部接收
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); //
初始化接收控制器
RxCTL
为默认
//
接收模式。
该模式下,只接收
Broadcast
和
Individual
的
CRC
//
正确的数据包,
具体可查看
cs8900
手册。
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; //
接收
OK
产生中断,
//
CRC
错产生中断
if (lp->isa_config & STREAM_TRANSFER)//
判断是否打开
cs8900
的
stream
传输模式
lp->curr_rx_cfg |= RX_STREAM_ENBL;//
使用
stream
模式
,
此处没有启用。
writereg(dev, PP_RxCFG, lp->curr_rx_cfg); //
初始化接收配置控制器
RxCFG
,
//
这里确定了接收中断源
//
初始化发送配置控制器
TxCFG
,
TxCFG
寄存器的全部有效位置为
1
,
//
也确定了发送中断源
writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, ENABLE_IRQ
//
开中断
| (dev->mem_start?MEMORY_ON : 0)
//
没有设置共享内存空
//
间
dev->mem_start
为
0
,
memory
模式将被关闭
);
netif_start_queue(dev);
//
激活设备发送队列,以便内核可以开始发送数据
if (net_debug > 1)
printk("cs89x0: net_open() succeeded\n");
return 0;
bad_out:
return ret;
}
net_close(struct net_device *dev)
{
......//
略去
DMA
部分
netif_stop_queue(dev);//
停止设备发送队列,通知内核不能使用该设备发送数据
writereg(dev, PP_RxCFG, 0);//
禁用接收
writereg(dev, PP_TxCFG, 0);//
禁用发送
writereg(dev, PP_BufCFG, 0);//
关闭
cs8900
内部缓冲区
writereg(dev, PP_BusCTL, 0);//
停止总线
free_irq(dev->irq, dev);//
释放占用的中断线
......//
略去
DMA
部分
/* Update the statistics here. */
return 0;
}
2.2
net_interrupt
该函数的大体流程如下:(此段总结来源同上)
A
.获取设备私有数据
net_priv()
;
B
.读取
CS8900
的中断端口状态
readword()
;
C
.判断中断类型:
a.
接收事件:调用
net_rx()
接收数据;
b.
传输事件:调用
netif_wake_queue()
唤醒传输队列,进行异常处理;
c.
缓冲区事件:可以发送数据,调用
netif_wake_queue()
唤醒传输队列;
d.
接收包丢失事件:初始化相关
error
,错误计数
e.
传输冲突时间:初始化相关
error
D
.返回中断句柄。
net_interrupt
中断处理函数的实现非常简单,它首先读出
cs8900
的
ISQ
寄存器的值,然后根据
ISQ
的值分别处理各种情况。当中断发生时,这些中断实际反映在相应的寄存器中,
ISQ
寄存器用低
6
位记录了当前寄存器的编号,高
10
位记录了当前寄存器的实际内容。这些寄存器有:
RxEvent(Register 4)
,
TxEvent(Register 8)
,
BufEvent(RegisterC)
,
RxMISS(Register 10)
和
TxCOL(Register 12)
。比如,传输成功后,
cs8900
将
TxEvent
的第
0bit
置为
1
,如果允许该事件中断,那么
ISQ
寄存器的低
6
位将记录
TxEvent
的编号
8
,并且将
TxEvent
寄存器的高
10
位
copy
到它的高
10
位中。
net_interrupt
注解如下:
static irqreturn_t net_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
int handled = 0;
ioaddr = dev->base_addr;
lp = netdev_priv(dev);
/* we MUST read all the events out of the ISQ, otherwise we'll never
get interrupted again.
As a consequence, we can't have any limit
on the number of times we loop in the interrupt handler.
The
hardware guarantees that eventually we'll run out of events.
Of
course, if you're on a slow machine, and packets are arriving
faster than you can read them off, you're screwed.
Hasta la
vista, baby!
*/
while ((status = readword(dev->base_addr, ISQ_PORT))) {
//ISQ_PORT=08h,
根据
cs8900
的用户手册,
这里再次说明了
cs8900
工作在
I/O
模式
if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status);
handled = 1;
switch(status & ISQ_EVENT_MASK) {
//ISQ_EVENT_MASK=0x3f
,
//
确定
ISQ
的低
6
位,
该
6
位纪录了发生中断的寄存器
case ISQ_RECEIVER_EVENT:
//ISQ_RECEIVER_EVENT=0x04,
//
中断源来自
RxEvent
,
表示接收到了数据包
/* Got a packet(s). */
net_rx(dev);
break;
case ISQ_TRANSMITTER_EVENT:
//ISQ_RECEIVER_EVENT=0x08,
//
中断源来自
TxEvent
,根据
net_open
中设置,有很多发送事件
//
可以产生中断,需要分别处理
lp->stats.tx_packets++;
//
累加发送包的总数
netif_wake_queue(dev);
/* Inform upper layers. */
if ((status & (
TX_OK |
//ISQ
的高
10
位描述了
TxEvent
的实际内容,
//
也即实际传输的信息
这里似乎
status
应该右移
6
位?的确应该
//
这样,这里之所以没这样做,
是因为
TX_OK
等这些值,在设
//
计时已经左移了
6
位
TX_LOST_CRS |
TX_SQE_ERROR |
TX_LATE_COL |
TX_16_COL)) != TX_OK) {
//
做些错误统计工作
if ((status & TX_OK) == 0) lp->stats.tx_errors++;
if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
}
break;
case ISQ_BUFFER_EVENT:
//ISQ_RECEIVER_EVENT=0x0c,
//
中断源来自
BufEvent
if (status & TX_UNDERRUN) {
//
这里说明估计的发送长度过短,可能需要做调整
if (net_debug > 0) printk("%s: transmit underrun\n", dev->name);
lp->send_underrun++;
if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; //
此值
cs89x0_probe1
时初始化为
5
,这里修正。
else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;
/* transmit cycle is done, although
frame wasn't transmitted - this
avoids having to wait for the upper
layers to timeout on us, in the
event of a tx underrun */
netif_wake_queue(dev);
/* Inform upper layers. */
}
......//DMA
部分
break;
case ISQ_RX_MISS_EVENT:
//ISQ_RX_MISS_EVENT=0x10,
//
中断来自于
RxMISS
,
该寄存器的高
10
位记录丢失的数据包
lp->stats.rx_missed_errors += (status >>6);
break;
case ISQ_TX_COL_EVENT:
//ISQ_TX_COL_EVENT=0x12,
中断来自于
//
TxCOL
,
该寄存器的高
10
位记录发了生冲突的数据包
lp->stats.collisions += (status >>6);
break;
}
}
return IRQ_RETVAL(handled);
}
To be continued……
------ anmnmnly
------ 2007.12.5
chinaunix网友2010-09-14 14:53:35
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com