SlideShare une entreprise Scribd logo
1  sur  33
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 sleepn”, 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;
}

Contenu connexe

En vedette

Lights in world
Lights in worldLights in world
Lights in worldAlka Sahni
 
Life insurance after retirement
Life insurance after retirementLife insurance after retirement
Life insurance after retirementInfiniteYou
 
JOJOB_forum PA Challenge a #SCE2014-1
JOJOB_forum PA Challenge a #SCE2014-1JOJOB_forum PA Challenge a #SCE2014-1
JOJOB_forum PA Challenge a #SCE2014-1Cristina Costanzo
 
Lu siau vay_616_wds_
Lu siau vay_616_wds_Lu siau vay_616_wds_
Lu siau vay_616_wds_Vay Lu
 
Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?
Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?
Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?experiannederland
 
ApresentaMilenniumPrime
ApresentaMilenniumPrimeApresentaMilenniumPrime
ApresentaMilenniumPrimeAndre Santos
 
Photos from gaza(1)
Photos from gaza(1)Photos from gaza(1)
Photos from gaza(1)Dolfi Diwald
 
tik icha smpit rpi
tik icha smpit rpi tik icha smpit rpi
tik icha smpit rpi ichaa17
 
2014 08-06 briley
2014 08-06 briley2014 08-06 briley
2014 08-06 brileyRespect1
 
Slideshare presentation XIAXIONG
Slideshare presentation XIAXIONGSlideshare presentation XIAXIONG
Slideshare presentation XIAXIONGSean Xiong
 

En vedette (20)

Agenda and list
Agenda and list Agenda and list
Agenda and list
 
Lights in world
Lights in worldLights in world
Lights in world
 
Balance of payments
Balance of paymentsBalance of payments
Balance of payments
 
Creative, Digital & Design Business Briefing - August 2015
Creative, Digital & Design Business Briefing - August 2015Creative, Digital & Design Business Briefing - August 2015
Creative, Digital & Design Business Briefing - August 2015
 
Psy final (1)
Psy final (1)Psy final (1)
Psy final (1)
 
Life insurance after retirement
Life insurance after retirementLife insurance after retirement
Life insurance after retirement
 
Forever Living Products… where ordinary people achieve extraordinary results
Forever Living Products… where ordinary people achieve extraordinary resultsForever Living Products… where ordinary people achieve extraordinary results
Forever Living Products… where ordinary people achieve extraordinary results
 
JOJOB_forum PA Challenge a #SCE2014-1
JOJOB_forum PA Challenge a #SCE2014-1JOJOB_forum PA Challenge a #SCE2014-1
JOJOB_forum PA Challenge a #SCE2014-1
 
Lu siau vay_616_wds_
Lu siau vay_616_wds_Lu siau vay_616_wds_
Lu siau vay_616_wds_
 
Cot safety
Cot safetyCot safety
Cot safety
 
Cvs
CvsCvs
Cvs
 
Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?
Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?
Experian lunchsessie 18 juli: Hoe zet ik mijn klantdata om in klantwaarde?
 
ApresentaMilenniumPrime
ApresentaMilenniumPrimeApresentaMilenniumPrime
ApresentaMilenniumPrime
 
Tik1
Tik1Tik1
Tik1
 
Photos from gaza(1)
Photos from gaza(1)Photos from gaza(1)
Photos from gaza(1)
 
Creative Business Development Briefing - February 2015
Creative Business Development Briefing - February 2015Creative Business Development Briefing - February 2015
Creative Business Development Briefing - February 2015
 
tik icha smpit rpi
tik icha smpit rpi tik icha smpit rpi
tik icha smpit rpi
 
2014 08-06 briley
2014 08-06 briley2014 08-06 briley
2014 08-06 briley
 
Slideshare presentation XIAXIONG
Slideshare presentation XIAXIONGSlideshare presentation XIAXIONG
Slideshare presentation XIAXIONG
 
Cs437 lecture 09
Cs437 lecture 09Cs437 lecture 09
Cs437 lecture 09
 

Similaire à Device Driver - Chapter 6字元驅動程式的進階作業

Device Driver - Chapter 3字元驅動程式
Device Driver - Chapter 3字元驅動程式Device Driver - Chapter 3字元驅動程式
Device Driver - Chapter 3字元驅動程式ZongYing Lyu
 
Arduino應用系統設計 - Arduino程式快速入門
Arduino應用系統設計 - Arduino程式快速入門Arduino應用系統設計 - Arduino程式快速入門
Arduino應用系統設計 - Arduino程式快速入門吳錫修 (ShyiShiou Wu)
 
线程与并发
线程与并发线程与并发
线程与并发Tony Deng
 
Android 源码分析 -- (一) Android启动过程
Android 源码分析 -- (一) Android启动过程Android 源码分析 -- (一) Android启动过程
Android 源码分析 -- (一) Android启动过程manateew
 
Keep your code clean
Keep your code cleanKeep your code clean
Keep your code cleanmacrochen
 
嵌入式inux應用專題文件-智慧家庭系統
嵌入式inux應用專題文件-智慧家庭系統嵌入式inux應用專題文件-智慧家庭系統
嵌入式inux應用專題文件-智慧家庭系統艾鍗科技
 
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutorInside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutorAdy Liu
 
Erlang jiacheng
Erlang jiachengErlang jiacheng
Erlang jiachengAir-Smile
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)Simen Li
 
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack FirmwareSimen Li
 
181201_CoAP_coding365
181201_CoAP_coding365181201_CoAP_coding365
181201_CoAP_coding365Peter Yi
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍gowell
 
ch13-pv1-system-calls
ch13-pv1-system-callsch13-pv1-system-calls
ch13-pv1-system-callsyushiang fu
 
Java memory model
Java memory modelJava memory model
Java memory modelShawn Wang
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程Bobby Zhou
 
Erlang Practice
Erlang PracticeErlang Practice
Erlang Practicelitaocheng
 
高效能執行緒
高效能執行緒高效能執行緒
高效能執行緒Rick Wu
 
Avm2虚拟机浅析与as3性能优化(陈士凯)
Avm2虚拟机浅析与as3性能优化(陈士凯)Avm2虚拟机浅析与as3性能优化(陈士凯)
Avm2虚拟机浅析与as3性能优化(陈士凯)FLASH开发者交流会
 

Similaire à Device Driver - Chapter 6字元驅動程式的進階作業 (20)

Device Driver - Chapter 3字元驅動程式
Device Driver - Chapter 3字元驅動程式Device Driver - Chapter 3字元驅動程式
Device Driver - Chapter 3字元驅動程式
 
Arduino應用系統設計 - Arduino程式快速入門
Arduino應用系統設計 - Arduino程式快速入門Arduino應用系統設計 - Arduino程式快速入門
Arduino應用系統設計 - Arduino程式快速入門
 
Arduino程式快速入門
Arduino程式快速入門Arduino程式快速入門
Arduino程式快速入門
 
线程与并发
线程与并发线程与并发
线程与并发
 
Android 源码分析 -- (一) Android启动过程
Android 源码分析 -- (一) Android启动过程Android 源码分析 -- (一) Android启动过程
Android 源码分析 -- (一) Android启动过程
 
Keep your code clean
Keep your code cleanKeep your code clean
Keep your code clean
 
嵌入式inux應用專題文件-智慧家庭系統
嵌入式inux應用專題文件-智慧家庭系統嵌入式inux應用專題文件-智慧家庭系統
嵌入式inux應用專題文件-智慧家庭系統
 
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutorInside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
Inside.java.concurrency 35.thread pool.part8_future.scheduledthreadpoolexecutor
 
Erlang jiacheng
Erlang jiachengErlang jiacheng
Erlang jiacheng
 
Java Thread
Java ThreadJava Thread
Java Thread
 
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)
 
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
[ZigBee 嵌入式系統] ZigBee Architecture 與 TI Z-Stack Firmware
 
181201_CoAP_coding365
181201_CoAP_coding365181201_CoAP_coding365
181201_CoAP_coding365
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍
 
ch13-pv1-system-calls
ch13-pv1-system-callsch13-pv1-system-calls
ch13-pv1-system-calls
 
Java memory model
Java memory modelJava memory model
Java memory model
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程
 
Erlang Practice
Erlang PracticeErlang Practice
Erlang Practice
 
高效能執行緒
高效能執行緒高效能執行緒
高效能執行緒
 
Avm2虚拟机浅析与as3性能优化(陈士凯)
Avm2虚拟机浅析与as3性能优化(陈士凯)Avm2虚拟机浅析与as3性能优化(陈士凯)
Avm2虚拟机浅析与as3性能优化(陈士凯)
 

Plus de ZongYing Lyu

Performance improvement techniques for software distributed shared memory
Performance improvement techniques for software distributed shared memoryPerformance improvement techniques for software distributed shared memory
Performance improvement techniques for software distributed shared memoryZongYing Lyu
 
Architecture of the oasis mobile shared virtual memory system
Architecture of the oasis mobile shared virtual memory systemArchitecture of the oasis mobile shared virtual memory system
Architecture of the oasis mobile shared virtual memory systemZongYing Lyu
 
A deep dive into energy efficient multi core processor
A deep dive into energy efficient multi core processorA deep dive into energy efficient multi core processor
A deep dive into energy efficient multi core processorZongYing Lyu
 
Libckpt transparent checkpointing under unix
Libckpt transparent checkpointing under unixLibckpt transparent checkpointing under unix
Libckpt transparent checkpointing under unixZongYing Lyu
 
Web coding principle
Web coding principleWeb coding principle
Web coding principleZongYing Lyu
 
提高 Code 品質心得
提高 Code 品質心得提高 Code 品質心得
提高 Code 品質心得ZongYing Lyu
 
Consistency protocols
Consistency protocolsConsistency protocols
Consistency protocolsZongYing Lyu
 
Compiler optimization
Compiler optimizationCompiler optimization
Compiler optimizationZongYing Lyu
 
MPI use c language
MPI use c languageMPI use c language
MPI use c languageZongYing Lyu
 
Parallel program design
Parallel program designParallel program design
Parallel program designZongYing Lyu
 

Plus de ZongYing Lyu (14)

Vue.js
Vue.jsVue.js
Vue.js
 
Performance improvement techniques for software distributed shared memory
Performance improvement techniques for software distributed shared memoryPerformance improvement techniques for software distributed shared memory
Performance improvement techniques for software distributed shared memory
 
Architecture of the oasis mobile shared virtual memory system
Architecture of the oasis mobile shared virtual memory systemArchitecture of the oasis mobile shared virtual memory system
Architecture of the oasis mobile shared virtual memory system
 
A deep dive into energy efficient multi core processor
A deep dive into energy efficient multi core processorA deep dive into energy efficient multi core processor
A deep dive into energy efficient multi core processor
 
Libckpt transparent checkpointing under unix
Libckpt transparent checkpointing under unixLibckpt transparent checkpointing under unix
Libckpt transparent checkpointing under unix
 
Web coding principle
Web coding principleWeb coding principle
Web coding principle
 
提高 Code 品質心得
提高 Code 品質心得提高 Code 品質心得
提高 Code 品質心得
 
SCRUM
SCRUMSCRUM
SCRUM
 
Consistency protocols
Consistency protocolsConsistency protocols
Consistency protocols
 
Compiler optimization
Compiler optimizationCompiler optimization
Compiler optimization
 
MPI use c language
MPI use c languageMPI use c language
MPI use c language
 
Parallel program design
Parallel program designParallel program design
Parallel program design
 
MPI
MPIMPI
MPI
 
OpenMP
OpenMPOpenMP
OpenMP
 

Device Driver - Chapter 6字元驅動程式的進階作業

  • 1. Chapter 6 字元驅動程式的進階作業 Speaker :呂宗螢 Adviser :梁文耀 老 師 Date : 2007/1/29
  • 2. 嵌入式及平行系統 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 取得 該引數
  • 3. 嵌入式及平行系統 選擇 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)
  • 4. 嵌入式及平行系統 選擇 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() 的慣例,應用指標來交換數值
  • 5. 嵌入式及平行系統 選擇 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
  • 6. 嵌入式及平行系統 ioctl 的回傳值 如果 cmd 不符合任何命令編號,則 default 應做?  許多核心合適採取的行為是回傳 -EINVAL(Invalid Argument)  但, POSIX 標準規定回傳 -ENOTTY(Not a typewriter) , C 函式庫的解釋為 Inappropriate ioctl for device  常用為 -EINVAL
  • 7. 嵌入式及平行系統 預先定義 ioctl 指令 (1) 雖然 ioctl( ) 系統呼叫的主要作用對象是硬體裝置, 但是核心本身仍能辨認少數幾個命令 ( 預設指 令 ) 。因此,當你挑選的 ioctl 指令編號剛好與預定 指令相同,則你寫出來的 ioctl 作業方法將永遠收 不到該指令,而應用程式也會因為發出衝突的 ioctl 指令而遭遇到意外。 預設指令分為三大類:  可作用於任何 案檔 ( 正常檔 , 裝置檔 , FTFO 或 socket)  只對正常 案有作用檔  只能用於特定 案系統類型檔
  • 8. 嵌入式及平行系統 預先定義 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 指令來 改變
  • 9. 嵌入式及平行系統 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( )( 記憶體存取程 序會幫忙處理 )
  • 10. 嵌入式及平行系統 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; }
  • 11. 嵌入式及平行系統 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
  • 12. 嵌入式及平行系統 機能管制 (1) 使用者能否存取裝置,需借助作業系統的權限控管 機制 ( 限制對象為人 ) 對於限制對象是操作項目而言,驅動程式自己必須 作一些額外檢 ,判斷使用者是否有權操作要求機查 能 機能 (capabilities) ,不在只分成「特權」與「非特 權」,而細分成更多類細目:  可將某種開放給某特定程式 ( 或使用者 ) ,而不必將 無關的其他權力也一併交出  核心只將機能用於權限管理,並釋出兩個 linux 特有 的系統呼叫, capget() 與 capset()
  • 13. 嵌入式及平行系統 機能管制 (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;
  • 14. 嵌入式及平行系統 實作 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;
  • 15. 嵌入式及平行系統 實作 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;
  • 16. 嵌入式及平行系統 實作 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 */
  • 17. 嵌入式及平行系統 除了 ioctl 之外的裝置控制法 指令導向式控制法 (command-oriented)  優點:簡便,使用者只要寫入特殊資料 ( 指令 ) 就能 控制裝置,而不需要使用額外的工具程式  缺點:在操作法則 (policy) 上有所限制,才能避免 將一般資料當成控制命令來處理 對於不會傳輸資料,只會對命令作出回應的裝置 ( 例如 : 機械手臂 ) ,指令導向式控制法就肯定是最 理想的選擇 如果目標裝置適合使用指令導向式的控制法,驅動 程式自然不必提供 ioctl ,而是一段能解讀控制指 令的程式 (interpreter)
  • 18. 嵌入式及平行系統 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);
  • 19. 嵌入式及平行系統 簡易休眠  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);  只喚醒當初願意因中斷事件而甦醒的行程
  • 20. 嵌入式及平行系統 簡易休眠範例 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 sleepn”, 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 */ } 此程式可能有相競現象
  • 21. 嵌入式及平行系統 遲滯 (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 的資料傳輸到輸出緩衝區,傳輸資料量可少於要求的量
  • 22. 嵌入式及平行系統 遲滯 (blocking) 作業模式 (2) 驅動程式自備 I/O 緩衝區 輸入緩衝區  用來暫存來自硬體的資料,其作用讓行程不必等待 就可取走資料  也避免漏失了硬體進來的資料 輸出緩衝區  較不重要,因為 write 不會丟失資料  但其效率增益比輸入緩衝區大,可以大幅減少 context switch 和 user-level / kernel-level 過渡
  • 23. 嵌入式及平行系統 非遲滯 (non-blocking) 作業模式 主要讓應用程式可以輪詢 (poll) 資料 O_NONBLOCK 被設立時, read() 無法即時提供 資料 ( 輸入緩衝區空 ) 和 write 輸出動作會造成休眠 ( 輸出緩衝區滿 ) ,則須回傳 -EAGAIN(try again) 只有 read 、 write 、 open 會被影響
  • 24. 嵌入式及平行系統 輪詢作業 (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 所指的輪詢表
  • 25. 嵌入式及平行系統 輪詢作業 (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 有意義
  • 26. 嵌入式及平行系統 輪詢作業 (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; }
  • 27. 嵌入式及平行系統 讀、寫、輪詢的作業準則  從裝置讀出資料  如果資料己在輸入緩衝區裡, 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 作業方法
  • 28. 嵌入式及平行系統 出清延宕資料 fsync 系統呼叫 return 時,就可認定先前用 write() 寫出的資料己經全數出清 (flush) 到裝置上 int (*fsync) (struct file *file, struct dentry *dentry, int datasync);  datasync :用於區別 fsync() 或 fdatasync() 系統呼 叫
  • 29. 嵌入式及平行系統 臨時通知 (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);
  • 30. 嵌入式及平行系統 驅動程式對「臨時通知」的支援 (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)
  • 31. 嵌入式及平行系統 驅動程式對「臨時通知」的支援 (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);
  • 32. 嵌入式及平行系統 定位作業 發生 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>)
  • 33. 嵌入式及平行系統 定位作業範例 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; }