Sunteți pe pagina 1din 35

Network Driver in Linux 2.4 Network Driver in Linux 2.

CCU COMM
Overview Overview
Bus
Device Operating System
Auto Configuration
Direct Memory Access
Power management
/O access
Byte ordering
Address translation
nterrupt handling
Bus cycles
Driver framework
Timer management
Memory management
Race condition handling (SMP)
CPU/Memory cache consistency
Device operations
Outline Outline
Driver framework
Linux network drivers
Device operation
RTL8139 programming
Driver example
A piece oI code Ior 93C46 series
EEPROM, 93C46 64 x 16 bits, 93C66 256 x 16 bits
pciskeleton.c (Ior RTL8139)
Linux network driver framework Linux network driver framework
Connecting to the Kernel (1/2) Connecting to the Kernel (1/2)
Module_loading
struct netdevice snulldev init : snullinit, }; //||}.
iI((result registernetdev(snulldev)))) printk('error);
|", ,]name 'ethd, 'ethX
}.|"devinit()
snullinit( )
!robe function
Called when register_netdev()
Usually avoid registering /O and RQ, delay until devopen() time
%o fiII in the ~dev strcuture
ethersetup(dev)
,](,)]'priv; };)], }),,
Moduleunloading
kIree(priv);
unregisternetdev (snulldev);
Linux network driver framework Linux network driver framework
Connecting to the Kernel (2/2) Connecting to the Kernel (2/2)
struct net_device {
char name|IFNAMSIZ|; // ethd
unsigned long baseaddr, unsigned char irq;
unsigned char broadcast||, devaddr|MAXADDRLEN|;
unsigned short Ilags; // IFFUP, IFFPROMISC, IFFALLMULTI
Function pointers:
(*init) ||
(*open) }
(*stop) {)}
(*do_ioctl)()
(*tx_timeout) _
(*get_stats) )),,,
(*hard_start_xmit) ,|
(*set_multicast_list) j]flag]_
unsigned long transstart, lastrx; // Ior watchdog and power management
struct devmclist *mclist; // multicast address list
Linux network driver framework Linux network driver framework
Opening and closing Opening and closing
}||_,ifconfig},|P
ifconfig,]P)}
ioctl(SIOCSIFADDR),])}
Ioctl(SIOCSIFILAGS)$]|.}]open]stop
open()
,|),j(#IRQ, IObase, buIIer)
}]
,MAC, |])devdevaddr (initprobe)
[devdevaddr,]}MAC
stop()
{1}
),j
Linux network driver framework Linux network driver framework
Packet transmission: Packet transmission: T!`:(" T!`:("
[,,j|](outgoing queue)
|",
hardstarttransmit(struct skbuII *skb, struct netdevice *dev)
[|){[|(RTL8139)
Spinlock_t xmit_lock; ({(|"
||d,}_{,]||)|
y,[]=,||
netif_stop_queue()netif_wake_queue(),netif_start_queue()
: (Carrier loss detection/Watchdog netif_carrier_on/off()
Hot-plugging/power management netif_device_attach/detach()
]|,{struct sk_buff
socket buIIer
sk_buff,](skb
skbdata[,|
skblen||,octet
Linux network driver framework Linux network driver framework
Transmission queuing model Transmission queuing model
netif_start_queue()
netif_wake_queue()
netif_stop_queue()
netif_carrer_on()
netif_carrer_off()
netif_device_attach()
netif_device_detach()
f ( present &&
carrier_ok && queue_stopped &&
( jiffies trans_start ) > watchdog_timeo
) Then
aII tx_timeout( )
`
Present?
Queue stopped ?
Carrier ok ?
Packets
from OS
Packets
from OS
!ackets go to
the LAN
!ackets go to
the LAN
Linux network driver framework Linux network driver framework
Packet reception Packet reception
|,]j
`interrupt handler
skbuII,)])
Interrupt-based polling,.(
Example: snull_rx()
skb devallocskb(len2); // )GFPATOMIC,ISR)
skbreserve(skb, 2); // 16 byte align the IP Iield
memcpy(skbput(skb, len), receivepacket, len); //skbput()skbuII
,,
skbdev = dev;
skbprotocol = eth_type_trans(skb, dev);
skbip_summed = CHECKSUM_UNNECESSARY; /* ] */
CHECKSUM_HW()/NONE(,),)/UNNECESSARY()
netiIrx(skb); // )])
Linux network driver framework Linux network driver framework
The interrupt handler The interrupt handler
nterrupt happen when
A new packet has arrived
Transmission oI an outgoing packet is completed
Something happened: PCI bus error, cable length change, time out
nterrupt status register (SR)
Packet reception
Pass to the kernel
Packet transmission is completed
Reset the transmit buffer of the interface
Statistics
Linux network driver framework Linux network driver framework
The socket buffers (struct sk_buff) The socket buffers (struct sk_buff)
payload headroom tailroom
head data tail
end
len
struct sk_buff *dev_alloc_skb(len) =(
void dev_kfree_skb(struct sk_buff *)!.
An empty sk_buff
void skb_reserve(skb, len)+"
unsigned char *skb_put(skb, len)/(
unsigned char*skb_push(skb, len)((
unsigned char *skb_pull(skb, len)1(
Linux network driver framework Linux network driver framework
Setup receive mode and multicast accept list Setup receive mode and multicast accept list
Unicast, broadcast (all 1), multicast (bit0==1)
Receive all, receive all multicast, receive a list oI multicast address
Transmit
the same as unicast
Receive
Hardware Iiltering Ior a list oI multicast addresses
void (*set_multicast_list)(dev)
jdev-~Ilags(, ]|"
struct dev_mc_list *mc_list; // int mc_count
(dev(j
FF_PROMSC
,|,].()
FF_ALLMULT
(j|
Outline Outline
Driver framework
Linux network drivers
Device operation
RTL8139 programming
Driver example
A piece oI code Ior 93C46 series
EEPROM, 93C46 64 x 16 bits, 93C66 256 x 16 bits
pciskeleton.c (Ior RTL8139)
RTL8139 block diagram RTL8139 block diagram
Device operation Device operation
RTL8139(A/B) programming RTL8139(A/B) programming
Packet transmission
4 transmit descriptors in round-robin
Transmit FIFO and Early Transmit
Packet reception
Ring buIIer in a physical continuous memory
Receive FIFO and FIFO Threshold
Hardware initialization
Command register (0x37)
Reset (4) / Transmit Enable (2) / Receive Enable (3) / Buffer empty (0)
Transmit (Tx) ConIiguration Register (0x40~0x43)
nterframe Gap time () (25~24)
Receive (Rx) ConIiguration Register (0x44~0x47)
Rx FFO threshold (15~13)
Accept Broadcast (3) / Multicast (2) / All (0, Promiscuous mode) packet
Rx buffer length (12~11)
Interrupt Mask Register (0x3C~0x3D)
Software initialization (TxDescriptor and Ring buffer)
RTL8139 RTL8139 Packet transmission Packet transmission
Transmit descriptor Transmit descriptor
Transmit start address (TSAD0-3)
The physical address oI packet
The packet must be in a continuous physical memory
Transmit status(TSD0-3)
TOK(15R)
Set to 1 indicates packet transmission was completed successfully and
no transmit underrun (14R) has occurred
OWN(13R/W)
Set to 1 when the Tx DMA operation of this descriptor was completed
The driver must set this bit to 0 when the "Size is written
Size(12~0R/W)
The total size in bytes of the data in this descriptor
Early Tx Threshold(21~16R/W)
When the byte count in the Tx FFO reaches this, the transmit happens.
From 000001 to 111111 in unit of 32 bytes (000000 = 8 bytes)
RTL8139 RTL8139 Packet transmission Packet transmission
Process of transmitting a packet Process of transmitting a packet
1. copy the packet to a physically continuous buffer in memory
2. Write the functioning descriptor
Address, Size, Early transmit threshold, Clear OWN bit (this starts PCI operation)
3. As TxFFO meet threshold, the chip start to move from FFO to line
4. When the whole packet is moved to FFO, the OWN bit is set to 1
5. When the whole packet is moved to line, the TOK(TSD) is set to 1
6. f TOK(MR) is set, then TOK(SR) is set and a interrupt is triggered
7. nterrupt service routine called, driver should clear TOK(SR)
Packet reception Packet reception
Ring buffer Ring buffer
1. Data goes to RxFFO
coming Irom line
2. Move to the buffer
when early receive
threshold is meet.
Ring buffer
physical continuous
CBR (0x3A~3B R)
the Current address
oI data moved to
BuIIer
CAPR (0x38~39 R/W)
the pointer keeps
Current Address oI
Pkt having been read
Status of receiving a
packet
stored in Iront oI the
packet (packet header)
Packet reception Packet reception
The Packet Header (32 bits, i.e. 4 bytes) The Packet Header (32 bits, i.e. 4 bytes)
Bit 31~16: rx_size, including 4 bytes CRC in the tail
pktsize rxsize - 4
Packet reception Packet reception
PProcess of packet receive in detail
Data received from line is stored in the receive FFO
When Early Receive Threshold is meet, data is
moved from FFO to Receive Buffer
After the whole packet is moved from FFO to
Receive Buffer, the receive packet header (receive
status and packet length) is written in front of the
packet.
CBA is updated to the end oI the packet. 4 byte alignment
CMD (BufferEmpty) is clear and SR(TOK) is set.
SR routine called and then driver clear SR(TOK)
and update CAPR
currx (currx rxsize 4 3) & ~3;
NETDRVW16F (RxBuIPtr, currx - 16);
Packet
header
Avoid
overflow
Outline Outline
Driver framework
Linux network drivers
Device operation
RTL8139 programming
Driver example
A piece oI code Ior 93C46 series
EEPROM, 93C46 64 x 16 bits, 93C66 256 x 16 bits
pciskeleton.c (Ior RTL8139)
EEPROM 93C46 operations EEPROM 93C46 operations
ommand Register (0x50 R/W)
A piece code for EEPROM 93C46 A piece code for EEPROM 93C46
11. /* Shift the read command bits out. */
12. for (i = 4 + addr_len; i >= 0; i--) {
13. int dataval = (read_cmd & (1 << i))
? EE_DATA_WRTE : 0;
14. writeb (EE_ENB | dataval, ee_addr);
15. eeprom_delay ();
16. writeb (EE_ENB | dataval | EE_SHFT_CLK,
ee_addr);
17. eeprom_delay ();
18. }
19. writeb (EE_ENB, ee_addr);
20. eeprom_delay ();
21. for (i = 16; i > 0; i--) {
22. writeb (EE_ENB | EE_SHFT_CLK, ee_addr);
23. eeprom_delay ();
24. retval = (retval << 1) | ((readb (ee_addr) &
EE_DATA_READ) ? 1 : 0);
25. writeb (EE_ENB, ee_addr);
26. eeprom_delay ();
27. }
28. /* Terminate the EEPROM access. */
29. writeb (~EE_CS, ee_addr);
30. eeprom_delay ();
31. return retval;
32. }
#define EE_SHFT_CLK 0x04 /* EEPROM shift clock. */
#define EE_CS 0x08 /* EEPROM chip select. */
#define EE_DATA_WRTE 0x02 /* EEPROM chip data in. */
#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
#define EE_ENB (0x80 | EE_CS)
#define eeprom_delay() readl(ee_addr)
/* EEPROM commands include the alway-set leading bit */
#define EE_WRTE_CMD (5)
#define EE_READ_CMD (6)
#define EE_ERASE_CMD (7)
1. static int __devinit read_eeprom (
2. void *ioaddr, int location, int addr_len)
3. {
4. int i;
5. unsigned retval = 0;
6. void *ee_addr = ioaddr + Cfg9346;
7. int read_cmd = location |
(EE_READ_CMD << addr_len);
8. writeb (EE_ENB & ~EE_CS, ee_addr);
9. writeb (EE_ENB, ee_addr);
10. eeprom_delay ();
addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6;
for (i = 0; i < 3; i++) ((u16 *) (dev->dev_addr))[i]
= le16_to_cpu (read_eeprom (ioaddr, i + 7, addr_len));
Outline Outline
Driver framework
Linux network drivers
Device operation
RTL8139 programming
Driver example
A piece oI code Ior 93C46 series
EEPROM, 93C46 64 x 16 bits, 93C66 256 x 16 bits
pciskeleton.c (Ior RTL8139)
#include<> of the RTL8139 #include<> of the RTL8139
pciskeleton.c
module.h kernel.h
pci.h
init.h
netdevice.h etherdevice.h
delay.h
mii.h
asm/io.h
skbuff.h
Definitions for Ethernet
eth_type_trans()
alloc_ethdev()
Definitions for
struct net_device
register_netdev()
netif_*()
skbuff.h
Definitions of !/O
port read/write and
ioremap()
PC! defines and prototypes
pci_alloc_consistent()
pci_resource_*()
pci_request_regions()
pci_set_master()
pci_read_config_word()(err)
Definitions for N!!_ADvERT!SE, N!!_LPA
ADvERT!SE_FULL, LPA_100FULL.
barrier()
printk()
byteorder.h
udelay()
definition
multicast
ether_crc()
module_init()
module_exit()
spinlock.h
config.h
NOD_*
NODULE_*()
crc32.h
"+:
sched.h (irq,
jiffies,capable)
slab.h
time.h
spinlock.h
asm/atomic.h
PC BUS
Network Device
Operating System
Driver structure of the RTL8139 Driver structure of the RTL8139
pci_module_init() / pci_unregister_driver()
static struct pcidriver netdrvpcidriver
name: "netdrv",
idtable: netdrvpcitbl,
probe: 3etdrv_i3it_o3e,
remove: netdrvremoveone,
#iIdeI CONFIGPM
suspend: netdrvsuspend,
resume: netdrvresume,
static struct pci_device_id netdrv_pci_tbl[] __devinitdata = {
0x10ec, 0x8139, PCIANYID, PCIANYID, 0, 0, RTL8139 },
MODULEDEVICETABLE (pci, netdrvpcitbl);
pci_device_id
driver_data
(Private, Sq# here)
PC device probe function PC device probe function
netdrv_init_one() netdrv_init_one()
netdrv_init_one()
call netdrv_init_board()
to get net_device dev,
void *ioaddr
nitial net_device dev
Set up dev_addr[], irq, base_addr
Set up method:
dev->open,
dev->hard_start_transmit,
dev->stop,
dev->get_stats,
dev->set_multicast_list,
dev->do_ioctl,
dev->tx_timeout
struct pci_dev *pdev,
struct pci_device_id *ent
Linux invoke when probing
netdrv_init_board()
dev = alloc_etherdev(sizeof())
pci_enable_device (pdev);
pci_request_regions (pdev, "pci-sk");
pci_set_master (pdev);
mmio_start = pci_resource_start (pdev, 1);
ioaddr = ioremap (mmio_start, len);
Soft reset the chip.
NETDRV_W8 (ChipCmd, (NETDRV_R8 (ChipCmd)
& ChipCmdClear) | CmdReset);
identify chip attached to board
register_netdev (dev); // ethX
/O port
and memory
NETDRV_W?() NETDRV_W?()
/* write MMO register, with flush */
/* Flush avoids rtl8139 bug w/ posted MMO writes */
#define NETDRV_W8_F(reg, val8)
do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0)
#define NETDRV_W16_F(reg, val16)
do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0)
#define NETDRV_W32_F(reg, val32)
do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0)
#define NETDRV_W8 NETDRV_W8_F
#define NETDRV_W16 NETDRV_W16_F
#define NETDRV_W32 NETDRV_W32_F
#define NETDRV_R8(reg) readb (ioaddr + (reg))
#define NETDRV_R16(reg) readw (ioaddr + (reg))
#define NETDRV_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
Device methods Device methods
dev->open
int netdrv_open (struct net_device *dev);
dev->hard_start_transmit
int netdrv_start_xmit (struct sk_buff *skb, struct net_device *dev);
dev->stop
int netdrv_close (.);
dev->get_stats
struct net_device_stats * netdrv_get_stats (struct net_device *);
dev->set_multicast_list
void netdrv_set_rx_mode (.);
dev->do_ioctl
int netdrv_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
dev->tx_timeout
void netdrv_tx_timeout (struct net_device *dev);
Up up.. Up up..
netdrv_open netdrv_open() ()
request_irq (dev->irq, netdrv_interrupt,
SA_SHIRQ, dev->name, dev)
tx_bufs = pci_alloc_consistent(pdev,
TXBUFLEN, &tx_bufs_dma);
rx_ring = pci_alloc_consistent(pdev,
RXBUFLEN, &rx_ring_dma);
netdrv_init_ring (dev);
netdrv_hw_start (dev);
Set the timer to check for link beat
netdrv_open()
Soft reset the chip
/* Restore our idea of the MAC address. */
NETDRV_W32_F (MAC0 + 0, cpu_to_le32
(*(u32 *) (dev->dev_addr + 0)));
NETDRV_W32_F (MAC0 + 4, cpu_to_le32
(*(u32 *) (dev->dev_addr + 4)));
NETDRV_W8_F (ChipCmd,
(NETDRV_R8 (ChipCmd) & ChipCmdClear) |
CmdRxEnb | CmdTxEnb);
Setting RxConfig and TxConfig
NETDRV_W32_F (RxBuf, tp->rx_ring_dma);
init Tx buffer DMA addresses
netdrv_set_rx_mode (dev);
NETDRV_W16_F (ntrMask, netdrv_intr_mask);
netif_start_queue (dev);
netdrv_hw_start (dev)
Setup receive mode and multicast Setup receive mode and multicast hashtable hashtable
(*set_multicast_list)() (*set_multicast_list)()
netdrv_set_rx_mode() netdrv_set_rx_mode()
if (flags & FF_PROMSC)
AcceptBroadcast , AcceptMulticast , AcceptMyPhys , ccept!
mcIilter|1| mcIilter|0| 0xIIIIIIII
else if ((mc_count > multicast_filter_limit)
|| (flags & FF_ALLMULT))
AcceptBroadcast , AcceptMulticast , AcceptMyPhys
mcIilter|1| mcIilter|0| 0xIIIIIIII
else
AcceptBroadcast , AcceptMulticast , AcceptMyPhys
mclist[0].dmi_addr
mclist[1].dmi_addr
mclist[2].dmi_addr
ether_crc()
31 30 29282726 25...0
63 62 1 0
Transmit a packet Transmit a packet
netdrv_start_xmit() netdrv_start_xmit()
if (skb->len < ETH_ZLEN) skb = skb_padto(skb, ETH_ZLEN);
entry = atomic_read (&cur_tx) % NUM_TX_DESC;
tx_info[entry].skb = skb;
memcpy (tx_buf[entry], skb->data, skb->len);
NETDRV_W32 (TxStatus[entry], tx_flag | skb->len);
dev->trans_start = jiffies;
atomic_inc (&cur_tx);
if ((atomic_read (&cur_tx) - atomic_read (&dirty_tx)) >= NUM_TX_DESC)
netif_stop_queue (dev);
netdrv_start_xmit()
dirty_tx
0 1 2 3 0 1 2
cur_tx
nterrupt handling nterrupt handling
netdrv_interrupt() netdrv_interrupt()
spin_lock (&tp->lock);
status = NETDRV_R16 (ntrStatus);
NETDRV_W16_F (ntrStatus, status); // Acknowledge
Spec says, 'The ISR bits are always set to 1 iI the condition is present.
Spec says, 'Reading the ISR clears all. Writing to the ISR has no eIIect.
if (status & (PCErr | PCSTimeout | RxUnderrun |
RxOverflow |RxFFOOver | TxErr | RxErr))
netdrvweirdinterrupt (dev, tp, ioaddr, status, linkchanged);
if (RxOK | RxUnderrun | RxOverflow | RxFFOOver)
netdrvrxinterrupt (dev, tp, ioaddr);
if (status & (TxOK | TxErr))
netdrvtxinterrupt (dev, tp, ioaddr);
spin_unlock (&tp->lock);
0 1 1 1 0 0 1 1
0 0 1 1 0 0 1 0
nterrupt
SR
MR
nterrupt handling nterrupt handling
netdrv_tx_interrupt(dev, tp, ioaddr) netdrv_tx_interrupt(dev, tp, ioaddr)
1. dirty_tx = atomic_read (&tp->dirty_tx);
2. cur_tx = atomic_read (&tp->cur_tx);
3. tx_left = cur_tx - dirty_tx;
4. while (tx_left > 0) {
5. int entry = dirty_tx % NUM_TX_DESC;
6. int txstatus = NETDRV_R32 (TxStatus[entry]);
7. if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) break; /* t still hasn't been Txed */
8. if (txstatus & (TxOutOfWindow | TxAborted)) { /* There was an major error, log it. */
9. tp->stats.tx_errors++;
10. } else {
11. if (txstatus & TxUnderrun) /* Add 64 to the Tx FFO threshold. */
12. tp->tx_flag += 0x00020000;
13. tp->stats.tx_bytes += txstatus & 0x7ff;
14. tp->stats.tx_packets++;
15. }
16. dev_kfree_skb_irq (tp->tx_info[entry].skb);
17. tp->tx_info[entry].skb = NULL;
18. dirty_tx++;
19. if (netif_queue_stopped (dev))
20. netif_wake_queue (dev);
21. cur_tx = atomic_read (&tp->cur_tx);
22. tx_left = cur_tx - dirty_tx;
23. }
24. atomic_set (&tp->dirty_tx, dirty_tx);
nterrupt handling nterrupt handling
Packet reception Packet reception
netdrv_rx_interrupt (dev,tp, ioaddr) netdrv_rx_interrupt (dev,tp, ioaddr)
1. rx_ring = tp->rx_ring;
2. cur_rx = tp->cur_rx;
3. while ((NETDRV_R8 (ChipCmd) & RxBufEmpty) == 0) {
4. ring_offset = cur_rx % RX_BUF_LEN;
5. rx_status = le32_to_cpu (*(u32 *) (rx_ring + ring_offset));
6. rx_size = rx_status >> 16;
7. pkt_size = rx_size - 4;
8. skb = dev_aIIoc_skb (pkt_size + 2);
9. skb->dev = dev;
10. skb_reserve (skb, 2); /* 1 byte aIign the P fields. */
11. eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0);
12. skb_put (skb, pkt_size);
13. skb->protocol = eth_type_trans (skb, dev);
14. netif_rx (skb);
15. dev->last_rx = jiffies;
16. tp->stats.rx_bytes += pkt_size;
17. tp->stats.rx_packets++;
18. cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
19. NETDRV_W16_F (RxBufPtr, cur_rx - 16);
20. }
21. tp->cur_rx = cur_rx; Status packet CRC

S-ar putea să vă placă și