中断
快速中断具有原子性,不允许嵌套;普通中断不具有原子性且允许嵌套
中断共享是指将不同的设备挂在同一条中断线上。
中断注册:
int request_irq(unsignedint irq,void (*handler)(int,void*,struct pt_regs*),unsigned long flags,constchar *devname,void *dev_id)返回0成功,失败返回一个错误码。irq为中断号,handler中断处理函数,flags中断标志(IRQF_DISABLED快速中断,IRQF_SHARED可在设备间共享),devname设备名,dev_id共享中断时使用。
中断处理程序:
运行在中断上下文中。不能向用户空间发送或接受数据;不能使用可能引起阻塞的函数;不能使用可能引起调度的函数。
中断处理程序流程:
Ø 判断是否是本设备产生了中断;inb()
Ø 清除中断标志位;(outb())
Ø 中断处理;
Ø 唤醒等待数据的进程(wait_up_interruptible(&short_queue));
释放中断处理程序:
voidfree_irq(unsigned int irq,)
Linux网路体系结构
采用四层的internet模型
Linux网路子系统从上到下分别是:用户层、系统调用接口层、协议无关接口层、网络协议层、设备无关接口层、设备驱动层、物理层。
设备无关接口层:
设备无关接口将协议与各种网络设备驱动连接在一起。这一层提供一组通用函数供底层网络设备程序使用,让他们对高层协议栈进行操作。首先驱动程序通过register_netdevice()或unregister_netdevice在内核中进行注册或注销。调用前必须先填写net_device结构,然后传递这个结构进行注册,内核调用它的init函数,然后执行一组健全性检查,并将新设备添加到设备列表中(内核中的活动设备链表)。要从协议层向设备发送数据,需要使用dev_queue_xmit函数,这个函数对数据进行排队,并交由底层设备驱动程序进行最终传输报文的,接收通常使用netif_rx执行的。当底层设备驱动程序接收到一个报文(包含在所分配的sk_buff中)时,就会通过调用netif_rx将数据上传置设备无关层,然后,这个函数通过netif_rx_schedule将sk_buff在上层协议队列中进行排队,供以后进行处理。
网卡驱动设计
网络接口设备描述结构体:(net_device)可以有以下函数动态分配
struct net_device *alloc_netdev(int sizeof_priv,constchar *mask,void (*setup)(struct net_device *))sizeof_priv私有数据去大小;mask设备名,setup初始化函数
struct net_device *alloc_etherdev(int sizeof_priv)
初始化函数,在register_netdev时被调用:
int (*ndo_init)(struct net_device *dev);
打开接口:ifconfig激活时,接口被打开
int (*ndo_open)(structnet_device *dev);在打开时申请中断DMA等,设置寄存器、启动设备,启动发送队列。
停止接口:
int (*ndo_stop)(structnet_device *dev);
数据发送函数:
netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev);
处理特定于接口的ioctl命令:
int (*ndo_do_ioctl)(structnet_device *dev,struct ifreq *ifr, int cmd);
改变MAC地址的函数,需要硬件支持:
int (*ndo_set_mac_address)(struct net_device *dev,void *addr);
网卡注册:
int register_netdevice(structnet_device *dev);
网卡注销:
void unregister_netdevice(struct net_device *dev)
Linux内核中的每个网络包都有一个套接字缓冲区结构srtuct sk_buff描述,即一个sk_buff结构就是一个包,指向sk_buff的指针通常被称为skb。
Skb操作函数:
分配一个sk_buff结构,供协议栈代码使用:
struct sk_buff *alloc_skb(unsigned int size,gfp_tpriority);
分配一个sk_buff结构,供驱动代码使用:
struct sk_buff *dev_alloc_skb(unsigned int length);
向前移动skb的head指针,并返回head移动之后的值:
unsigned char *skb_put(struct sk_buff *skb, unsigned intlen);
释放skb:
void kfree_skb(struct sk_buff *skb);协议栈代码使用
void dev_kfree_skb(struct sk_buff *skb);驱动代码使用
数据接收流程:
网络接口支持三种中断:新报文到达中断、报文发送完成中断、出错中断。中断处理程序可通过查看网卡中的中断寄存器,来分辨出中断类型。
Input输入子系统
分为三部分:驱动、input core、event handler。
Input设备用input_dev结构体描述,使用input子系统实现输入设备驱动时,驱动的核心工作时想系统报告按键、触摸屏、键盘、鼠标等事件(event、通过input_event结构体描述),不在关心文件操作接口,以为input子系统已经完成了文件操作接口。驱动报告事件经过inputcore和eventhandler最终到达用户空间。
注册输入设备:
int input_register_device(struct input_dev *dev);
注销输入设备:
void input_unregister_device(struct input_dev *dev);
驱动实现:
设备驱动程序通过set_bit()告诉input子系统它支持哪些事件。例如:set_bit(EV_KEY,button_dev.evbit)
struct iput_dev中的evbit用来表示设备所支持的实践类型,keybit用来表示设备支持的按键类型。
事件类型:
EV_RST Reset EV_KEY 按键 EV_REL 相对坐标 EV_ABS 绝对坐标
EV_MSC 其他 EV_LEDLED EV_SND 声音 EV_BEP repeat
EV_FF 力反馈
用于报告EV_KEY:(code事件代码,value事件值)
void input_report_key(struct input_dev *dev,unsigned intcode,int value);
用于报告EV_REL:
void input_report_rel(struct input_dev *dev,unsigned intcode,int value);
用于报告EV_ABS:
void input_report_abs(struct input_dev *dev,unsigned intcode,int value);
input_sync用于事件同步,表示一次所有报告完毕。
PCI总线
PCI是(Peripheral Component Interconnect外围设备互联)简称。有三大优点:在计算机和外设间传输数据时具有更好的性能;能够尽量独立于具体的平台;可以方便的实现即插即用。
每一个PCI设备由一个总线号、一个设备号、一个功能号确定。PCI规范允许一个系统最多拥有256条总线,每条总线最多带32个设备,每个设备最多8个功能。
每个PCI设备都有一组固定格式的寄存器,即配置寄存器;由Linux内核中的PCI初始化代码与驱动程序共同使用,内核启动时对其进行初始化,包括设置中断号以及I/O基址。
00H-01H Vendor ID 制造商标识;02H-03H Device ID 设备标识;04H-05H Command 命令寄存器
06H-07H Status 状态寄存器;08H Revision ID 版本识别号寄存器;
09H-0BH Class Code 分类代码寄存器;0CH Cache LineSize CACHE行长度寄存器
10H-13H基地址寄存器0;14H-17H基地址寄存器1;18H-1BH基地址寄存器2;1CH-19H基地址寄存器3
20H-23H基地址寄存器4;24H-27H基地址寄存器5;
3H Interrupt Line 中断线寄存器;3DH Interrupt Pin 中断引脚寄存器。
PCI驱动使用struct pci_driver结构来描述
struct pci_driver{
…………
const structpci_device_id *id_table;//设备列表
int(*probe)(struct oci_dev *dev,const struct pci_device_id *id);
void(*remove)(struct pci_dev *dev);
…………
};
PCI设备注册函数:
pci_register_driver(struct pci_device *dev);
使能PCI设备;
int pci_enable_device(struct pci_dev *dev);
获取基地址:
pci_resource_start(struct pci_dev *dev,int bar);返回指定区域的起始地址,这个区域由参数bar指定,范围0-5,表示6个PCI区域中的一个。
pci_resource_end(struct pci_dev *dev,int bar);返回指定区域的末地址。