Current location - Quotes Website - Team slogan - Embedded linux asks if the serial console works.
Embedded linux asks if the serial console works.
What is the hierarchical relationship between 1 and TTY, console and serial port under LINUX? What is the specific functional interface? What is the serial port called?

2. The 2.printk function sends information to the console, right? How to make PRINTK send information through serial port? Or where does the system decide whether to send information to the monitor or serial port?

3.start_kernel used the printk function from the beginning (like printk(linux_banner or something). At this time, the whole kernel is not running yet. So what is printk called at this time? In our system, the BOOTLOADER program of Hyundai Company is used to start the system. Later, it seems to jump to head-armv.s under LINUX, and then jump to start_kernel. There is already a serial port in bootloader, so do you want to reset it after entering the kernel?

The above questions may be confusing, because my own brain is also confusing, mainly about the relationship between TTY, console and serial port, especially how the serial port is called. There is little information in this respect (just a little in the scene analysis). I hope the expert can give me some advice, thank you very much!

I'm also doing this recently, and I'm also writing a driver for serial devices.

I've been doing it for almost a month, and everything is done by looking for information on the Internet and looking at the source code.

But it's still messy. I still don't understand some questions. Hope to discuss together

In /proc/device (I remember correctly, it should be this file)

There is a driver named serial in it, and its master device number is 4 and slave device number is 64- 12X (I remember correctly that it should be within this range).

As we all know, the serial port's secondary device number starts from 64, and the serial port 1 /dev/ttyS0 corresponds to the secondary device number 64, and the serial port 2 corresponds to 65.

The problem is that my computer only has two serial ports now. Why did it register the device number so many times?

For devices connected to the serial port 1, when I register the driver.

Do I need to find a main equipment number myself?

Or directly use the primary equipment number 4 and secondary equipment number of the upper 12X?

Or just use the main equipment number 4 and the auxiliary equipment number 64?

There is a tty layer in the kernel of linux. It seems to me that some serial drivers started here.

For example, call tty_register_driver () to register the driver.

Just like calling pci _ register _ driver () in the PCI subsystem.

So, drivers who have registered for this mechanism,

It works directly on the serial port (for example, using inb (), outb () ...).

Or some underlying driver interface?

These questions have been bothering me for a long time, and I finally want to give up.

Now turn to user-space applications and see if there are some more effective ways to implement them.

(In user space, it can only be realized through open("/dev/ttyS0 ",O_RDWR)). )

In addition, the system also realized the serial port driver for us.

So we can open it directly in the user space program ("/dev/ttyS0").

But what I want to write now is the driver of the device connected by serial port.

Can you include a header file in the kernel module and then drive the interface directly in the serial port?

After seeing your question, I felt very typical, so I took some time to look at it and posted some ideas here. Welcome to discuss and correct:

What is the hierarchical relationship between 1 and TTY, console and serial port under LINUX? What is the specific functional interface? What is the serial port called?

The concepts of tty and console are mainly the concepts of virtual devices. Serial port refers to a real device-driven Tty, which is actually an abstraction of terminal I/O devices. It is actually a management concept, which is combined with tty_ldisc (line discipline) and tty_driver (real device driver) to provide a unified interface to the upper VFS through tty in the file_operations structure. If you look up tty_driver, you will get n results, which are actually drivers of related chips. Therefore, it can be concluded (the actual situation is much more complicated than this) that each tty _ struct describing TTY devices must be linked to a specific chip's character device driver (not necessarily the character device driver) at initialization, which can be many, including graphics cards or serial chips. I don't know which football is on your arm. But it depends, you have to use ordinary chips. These drivers actually have the concept of the console as a buffer. Its use is similar to tty. In fact, the console is not only connected to tty, but also connected to framebuffer. The specific reason is that a subset of Tty's keyboard interrupt handling process needs to use the console (usually the primary device number is 4 and the secondary device number is 1-64), but it should be noted that there is no console.

Serial port references tty_driver. Give a typical example:

Analyze the interrupt handling process of the keyboard:

Keyboard interrupt-―& gt;; handle _ kbd _ event ―& gt; handle _ keyboard _ event ―& gt; Handle _ Scan Code

Void handle_scancode (unsigned character scancode, int down)

{

…… ..

tty = ttytab? tty tab[fg _ console]:NULL;

If (tty & amp& amp (! tty-& gt; driver_data)) {

……………

tty = NULL

}

………….

Schedule _ console _ callback ();

}

There are two points worth noting in this code, that is, in addition to obtaining tty (recorded by global tty), it also echoes the relationship between schedule_console_callbackTty and console, which is already clear here! ! !

