device driver - chapter 6字元驅動程式的進階作業
TRANSCRIPT
Chapter 6 字元驅動程式的進階作業Speaker :呂宗螢Adviser :梁文耀 老師Date : 2007/1/29
嵌入式及平行系統實驗室
ioctl
驅動程式需提供控制硬體的各種能力,例如格式化軟碟、鎖住機門、退片、狀態回報、改變通訊模式或傳輸速率..等。就是利用 ioctl 來實現int ioctl(int fd, unsigned long cmd, …);int (*ioctl) (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg); cmd : user-space 要求執行的 ioctl 指令,等於
ioctl() 第二引數,用 switch 實做 如 ioctl() 系統呼叫有第三個引數,則可自 arg 取得該引數
嵌入式及平行系統實驗室
選擇 ioctl 指令編號 (1)
cmd 與各指令之間的對應關係,需獨一無二,以免正確指令下達到錯誤裝置 查閱 include/asm/ioctl.h 和 Documentation/ioctl-number.txt 選出沒有使用的魔數 #include <linux/ioctl.h>
type• 魔數 (magic number)• 長度為 _IOC_TYPEBITS(8 bit)
number• 流水號 (ordinal number) 或稱序號 (sequential number)• 長度為 _IOC_NRBITS(8 bit)
direction• 傳輸方向• _IOC_NONE( 不傳輸資料 ) 、 _IOC_READ( 資料從裝置讀
出 ) 、 IOC_WRITE( 資料流入裝置 ) 、 _IOC_READ | _IOC_WRITE 、( 雙向 )
• 以應用程式觀點來看 size
• 資料量• 長度為 _IOC_SIZEBITS(13 of 14 bit)
嵌入式及平行系統實驗室
選擇 ioctl 指令編號 (2)
#include <linux/ioctl.h>(<asm/ioctl.h>)編制指令編號的巨集:
_IO( type, number) :沒有引數指令 _IOR( type, number, datatype) :從驅動程式讀出資料 _IOW( type, number, datatype) :傳輸資料到驅動程式 _IOWR( type, number, datatype) :雙向傳輸
解碼巨集:_IOC_DIR(nr) 、 _IOC_TYPE(nr) 、 _IOC_NR(nr) 、 _IOC_SIZE(nr)
整數引數傳遞方式有:透過指標,直接給明確數值按照 ioctl() 的慣例,應用指標來交換數值
嵌入式及平行系統實驗室
選擇 ioctl 指令編號 (3)
/* 使用” k” 為魔數,你的驅動程式應該另選一個不同的 8-bits 數值 */#define SCULL_IOC_MAGIC 'k'#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)/* * S 代表 “ Set” ( 設定 ) ,需要一個指標 * T 代表 “ Tell” ( 通知 ) ,直接使用引數值 * G 代表 “ Get“ ( 取得 ) ,以指向查詢結果的一個指標回覆 * Q 代表 “ Query“ ( 查詢 ) ,以回傳值答覆查詢結果 * X 代表 “ eXchange” ( 交換 ) ,連續執行 G 和 S * H 代表 “ sHift” ( 移位 ) ,連續執行 T 和 Q */#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)
#define SCULL_IOC_MAXNR 14
嵌入式及平行系統實驗室
ioctl 的回傳值如果 cmd 不符合任何命令編號,則 default 應做?
許多核心合適採取的行為是回傳 -EINVAL(Invalid Argument)
但, POSIX 標準規定回傳 -ENOTTY(Not a typewriter) , C 函式庫的解釋為 Inappropriate ioctl for device
常用為 -EINVAL
嵌入式及平行系統實驗室
預先定義 ioctl 指令 (1)
雖然 ioctl( ) 系統呼叫的主要作用對象是硬體裝置,但是核心本身仍能辨認少數幾個命令 ( 預設指令 ) 。因此,當你挑選的 ioctl 指令編號剛好與預定指令相同,則你寫出來的 ioctl 作業方法將永遠收不到該指令,而應用程式也會因為發出衝突的 ioctl 指令而遭遇到意外。預設指令分為三大類:
可作用於任何檔案 ( 正常檔 , 裝置檔 , FTFO 或socket)
只對正常檔案有作用 只能用於特定檔案系統類型
嵌入式及平行系統實驗室
預先定義 ioctl 指令 (2)
以下是核心預先定義的 ioctl 指令,可作用於任何檔案: FIOCLEX
• 設立 close-on-exec 旗標 (File IOctl Close on Exec) 。當行程以exec() 系統呼叫執行另一個程式時,曾被該行程設立本旗標的檔案會被自動關閉
FIONCLEX• 撤銷 close-on-exec 旗標
FIOASYNC• 設立或撤銷檔案的“臨時通知” (asynchronous notification)
FIONBIO :• ”File IOctl Nonblock I/O” 此指令會修改 filp->f_flags裡的
O_NONBBOCK 旗標• 發出此指令的行程,必須在 ioctl( ) 的第三引數註明它到底想要「設立」或「撤銷」的動作• O_NONBBOCK 通常透過 fcntl() 系統呼叫的 F_SETFL 指令來改變
嵌入式及平行系統實驗室
ioctl 的額外引數之用法 (1)
在開始研究 scull 如何實作 ioctl 作業方法之前,有必要先搞清楚如何使用它的額外引數 ( 第三引數 ) 。 若該引數為整數時,那就直接拿來用。 如果是指標,就必須多費點心。 若指標指向 user-space 的位址,必須先確定該位址是有效的
int access_ok(int type, const void *addr, unsigned long size) #include <asm/uaccess.h>
• type :必須是 VERIFY_READ 或 VERIFY_WRITE(包含雙向 ) 的其中之一,取決於想對 user-space 進行的動作是讀入或寫出• addr :是被檢查的 user-space 位址• size :是檢查範圍 ( 以 byte 為計算單位 )• 回傳值: 1 代表成功 ( 可存取 ) , 0 代表失敗 ( 不能存取 ) ,如為失敗
ioctl 通常回傳 -EFAULT 它並非徹底檢驗指定範圍內的每一個位址,而是確認受檢位址是否在行程的合理存取範圍 ( 不會侵犯到 kernel-space) 大部份的驅動程式並不需要刻意呼叫 access_ok( )(記憶體存取程序會幫忙處理 )
嵌入式及平行系統實驗室
ioctl 的額外引數之用法 (2)
int scull_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){int err = 0, tmp;int retval = 0;
/* 分離出 type 和 number 位元欄位,如果遇到錯誤的 cmd ,直接傳回 ENOTTY*/ if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY; /* direction 是一個位元遮罩 , 而 VERIFY_WRITE 代表雙向傳輸 (R/W) * type 是從 user-space 來看 * access_ok卻是從 kernel 來看 * 所以“ read” 和“ write” 剛好相反 */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT;}
嵌入式及平行系統實驗室
ioctl 的額外引數之用法 (3)
除了使用 copy_from_user 以及 copy_to_user 函式之外, <asm/uaccess.h>還提供了一組針對常用資料規格而設計的傳輸工具: put_user(datum, ptr); __put_user(datum, ptr);
將 datum 寫到 ptr所指的 user-space put_user() 會確認行程是否有資格寫入指定的記憶位址
( 會使用 access_ok()) ,傳輸成功回傳 0 ,失敗則 -EFAULT
__put_user()所作檢查較少 ( 不會呼叫 access_ok()) ,但所指位址是使用者無權寫入,會回傳 -EFAULT get_user(local, ptr); __get_user(local, ptr);
從 ptr所指的 user-space 位址取得單一資料項,並將得所得資料存放在 local
嵌入式及平行系統實驗室
機能管制 (1)
使用者能否存取裝置,需借助作業系統的權限控管機制 (限制對象為人 )對於限制對象是操作項目而言,驅動程式自己必須作一些額外檢查,判斷使用者是否有權操作要求機能機能 (capabilities) ,不在只分成「特權」與「非特權」,而細分成更多類細目:
可將某種開放給某特定程式 ( 或使用者 ) ,而不必將無關的其他權力也一併交出 核心只將機能用於權限管理,並釋出兩個 linux 特有的系統呼叫, capget() 與 capset()
嵌入式及平行系統實驗室
機能管制 (2)
#include <linux/capability.h> 機能分類:
CAP_DAC_OVERRIDE :改變檔案或目錄之存取權限的能力 (Data Access Control)
CAP_NET_ADMIN :執行網路控管工作的能力 (包括會影響網路介面的動作 ) CAP_SYS_MODULE :將模組載入,移出核心的能力 CAP_SYS_RAWIO :執行「原始 I/O 」 (raw I/O) 作業能力
• ex:存取裝置的 I/O port ,直接與 USB 裝置通訊 CAP_SYS_ADMIN :一種無所不能的能力,提供系統管理作業所需的一切存取能力 ( 給予 administrator 能力 ) CAP_SYS_TTY_CONFIG :設定 tty組態的能力
驅動程式應先檢查系統呼叫的行程是否有足夠的權限。 #include <sys/sched.h> int capable (int capability); ex
if (! capable (CAP_SYS_ADMIN))return -EPERM;
嵌入式及平行系統實驗室
實作 ioctl 指令範例 (1)
switch(cmd) {case SCULL_IOCRESET:
scull_quantum = SCULL_QUANTUM;scull_qset = SCULL_QSET;break;
case SCULL_IOCSQUANTUM: /* Set: arg points to the value */if (! capable (CAP_SYS_ADMIN))
return -EPERM;retval = __get_user(scull_quantum, (int __user *)arg);break;
case SCULL_IOCTQUANTUM: /* Tell: arg is the value */if (! capable (CAP_SYS_ADMIN))
return -EPERM;scull_quantum = arg;break;
case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */retval = __put_user(scull_quantum, (int __user *)arg);break;
case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */return scull_quantum;
嵌入式及平行系統實驗室
實作 ioctl 指令範例 (2)
case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */if (! capable (CAP_SYS_ADMIN))
return -EPERM;tmp = scull_quantum;retval = __get_user(scull_quantum, (int __user *)arg);if (retval = = 0)
retval = __put_user(tmp, (int __user *)arg);break;
case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */if (! capable (CAP_SYS_ADMIN))
return -EPERM;tmp = scull_quantum;scull_quantum = arg;return tmp;
default: /* redundant, as cmd was checked against MAXNR */return -ENOTTY;
}return retval;
嵌入式及平行系統實驗室
實作 ioctl 指令範例 (3)
由 user-space 觀點來看int quantum;ioctl(fd,SCULL_IOCSQUANTUM, &quantum); /* Set by pointer */ioctl(fd,SCULL_IOCTQUANTUM, quantum); /* Set by value */ioctl(fd,SCULL_IOCGQUANTUM, &quantum); /* Get by pointer */quantum = ioctl(fd,SCULL_IOCQQUANTUM); /* Get by return value */ioctl(fd,SCULL_IOCXQUANTUM, &quantum); /* Exchange by pointer */quantum = ioctl(fd,SCULL_IOCHQUANTUM, quantum); /* Exchange by
value */
嵌入式及平行系統實驗室
除了 ioctl 之外的裝置控制法指令導向式控制法 (command-oriented)
優點:簡便,使用者只要寫入特殊資料 ( 指令 ) 就能控制裝置,而不需要使用額外的工具程式 缺點:在操作法則 (policy)上有所限制,才能避免將一般資料當成控制命令來處理
對於不會傳輸資料,只會對命令作出回應的裝置( 例如 : 機械手臂 ) ,指令導向式控制法就肯定是最理想的選擇
如果目標裝置適合使用指令導向式的控制法,驅動程式自然不必提供 ioctl ,而是一段能解讀控制指令的程式 (interpreter)
嵌入式及平行系統實驗室
Blocking I/O
read 被觸發時,還沒有資料可提供,但是即將有更多資料到齊 write 被觸發時,目標裝置還沒準備好接受資料,因為暫存區己滿 可用 block 的方式,使其進入休眠,直到滿足作業為止 休眠 (sleep) :休眠狀態的行程,會被移出排程器的運行佇列 (run queue)
在連動環境 (atomic context) 下,絕對不能休眠, 醒來之後,人事全非。你不可能知道自己被 cpu撇下多久,也不知道休眠過程中發生什麼變化 有把握不會一睡不醒。得確定要等待的事件一定會發生,或至少某人會在某處喚醒你
待命佇列 (wait queue) ,一系列的休眠行程,由待命佇列首項 (wait queue head, wqh) 來管理 #include <linux/wait.h> 編釋期的靜態方式
DECLARE_WAIT_QUEUE_HEAD(name); 執行程的動態配置方式
wait_queue_head_t name;Init_waitqueue_head(&name);
嵌入式及平行系統實驗室
簡易休眠 wait_event(queue, condition)( 不可中斷 ) wait_event_interruptible(queue, condition)( 可中斷 )
queue 是所要使用的待命佇列頭 (pass by value) condition 是休眠條件,可以是任何布林結果運算,條件成立則結束休眠;會在休眠之前與之後各被執行一次 wait_event_interruptible 會回傳值,若該值不為 0 表示有某種中斷,則驅動程式或許應該回傳 -ERESTARTSYS
wait_event_timeout(queue, condition, timeout) wait_event_interruptible_timeout(queue, condition, timeout)
會等待一段有限的時間,不管 condition 的計算結果如何,傳回值固定是 0 Timeout 是以 jiffies 為單位 (開機至今,計時器中斷的次數 )
void wake_up(wait_queue_head_t *queue); 喚醒在 queue 的所有行程
void wake_up_interruptible(wait_queue_head_t *queue); 只喚醒當初願意因中斷事件而甦醒的行程
嵌入式及平行系統實驗室
簡易休眠範例static DECLARE_WAIT_QUEUE_HEAD(wq);static int flag = 0;ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos){
printk(KERN_DEBUG “process %i (%s) going to sleep\n”, current->pid, current->comm);
wait_event_interruptible(wq, flag != 0);flag = 0;printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);return 0; /* EOF */
}ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,loff_t *pos){
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",current->pid, current->comm);
flag = 1;wake_up_interruptible(&wq);return count; /* succeed, to avoid retrial */
}此程式可能有相競現象
嵌入式及平行系統實驗室
遲滯 (blocking) 作業模式 (1)
檢查 filp->f_flags 的 O_NONBLOCK 旗標 定義在 <linux/fs> 引入的 <linux/fcntl.h> 可藉由此旗標來表達是否同意休眠的意願 O_NDELAY 同義 O_NONBLOCK 預設值為 0( 代表 blocking)
read 作業方法 當輸入緩衝區空了,還沒有資料可提供給 user-space ,必須讓行程休眠 當資料到達,必須立刻喚醒行程,將資料傳到 user-space ,資料量可少於行程要求的量
write 作業方法 當輸出緩衝區己沒有空間了,必須讓行程休眠,但不能與
read 同一個待命佇列 當輸出緩衝區挪出空間,必須立刻喚醒行程,將 user-space的資料傳輸到輸出緩衝區,傳輸資料量可少於要求的量
嵌入式及平行系統實驗室
遲滯 (blocking) 作業模式 (2)
驅動程式自備 I/O緩衝區輸入緩衝區
用來暫存來自硬體的資料,其作用讓行程不必等待就可取走資料 也避免漏失了硬體進來的資料
輸出緩衝區 較不重要,因為 write 不會丟失資料 但其效率增益比輸入緩衝區大,可以大幅減少
context switch 和 user-level / kernel-level 過渡
嵌入式及平行系統實驗室
非遲滯 (non-blocking) 作業模式主要讓應用程式可以輪詢 (poll) 資料O_NONBLOCK 被設立時, read() 無法即時提供資料 ( 輸入緩衝區空 ) 和 write 輸出動作會造成休眠
( 輸出緩衝區滿 ) ,則須回傳 -EAGAIN(try again)只有 read 、 write 、 open 會被影響
嵌入式及平行系統實驗室
輪詢作業 (poll 與 select)(1)
會使用 non-blocking I/O 的應用程式,通常會使用到 select() 、 poll() 、epoll() 系統呼叫。 同樣都是判斷下次對於特定檔案的 I/O 作業會不會 blocking 也可以阻檔一個行程,直到給定一組 FD成為可供讀寫的狀態為止
呼叫一或多次 poll_wait() ,將一或多個可能改變輪詢狀態的待命佇列放入輪詢表 (poll table) 。若當時還沒有可供 I/O 的 FD ,核心會使行程休眠於待命佇列,等待所有 FD都傳送給系統呼叫 傳回一個位元遮罩,描述哪些操作項目可立即執行而不會 blocking 輪詢狀態資訊只有驅動程式自己知道,核心無法得知 unsigned int (*poll) (struct file *filp, poll_table *wait);
wait :指向輪詢表 (poll table) 指標 ( 定義於 <linux/poll.h>) ,可以不必了解 poll_table 結構,只為配合 poll_wait 而需要 void poll_wait (struct file *filp, wait_queue_head_t *sync, poll_table
*pt); 讓驅動程式將每一個會喚醒行程 ( 或改變 poll 作業狀態 ) 的待命佇列項目 (sync)填入 pt所指的輪詢表
嵌入式及平行系統實驗室
輪詢作業 (poll 與 select)(2)
#incldude <linux/poll.h> 定義一系列旗標來建立狀態遮罩: POLLIN :裝置可被 read 而不 blocking POLLRENORM :平常資料己準備好可被讀取。可被正常讀取的裝置,應傳回 (POLLIN | POLLRDNORM) POLLPRI :可讀出高度優先度資料 (緊急資料 ) 而不必
blocking POLLHUP :每當讀取裝置的行程見到 EOF ,驅動程式就必須設立 POLLHUP 位元 (hang-up) POLLER :裝置發生錯誤狀況 POLLOUT :若裝置可被逕行寫入而不 blocking ,則應設此 POLLWRNORM :和 POLLOUT 意義相同。可寫入的裝置應該傳回 (POLLOUT | POLLWRNORM) POLLRDBAND :代表可從裝置讀出緊急 (out-of-band) 資料 POLLWRBAND :如同 POLLRDBAND ,代表優先度不為 0的資料可被寫入裝置
POLLRDBAND 和 POLLWRBAND 只對 socket 有意義
嵌入式及平行系統實驗室
輪詢作業 (poll 與 select)(3)
static unsigned int scull_p_poll(struct file *filp, poll_table *wait){
struct scull_pipe *dev = filp->private_data;unsigned int mask = 0;/*緩衝區是環狀的若 wp 與 rp 指向同一個位置,表示緩衝區是空的 */down(&dev->sem);poll_wait(filp, &dev->inq, wait);poll_wait(filp, &dev->outq, wait);if (dev->rp != dev->wp)
mask |= POLLIN | POLLRDNORM; /* readable */if (spacefree(dev))
mask |= POLLOUT | POLLWRNORM; /* writable */up(&dev->sem);return mask;
}
嵌入式及平行系統實驗室
讀、寫、輪詢的作業準則 從裝置讀出資料
如果資料己在輸入緩衝區裡, read 應該在感覺不出延遲的時間內,傳回資料。即使不能滿足應用程式要求的量或是即將有資料到達。 poll 應回傳 POLLIN | POLLRDNORM 輸入緩衝區為空的, read 的預設行為應該 blocking ,直到至少傳來
1byte 的資料為止。如設立了 O_NONBLOCK ,則應該立即 return –EAGAIN 。 poll 應回傳不可讀狀態 ( 將 POLLIN 與 POLLRDNORM歸零 )
如遇 EOF ,必須立即回傳 0 。 poll 應該回傳 POLLHUP 將資料寫入裝置
如果輸出緩衝區有可用空間, write 立刻完成 return ,毫無延遲。即使不能滿足應用程式要求的量。 poll 應回傳 POLLOUT | POLLWRNORM
輸出緩衝區為滿的, write 的預設行為應該 blocking ,直到緩衝區有空位為止。如設立了 O_NONBLOCK ,則應該立即 return –EAGAIN 。poll 應回傳不可寫狀態
即使在 blocking 作業模式下,也絕不可讓 write() 因為等待資料傳輸而可返回 user-space 。如果應用程式希望能確定排入輸出緩衝區的資料,己經全數寫到硬體裝置上,驅動程式應提供 fsync 作業方法
嵌入式及平行系統實驗室
出清延宕資料fsync 系統呼叫 return 時,就可認定先前用 write()寫出的資料己經全數出清 (flush) 到裝置上int (*fsync) (struct file *file, struct dentry *dentry,
int datasync); datasync :用於區別 fsync() 或 fdatasync() 系統呼叫
嵌入式及平行系統實驗室
臨時通知 (asynchronous notification)
可在輸入檔 (包括裝置檔 ) 有異動時,對應用程式發出SIGIO信號
要獲得臨時通知的程式1. 讓自己成為檔案的擁有者;可用 fcntl() 系統呼叫的
F_SETOWN 來完成,該指令會將行程的 PID存入 filp->f_owner
2. 啟動臨時通知:使用 fcntl() 系統呼叫發出 F_SETFL 指令,設定裝置的 FASYNC 指標 收到信號的行程,不知道信號的來源
signal(SIGIO, &input_handler); /* 簡單示範。 sigaction( ) 會更好 */fcntl(STDIN_FILENO, F_SETOWN, getpid( ));oflags = fcntl(STDIN_FILENO, F_GETFL);fcntl(STDIN_FILENO, F_SETFL, oflags | FASYNC);
嵌入式及平行系統實驗室
驅動程式對「臨時通知」的支援 (1)
從核心觀點來看 收到 fcntl() 系統呼叫的 F_SETOWN 指令時,將目前行程的 PID 設定給
filp->f_owner 收到 fcntl() 系統呼叫的 F_SETFL 指令來打開 filp->f_flags 的 FASYNC 旗標時,則呼叫 fasync 作業方法。每當 filp->f_flags 的 FASYNC 旗標出現變化時,核心就會觸發一次 fasync ,該驅程程式作出適當回應。以預設模式開啟檔案時, FASYNC 應為 0 當資料到達,必須送出 SIGIO信號給曾登記要求收到臨時通知的所有行程
Linux 實作通知機制主要由 struct fasync_struct 結構與兩個函式構成 #include <linux/fs.h> int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
• 驅動程式發覺己開啟的裝置檔的 FASYNC 旗標出現變化,就可使用fasync_helper() 將行程移出或編入通知名單
void kill_fasync(struct fasync_struct **fa, int sig, int band);• 收到裝置傳來的資料時,則可用 kill_fasync() ,將信號送給通知名單的中的行程• sig :被送出的信號值 (SIGIO)• band :緊急度 (POLL_IN)
嵌入式及平行系統實驗室
驅動程式對「臨時通知」的支援 (2)
fasync作業方法static int scull_p_fasync(int fd, struct file *filp, int mode){
struct scull_pipe *dev = filp->private_data;return fasync_helper(fd, filp, mode, &dev->async_queue);
}當有新資料寫入時,送出 SIGIO信號給等待通知的所有行程
if (dev->async_queue)kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
將這個 filp移出臨時通知名單scull_p_fasync(-1, filp, 0);
嵌入式及平行系統實驗室
定位作業發生 lseek() 或 llseek() 系統呼叫時,都會觸動驅動程式的 lseek 作業方法。如定位作業涉及實際硬體的動作,則必須提
供 lseek 作業方法對於只有串流的字元裝置 ( 序列埠、鍵盤 ) ,定位沒有意義,但不宣告 llseek 作業方法是沒用的。應呼叫 nonseekable_open() 該核心知道你的裝置不支援 llseek 作業方法 int nonseekable_open(struct inode *inode; struct file *filp);
會將指定的 filp 標示為無法定位 當核心收到 lseek() 或 llseek() 系統呼叫時,會回覆一個錯誤代碼
為完整起見,將 llseek 函式指向 no_llseek()( 定義於<linux/fs.h>)
嵌入式及平行系統實驗室
定位作業範例loff_t scull_llseek(struct file *filp, loff_t off, int whence){
struct scull_dev *dev = filp->private_data;loff_t newpos;switch(whence) {
case 0: /* SEEK_SET */newpos = off;break;
case 1: /* SEEK_CUR */newpos = filp->f_pos + off;break;
case 2: /* SEEK_END */newpos = dev->size + off;break;
default: /* can't happen */return -EINVAL;
}if (newpos < 0) return -EINVAL;filp->f_pos = newpos;return newpos;
}