?? tty_ioctl.c
字號:
/* passed* linux/kernel/chr_drv/tty_ioctl.c** (C) 1991 Linus Torvalds*/#include <set_seg.h>
#include <errno.h> // 錯誤號頭文件。包含系統中各種出錯號。(Linus 從minix 中引進的)。#include <termios.h> // 終端輸入輸出函數頭文件。主要定義控制異步通信口的終端接口。#include <linux/sched.h> // 調度程序頭文件,定義了任務結構task_struct、初始任務0 的數據,// 還有一些有關描述符參數設置和獲取的嵌入式匯編函數宏語句。#include <linux/kernel.h> // 內核頭文件。含有一些內核常用函數的原形定義。#include <linux/tty.h> // tty 頭文件,定義了有關tty_io,串行通信方面的參數、常數。#include <asm/io.h> // io 頭文件。定義硬件端口輸入/輸出宏匯編語句。#include <asm/segment.h> // 段操作頭文件。定義了有關段寄存器操作的嵌入式匯編函數。#include <asm/system.h> // 系統頭文件。定義了設置或修改描述符/中斷門等的嵌入式匯編宏。// 這是波特率因子數組(或稱為除數數組)。波特率與波特率因子的對應關系參見列表后的說明。static unsigned short quotient[] = { 0, 2304, 1536, 1047, 857, 768, 576, 384, 192, 96, 64, 48, 24, 12, 6, 3};//// 修改傳輸速率。// 參數:tty - 終端對應的tty 數據結構。// 在除數鎖存標志DLAB(線路控制寄存器位7)置位情況下,通過端口0x3f8 和0x3f9 向UART 分別寫入// 波特率因子低字節和高字節。static voidchange_speed (struct tty_struct *tty){ unsigned short port, quot;// 對于串口終端,其tty 結構的讀緩沖隊列data 字段存放的是串行端口號(0x3f8 或0x2f8)。 if (!(port = tty->read_q.data)) return;// 從tty 的termios 結構控制模式標志集中取得設置的波特率索引號,據此從波特率因子數組中取得// 對應的波特率因子值。CBAUD 是控制模式標志集中波特率位屏蔽碼。 quot = quotient[tty->termios.c_cflag & CBAUD]; cli (); // 關中斷。 outb_p (0x80, port + 3); /* set DLAB */// 首先設置除數鎖定標志DLAB。 outb_p (quot & 0xff, port); /* LS of divisor */// 輸出因子低字節。 outb_p (quot >> 8, port + 1); /* MS of divisor */// 輸出因子高字節。 outb (0x03, port + 3); /* reset DLAB */// 復位DLAB。 sti (); // 開中斷。}//// 刷新tty 緩沖隊列。// 參數:gueue - 指定的緩沖隊列指針。// 令緩沖隊列的頭指針等于尾指針,從而達到清空緩沖區(零字符)的目的。static voidflush (struct tty_queue *queue){ cli (); queue->head = queue->tail; sti ();}//// 等待字符發送出去。static voidwait_until_sent (struct tty_struct *tty){/* do nothing - not implemented *//* 什么都沒做 - 還未實現 */}//// 發送BREAK 控制符。static voidsend_break (struct tty_struct *tty){/* do nothing - not implemented *//* 什么都沒做 - 還未實現 */}//// 取終端termios 結構信息。// 參數:tty - 指定終端的tty 結構指針;termios - 用戶數據區termios 結構緩沖區指針。// 返回0 。static intget_termios (struct tty_struct *tty, struct termios *termios){ int i;// 首先驗證一下用戶的緩沖區指針所指內存區是否足夠,如不夠則分配內存。 verify_area (termios, sizeof (*termios));// 復制指定tty 結構中的termios 結構信息到用戶 termios 結構緩沖區。 for (i = 0; i < (sizeof (*termios)); i++) put_fs_byte (((char *) &tty->termios)[i], i + (char *) termios); return 0;}//// 設置終端termios 結構信息。// 參數:tty - 指定終端的tty 結構指針;termios - 用戶數據區termios 結構指針。// 返回0 。static intset_termios (struct tty_struct *tty, struct termios *termios){ int i;// 首先復制用戶數據區中termios 結構信息到指定tty 結構中。 for (i = 0; i < (sizeof (*termios)); i++) ((char *) &tty->termios)[i] = get_fs_byte (i + (char *) termios);// 用戶有可能已修改了tty 的串行口傳輸波特率,所以根據termios 結構中的控制模式標志c_cflag// 修改串行芯片UART 的傳輸波特率。 change_speed (tty); return 0;}//// 讀取termio 結構中的信息。// 參數:tty - 指定終端的tty 結構指針;termio - 用戶數據區termio 結構緩沖區指針。// 返回0。static intget_termio (struct tty_struct *tty, struct termio *termio){ int i; struct termio tmp_termio;// 首先驗證一下用戶的緩沖區指針所指內存區是否足夠,如不夠則分配內存。 verify_area (termio, sizeof (*termio));// 將termios 結構的信息復制到termio 結構中。目的是為了其中模式標志集的類型進行轉換,也即// 從termios 的長整數類型轉換為termio 的短整數類型。 tmp_termio.c_iflag = tty->termios.c_iflag; tmp_termio.c_oflag = tty->termios.c_oflag; tmp_termio.c_cflag = tty->termios.c_cflag; tmp_termio.c_lflag = tty->termios.c_lflag;// 兩種結構的c_line 和c_cc[]字段是完全相同的。 tmp_termio.c_line = tty->termios.c_line; for (i = 0; i < NCC; i++) tmp_termio.c_cc[i] = tty->termios.c_cc[i];// 最后復制指定tty 結構中的termio 結構信息到用戶 termio 結構緩沖區。 for (i = 0; i < (sizeof (*termio)); i++) put_fs_byte (((char *) &tmp_termio)[i], i + (char *) termio); return 0;}/** This only works as the 386 is low-byt-first*//** 下面的termio 設置函數僅在386 低字節在前的方式下可用。*///// 設置終端termio 結構信息。// 參數:tty - 指定終端的tty 結構指針;termio - 用戶數據區termio 結構指針。// 將用戶緩沖區termio 的信息復制到終端的termios 結構中。返回0 。static intset_termio (struct tty_struct *tty, struct termio *termio){ int i; struct termio tmp_termio;// 首先復制用戶數據區中termio 結構信息到臨時termio 結構中。 for (i = 0; i < (sizeof (*termio)); i++) ((char *) &tmp_termio)[i] = get_fs_byte (i + (char *) termio);// 再將termio 結構的信息復制到tty 的termios 結構中。目的是為了其中模式標志集的類型進行轉換,// 也即從termio 的短整數類型轉換成termios 的長整數類型。 *(unsigned short *) &tty->termios.c_iflag = tmp_termio.c_iflag; *(unsigned short *) &tty->termios.c_oflag = tmp_termio.c_oflag; *(unsigned short *) &tty->termios.c_cflag = tmp_termio.c_cflag; *(unsigned short *) &tty->termios.c_lflag = tmp_termio.c_lflag;// 兩種結構的c_line 和c_cc[]字段是完全相同的。 tty->termios.c_line = tmp_termio.c_line; for (i = 0; i < NCC; i++) tty->termios.c_cc[i] = tmp_termio.c_cc[i];// 用戶可能已修改了tty 的串行口傳輸波特率,所以根據termios 結構中的控制模式標志集c_cflag// 修改串行芯片UART 的傳輸波特率。 change_speed (tty); return 0;}//// tty 終端設備的ioctl 函數。// 參數:dev - 設備號;cmd - ioctl 命令;arg - 操作參數指針。inttty_ioctl (int dev, int cmd, int arg){ struct tty_struct *tty;// 首先取tty 的子設備號。如果主設備號是5(tty 終端),則進程的tty 字段即是子設備號;如果進程// 的tty 子設備號是負數,表明該進程沒有控制終端,也即不能發出該ioctl 調用,出錯死機。 if (MAJOR (dev) == 5) { dev = current->tty; if (dev < 0) panic ("tty_ioctl: dev<0");// 否則直接從設備號中取出子設備號。 } else dev = MINOR (dev);// 子設備號可以是0(控制臺終端)、1(串口1 終端)、2(串口2 終端)。// 讓tty 指向對應子設備號的tty 結構。 tty = dev + tty_table;// 根據tty 的ioctl 命令進行分別處理。 switch (cmd) { case TCGETS://取相應終端termios 結構中的信息。 return get_termios (tty, (struct termios *) arg); case TCSETSF:// 在設置termios 的信息之前,需要先等待輸出隊列中所有數據處理完,并且刷新(清空)輸入隊列。// 再設置。 flush (&tty->read_q); /* fallthrough */ case TCSETSW:// 在設置終端termios 的信息之前,需要先等待輸出隊列中所有數據處理完(耗盡)。對于修改參數// 會影響輸出的情況,就需要使用這種形式。 wait_until_sent (tty); /* fallthrough */ case TCSETS:// 設置相應終端termios 結構中的信息。 return set_termios (tty, (struct termios *) arg); case TCGETA:// 取相應終端termio 結構中的信息。 return get_termio (tty, (struct termio *) arg); case TCSETAF:// 在設置termio 的信息之前,需要先等待輸出隊列中所有數據處理完,并且刷新(清空)輸入隊列。// 再設置。 flush (&tty->read_q); /* fallthrough */ case TCSETAW:// 在設置終端termio 的信息之前,需要先等待輸出隊列中所有數據處理完(耗盡)。對于修改參數// 會影響輸出的情況,就需要使用這種形式。 wait_until_sent (tty); /* fallthrough *//* 繼續執行 */ case TCSETA:// 設置相應終端termio 結構中的信息。 return set_termio (tty, (struct termio *) arg); case TCSBRK:// 等待輸出隊列處理完畢(空),如果參數值是0,則發送一個break。 if (!arg) { wait_until_sent (tty); send_break (tty); } return 0; case TCXONC:// 開始/停止控制。如果參數值是0,則掛起輸出;如果是1,則重新開啟掛起的輸出;如果是2,則掛起// 輸入;如果是3,則重新開啟掛起的輸入。 return -EINVAL; /* not implemented *//* 未實現 */ case TCFLSH://刷新已寫輸出但還沒發送或已收但還沒有讀數據。如果參數是0,則刷新(清空)輸入隊列;如果是1,// 則刷新輸出隊列;如果是2,則刷新輸入和輸出隊列。 if (arg == 0) flush (&tty->read_q); else if (arg == 1) flush (&tty->write_q); else if (arg == 2) { flush (&tty->read_q); flush (&tty->write_q); } else return -EINVAL; return 0; case TIOCEXCL:// 設置終端串行線路專用模式。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCNXCL:// 復位終端串行線路專用模式。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCSCTTY:// 設置tty 為控制終端。(TIOCNOTTY - 禁止tty 為控制終端)。 return -EINVAL; /* set controlling term NI *//* 設置控制終端NI */ case TIOCGPGRP: // NI - Not Implemented。// 讀取指定終端設備進程的組id。首先驗證用戶緩沖區長度,然后復制tty 的pgrp 字段到用戶緩沖區。 verify_area ((void *) arg, 4); put_fs_long (tty->pgrp, (unsigned long *) arg); return 0; case TIOCSPGRP:// 設置指定終端設備進程的組id。 tty->pgrp = get_fs_long ((unsigned long *) arg); return 0; case TIOCOUTQ:// 返回輸出隊列中還未送出的字符數。首先驗證用戶緩沖區長度,然后復制隊列中字符數給用戶。 verify_area ((void *) arg, 4); put_fs_long (CHARS (tty->write_q), (unsigned long *) arg); return 0; case TIOCINQ:// 返回輸入隊列中還未讀取的字符數。首先驗證用戶緩沖區長度,然后復制隊列中字符數給用戶。 verify_area ((void *) arg, 4); put_fs_long (CHARS (tty->secondary), (unsigned long *) arg); return 0; case TIOCSTI:// 模擬終端輸入。該命令以一個指向字符的指針作為參數,并假裝該字符是在終端上鍵入的。用戶必須// 在該控制終端上具有超級用戶權限或具有讀許可權限。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCGWINSZ:// 讀取終端設備窗口大小信息(參見termios.h 中的winsize 結構)。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCSWINSZ:// 設置終端設備窗口大小信息(參見winsize 結構)。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCMGET:// 返回modem 狀態控制引線的當前狀態比特位標志集(參見termios.h 中185-196 行)。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCMBIS:// 設置單個modem 狀態控制引線的狀態(true 或false)。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCMBIC:// 復位單個modem 狀態控制引線的狀態。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCMSET:// 設置modem 狀態引線的狀態。如果某一比特位置位,則modem 對應的狀態引線將置為有效。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCGSOFTCAR:// 讀取軟件載波檢測標志(1 - 開啟;0 - 關閉)。 return -EINVAL; /* not implemented *//* 未實現 */ case TIOCSSOFTCAR:// 設置軟件載波檢測標志(1 - 開啟;0 - 關閉)。 return -EINVAL; /* not implemented *//* 未實現 */ default: return -EINVAL; }}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -