UGRC hints
TL;DR: Debugging pro max
As of now printing everything using printk
functions.
From drivers/base/init.c
we call the devices_init
function
Defined in drivers/base/core.c
at line 2895 and calls kset_create_and_add
that adds a kset
to the sysfs
(basically a collection of kobjects
, that represents the base type for any driver)
The above mentioned call happens for a static const struct kset_uevent_ops
which basically
controls how the kset
reacts to udev
events.
After that we add block and char to the freestanding dev kobject
.
After this I'm a little bit clueless on what happens. Like sure you init the devices but now what?
Then in drivers/tty/serial/8250/8250_core.c
somehow:
We call
serial8250_init
on line 1163.There is an
isa_init_ports
functions but that's already executed once and is meant to execute once so it doesn't matter. The first execution happens for the console (ig).We pick up a
static struct uart_driver
serial8250_reg
defined at line 56 in the same file. Probably meant for registering shit.The struct itself is defined in
include/linux/serial_core.h
and has the following below (line 303)
struct uart_driver { struct module *owner; const char *driver_name; const char *dev_name; int major; int minor; int nr; struct console *cons; /* * these are private; the low level driver should not * touch these; they should be initialised to NULL */ struct uart_state *state; struct tty_driver *tty_driver; };
- It has a lot of module related stuff, basically a pointer to struct module in
include/linux/module.h:350
Also has a driver name, device name, major/minor count and a count
Most importantly has a pointer to struct console *
which has a couple of interesting functions
(include/linux/console.h:145
)
struct console { char name[16]; void (*write)(struct console *, const char *, unsigned); int (*read)(struct console *, char *, unsigned); struct tty_driver *(*device)(struct console *, int *); void (*unblank)(void); int (*setup)(struct console *, char *); int (*match)(struct console *, char *name, int idx, char *options); short flags; short index; int cflag; void *data; struct console *next; };
This static struct is picked up by a function call to uart_register_driver()
that is in
drivers/tty/serial/serial_core.c:2528
The register driver function does a lot of its own shit. Allocates space for all the ports each of size uartstate thereby maintaining an array with each state stored.
The state stores a lot of stuff, and is defined in include/linux/serial_core.h:283
.
Most importantly it stores the circular buffer that we need to look at. Also has a
uart_port
struct inside uartstate that has read and write functions to the serial thing.
This is in the same file, line 108.
Subsequently, it gets a tty_driver
pointer with alloc_tty_driver
function present in
include/linux/tty_driver.h:355
The tty_driver
itself is described in include/linux/tty_driver.h
and is:
struct tty_driver { int magic; /* magic number for this structure */ struct kref kref; /* Reference management */ struct cdev **cdevs; struct module *owner; const char *driver_name; const char *name; int name_base; /* offset of printed name */ int major; /* major device number */ int minor_start; /* start of minor device number */ unsigned int num; /* number of devices allocated */ short type; /* type of tty driver */ short subtype; /* subtype of tty driver */ struct ktermios init_termios; /* Initial termios */ unsigned long flags; /* tty driver flags */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ /* * Pointer to the tty data structures */ struct tty_struct **ttys; struct tty_port **ports; struct ktermios **termios; void *driver_state; /* * Driver methods */ const struct tty_operations *ops; struct list_head tty_drivers; }
We then do the following changes to the generated tty
drv->tty_driver = normal; normal->driver_name = drv->driver_name; normal->name = drv->dev_name; normal->major = drv->major; normal->minor_start = drv->minor; normal->type = TTY_DRIVER_TYPE_SERIAL; normal->subtype = SERIAL_TYPE_NORMAL; normal->init_termios = tty_std_termios; normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; normal->driver_state = drv;
It sets the operations to uart_ops
options specified at line 2472 in the same file.
It inits all the tty ports of the uart_state
array that we kcalloc'd earlier. This is done using
tty_port_init
in drivers/tty/tty_port.c:60
tty_port
itself is defined in include/linux/tty.h
It also registers the tty_driver
we allocated earlier.
After the uart_register_driver
is found, we call serial8250_pnp_init
which is defined in
drivers/tty/serial/8250/8250_pnp.c:533
that itself calls pnp_register_driver
from
drivers/pnp/driver.c
at line 267
The pnp driver inited is a static struct right above serial8250_pnp_init
whose type definition
is in include/linux/pnp.h:381
That itself has a struct device_driver
that is just a common type for all device drivers.
Doesn't do much but basically add the driver somewhere in the grand list of all drivers.
After that, we do some platform_device_alloc
and platform_device_add
which do something I don't
bother enough to check.
After this, we do a serial8250_register_ports
that registers the uart ports. Defined in
drivers/tty/serial/8250/8250_core.c:576
That function in turn takes a static uartport struct whose structure is as follows
struct uart_8250_port { struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ u32 capabilities; /* port capabilities */ unsigned short bugs; /* port bugs */ bool fifo_bug; /* min RX trigger if enabled */ unsigned int tx_loadsz; /* transmit fifo load size */ unsigned char acr; unsigned char fcr; unsigned char ier; unsigned char lcr; unsigned char mcr; unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ unsigned char cur_iotype; /* Running I/O type */ unsigned int rpm_tx_active; unsigned char canary; /* non-zero during system sleep * if no_console_suspend */ unsigned char probe; struct mctrl_gpios *gpios; #define UART_PROBE_RSA (1 << 0) /* * Some bits in registers are cleared on a read, so they must * be saved whenever the register is read but the bits will not * be immediately processed. */ #define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS unsigned char lsr_saved_flags; #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; struct uart_8250_dma *dma; const struct uart_8250_ops *ops; /* 8250 specific callbacks */ int (*dl_read)(struct uart_8250_port *); void (*dl_write)(struct uart_8250_port *, int); struct uart_8250_em485 *em485; /* Serial port overrun backoff */ struct delayed_work overrun_backoff; u32 overrun_backoff_time_ms; };
This struct is also defined in the same file. We basically allot the port's device to the one we passed in the argument.
Go to uart_add_one_port
in drivers/tty/serial/serial_core.c:2805
This function is intense. There's a lot of stuff happening here and I don't have much idea of
what's happening exactly.
This takes the state and the port and links them with each other. Then does some variable
defining for both
Now events are handled as mentioned below:
- user calls open on
dev/ttyS0
which eventually reaches the tty subsystem - tty subsystem calls open method of the ttyoperations struct and is a function pointer (so specialized at runtime)
- The exact one is defined at
serial_core.c
at line 2476 and is theuart_open
function in line 1781 in the same file uart_open
in turn callstty_port_open
intty_port.c
which in turn callsport->ops->activate
which is defined inserial_core.c
at line 2512- this calls
uart_startup()
of fileserial_core.c
- This calls
uart_port_startup
inside of it and that inits the circular buffer if needed - This also calls
uport->ops->startup
which isserial8250_startup
- Startup:
serial8250_startup
handles the startup of the ports. In most cases, it'll callserial8250_do_startup
which is right above it. This is defined indrivers/tty/serial/8250_port.c:2328
serial8250_do_startup
will callup->ops->setup_irq
which is defined in line 398 of8250_core.c
- The value for that is
univ8250_setup_irq
in line 317 which callsserial_link_irq_chain
which is right above it - That calls
request_irq
which registers interrupt request handlers, and that in turn isserial8250_interrupt
- Interrupt handler:
serial8250_interrupt
at line 109 in8250_core.c
- That defines the interrupt handler for uart and handles all interrupts
- It calls
port->handle_irq
for each of the port which is responsible for handling irqs. - That is by default set up to be
serial8250_default_handle_irq
in line 511 of8250_port.c
- The function
serial8250_default_handle_irq
is defined in line 1842 in8520_port.c
- Finds the interrupt type using IIR macro (used by the hardware to set the type)
- After this it passes it to
serial_8250_handle_irq
which is the final interrupt handler once and for all. After this specialized functions are called.