18.4. ioctls 函數(shù)

2018-02-24 15:50 更新

18.4.?ioctls 函數(shù)

在 struct tty_driver 中的 ioctl 函數(shù)被 tty 核心調(diào)用當(dāng) ioctl(2) 被在設(shè)備節(jié)點(diǎn)上調(diào)用. 如果這個(gè) tty 驅(qū)動(dòng)不知道如何處理傳遞給它的 ioctl 值, 它應(yīng)當(dāng)返回 -ENOIOCTLCMD 來試圖讓 tty 核心實(shí)現(xiàn)一個(gè)通用的調(diào)用版本.

2.6 內(nèi)核定義了大約 70 個(gè)不同的 tty ioctls, 可被用來發(fā)送給一個(gè) tty 驅(qū)動(dòng). 大部分的 tty 驅(qū)動(dòng)不處理它們?nèi)? 但是只有一個(gè)小的更普通的子集. 這是一個(gè)更通用的 tty ioctls 列表, 它們的含義, 以及如何實(shí)現(xiàn)它們:

TIOCSERGETLSR
獲得這個(gè) tty 設(shè)備的線路狀態(tài)寄存器( LSR )的值.

TIOCGSERIAL
獲得串口線信息. 調(diào)用者可以潛在地從 tty 設(shè)備獲得許多串口線路信息, 在這個(gè)調(diào)用中一次全部. 一些程序( 例如 setserial 和 dip) 調(diào)用這個(gè)函數(shù)來確保波特率被正確設(shè)置, 以及來獲得通常的關(guān)于驅(qū)動(dòng)控制的設(shè)備類型信息. 調(diào)用者傳遞一個(gè)指向一個(gè)大的 serial_struct 結(jié)構(gòu)的指針, 這個(gè)結(jié)構(gòu)應(yīng)當(dāng)由 tty 驅(qū)動(dòng)填充正確的值. 這是一個(gè)如何實(shí)現(xiàn)這個(gè)的例子:


static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
        struct tiny_serial *tiny = tty->driver_data;
        if (cmd == TIOCGSERIAL)
        {
                struct serial_struct tmp;
                if (!arg)
                        return -EFAULT;
                memset(&tmp, 0, sizeof(tmp));
                tmp.type  = tiny->serial.type;
                tmp.line  = tiny->serial.line;
                tmp.port  = tiny->serial.port;
                tmp.irq  = tiny->serial.irq;
                tmp.flags  = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;

                tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
                tmp.baud_base = tiny->serial.baud_base;
                tmp.close_delay = 5*HZ;
                tmp.closing_wait = 30*HZ;
                tmp.custom_divisor = tiny->serial.custom_divisor;
                tmp.hub6 = tiny->serial.hub6;
                tmp.io_type = tiny->serial.io_type;
                if (copy_to_user((void __user *)arg, &tmp, sizeof(tmp)))

                        return -EFAULT;
                return 0;
        }
        return -ENOIOCTLCMD;
}

TIOCSSERIAL
設(shè)置串口線路信息. 這是 IOCGSERIAL 的反面, 并且允許用戶一次全部設(shè)置 tty 設(shè)備的串口線狀態(tài). 一個(gè)指向 struct serial_struct 的指針被傳遞給這個(gè)調(diào)用, 填滿這個(gè) tty 設(shè)備應(yīng)當(dāng)被設(shè)置的數(shù)據(jù). 如果這個(gè) tty 驅(qū)動(dòng)沒有實(shí)現(xiàn)這個(gè)調(diào)用, 大部分程序仍然正確工作.

TIOCMIWAIT
等待 MSR 改變. 用戶在非尋常的情況下請求這個(gè) ioctl, 它想在內(nèi)核中睡眠直到這個(gè) tty 設(shè)備的 MSR 寄存器發(fā)生某些事情. arg 參數(shù)包含用戶在等待的事件類型. 這通常用來等待直到一個(gè)狀態(tài)線變化, 指示有更多的數(shù)據(jù)發(fā)送給設(shè)備.

當(dāng)實(shí)現(xiàn)這個(gè) ioctl 時(shí)要小心, 并且不要使用 interruptible_sleep_on 調(diào)用, 因?yàn)樗遣话踩?有很多不好的競爭條件涉及它). 相反, 一個(gè) wait_queue 應(yīng)當(dāng)用來避免這個(gè)問題. 這是一個(gè)如何實(shí)現(xiàn)這個(gè) ioctl 的例子:


static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
        struct tiny_serial *tiny = tty->driver_data;
        if (cmd == TIOCMIWAIT)
        {

                DECLARE_WAITQUEUE(wait, current);
                struct async_icount cnow;
                struct async_icount cprev;
                cprev = tiny->icount;
                while (1) {

                        add_wait_queue(&tiny->wait, &wait);
                        set_current_state(TASK_INTERRUPTIBLE);
                        schedule();
                        remove_wait_queue(&tiny->wait, &wait); /* see if a signal woke us up */
                        if (signal_pending(current))
                                return -ERESTARTSYS;
                        cnow = tiny->icount;
                        if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
                                        cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
                                return -EIO; /* no change => error */
                        if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
                                return 0;
                        }
                        cprev = cnow;

                }
        }
        return -ENOIOCTLCMD;

}

在 tty 驅(qū)動(dòng)的代碼中能知道 MSR 寄存器改變的某些地方, 下面的代碼行必須調(diào)用以便這個(gè)代碼能正常工作:


wake_up_interruptible(&tp->wait);

TIOCGICOUNT
獲得中斷計(jì)數(shù). 當(dāng)用戶要知道已經(jīng)產(chǎn)生多少串口線中斷時(shí)調(diào)用. 如果驅(qū)動(dòng)有一個(gè)中斷處理, 它應(yīng)當(dāng)定義一個(gè)內(nèi)部計(jì)數(shù)器結(jié)構(gòu)來跟蹤這些統(tǒng)計(jì)和遞增適當(dāng)?shù)挠?jì)數(shù)器, 每次這個(gè)函數(shù)被內(nèi)核運(yùn)行時(shí).

這個(gè) ioctl 調(diào)用傳遞內(nèi)核一個(gè)指向結(jié)構(gòu) serial_icounter_struct 的指針, 它應(yīng)當(dāng)被 tty 驅(qū)動(dòng)填充. 這個(gè)調(diào)用常常和之前的 IOCMIWAIT ioctl 調(diào)用結(jié)合使用. 如果 tty 驅(qū)動(dòng)跟蹤所有的這些中斷在驅(qū)動(dòng)操作時(shí), 實(shí)現(xiàn)這個(gè)調(diào)用的代碼會(huì)非常簡單:


static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
        struct tiny_serial *tiny = tty->driver_data;
        if (cmd == TIOCGICOUNT)
        {
                struct async_icount cnow = tiny->icount;
                struct serial_icounter_struct icount;
                icount.cts = cnow.cts;
                icount.dsr = cnow.dsr;
                icount.rng = cnow.rng;
                icount.dcd = cnow.dcd;
                icount.rx = cnow.rx;
                icount.tx = cnow.tx;
                icount.frame = cnow.frame;
                icount.overrun = cnow.overrun;
                icount.parity = cnow.parity;
                icount.brk = cnow.brk;
                icount.buf_overrun = cnow.buf_overrun;
                if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))

                        return -EFAULT;
                return 0;
        }
        return -ENOIOCTLCMD;
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號