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)用. 如果這個 tty 驅(qū)動不知道如何處理傳遞給它的 ioctl 值, 它應(yīng)當(dāng)返回 -ENOIOCTLCMD 來試圖讓 tty 核心實(shí)現(xiàn)一個通用的調(diào)用版本.

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

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

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


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). 一個指向 struct serial_struct 的指針被傳遞給這個調(diào)用, 填滿這個 tty 設(shè)備應(yīng)當(dāng)被設(shè)置的數(shù)據(jù). 如果這個 tty 驅(qū)動沒有實(shí)現(xiàn)這個調(diào)用, 大部分程序仍然正確工作.

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

當(dāng)實(shí)現(xiàn)這個 ioctl 時要小心, 并且不要使用 interruptible_sleep_on 調(diào)用, 因?yàn)樗遣话踩?有很多不好的競爭條件涉及它). 相反, 一個 wait_queue 應(yīng)當(dāng)用來避免這個問題. 這是一個如何實(shí)現(xiàn)這個 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ū)動的代碼中能知道 MSR 寄存器改變的某些地方, 下面的代碼行必須調(diào)用以便這個代碼能正常工作:


wake_up_interruptible(&tp->wait);

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

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


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

公眾號
微信公眾號

編程獅公眾號