2. The 2.printk function sends information to the console, right? How to make PRINTK send information through serial port? Or where does the system decide whether to send information to the monitor or serial port?

Just look at the implementation of printk function. Printk does not necessarily output information to the console. Setting the startup parameters of the kernel can achieve the effect of sending information to the display. There is an interesting English before the function:

/* This is printk. It can be called from any context. We hope it will succeed.

*

* We tried to grab the console_sem. If we succeed, it's simple-we record the output.

* Call the console driver. If we don't get the semaphore, we output it.

* put in the log buffer and return. The current holder of the console_sem will

* note the new output in release_console_sem () and send it to.

* console before releasing semaphore.

*

* One effect of this delayed printing is to call printk () and.

* Then changing the console_loglevel may be interrupted. This is because console_loglevel.

* Check during actual printing.

*/

If you want to operate the console, you must first obtain the console_sem semaphore. If you get the console_sem semaphore, you can "record the output and call the console driver", otherwise you can "put the output into the log buffer and return". In fact, in the code:

ASM linkage int printk(const char * fmt...)

{

Va_list parameter;

Unsigned long sign;

int printed _ len

char * p;

Static charprintk _ buf [1024];

static int log _ level _ unknown = 1;

If (oops_in_progress) {/* If it is 1, the system crash is inevitable */

/* If there is a crash, make sure we don't deadlock */

spin_lock_init。 logbuf _ lock);

/* and make sure we print it immediately */

Initializes a mutex lock. console _ SEM);

}

/* This will stop the holder of the console_sem at the position we want */

spin _ lock _ IRQ save(& amp; logbuf_lock,flags);

/* Send output to temporary buffer */

va_start(args,fmt);

printed _ len = vsnprintf(printk _ buf,sizeof(printk_buf),fmt,args); /* Handles the incoming buffer, noting that it has not been.

The real writing to the terminal is only the format analysis of the incoming string */

va _ end(args);

/* Copy the output to log_buf. If the caller does not provide the appropriate log level tags, we insert them here */

/* Clear notes */

for(p = printk _ buf; * p; p++) {

if (log_level_unknown) {

if (p[0]! = ' & lt'| | p[ 1]& lt; 0 ' | | p[ 1]& gt; 7' || p[2]! = ' & gt') {

emit _ log _ char(' & lt;' );

emit _ log _ char(default _ message _ log level+' 0 ');

emit _ log _ char(' & gt;' );

}

log _ level _ known = 0;

}

emit _ log _ char(* p);

if (*p == '\n ')

log _ level _ unknown = 1;

}

If (! arch_consoles_callable()) {

/* On some architectures, the console cannot be used on the auxiliary CPU at the early stage of the startup process. */

spin _ unlock _ irqrestore(& amp; logbuf_lock,flags);

Go out;

}

If (! down _ try lock(& amp; console_sem)) {

/* We have the driver. We can remove the spin lock and let release_console_sem () print the text */

spin _ unlock _ irqrestore(& amp; logbuf_lock,flags);

console _ may _ schedule = 0;

Release _ console _ SEM ();

} Otherwise {

/* Someone else owns the driver. We abandoned the spin lock, which allowed the semaphore holder.

Continue to use the output we just generated to call the console driver. */

spin _ unlock _ irqrestore(& amp; logbuf_lock,flags);

}

Out:

Return printed _ len

}

In fact, printk puts the formatted string in a buffer and displays it at an appropriate time, which also answers the reason why start_kernel used printk function from the beginning.

3.printk function (like printk(linux_banner, etc. ) was used in start_kernel from the beginning, when the whole kernel was not running. So what is the name of printk at this time? In our system, the BOOTLOADER program of Hyundai Company is used to start the system. Later, it seems to jump to head-armv.s under LINUX, and then to start_kernel. There is already a serial port in bootloader, so do you want to reset it after entering the kernel?

Bootloader will generally do some basic initialization, copy the kernel into physical space, and then jump to the kernel for execution. To be sure, the kernel must reset the serial port, because there are many kinds of boot loaders, some of which don't necessarily have serial ports, and the kernel can't rely on bootloader to exist.

Thank you for your incisive analysis. I am looking at the printk function.

The CPU we use is hms7202 of Hynix. Serial port 0 is used on the evaluation board.

Console, all information during startup is sent through this serial port.

In bootloader, the function ser_printf is defined as interaction through a serial port.

But I still don't understand, I have jumped to the linux kernel, and the console and serial port haven't.

How does printk work during initialization? I see start_kernel.

The process (and through the HyperTerminal to do some tracking), the initialization of the console.

It is in the console_init function, and the initialization of the serial port is actually the number 1.

In the process (init->; do _ basic _ setup-& gt; do _ init calls-& gt; rs_init),

So how does prink work before the serial port is initialized? Especially, in

Start_kernel has printk(linux_banner) from the beginning, and at this time,

Neither the serial port nor the console is initialized.

1.start_kernel started with printk(linux_banner), but neither the serial port nor the console was initialized at this time?

Careful analysis of printk can answer this question:

/* Send output to 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, next.

for(p = printk _ buf; * p; p++) {

if (log_level_unknown) {

if (p[0]! = ' & lt'| | p[ 1]& lt; 0 ' | | p[ 1]& gt; 7' || p[2]! = ' & gt') {

emit _ log _ char(' & lt;' );

emit _ log _ char(default _ message _ log level+' 0 ');

emit _ log _ char(' & gt;' );

}

log _ level _ known = 0;

}

emit _ log _ char(* p);

if (*p == '\n ')

log _ level _ unknown = 1;

}

Then parse the contents in printk_buf and put it in the global log_buf (in the emit_log_char function) if (! down _ try lock(& amp; console_sem)) {

/*

* We have the driver. We can put down the rotary lock.

* release_console_sem () prints the text.

*/

spin _ unlock _ irqrestore(& amp; logbuf_lock,flags);

console _ may _ schedule = 0;

Release _ console _ SEM ();

} Otherwise {

/*

* Someone else owns the driver. We put down the rotary lock.

* Allow semaphore holders to continue and call

* Console driver with the output we just generated.

*/

spin _ unlock _ irqrestore(& amp; logbuf_lock,flags);

}

Is based on down _ trylock (&; As a result of console_sem) calling release_console_sem (), only in release_console_sem () can the corresponding console device driver in the global log_buf be truly processed. At this point, we can draw the following conclusions:

The main operation of (1)printk is actually aimed at a buffer (log_buf). Whether the contents in the buffer are displayed (or output to the terminal) depends on whether the file where the console_sem(2)printk is located is printk.c, which has nothing to do with the architecture, so it is the same for any platform. It can be inferred that:

The (1) kernel marks the console_sem as locked during initialization, so in the printk(linux_banner) at the beginning of start_kernel, only the input is actually written into the buffer. After the serial port and console are initialized, the call to printk only outputs the contents in the buffer to the serial port and console once. (2) During the initialization of serial port and console, there must be an up operation on console_sem.

(3) Therefore, in embedded debugging, if there is a problem with the system before the console is initialized, there will be no output. The only thing that can be used is led or jtag. (4) Therefore, you can see the answer to your question. 2.2. Console initialization

I don't know which kernel version you are using. In 2.4. 18 and 2.4. 19, I saw that the console was initialized in start_kernel. From the previous analysis, the initialization of the console should not be too late, otherwise the log_buf may overflow.

Thank you upstairs, the analysis is wonderful!

The kernel version we use is 2.4. 18, and the initialization of the console is indeed in.

start _ kernel->; Console->; Initialization. About tty and serial port, I also want to ask about the general entrance of tty device operation.

be

Static structure file _ operationtty _ 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,

};

The operation of the serial port is defined in:

In the structure of static structtty _ driver serial _ driver.

Most functions in serial.c are filled with function pointers in serial_driver.

Then when operating the serial port, you should first call the operation in tty_fops (for example

Tty_open and so on. ), and then branch to a specific serial port operation (rs_open, etc.). )?

But tty_driver (serial_driver of serial port) contains many function pointers.

Does not correspond to the function pointers in file_operations, and I don't know these corresponding relationships.

Why don't you do the operation? Such as put_char, flush_char, read_proc,

Write_proc, start, stop, etc.

The following is my understanding of this problem:

This actually goes back to the old question, that is, the relationship between tty and tty_driver. From the implementation point of view, tty_driver is actually one of the components of tty mechanism. Borrowing common examples from object-oriented design, tty_driver is like the tire of a tty car. Tty cars need tty_ldisc (wire gauge), termios, and even struct tq_struct tq_hangup (see tty_struct) to run normally. Their relationship is not inheritance. As for the function pointer in tty_driver, let's make another metaphor in C++. They are actually very similar to virtual functions, that is, they can be defined, but they don't have to be implemented. Actually, tty_driver is unnecessary. As long as you look up serial_driver, you will find n kinds of specific implementations, but not all the functions in tty_driver are realized for each specific device, so the functions of put _ char, flush _ char, read _ proc, write _ proc, start and stop may or may not be realized, and even if they are realized, they may not be used by the upper layer (VFS layer).