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_initon line 1163.There is an
isa_init_portsfunctions 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_driverserial8250_regdefined at line 56 in the same file. Probably meant for registering shit.The struct itself is defined in
include/linux/serial_core.hand 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/ttyS0which 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.cat line 2476 and is theuart_openfunction in line 1781 in the same file uart_openin turn callstty_port_openintty_port.cwhich in turn callsport->ops->activatewhich is defined inserial_core.cat line 2512- this calls
uart_startup()of fileserial_core.c - This calls
uart_port_startupinside of it and that inits the circular buffer if needed - This also calls
uport->ops->startupwhich isserial8250_startup - Startup:
serial8250_startuphandles the startup of the ports. In most cases, it'll callserial8250_do_startupwhich is right above it. This is defined indrivers/tty/serial/8250_port.c:2328 serial8250_do_startupwill callup->ops->setup_irqwhich is defined in line 398 of8250_core.c- The value for that is
univ8250_setup_irqin line 317 which callsserial_link_irq_chainwhich is right above it - That calls
request_irqwhich registers interrupt request handlers, and that in turn isserial8250_interrupt - Interrupt handler:
serial8250_interruptat line 109 in8250_core.c - That defines the interrupt handler for uart and handles all interrupts
- It calls
port->handle_irqfor each of the port which is responsible for handling irqs. - That is by default set up to be
serial8250_default_handle_irqin line 511 of8250_port.c - The function
serial8250_default_handle_irqis 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_irqwhich is the final interrupt handler once and for all. After this specialized functions are called.
