(/device/console操控臺原理分析,通過調(diào)用此操控臺來輸出信息,同時這兒觸及到/device/console調(diào)用TTY,然后TTY調(diào)用低層串口的分析 安桌LOG輸出原理)
LINUX內(nèi)核源碼版本:linux⑶.0.86
/dev/console即控制臺,是與操作系統(tǒng)交互的裝備,系統(tǒng)將1些調(diào)試信息直接輸出到控制臺上,是TTY裝備的1個子集
Tty:teltypewriters Teletypes簡稱電傳打印機:終端是1種字符型裝備,它有多種類型,通常使用tty來簡稱各種類型的終端裝備。Console是TTY裝備的1種。Console調(diào)用到最低層就是TTY裝備驅(qū)動。
TTY CONSOLE UART關(guān)系圖(由我的平臺有4路串口,要分析到USER空間/dev/console最后是通過那1路口輸出的,注意和內(nèi)核空間的printk的console操控臺輸出端口是不是1致,分析原理。由前面幾節(jié)分析可知printk輸出是由uboot傳入的參數(shù)來肯定那1路串口輸出的的。Console=ttySAC0,115200n8第0路)
分析代碼前根據(jù)網(wǎng)絡(luò)資料得出的1張圖:
自己分析代碼后得到的圖:
紅色部份是分析了所有代碼后得出的結(jié)論:
下面進程很雜亂,到現(xiàn)在還是沒有分析清楚,只能是明白了個大概,由于結(jié)構(gòu)體太多,沒交繁復(fù)雜。現(xiàn)在還是亂。只能再次分析1下流程(只針對/dev/console的寫進程。我們要知道用戶空間的console是支持輸入輸出的。Kernel空音的console只支持輸出信息。這是很重要的區(qū)分。)。
/dev/console
裝備打開。得到tty_struct結(jié)構(gòu)體,并且賦值在file中1個private_data中。同時由于
struct tty_driver *console_driver = console_device(&index);得來是由uboot傳入的參數(shù)來決定的。因此這兒/dev/console輸出0串口來決定。由其它節(jié)部份的參數(shù)解析部份得知。
struct tty_driver *console_driver = console_device(&index)->console_drivers(內(nèi)核層register_console注冊函數(shù)時1個指針鏈表)->driver = c->device(c, index);
寫操作//把數(shù)據(jù)寫入file文件中的private_data指向的tty_struct中的1xmit變量(這兒有好層轉(zhuǎn)換),同時打開中斷。產(chǎn)生中斷條件。
Write->[ld->ops->write=n_tty_write]->[tty->ops=driver->ops]=[tty_operations uart_ops]=[.write= uart_write,]
3.中斷發(fā)送://中斷的注冊是在open函數(shù)中履行的。這兒自動過來履行的。
ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);---->
s3c24xx_serial_tx_chars->wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);發(fā)送數(shù)據(jù)
//下面是上面結(jié)論分析進程。
/dev/console字符裝備注冊流程分析:
#define fs_initcall(fn)__define_initcall("5",fn,5)
chr_dev_init->tty_init->MKDEV(TTYAUX_MAJOR, 1)->console_fops操作函數(shù)
static const struct file_operationsconsole_fops = {
.llseek= no_llseek,
.read= tty_read,
.write= redirected_tty_write,
.poll= tty_poll,
.unlocked_ioctl= tty_ioctl,
.compat_ioctl= tty_compat_ioctl,
.open= tty_open,
.release= tty_release,
.fasync= tty_fasync,
};
完成了對裝備的注冊,然后是打開裝備和讀寫裝備了,這個部份應(yīng)當(dāng)是用戶空間來完成的。我們不去看相干代碼,只從通用的打開讀寫通用操作方法來作分析。Open write read這3個函數(shù)由用戶空間來調(diào)用。當(dāng)用戶空間調(diào)打開讀寫函數(shù)時會調(diào)用上面注冊的驅(qū)動的操作函數(shù)對應(yīng)的函數(shù)。所以接下來分析Open write read
Open:tty_open
struct tty_driver *console_driver = console_device(&index)->console_drivers(內(nèi)核層register_console注冊函數(shù)時1個指針鏈表)->driver = c->device(c, index);
retval = tty_alloc_file(filp);
struct tty_driver *console_driver = console_device(&index);
tty = tty_init_dev(driver, index, 0);//這個函數(shù)里去構(gòu)建LDISC結(jié)構(gòu)體了,叫做線規(guī)程/線路規(guī)程line discipline
/*
//ldisc構(gòu)建進程:
tty_init_dev(driver, index, 0);//tty_driver 0 0
initialize_tty_struct(tty, driver, idx);//tty_struct tty_driver 0
tty_ldisc_init(tty);
Ld->ops=tty_ldiscs[disc]=tty_ldiscs[0]
/*
ty_ldiscs怎樣得來的是關(guān)鍵:
start_kernel->console_init()->/* Setup the default TTY line discipline. */
tty_ldisc_begin();->/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);->->
N_TTY=0
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
.read = n_tty_read,
.write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
tty_ldiscs[disc] = new_ldisc;
tty_ldiscs[0] = tty_ldisc_N_TTY ;等于上面TTY裝備
通過上面分析可知
Ld->ops=tty_ldiscs[disc]=tty_ldiscs[0]=tty_ldisc_N_TTY
*/
tty->ldisc = ld;
tty_add_file(tty, filp);
struct tty_file_private *priv = file->private_dat=tty_struct;因此通過file文件中的private_data來操作tty_sruct。這個結(jié)構(gòu)體時里面包括tty_driver tty->ldisc多個變量。
*/
tty_add_file(tty, filp);
上面代碼完成通過文件的struct tty_file_private *priv = file->private_data變量來保存tty_struct(同時TTY結(jié)構(gòu)體包括tty_drivers)驅(qū)動變量。
我們代碼的console_drivers以下:
static struct console s3c24xx_serial_console = {
.name= S3C24XX_SERIAL_NAME,
.device=uart_console_device,
.flags= CON_PRINTBUFFER,
.index= ⑴,
.write= s3c24xx_serial_console_write,
.setup= s3c24xx_serial_console_setup,
.data= &s3c24xx_uart_drv,
};
struct tty_driver *uart_console_device(struct console *co, int *index)
{
struct uart_driver *p = co->data;
*index = co->index;//0通過UBOOT傳入?yún)?shù)等到0值
returnp->tty_driver;
}
static struct uart_driver s3c24xx_uart_drv = {
.owner= THIS_MODULE,
.driver_name= "s3c2410_serial",
.nr= CONFIG_SERIAL_SAMSUNG_UARTS,
#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE_SWITCH
.cons= NULL,
#else
.cons= S3C24XX_SERIAL_CONSOLE,
#endif
.dev_name= S3C24XX_SERIAL_NAME,
.major= S3C24XX_SERIAL_MAJOR,
.minor= S3C24XX_SERIAL_MINOR,
};
p->tty_driver是s3c24xx_uart_drv結(jié)構(gòu)體的成員,但在上面數(shù)組的初始化中并沒有初始化此值,因此可以肯定是在在其它地方初始化的tty_driver的。
下面來分析tty_driver的初始化流程:
s3c24xx_serial_modinit->uart_register_driver(&s3c24xx_uart_drv)->:
struct tty_driver *normal;
drv->tty_driver = normal;
tty_set_operations(normal, &uart_ops); //driver->ops = op;
/*
static const struct tty_operations uart_ops = {
.open= uart_open,
.close= uart_close,
.write= uart_write,
.put_char= uart_put_char,
.flush_chars= uart_flush_chars,
.write_room= uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer= uart_flush_buffer,
.ioctl= uart_ioctl,
.throttle= uart_throttle,
.unthrottle= uart_unthrottle,
.send_xchar= uart_send_xchar,
.set_termios= uart_set_termios,
.set_ldisc= uart_set_ldisc,
.stop= uart_stop,
.start= uart_start,
.hangup= uart_hangup,
.break_ctl= uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops= &uart_proc_fops,
#endif
.tiocmget= uart_tiocmget,
.tiocmset= uart_tiocmset,
.get_icount= uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init= uart_poll_init,
.poll_get_char= uart_poll_get_char,
.poll_put_char= uart_poll_put_char,
#endif
};
tty->ops = driver->ops;
struct tty_driver *driver;
struct tty_driver *console_driver = console_device(&index);
tty->ops=driver->ops->console_drivers->driver = c->device(c, index)->
tty_operations uart_ops=tty_operations uart_ops;--->tty->ops=driver->ops=tty_operations uart_ops
*/
通過上面代碼分析可知:用操作/dev/console寫函數(shù)時
.write= redirected_tty_write,
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
struct tty_struct *tty = file_tty(file);//包括tty_driver結(jié)構(gòu)體。
ld = tty_ldisc_ref_wait(tty);
ld->ops->write//////因此找到這個函數(shù)才是我們的根本,看是如何實現(xiàn)的。????????????????
通過前面分析知:
Ld->ops=tty_ldiscs[disc]=tty_ldiscs[0]=tty_ldisc_N_TTY
因此ld->ops->write=n_tty_write//tty_ldisc_N_TTY中的函數(shù)完成線程規(guī)程檢查
最后還是調(diào)用tty->ops->write(tty, b, nr);來輸出數(shù)據(jù)。規(guī)則檢查部份不能把數(shù)據(jù)輸出的串口
綜上:對/dev/console裝備的寫函數(shù)的履行流程以下:
Write->[ld->ops->write=n_tty_write]->[tty->ops=driver->ops]=[tty_operations uart_ops]=[.write= uart_write,]
static int uart_write(struct tty_struct *tty,
const unsigned char *buf, int count)
__uart_start(tty);
port->ops->start_tx(port);//ops= s3c24xx_serial_ops;
static struct uart_ops s3c24xx_serial_ops = {
.pm= s3c24xx_serial_pm,
.tx_empty= s3c24xx_serial_tx_empty,
.get_mctrl= s3c24xx_serial_get_mctrl,
.set_mctrl= s3c24xx_serial_set_mctrl,
.stop_tx= s3c24xx_serial_stop_tx,
.start_tx= s3c24xx_serial_start_tx,
.stop_rx= s3c24xx_serial_stop_rx,
.enable_ms= s3c24xx_serial_enable_ms,
.break_ctl= s3c24xx_serial_break_ctl,
.startup= s3c24xx_serial_startup,
.shutdown= s3c24xx_serial_shutdown,
.set_termios= s3c24xx_serial_set_termios,
.type= s3c24xx_serial_type,
.release_port= s3c24xx_serial_release_port,
.request_port= s3c24xx_serial_request_port,
.config_port= s3c24xx_serial_config_port,
.verify_port= s3c24xx_serial_verify_port,
.wake_peer= s3c24xx_serial_wake_peer,
};
s3c24xx_serial_start_tx//這個函數(shù)是打開中斷 我們/dev/console應(yīng)當(dāng)是通過中斷來把數(shù)據(jù)發(fā)送出去的。數(shù)據(jù)存在struct circ_buf xmit結(jié)構(gòu)體中。這個函數(shù)只是把數(shù)據(jù)緩存在變量中和打開串口,發(fā)送數(shù)據(jù)是在中斷中進行的。tty->ops->open(tty, filp);====s3c24xx_serial_startup打開中斷ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);注冊串口發(fā)送函數(shù)。
static irqreturn_t
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
struct s3c24xx_uart_port *ourport = id;
struct circ_buf *xmit = &port->state->xmit;//這個是在s3c24xx_serial_start_tx中去指定的。
/*
ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);*/
上面中斷函數(shù)的id與/dev/console的tty_drvier是同1個變量。所以在觸發(fā)中斷發(fā)送時能正確的發(fā)送數(shù)據(jù)。wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
/dev/tty字符裝備注冊流程分析:
#define fs_initcall(fn)__define_initcall("5",fn,5)
chr_dev_init->tty_init->MKDEV(TTYAUX_MAJOR, 0)->tty_fops
上面只是注冊了1個tty裝備。Tty實際上是1多個的。最后是tty_register_driver-》for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);}-》device_create(tty_class, device, dev, NULL, name);
上面完成多個裝備的注冊tty裝備的注冊,這兒主要是串串的。所有的tty1字符輸輸入輸出裝備都可以用tty_register_driver來注冊。可以注冊多個。上面/dev/console只是利用了其中的1類來解決問題。Tty裝備是可以單獨來解決問題的。用tty_register_driver注冊。但我們分析的串口部份是用的tty_init來注冊裝備。然后通過打開函數(shù)來與tty_struct和tty_drvier關(guān)聯(lián)解決問題。大致分析了代碼發(fā)/dev/conosle/ /dev/tty邏輯差不多。細(xì)節(jié)就不去分析了。可以明確的是對這兩個裝備的輸入輸出都是由uboot傳入?yún)?shù)來決是那1個口的。前1個用于系統(tǒng)信息調(diào)試 ,后1個應(yīng)當(dāng)是作其它功能。對這裝備當(dāng)作1般裝備來理解。只是加入很多中間層,所以分析起來比較麻煩。暫不去細(xì)究。分析了好幾天還是亂。不是清晰的邏輯。
static const struct file_operations tty_fops = {
.llseek= no_llseek,
.read= tty_read,
.write= tty_write,
.poll= tty_poll,
.unlocked_ioctl
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機掃描二維碼進行捐贈