?? 網口程序實例.txt
字號:
CPU將調用elintr中斷例程,并帶有參數unit即該種網卡的第幾塊(因為在計算機中,你有可能
裝了相同的網卡有幾塊),elintr的作用是把數據從網卡的數據存儲器中讀到我們在該網卡初始化時預先分配好
的數據緩沖區中,他調用的函數就只有elread,同樣elread也只調用了elget一個函數.elread函數比較簡單,就是
調用elget,elget則相對比較復雜一點,涉及到核心內存分配mbuf,mbuf是比較恐怖的東西,正如STEVEN所寫的,為
了節約當時"巨大"的4M內存,犧牲了性能搞出了這個mbuf東東,mbuf是必須要弄懂的,雖然在設備驅動程序中調用
他的宏和函數不多,但在后面的IP協議,TCP協議中有不少涉及的地方.
關于數據發送方面和接收差不多,在上層協議放置好數據到mbuf鏈后,調用el_start函數,該函數把mbuf鏈中
的數據放置到本塊網卡的發送隊列緩沖el_pktbuf中,然后再調用el_xmit函數,此函數把發送隊列緩沖el_pktbuf
中的數據有傳遞到網卡的數據存儲器中.
*/
#include "opt_inet.h"
#include "opt_ipx.h"
#include <sys/param.h>;
#include <sys/systm.h>;
#include <sys/sockio.h>;
#include <sys/mbuf.h>;
#include <sys/socket.h>;
#include <sys/syslog.h>;
#include <net/ethernet.h>;
#include <net/if.h>;
#include <netinet/in.h>;
#include <netinet/if_ether.h>;
#include <net/bpf.h>;
#include <machine/clock.h>;
#include <i386/isa/isa_device.h>;
#include <i386/isa/if_elreg.h>;/*此頭文件是3COM卡的寄存器常量*/
/* 為了調試方便 */
#ifdef EL_DEBUG
#define dprintf(x) printf x /*如果定義了DEBUG調試,則打印到屏幕*/
#else
#define dprintf(x)
#endif
/* softc結構,每種網卡的該結構是不同的,主要是該第一個成員必須是一以太網的共用結構arpcom*/
static struct el_softc {
struct arpcom arpcom; /* 以太網公共部分 */
u_short el_base; /* 基本輸入輸出地址 */
char el_pktbuf[EL_BUFSIZ]; /* 幀緩沖大小2048 */
} el_softc[NEL]; /*NEL在el.h中定義,即編譯時產生的頭文件,意思為支持的網卡數*/
/*
看看arpcom結構吧
* 該結構是以太網設備驅動程序和ARP程序所共享.
struct arpcom {
/*
* ifnet 結構必須在此結構的第一個位置.
/
struct ifnet ac_if;
u_char ac_enaddr[6]; /* 以太網硬件地址/
int ac_multicnt; /* 多播地址列表數 /
void *ac_netgraph; /* netgraph 節點信息,即我們所說的PPPoE,也就是ADSL寬帶所用到的 /
};
*/
/* 一些函數申明 */
static int el_attach(struct isa_device *);/*第二步:填充相關的數據結構*/
static void el_init(void *); /*不用說,是初始化,在probe,attach之后被調用*/
static int el_ioctl(struct ifnet *,u_long,caddr_t);/*控制網卡的函樹指針*/
static int el_probe(struct isa_device *);/*第一步:探測程序.查看是否卡存在和是否在正確的位置.*/
static void el_start(struct ifnet *);/*把數據包從硬件接口輸出去*/
static void el_reset(void *);/* 該例程重設接口. 在el_watchdog()中調用*/
static void el_watchdog(struct ifnet *);/*一般該函數用于包在一定時間內沒發送出去,就調用他,在
本驅動程序中并不支持該函數,在我的rtl8139說明中有*/
static void el_stop(void *);/*停止接口,在el_ioctl()和el_reset()中調用*/
static int el_xmit(struct el_softc *,int);/*把數據包放到芯片內,發送到以太網上*/
static ointhand2_t elintr;/*中斷例程*/
static __inline void elread(struct el_softc *,caddr_t,int);/* 傳遞包到更高一級協議處理,即ether_input()例程.由elintr()調用 */
static struct mbuf *elget(caddr_t,int,struct ifnet *); /* 從網卡上下載數據包,len是數據的長度,本地以太網頭部被分開*/
static __inline void el_hardreset(void *);/* 這是一個子程序,目的是重設硬件.*/
/* isa_driver結構 為 autoconf準備 */
/* isa_driver結構說明:
該結構來之于文件isa_device.h頭文件
結構成員:
/*
* 通用的設備驅動程序結構.
*
* 沒一設備驅動程序定義一組例程入口,由設置程序在啟動時使用.
struct isa_driver {
int (*probe) __P((struct isa_device *idp));
/* 測試設備是否存在
int (*attach) __P((struct isa_device *idp));
/* 為設備設置驅動程序
char *name; /* 設備名稱
int sensitive_hw; /* 探測發生有沖突時為真,ISA設備的老毛病
};
*/
struct isa_driver eldriver = {
el_probe, el_attach, "el"
};
/* 探測程序.查看是否卡存在和是否在正確的位置. */
static int
el_probe(struct isa_device *idev)
{
/*
isa_device 是設備的通用結構,該結構說明在isa_device.h頭文件中,說明如下:
struct isa_device {
int id_id; /* 設備的 id
struct isa_driver *id_driver; 指向設備的驅動程序結構
int id_iobase; /* 基本IO地址
int id_iosize; /* 基本IO地址的長度
u_int id_irq; /* 中斷
int id_drq; /* DMA
caddr_t id_maddr; /* 在總線中的物理IO內存地址(即便要)
int id_msize; /* IO內存的大小
union {
inthand2_t *id_i;
ointhand2_t *id_oi;中斷例程指針
} id_iu; /* 中斷接口例程
#define id_intr id_iu.id_i
#define id_ointr id_iu.id_oi
int id_unit; /* 在該類設備中是第幾個
int id_flags; /* flags
int id_enabled; /* 設備激活了嗎
struct isa_device *id_next; /* 在 userconfig()中用于isa_devlist
struct device *id_device;
};
*/
struct el_softc *sc;
u_short base; /* 僅僅為了方便 */
u_char station_addr[ETHER_ADDR_LEN];/*以太網的硬件地址*/
int i;
/* 搜集一些信息 */
sc = &el_softc[idev->;id_unit];/*sc是softc結構,如果你有NEL塊el卡的話就有NEL個softc
結構,當然也有可能同時還有其他的xx_softc結構*/
sc->;el_base = idev->;id_iobase;/*該塊卡的基本I/O地址*/
base = sc->;el_base;/*有一點多余,只是為了方便下面的引用*/
/* 第一次檢查地址,看看基本地址是否在0X280到0X3F0之內 */
if((base < 0x280) || (base >; 0x3f0)) {
printf("el%d: ioaddr must be between 0x280 and 0x3f0\n",
idev->;id_unit);
return(0);
}
/* 現在嘗試從PROM中獲取地址,看看是否包含了3COM供應商的標識代碼.
*/
dprintf(("Probing 3c501 at 0x%x...\n",base));/*在調試時會打印出*/
/* 重置板卡 */
dprintf(("Resetting board...\n"));
outb(base+EL_AC,EL_AC_RESET);/*我們一般定義基地址為0X300,EL_AC=0E,是輔助命令寄存器*/
DELAY(5);/*延遲5毫秒*/
outb(base+EL_AC,0);
dprintf(("Reading station address...\n"));
/* 讀硬件地址,共六次 */
for(i=0;i<ETHER_ADDR_LEN;i++) {
outb(base+EL_GPBL,i);
station_addr = inb(base+EL_EAW);/*EL_EAW是該卡的地址口,station_addr是函數內部變量,
下面判斷了生產廠家后就沒用的*/
}
dprintf(("Address is %6D\n",station_addr, ":"));
/* 如果廠商標識代碼正確,那么返回1.
*/
if((station_addr[0] != 0x02) || (station_addr[1] != 0x60)
|| (station_addr[2] != 0x8c)) {
dprintf(("Bad vendor code.\n"));/*3COM廠商此種卡的代碼為02608C*/
return(0);
} else {
dprintf(("Vendor code ok.\n"));
/* 把地址拷貝到arpcom結構中 */
bcopy(station_addr,sc->;arpcom.ac_enaddr,ETHER_ADDR_LEN);
return(1);
}
}
/* 這是一個子程序,目的是重設硬件. 在el_init()中調用,在elintr()中調用,產生中斷,有溢出發生時調用*/
static __inline void
el_hardreset(xsc)
void *xsc;
{
register struct el_softc *sc = xsc;/*記住在C中,寄存器變量只能有三個,可加快速度*/
register int base;
register int j;
base = sc->;el_base;
/* 第一步,重設板卡,和el_probe中的一樣(前面) */
outb(base+EL_AC,EL_AC_RESET);
DELAY(5);
outb(base+EL_AC,0);
/* 又把地址填回去,為什么?沒有為什么,就是廠商規定的,一些端口填什么數據時會怎么樣,只有廠商知道,我相信,
在同一廠商之間的網卡,交換機,路由器進行秘密通訊是非常可能的,他可以不返回到CPU層*/
for(j=0;j<ETHER_ADDR_LEN;j++)
outb(base+j,sc->;arpcom.ac_enaddr[j]);
}
/* 連接該接口到核心數據結構.被調用時,我們已經知道該卡已經存在在給定的I/O
* 地址,我們還假定中斷號是正確的.
*/
static int
el_attach(struct isa_device *idev)
{
struct el_softc *sc;
struct ifnet *ifp;/*該結構是一個巨大的結構,在STEVEN的書中有描述,我也寫了一篇*/
u_short base;/*沒用上,可以去掉*/
dprintf(("Attaching el%d...\n",idev->;id_unit));
/* 放置一些指針. */
idev->;id_ointr = elintr;/*放置中斷例程指針,中斷例程在下面*/
sc = &el_softc[idev->;id_unit];/*定位本設備的softc結構指針*/
ifp = &sc->;arpcom.ac_if;/*定位ifnet結構*/
base = sc->;el_base;/*從程序來看,這一句可以去掉,根本沒用,因為在該函數中沒用到base*/
/* 重設板卡 */
dprintf(("Resetting board...\n"));
el_hardreset(sc);/*該程序在上面*/
/* 初始化ifnet結構,該結構的成員經常用來被ether網子程序,arp,bridge等調用 */
ifp->;if_softc = sc;/*該網卡的IFP(通用接口結構)的專用結構指針(softc結構)*/
ifp->;if_unit = idev->;id_unit;/*第幾塊網卡*/
ifp->;if_name = "el";/*網絡卡的名稱*/
ifp->;if_mtu = ETHERMTU;/*1500*/
ifp->;if_output = ether_output;/*以太網的輸出子程序指針(不要搞錯了,是向IP層
輸出,按我們的理解是數據輸入了,再轉送到上一層協議)*/
ifp->;if_start = el_start;/*把數據包從硬件接口輸出去*/
ifp->;if_ioctl = el_ioctl;/*控制網卡的函樹指針*/
ifp->;if_watchdog = el_watchdog;/*一般該函數用于包在一定時間內沒發送出去,就調用他,在
本驅動程序中并不支持該函數,在我的rtl8139說明中有*/
ifp->;if_init = el_init; /*不用說,是初始化,在probe,attach之后被調用*/
ifp->;if_flags = (IFF_BROADCAST | IFF_SIMPLEX);/*支持廣播和單播*/
/* 調用通用以太網初始化例程 */
dprintf(("Attaching interface...\n"));
ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
/*
在if_ethersubr.c中的ether_ifattach例程
void ether_ifattach(ifp, bpf) 調用時,ETHER_BPF_SUPPORTED是BSD的
包過濾器,如果在編譯時設置文件沒有
打開包過濾器,那么代表0,否則是1
register struct ifnet *ifp;
int bpf;
{
register struct ifaddr *ifa;
register struct sockaddr_dl *sdl;
if_attach(ifp); 此例程在if.c 中
ifp->;if_type = IFT_ETHER;代表以太網
ifp->;if_addrlen = 6;硬件地址長度是6
ifp->;if_hdrlen = 14;包的頭長度是6+6+2=14,其中2是協議類型
ifp->;if_mtu = ETHERMTU; 為1500,多此一舉,在前面你可看到,已
經填充了.
ifp->;if_resolvemulti = ether_resolvemulti; 以太網解析多播例程指針
if (ifp->;if_baudrate == 0) 波特率
ifp->;if_baudrate = 10000000;
ifa = ifnet_addrs[ifp->;if_index - 1];在ifnet_addrs[]數組中找到本地址指針
KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__));
sdl = (struct sockaddr_dl *)ifa->;ifa_addr; ifa->;ifa_addr在此時指向的是sockaddr_dl結構.
sdl->;sdl_type = IFT_ETHER;
sdl->;sdl_alen = ifp->;if_addrlen;
bcopy((IFP2AC(ifp))->;ac_enaddr, LLADDR(sdl), ifp->;if_addrlen);把硬件地址拷貝到sdl結構中
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -