As an embedded product, the company uses the ARM kernel and Linux operating system (not uClinux ). My recent work is to mount a previously prepared module (via serial port for communication) to the system, and control a series of work of this module through serial port, and require
Into a separate driver (not controlled by an application ). I also want to familiarize myself with the development method of the device driver in Linux. The Development Board of another company we bought, Linux, can now run, but there are basically no technical support and documentation. I have some knowledge about Linux recently, but I am confused about the relationship between tty devices, consoles, and serial ports. Here are a few questions:
1. What is the hierarchical relationship between tty, console, and serial ports in Linux? What is the specific function interface? How is the serial port called?
2. Does the printk function send information to the console? How can I allow printk to send information via serial port? Or where does the system decide whether to send the information to the monitor or serial port?
3. The printk function (like printk (linux_banner or something) was used at the beginning of start_kernel. At this time, the entire kernel has not run yet. How is the printk called at this time? In our system, the system is started with the modern company bootloader program, and later it seems to jump to the head-armv.s under Linux, and then jump to start_kernel, In the bootloader serial port has been available, after entering the kernel, do you want to reset it?
The above questions may be messy, because I am also confused about the relationship between tty, console, and serial, especially how the serial port is called. There is a small amount of information in this area (I have mentioned a little about the situation analysis ).
Thank you very much for your advice! ========================================================== ============================================== See after your questions, I felt very typical. So I took some time to read it and posted some tips here. Welcome to discuss and correct me:
1. What is the hierarchical relationship between tty, console, and serial ports in Linux? What is the specific function interface? How is the serial port called?
TTY and console are concepts of virtual devices, while serial ports refer to a real device driver.
Tty is actually an abstraction of A Class of terminal I/O devices. It is actually more of a management concept, IT and tty_ldisc (line of procedure) and tty_driver (real device driver) the combination aims to provide a unified interface for the VFS at the upper layer. You can configure tty_ioctl in the file_operations structure. Check tty_driver. You will get n results. Actually, they are all related chip drivers. Therefore, we can conclude that (the actual situation is much more complex than this ): the tty_struct of each description tty device is bound to be a character Device Driver (not necessarily a character device driver) of a specific chip during initialization, which can be many, including the video card or serial chip. I don't know what your arm SOC is, but you should use a common chip. These drivers are actually available.
The console is a buffer concept, and its purpose is similar to TTY. In fact, the console is not only connected with tty, but also with framebuffer. The specific reason is that the keyboard interrupt handling process is shown below. A subset of tty must use the console (for example, master device number 4 and secondary device number 1-64). However, note that tty does not exist.
The serial port refers to tty_driver.
A typical example:
Analyze the keyboard interrupt handling process:
Keyboard_interrupt-> handle_kbd_event-> handle_keyboard_event-> handle_scancode
Void handle_scancode (unsigned char scancode, int down)
{
........
Tty = ttytab? Ttytab [fg_console]: NULL;
If (TTY &&(! Tty-> driver_data )){
...............
Tty = NULL;
}
.............
Schedule_console_callback ();
}
Two points in this Code are worth noting, that is, in addition to obtaining tty records (using the Global tty record), the console also displays schedule_console_callback. The relationship between TTY and console is already clear !!!
2. Does the printk function send information to the console? How can I allow printk to send information via serial port? Or where does the system decide whether to send the information to the monitor or serial port?
Let's take a look at the implementation of the printk function. printk does not necessarily output information to the console. Setting the startup parameters of the kernel may bring the information to the display.
There is an English paragraph before the function, which is very interesting:
/* This is printk. It can be called from any context. We want it to work.
*
* We try to grab the console_sem. If we succeed, it's easy-we log the output and
* Call the console drivers. If we fail to get the semaphore we place the output
* Into the log buffer and return. The current holder of the console_sem will
* Notice the new output in release_console_sem () and will send it to
* Using LES before releasing the semaphore.
*
* One effect of this deferred printing is that code which callprintk () and
* Then changes console_loglevel may break. This is because console_loglevel
* Is inspected when the actual printing occurs.
*/
To operate the console, you must first obtain the lele_sem semaphore. If you get the lele_sem semaphore, you can "log the output and call the console drivers". Otherwise, "place the output into the log buffer and return" is actually in the Code:
Asmlinkage int printk (const char * FMT ,...)
{
Va_list ARGs;
Unsigned long flags;
Int printed_len;
Char * P;
Static char printk_buf [1024];
Static int log_level_unknown = 1;
If (oops_in_progress) {/* if it is set to 1, the crush occurs */
/* If a crash is occurring, make sure we can't deadlock */
Spin_lock_init (& logbuf_lock );
/* And make sure that we print immediately */
Init_mutex (& console_sem );
}
/* This stops the holder of console_sem just where we want him */
Spin_lock_irqsave (& logbuf_lock, flags );
/* Emit the output into the temporary buffer */
Va_start (ARGs, FMT );
Printed_len = vsnprintf (printk_buf, sizeof (printk_buf), FMT, argS);/* process the passed buffer. Note that it is not
Actually write to the terminal, only parse the format of the input string */
Va_end (ARGs );
/* Copy the output into log_buf. If the caller didn't provide appropriate Log Level tags, we insert them here */
/* The comment is clear */
For (P = printk_buf; * P; P ++ ){
If (log_level_unknown ){
If (P [0]! = '<' | P [1] <'0' | P [1]> '7' | P [2]! = '> '){
Emit_log_char ('<');
Emit_log_char (default_message_loglevel + '0 ');
Emit_log_char ('> ');
}
Log_level_unknown = 0;
}
Emit_log_char (* P );
If (* P = '/N ')
Log_level_unknown = 1;
}
If (! Arch_consoles_callable ()){
/* On some ubuntures, the specified les are not usable on secondary CPUs early in the boot process .*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
Goto out;
}
If (! Down_trylock (& console_sem )){
/* We own the drivers. We can drop the spinlock and let release_lele_sem () print the text */
Spin_unlock_irqrestore (& logbuf_lock, flags );
Lele_may_schedule = 0;
Release_console_sem ();
} Else {
/* Someone else owns the drivers. we drop the spinlock, which allows the semaphore holder
Proceed and to call the console drivers with the output which we just produced .*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
}
Out:
Return printed_len;
}
In fact, printk puts the string after format into a buffer and then shows it when appropriate. This also answers the reason why the printk function was used at the beginning of start_kernel.
3. The printk function (like printk (linux_banner or something) was used at the beginning of start_kernel. At this time, the entire kernel has not run yet. How is the printk called at this time? In our system, the system is started with the modern company bootloader program, and later seems to jump to the head-armv.s under Linux, and then jump to start_kernel, In the bootloader serial port has been available, after entering the kernel, do you want to reset it?
Generally, bootloader performs some basic initialization, copies the kernel to the physical space, and then jumps to the kernel for execution. It is certain that the kernel must re-set the serial port, because there are many types of bootloader, some do not necessarily set the serial port, and the kernel cannot rely on bootloader. ========================================================== =========================================================Thank you upstairs, the analysis is brilliant. I am reading the printk function.
The CPU we use is Hynix's hms7202. Serial Port 0 is used on the evaluation board.
Console, all the information in the startup process is sent through this serial port.
In bootloader, The ser_printf function is defined to interact through the serial port.
But I still don't want to understand that the console and serial port are not yet jumped to the Linux kernel.
How does printk work during initialization? I have read start_kernel
Console initialization (and tracking through the Super Terminal)
It is in the lele_init function, and the serial port Initialization is actually in the 1
In the process (init-> do_basic_setup-> do_initcils-> rs_init ),
How does prink work before the serial port is initialized? In particular
Start_kernel has printk (linux_banner) at the beginning.
The serial port and console have not been initialized yet. ========================================================== =======================================================1. at the beginning of start_kernel, printk (linux_banner) exists. At this time, the serial port and console have not been initialized yet?
After careful analysis, printk can answer this question. In the Code:
/* Emit the output into the temporary buffer */
Va_start (ARGs, FMT );
Printed_len = vsnprintf (printk_buf, sizeof (printk_buf), FMT, argS );
Va_end (ARGs );
Put the input in printk_buf.
For (P = printk_buf; * P; P ++ ){
If (log_level_unknown ){
If (P [0]! = '<' | P [1] <'0' | P [1]> '7' | P [2]! = '> '){
Emit_log_char ('<');
Emit_log_char (default_message_loglevel + '0 ');
Emit_log_char ('> ');
}
Log_level_unknown = 0;
}
Emit_log_char (* P );
If (* P = '/N ')
Log_level_unknown = 1;
}
The content in printk_buf is parsed and put into the global log_buf (in the emit_log_char function.
And the following
If (! Down_trylock (& console_sem )){
/*
* We own the drivers. We can drop the spinlock and let
* Release_console_sem () print the text
*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
Lele_may_schedule = 0;
Release_console_sem ();
} Else {
/*
* Someone else owns the drivers. we drop the spinlock, which
* Allows the semaphore holder to proceed and to call
* Console drivers with the output which we just produced.
*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
}
Release_console_sem () is called Based on down_trylock (& console_sem) results. In release_console_sem (), the console device driver corresponding to the content in the global log_buf is processed.
Now, we can draw the following conclusions:
(1) The main operation of printk is actually for a buffer (log_buf). Whether the content in the buffer is displayed (or output to the terminal) depends on whether the console_sem can be obtained.
(2) The file where printk is located is printk. C, which is unrelated to the architecture, so it is the same for any platform.
The speculative conclusion is:
(1) When the kernel is initialized, console_sem is marked as locked. Therefore, in the printk (linux_banner) at the beginning of start_kernel, only the input is written to the buffer. After the serial port and console are initialized, the call to printk outputs the content in the buffer to the string port and console only once.
(2) In the initialization process of the serial port and console, there must be an up operation for console_sem.
(3) Therefore, in Embedded debugging, if the system has a problem before the initialization of the console, there will be no output. Only LEDs or jtags can be used.
(4) Therefore, you can see the answer to your question.
2. Console initialization.
I don't know which kernel version you are using. In 2.4.18 and 2.4.19 I see, the console is initialized in start_kernel. According to the previous analysis, the initialization of the console should not be too late; otherwise, log_buf may overflow. ========================================================== =========================================================Thank you upstairs, wonderful analysis!
The kernel version we use is 2.4.18, And the console Initialization is indeed in
Start_kernel-> console-> init.
For TTY and serial ports, I would like to ask again here. Tty device Operation Entry
Yes
Static struct file_operations tty_fops = {
Llseek: no_llseek,
Read: tty_read,
Write: tty_write,
Poll: tty_poll,
IOCTL: tty_ioctl,
Open: tty_open,
Release: tty_release,
Fasync: tty_fasync,
};
Serial port operations are defined:
Static struct tty_driver serial_driver.
Most functions in serial. C are filled with function pointers in serial_driver.
Therefore, when performing serial port operations, you must first call the operations in tty_fops (for example
Tty_open and so on), and then to the specific serial port operation (rs_open, etc?
However, there are many function pointers in tty_driver (the serial port is serial_driver ).
It does not correspond to the function pointer in file_operations.
How is the failed operation executed? For example, put_char, flush_char, read_proc,
Write_proc, start, stop, etc. ========================================================== ========================================================
Here are some of my understandings about this problem:
This actually returns to the old problem, that is, the relationship between TTY and tty_driver. From the implementation point of view, tty_driver is actually one of the implementation components of the TTY mechanism. The common examples in the design of borrowed surface to object are as follows: tty_driver is like a tire of the TTY car, tty is running properly, and it also requires infrastructure such as tty_ldisc, termios, and even struct tq_struct tq_hangup (tty_struct. The relationship between them is not inherited.
As for the function pointer in tty_driver, let's call another analogy in C ++. They are actually like virtual functions, that is, they can be defined, but they are not necessarily implemented. In fact, not to mention tty_driver. As long as you check serial_driver, multiple implementations of N will be found, but functions of tty_driver may not be fully implemented for specific devices. Therefore, put_char, flush_char, read_proc, write_proc, start, and stop functions may be implemented or not implemented. Even if implemented, it is not necessarily used by the upper layer (VFS Layer.