[目录] PCI
PCI是一种广泛采用的总线标准,它提供了优于其他总线标准(比如EISA)的特性。在大多数奔腾主板上,PCI是高速、高带宽(32-bit和64-bit)、处理器无关的总线。对PCI的支持第一次加入Linux中时,其内核接口是PCI BIOS32函数的堆砌。这样做有几个问题:
* PCI BIOS仅存在于PC上;
* PCI BIOS只代表特定的结构,非PC类机器的某些PCI设置不能用PCI BIOS来描述;
* 个别机子的PCI BIOS函数不象预期的那样工作。
Linux 2.2提供了一个通用的PCI接口。Linux x86内核实际上努力直接驱动硬件,只有当它发现某些东西不能理解时,它才会调用PCI BIOS32函数。
驱动程序可以继续使用老的PCI接口,但是为了兼容将来的内核,可能需要更新。
如果驱动程序将要跨平台工作,那就更加需要更新了。多数新、老函数有简单的对应关系。PCI BIOS基于总线号/设备号/功能号的思想,而新的代码使用pci_bus和pci_dev结构。第一个新PCI函数是:
pci_present()
这个函数检查机器是否存在一条或更多的PCI总线。老内核有一个pcibios_present()函数,它们的用法完全相同。
确认PCI存在之后,你可以扫描PCI总线来查找设备。PCI设备通过几个配置寄存器来标识,主要是供应商ID和设备ID。每个供应商被分配了一个唯一的标识(ID),并且假设供应商给他们的设备(板子、芯片等)分配唯一的设备ID。PCI的一个好处是它提供了版本和编程接口信息,因此可以发现板子的变化。
在Linux 2.2中,扫描PCI总线一般用pci_find_device()函数。范例如下:
struct pci_dev *pdev = NULL;
while ((pdev = pci_find_device(PCI_MY_VENDOR,
PCI_MY_DEVICE, pdev)) != NULL)
{
/* Found a device */
setup_device(pdev);
}
pci_find_device()有3个参数:第一个是供应商ID,第二个是设备ID,第三个是函数的返回值,NULL表示你想从头开始查找。在这个例子中,对找到的设备调用setup_device()来进行设置。
另一个值得高兴的事情,是PCI为你处理了所有资源配置工作。一般来说PCI BIOS具体做这些工作,但是在其他平台上,这项工作由固件或者体系结构相关的Linux代码来做。到你的驱动程序查找PCI卡的时候,它已经被分配了系统资源。
Linux在pci_dev结构中提供了PCI相关的核心信息。同时还允许读写每个卡的PCI配置空间。当你可以直接查找资源数据时应该小心,对许多系统来说,卡上配置的数据与内核提供的数据并不相符。因为许多非PC机器有多条PCI总线,PCI总线以设备卡不知道的方式映射到系统中。
Linux直接提供了IRQ和PCI BARs(基址寄存器)。为了避免代码在非PC平台上出现意外,你应该总是使用内核提供的数据。下面代码列出了setup_device()例程:
Listing One: The setup_device () Function
void setup_device(struct pci_dev *dev)
{
int io_addr = dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
int irq = dev->irq;
u8 rev;
pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
if (revirq, dev_interrupt,
SA_SHIRQ, "wonderwidget",
dev))
return -EAGAIN;
结束时,用下面的语句来正确释放中断:
free_irq(dev->irq, dev)
中断处理例程被调用时收到dev参数,这使事情很简单了。你不必搜寻使用该中断的设备,通常可以这样做:
Listing Two: Using the dev_id
static void dev_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct wonderwidget *dev = dev_id;
u32 status;
/* It is important to exit interrupt handlers
* that are not for us as fast as possible */
if((status=inl(dev->port))==0) /* Not our interrupt */
return;
if(status&1)
handle_rx_intr(dev);
....
}
你必须总是小心处理中断。永远不要在安装中断处理例程之前产生中断。因为PCI中断是电平触发的,如果你产生了中断而又不能处理它,可能会导致死机。这意味着写初始化代码时必须特别小心,你必须在打开设备的中断之前注册中断处理例程。同样,关闭时必须在注销中断处理例程之前屏蔽设备的中断。与ISA总线相比,Linux对PCI总线的支持用到较多的函数,并且要小心处理中断。
作为回报,不需要你的介入,系统把一切都配置好了。
[目录] loopback
各位大侠,最近我看Linux源码中的网络驱动部分。
先从loopback.c入手的。
loopback.c中的loopback_xmit函数中有这么一段:
static int loopback_xmit(struct sk_buff * skb,struct net_device * dev)
{
struct net_device_stats * stats = (struct net_device_stats *)dev_priv;
if (atomic_read(&skb->users)!=1){
/*判断有几个人用skb. 是会有多出用skb,例如一边运行一边sniff.有些时候会修改skb, 这就要clone,如果这/个skb也被其他人用了.. */
struct sk_buff * skb2 = skb;
skb=skb_clone(skb,GFP_ATOMIC);
if(skb==NULL){
kfree_skb(skb2);
return 0;/*这里系统内存不足,为什么不报错?因为对kernel来说,mem 不够不是错,是会出现的实际情况,. 在这里的处理方式就是把这个包drop调.不loopback了. */
}
kfree_skb(skb2);
}
else
skb_orphan(skb);/*查中定义:
skb_orphan ---- orphan a buffer
@skb: buffer to orphan
If a buffer currently has an owner then we
call the owner's destructor function and
make the @skb unowned.The buffer continues
to exist but is no longer charged to its
former owner
那么skb_orphan以后,原来skb所指向的sk_buff
结构如何使用呢?skb是否成了一个空指针?
skb_orphan和kfree_skb有什么本质的区别?
其实这里应该不是free调的.还是可以用的.但是取消
原来的owner的引用而已. */
.
.
.
}