linux驅動
知識結構:
1. Linux驅動程序設計模式(40%)2.內核相關知識(30%)3. 硬件相關知識(30%)z
驅動分類:字符,網絡,塊
字符設備:以字節為最小單位,不可以亂序讀寫。
塊設備: 一次傳送一個整體數據(512字節),Linux可以以字節訪問塊設備(僅僅是驅動與內核的接口不同,訪問的順序的不同(字符隻可順序訪問,塊驅動可隨機訪問))
網絡接口:硬件(eth0),純軟件(lo)
驅動的安裝:模塊,編譯進內核(Linux啟動的時候會自動加載init段)
使用驅動程序:字符設備文件—〉字符設備驅動—〉字符設備
文件係統—〉塊設備文件—〉塊設備驅動—〉塊設備
套接字—〉協議棧—〉網絡設備驅動—網絡接口設備
主設備號用來標示與設備文件相連的驅動程序,次編號被驅動程序用來辨別操作哪個設備
主設備號反映設備類型,此設備號區分同類型的設備
dev_t 高12位為主設備號,低20位為次設備號
MAJOR(dev_t dev)從dev_t分解出主設備號
MINOR(dev_t dev)從dev_t分解出此設備號
MKDEV(major,minor) 構造設備號;
靜態申請:1.根據Documentation/devices.txt驅動沒有使用的主設備號
2. 使用register_chrdev_region(dev_t form, unsigned count ,const char *name)函數注冊(容易衝突)
From希望使用的設備號,count希望申請使用設備號的數目,name設備名(體現在/proc/devices)
2. 動態分配 alloc_chardev_region(安裝驅動前無法創建設備文件)創建設備文件後,通過/proc/devices 察看
alloc_chardev_region(dev_t *dev,unsigned baseminor,unsigned count, const char *name)
dev分配的設配號,baseminor起始的此設備號,count要分配的設備數目,name設備名
注銷設備號:unregister_chrdev_region(dev_t dev, unsigned baseminor)
創建設備節點:
Mknod filename type major minor type是字符或塊 mknod serial0 c 100 0
Linux字符設備驅動3個重要數據結構
Struct file每打開一次都有一個關聯的struct file 重要結構loff_t f_pos文件讀寫位置 struct file_operations*f_op
Struct inode 記錄文件的物理上的信息(設備號等),一個文件可以有多個file,但隻有一個inode
Struct file_operation *f_op函數指針的集合
struct file_operations mem_fops= {
.owner = THIS_MODULE,
.llseek = mem_seek,
.read = mem_read
…….
}
讀內核代碼應用程序怎樣訪問驅動程序(read_write,c)
係統調用read找到vfs_read根據file結構中找到file_operations中的read
讀文件過程:係統調用read -à vfs_read -à file_operations -à read
字符設備使用 structcdev 來描述
字符設備注冊可分為如下3個部分:
1.分配cdev struct cdev *cdev_alloc(void)
2.初始化cdevcdev_init(struct cedev *p, const struct file_operations *fops)
3. 添加cdevcdev_add(struct cdev *p ,dev_t dev ,unsigned count )
dev設備號count設備號的數目
字符設備的注銷:cdev_dev(struct cdev *p)
設備操作
int (*open)(struct *inode , struct file *)
如果該項為NULL,設備打開永遠成功
void(*release)(struct*inode , struct file *)
ssize_t (*read)(struct file *,char __user *buff , size_t , loff_t *)
ssize_t (*write)(struct file *,char __user *, size_t , loff_t *)
file是文件指針(來與內核),*buff是數據緩衝(用戶空間),count傳輸的數據量(用戶空間),offp訪問位置(來與內核)
*buff是用戶空間的,不能直接使用
int copy_from_user(void *to ,const void __user *from, int n)
int copy_to_user(void __user *to,const void *to , int n)
loff_t llseek(struct *file ,loff_t offset , intwhence)
open方法主要完成如下工作:1. 初始化設備 2. 標明此設備號
在open(struct inode *inode,struct file *filp) 函數可使用 MINOR(inode->i_rdev); 獲取此設備號
filp->private_data = dev; 將設備描述指針賦值給私有文件指針 ,區分出了那種設備
在read()函數使用struct mem_dev *dev =filp->private_data 可根據私有文件指針指定找到具體的設備,read函數參數沒有inode,無法獲取此設備號。
kmalloc分配內存,返回地址,根據其返回的地址就可操作內存中的數據
copy_to_user(buf,(void*)(dev->data+p),count) 這裏的(dev->data+p)為什麼要用(void *)強製轉換呢?copy_to_user是這麼定義的
int copy_to_user(void __user *to ,const void *to , int n),但還是不理解。
驅動程序調試分類:打印調試,調試器調試(kgdb),查詢調試(proc文件係統)
合理的使用printk可以全局的打開或關閉它們。
並發:多個執行單元同時被執行
竟態:並發的執行單元對共享資源(硬件資源或全局變量等)的共享訪問
通過semaphore機製和spin_lock機製實現
獲取信號量不成功該阻塞或者睡眠
1. 定義信號量 struct semaphore sem;
2. 初始化信號量 void sema_init(struct semaphore *sem,int val) 初始化信號量的初值為val
3. voidinit_MUTEX(struct semaphore *sem)初始化一個互斥鎖,把sem的值設為1
4. voidinit_MUTEX_LOCKED(struct semaphore *sem) 初始化一個互斥鎖,把sem的值設為0
定義與初始化工作可由如下宏一步完成
DECLARE_MUTEX(name)定義一個信號量,並初始化為1
DECLARE_MUTEX_LOCK(name)定義一個信號量,並初始化為0,為已鎖狀態
5. 獲取信號量void down(struct semaphore *sem)可能會導致進程睡眠,故不能在中斷上下文中使用,如果sem非負直接返回,否則掛起(TASK_UNINTERRUPTIBLE),不建議使用
6. voiddown_interrruptible(struct semaphore *sem)信號量不可用,置為TASK_INTERRUPTIBLE
7. voiddown_killable(struct semaphore *sem)信號量不可用 ,置為TASK_KILLABLE
8. voidup(struct semaphore *sem) 釋放信號量
自旋鎖不會引起調用者的睡眠,線程會移植忙循環,移植等待下去
1. spin_lock_init(x)初始化自旋鎖
2. spin_lock(lock)獲取自旋鎖,不成功自旋在那
3. spin_trylock(lock)不會一直等待
4. spin_unlock
信號量可以有多個持有者(1個互斥信號量),自旋鎖隻有一個持有者
信號量適合保持時間較長,自旋鎖適合保持時間較短
Ioctl對硬件進行控製(改變波特率,報告錯誤信息)
用戶使用方法:int ioctl(int fd, unsigned long cmd, …) 點表示可選參數
int(*ioctl)(struct inode*inode, struct file *filp, unsigned int cmd, unsigned long arg)
cmd用戶空間傳下來的,arg用戶傳下來的參數
ioctl命令實現方法:1.定義命令 2.實現命令
Documentation/ioctl-number.txt定義了使用的幻數
ioctl被劃分為幾個位段,include/asm/ioctl.h定義了這些字段:
1. 類型(幻數):8位寬,屬於哪一類設備
2. 序號:表明設備命令的第幾個
3. 傳送方向:可能的值是_IOC_NONE, _IOC_READ, _IOC_WRITE是從應用程序的觀點來看的
4 .參數的大小(數據的類型)
內核提供下列宏來幫助定義命令
_IO(type,nr)沒有參數傳遞
_IOR(type, nr, datatype)從驅動中讀數據
_I0W(type, nr, datatype)從數據到驅動
_IOWR(type, nr, datatype)type和number成員作為參數被傳遞
Ioctl函數的實現 1. 返回值 2. 參數使用 3. 命令操作
通常是個switch語句,不支持的返回 –EINVAL
使用ioctl中的參數:整數可以直接使用, 指針則使用前需進行正確的檢查
參數檢查
不需要檢測的函數:copy_from_user, copy_to_user, get_user, put_user
需要檢測的函數:__get_user, __put_user
int access_ok(int type, const void *addr, unsingned long size)
type是VERIFY_READ或者VERIFY_WRITE, addr是要操作的用戶內存的地址,size是操作的長度。access_ok返回一個布爾值:1.存取沒問題 0. 失敗,如果返回失敗,
則ioctl應當返回-EFAULT.
等待隊列:實現進程的阻塞,保存進程的容器,阻塞時放入等待隊列,喚醒時,取出進程
1. 定義等待隊列 wait_queue_head_t my_queue
2. 初始化等待隊列 wait_waitqueue_head(&my_queue)
3. 定義並初始化等待隊列 DECLARE_WAIT_QUEUE_HEAD(my_queue)
有條件睡眠
wait_event(queue, condition)當condition為真,返回。當condition為假,進入TASK_UNINTERRUTIBLE睡眠,並掛在queue指定的等待隊列上
wait_event_interruptible(queue, condition)
wait_event_killable(queue, conditon)
無條件睡眠(老版本,不建議使用)sleep_on(wait_queue_head_t *q) interruptible_sleep_on(wait_queue_head_t*q)
等待隊列中喚醒進程
Wake_up(wait_queue_t *q)喚醒等待隊列中的所有進程都喚醒
Wake_up_interruptible(wait_queue_t *q)喚醒為TASK_INTERRUPTIBLE進程
阻塞方式是文件讀寫的默認方式,應用程序可以使用O_NONBLOCK標誌非阻塞的
設置了O_NONBLOCK,係統隻是簡單的返回-EAGAIN
Select係統調用對應於poll
Select用於多路監控,如沒有一個文件滿足要求,select將阻塞進程
Int select(int maxfd, fd_set *reedfds, fd_set*writefds, fd_set *exceptfds, const struct timeval *timeout)
maxfd:文件描述符的範圍,比檢測的最大文件描述符大1
Readfds:被讀監控的文件描述符
Writefds:被寫監控的
Exceptfds:被異常監控的
Timeout:定時器
Timeout
1. 為0時不管有沒有文件滿足要求,立即返回,無文件滿足,返回0
2. 為NULL時,select將阻塞進程,直到文件滿足要求為止
3. 為正整數的時候,等待的最長時間,即select在timeout時間內阻塞進程
Select返回值
1. 正常返回滿足要求的文件描述符個數
2. 沒有滿足的返回0
3. select被某個信號打斷,返回-1 , errno為EINTR
Select係統調用:
1. 將要監控的文件添加到文件描述符集
2. 調用select開始監控
3. 判斷文件是否滿足要求
Void FD_SET(int fd, fd_set *fdset) 將fd添加到fdset中
Void FD_CLR(int fd, fd_set *fdset) 在fdset中清楚fd
Void FD_ZERO(fd_set *fdset) 清空fdset
Void FD_ISSET(int fd, fd_set *fdset) 檢測文件描述集中的需判斷的fd發生變化
驅動通常由poll實現
Unsigned int(*poll)(struct file *filp, poll_table *wait)
負責完成:
1. 使用poll_wait將等待隊列添加到poll_table中
2. 返回描述設備是否可讀可寫的掩碼
位掩碼:
POLLIN設備可讀,POLLRDNORM數據可讀,POLLOUT設備可寫,POLLWRNORM數據可寫
設備可讀通常返回(POLLIN | POLLRDNORM)
設備可寫通常返回(POLLOUT |POLLWRNORM)
Poll方法隻是做一個登記,真正的阻塞發生在select.c中的do_select函數(分析了do_select函數)
自動創建設備文件
2.4內核使用devfs_register(devfs_handle_tdir, const char *name, unsigned int flags, unsigned int major unsigned intminor, umode_t mode, void *ops, void *info)
Dir:目錄名,name:文件名;flags:創建標誌;major:主設備號;minor此設備號;mode:創建模式;ops:操作函數集;info:通常為空
從2.6.13開始,devfs不複存在,udev成為替代
使用
1. class_create為設備創建一個class,
2. 使用device_create創建對應的設備
例:struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);
Device_create(myclass, NULL,MKDEV(major_num, 0), NULL, “my_device”)
void *mmap(void*addr, size_t len, int prot, int flags, int fd, off_t offset)
負責把文件內容映射到進程的虛擬內存空間,通過對這段內存的讀取和修改,來實現對文件的讀取和修改,而不需要再調用read,write等操作。
addr: 映射的起始地址,通常為NULL,由係統指定
length:映射文件的長度
prot:映射區的保護方式 PROT_EXEC:映射區可被執行,PROT_READ:映射區可被讀取,PROT_WRITE:映射區可被寫入
flags:映射區的特性 MAP_SHARED:寫入映射區的數據會複製回文件,且允許其他映射該文件的進程共享。 MAP_PRIVATE:對映射區的寫入操作會產生一個映射區的複製(copy-on-write),對此區域的修改不會寫回源文件。
fd:由open返回的文件描述符,代表要映射的文件
offset:以文件開始處的偏移量,必須是分頁大小的整數倍,通常為0,表示從文件頭開始映射。
注意mmap不能映像原有文件的長度
int munmap(void *start, size_tlength)
成功返回0,失敗返回-1. start的取值一般是mmap返回的地址
虛擬內存區域:是虛擬地址空間的一個同質區間,即有同樣特性的連續地址範圍。一個進程的內存映像由以下幾部分組成:程序代碼,數據,BSS和棧區域,以及內存映射區域
一個進程的內存區域可以通過查看 /proc/pid/maps
每一行的域為:start_end perm offset major:minor inode
linux內核使用結構vm_area_struct來描述虛擬內存區域,其中主要成員如下:
unsigned long vm_start 虛擬內存區域起始地址
unsigned long vm_end 虛擬內存區域結束地址
unsigned long vm_flags
映射一個設備是指把用戶空間的一段地址關聯到設備內存上。
mmap做了三件事:1.找到用戶空間地址(內核完成) 2.找到設備的物理地址(原理圖) 3.關聯(通過頁式管理)\
mmap設備方法需要做的就是建立虛擬地址到物理地址的頁表。
int(*mmap)(struct file *, struct vm_area_struct *)
建立頁表有兩種方法:
1.使用remap_pfn_range一次建立所有頁表;
2.使用nopage VMA方法每次建立一個也表。
int remap_pfn_range(structvm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size,pgprot_t prot)
vma:虛擬內存區域指針 virt_addr:虛擬地址的起始值 pfn:要映射的物理地址所在的物理頁禎號,可將物理地址>>PAGE_SHIFT得到(PAGE_SHIFT為12,相當於除以4KB)
size:要映射的區域的大小 prot:VMA的保護屬性
硬件訪問
寄存器和內存的區別:寄存器和RAM主要不同在於寄存器操作由副作用(side
effect或邊際效果):讀取某刻地址可能導致該地址內容變化,讀中斷狀態寄存器,便自動清零
內存與IO
在X86存在IO空間(串口 並口等),在32為x86
IO空間是64K,內存空間是4G,ARM,PowerPC隻有內存地址空間的
IO端口:一個寄存器或內存位於IO空間時,稱為IO端口
IO內存:一個寄存器或內存位於內存空間時,稱為IO內存
對IO端口的操作需要按如下步驟完成:1,申請 2,訪問 3,釋放
申請:struct resource*request_region(unsigned long first, unsigned long n,
const char *name) 從first開始的n個端口,name設備名字
係統中端口的分配情況記錄在/proc/ioports中
訪問:intb outb intw outw intl outl
釋放:voidrelease_region(unsigned long start, unsigned long n)
IO內存有4步:1.申請 2,映射 3,訪問 4,釋放
申請:structresource *request_mem_region(unsigned long start, unsigned long
len,char
*name)從start開始,長度為len字節的內存區。成功,返回非NULL,否則返回NULL。
可在/proc/iomem中列出
訪問:在訪問IO內存之前,必須進行物理地址到虛擬地址的轉化,使用下麵函數
void *ioremap(unsignedlong phys_addr, unsigned long size)
訪問:ioread8(void *addr) iowrite8(u8 value, void*addr) 老版本 使用readbwriteb
釋放: 1,void iounmap(void *addr) 2,void release_mem_region(unsigned long
start, unsigned long len)
混雜設備驅動:共享一個主設備號10,成為混雜設備
Linux內核使用structmiscdevice描述混雜設備
struct miscdevice{
int minor;
const char *name;
const struct file_operations*fops;
struct list_head list;
struct device *parent;
struct device *this_device;
}
使用misc_register函數來注冊一個混雜設備驅動 misc_register(struct miscdevice *misc)
使用上拉下拉避免懸浮
Linux總線設備驅動模型(2.6內核難點)
Sysfs文件係統(基於內存,展示內核數據結構屬性,關係),與proc同類別的文件係統,sysfs把連接在係統上的設備和總線組織成分級的文件,使其從用戶空間可以訪問到
sysfs在/sys/目錄下
block目錄:塊設備信息
bus:總線(ide pci scsi sb pcmcia)裏邊還有devices和drivers目錄,devices目錄下都是軟鏈接
class目錄:按照功能進行分類(網絡)
devices:包含係統所有的設備
kernel:內核中的配置參數
Module:係統中所有模塊信息
firmware:係統中的固件
fs:描述係統中的文件係統
power:係統電源選項
Kobject實現了基本的麵向對象管理機製,與sysfs文件係統緊密相連,在內核中注冊的每個kobject對象對應sysfs文件係統中的一個目錄(作用:在sys下創建一個目錄)類似於C++中的基類。
void kobject_init(struct kobject *kobj) 初始化kobject結構
int kobject_add(strut kobject *kobj)將kobject對象注冊到Linux係統
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,struct
kobject *parent, bonst char *fmt, ...) 初始化kobject,並注冊到Linux
void kobject_del(struct kobject *kobj)從Linux係統中刪除kobject對象
struct kobject *kobject_get(struct kobject
*kobj)將kobject對象引用計數加1,同時返回該對象指針
void kobject_put(struct kobject
*kobj)將kobject對象的引用計數減1,如果引用計數降為0,則調用release方法釋放該kobject對象
kobject的ktype成員是一個特指向kobj_type結構的指針,該結構記錄了kobject對象的一些屬性
struct kobj_type{
void(*release)(struct kobject*kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
}
release:用於釋放kobject占用的資源,當kobject的引用為0時被調用
struct attribute{
char *name;
struct module *owner;
mode_t mode; /*屬性的保護位*/
}對應於kobject的目錄下的一個文件,name成員就是文件名
struct sysfs_ops
{
ssize_t(*show)(struct kobject *,struct attribute*, char *)
ssize_t(*store)(struct kobject *,struct attribute *, const char *,
size_t)
}
show:當用戶讀屬性文件時,該函數被調用,該函數將屬性值存入buffer中返回給用戶態
store:當用戶寫屬性文件時,該函數被調用,用於存儲用戶傳入的屬性值
(學習函數指針三點
1.參數是怎麼樣的(從哪來到哪去)2.這個函數指針什麼時候被調用3.這個函數用來做什麼)
kset是具有相同類型的kobject的結合,在sysfs中體現一個目錄,kobject裏邊隻能是文件不能是目錄
struct kset
{
struct list_head list; //鏈接該kset中所有kobject的鏈表頭
spinlock_t list_losk;
struct kobject kobj; //內嵌的kobject
struct kset_uevent_ops *uevent_ops//處理熱插拔事件的操作集合
}
kset操作:
int kset_register(struct kset *kset) 在內核中注冊一個kset
void kset_unregister(struct kset *kset) 在內核中注銷一個kset
熱插拔事件:當係統配置發生變化時,如:添加kset到係統;移動kobject,一個通知會從內核空間發送到用戶空間,這就是熱插拔事件。熱插拔事件導致用戶空間相應的處理程序(如udev,mdev)被調用,這些處理程序會通過加載驅動程序,創建設備節點等來響應熱插拔事件
操作集合
struct kset_uevent_ops{
int(*filter)(struct kset *kset,struct kobject *kobj);
const char *(*name)(struct kset*kset, struct kobject *kobj);
int(*uevent)(struct kset *kset,struct kobject *kobj, struct
kobj_uevent_env *env);
}
當該kset所管理的kobject和kset狀態發生變化時(如被加入,移動),這三個函數將被調用。
filter函數:決定是否將事件傳遞到用戶空間。如果filter返回0,不傳遞事件
name函數:用於將字符串傳遞給用戶空間的熱插拔處理程序
uevent函數:將用戶空間需要的參數添加到環境變量中
linux2.6內核提供了全新的內核設備模型
設備模型元素:總線,設備,驅動
總線:處理器和設備之間的通道,所有的設備都通過總線相連,包括platform總線(虛擬平台總線),總線由bus_type結構表示
總線的注冊使用:bus_register(struct bus_type *bus)
若成功,可在sysfs的/sys/bus下看到
總線的刪除使用:void bus_unregister(struct bus_type *bus)
int(*match)(struct device *dev, struct device_driver *drv)
當一個新設備或者驅動添加到這個總線時,用於判斷指定的驅動程序是否能處理指定的設備。可以返回非零值
int(*uevent)(struct device *dev, char **envp, int num_envp, char *buffer,
int buffer_size) 在為用戶空間產生熱插拔事件之前,這個方法允許總線添加環境變量
總線的屬性由bus_attribute描述,定義如下:
struct bus_attribute{
struct attribute attr;
ssize_t(*show)(struct bus-type *,char *buf);
ssize_t(*store)(struct bus_type *,const char *buf, size_t count);
}
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) 創建屬性
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) 刪除屬性
Linux係統中每個設備由一個struct device描述
int device_register(struct device *dev) 注冊設備
void device_unregister(struct device *dev)注銷設備
一條總線也是個設備,也必須按設備注冊
struct device_attribute
{
struct attribute attr;
ssize_t(*show)(struct device *dev,struct device_attribute *attr, char
*buf);
ssize_t(*store)(struct device *dev,struct device_attribute *attr,
const char *buf, size_t count);
}
int device_create_file(struct device *device, struct device_attribute*entry)
創建屬性
void device_remove_file(struct device *dev, struct device_attribute *attr)
刪除屬性
驅動程序由 struct device_driver描述
int driver_register(struct device_driver *drv) 注冊驅動
void driver_unregister(struct device_driver *drv) 注銷驅動
驅動的屬性使用struct driver_attribute來描述
stryct drever_attribute{
struct attribute attr;
ssize_t(*show)(structdevice_driver *drv, char *buf);
ssize_t(*store)(structdevice_driver *drv, const char *buf, size_t
count);
}
int driver_create_file(struct device_driver *drv, struct driver_attribute
*attr) 創建屬性
void driver_remove_file(struct device_driver *drv, struct driver_attribute
*attr) 刪除屬性
platform總線:2.6內核加入的虛擬總線,由platform_device和platform_driver兩部分組成
platform優勢在於platform機製將設備本身的資源注冊進內核,由內核統一管理,在驅動程序使用這些資源提供統一的接口,提高可以執行。
通過platform流程:1.定義platform_device2.注冊platform_device
3,定義platform_driver 4.注冊platform_driver
平台設備使用struct platform_device來描述
struct platform_device{
const char *name; 設備名
int id; 設備編號,配合設備名使用
struct device dev;
u32 num_resources;
struct resource *resource; 設備資源(中斷號,基地址)
}
platform_device的分配使用:
structplatform_device *platform_device_alloc(const char *name, int id)
name:設備名 id:設備id,一般為-1
注冊平台設備,使用函數
intplatform_device_add(struct platform_device *pdev)
平台設備資源使用struct resource來描述
struct resource{
resource_size_t start; //資源起始的物理地址
resource_size_t end; //資源結束物理地址
const char *name; //資源的名稱
unsigned long flags; //資源的類型,比如MEM,IO,IRQ類型
struct resource*parent,*sibling,*child; //資源鏈表指針
}
獲取資源: struct resource*platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
dev:資源所屬的設備 type:獲取的資源類型 num:獲取的資源數
平台驅動使用struct platfor_driver來描述
struct platform_driver{
......
int(*probe)(struct platform_device*)
......
}
平台驅動的注冊使用函數
intplatform_driver_register(struct platform_driver *)
platfor_driver_register 原理分析(什麼時候遍曆設備,能夠處理某個設備的標準)
為什麼需要中斷:
1.外設的處理速度一般慢於CPU
2.CPU不能一直等待外部事件
在Linux驅動中,實現中斷包含兩個步驟:
1. 向內核注冊中斷
2. 2.實現中斷處理函數
中斷注冊:intrequest_irq(unsigned int irq,void(handler)(int,void*,struct
pt_regs *),unsigned long flags, const char *devname, void *dev_id)
返回0表示成功,或者返回一個錯誤碼
irq:中斷號 handler中斷處理程序 flags:中斷管理有關的選項*devname:設備名
*dev_id:共享中斷時使用
flags參數:IRQF_DISABLED(SA_INTERRUPT)快速中斷處理程序
IPQF_SHARED(SA_SHIRQ)這位表明中斷可以在設備間共享
這兩者的區別:快速中斷處理的原子性(不被打斷),而慢速中斷不保證.快速中斷時處理程序是不會被其他類型的中斷打斷的,默認的都是慢速中斷處理程序.
共享中斷:將不同的設備掛到同一個中斷線上
1.申請共享中斷,必須在flags參數中指定IRQF_SHARED位
2.dev_id參數必須是唯一的
釋放中斷(通常在驅動卸載時):void free_irq(unsigned intirq, void *dev_id)
3. 共享中斷的處理程序中,不能使用disable_irq(unsigned int irq)把其他的頁關掉了
中斷處理程序是在中斷上下文中運行的,受到某些限製
1.不能向用戶空間發送或接受數據
2.不能使用可能引起阻賽的函數
3.不能使用可能引起調度的函數
中斷處理函數流程
void short_sh_inerrupt(intirq, void *dev_id, struct pt_regs *regs)
{
//判斷是否產生了中斷,避免共享中斷產生的誤調用
value = inb(short_base);
if(!(value & 0x80)) return;
//清除中斷位(如果設備支持自動清除,不需要這步)
outb(value & 0x7F, short_base);
//中斷處理,通常是數據接受
.......
//喚醒等待數據的進程
wake_up_interruptible(&short_queue);
}
最後更新:2017-04-03 20:19:50