//在/kernel/drivers/serial/samsung.c 中
/* Console code */
#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
static struct uart_port *
cons_uart;
static int
s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
//准備發送
{
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
unsigned long ufstat, utrstat;
if (ufcon & S3C2410_UFCON_FIFOMODE) {
/* fifo mode - check ammount of data in fifo registers... */
ufstat = rd_regl(port, S3C2410_UFSTAT);
return (ufstat & info->tx_fifofull) ? 0 : 1;
}
/* in non-fifo mode, we go and use the tx buffer empty */
utrstat = rd_regl(port, S3C2410_UTRSTAT);
return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
}
static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
//是最底層的函數
{
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
while (!
s3c24xx_serial_console_txrdy(port, ufcon))
barrier();
wr_regb(cons_uart, S3C2410_UTXH, ch);
}
static void
s3c24xx_serial_console_write(struct console *co, const char *s,
unsigned int count)
{
//通過調用上面的
s3c24xx_serial_console_putchar輸出字符
uart_console_write(cons_uart, s, count,
s3c24xx_serial_console_putchar);
}
static void __init
s3c24xx_serial_get_options(struct uart_port *port, int *baud,
int *parity, int *bits)
{
struct s3c24xx_uart_clksrc clksrc;
struct clk *clk;
unsigned int ulcon;
unsigned int ucon;
unsigned int ubrdiv;
unsigned long rate;
ulcon = rd_regl(port, S3C2410_ULCON);
ucon = rd_regl(port, S3C2410_UCON);
ubrdiv = rd_regl(port, S3C2410_UBRDIV);
dbg("s3c24xx_serial_get_options: port=%p\n"
"registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
port, ulcon, ucon, ubrdiv);
if ((ucon & 0xf) != 0) {
/* consider the serial port configured if the tx/rx mode set */
switch (ulcon & S3C2410_LCON_CSMASK) {
case S3C2410_LCON_CS5:
*bits = 5;
break;
case S3C2410_LCON_CS6:
*bits = 6;
break;
case S3C2410_LCON_CS7:
*bits = 7;
break;
default:
case S3C2410_LCON_CS8:
*bits = 8;
break;
}
switch (ulcon & S3C2410_LCON_PMASK) {
case S3C2410_LCON_PEVEN:
*parity = 'e';
break;
case S3C2410_LCON_PODD:
*parity = 'o';
break;
case S3C2410_LCON_PNONE:
default:
*parity = 'n';
}
/* now calculate the baud rate */
s3c24xx_serial_getsource(port, &clksrc);
clk = clk_get(port->dev, clksrc.name);
if (!IS_ERR(clk) && clk != NULL)
rate = clk_get_rate(clk) / clksrc.divisor;
else
rate = 1;
*baud = rate / (16 * (ubrdiv + 1));
dbg("calculated baud %d\n", *baud);
}
}
/* s3c24xx_serial_init_ports
*
* initialise the serial ports from the machine provided initialisation
* data.
*/
static int
s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info)
{
struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports;
struct platform_device **platdev_ptr;
int i;
dbg("s3c24xx_serial_init_ports: initialising ports...\n");
platdev_ptr = s3c24xx_uart_devs;
for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) {
s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr);
}
return 0;
}
static int __init
s3c24xx_serial_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
co, co->index, options);
/* is this a valid port */
if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
co->index = 0;
port = &s3c24xx_serial_ports[co->index].port;
/* is the port configured? */
if (port->mapbase == 0x0) {
co->index = 0;
port = &s3c24xx_serial_ports[co->index].port;
}
cons_uart = port;
dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
s3c24xx_serial_get_options(port, &baud, &parity, &bits);
dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
return uart_set_options(port, co, baud, parity, bits, flow);
}
/* s3c24xx_serial_initconsole
*
* initialise the console from one of the uart drivers
*/
static struct console
s3c24xx_serial_console = {
.name = S3C24XX_SERIAL_NAME,
//名字
.device = uart_console_device, //init進行、用戶進程打開/dev/console時用到
.flags = CON_PRINTBUFFER,
//打印先前在log_buf中保存的信息
.index = -1, //表示使用哪個串口由命令行參數決定
.write =
s3c24xx_serial_console_write,
//控制台輸出函數
.setup =
s3c24xx_serial_console_setup//控制台設置函數
};
int
s3c24xx_serial_initconsole(struct platform_driver *drv,
struct s3c24xx_uart_info **info)
{
struct platform_device *dev = s3c24xx_uart_devs[0];
dbg("s3c24xx_serial_initconsole\n");
/* select driver based on the cpu */
if (dev == NULL) {
printk(KERN_ERR "s3c24xx: no devices for console init\n");
return 0;
}
if (strcmp(dev->name, drv->driver.name) != 0)
return 0;
s3c24xx_serial_console.data = &
s3c24xx_uart_drv;
//這是控制台的驅動
s3c24xx_serial_init_ports(info);
register_console(&s3c24xx_serial_console);
//注冊
return 0;
}
#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
//在/kernel/drivers/serial/samsung.h中
#define
s3c24xx_console_init(__drv, __inf) \
static int __init
s3c_serial_console_init(void) \
{ \
struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \
int i; \
\
for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \
uinfo[i] = __inf; \
return
s3c24xx_serial_initconsole(__drv, uinfo); \
} \
\
console_initcall(
s3c_serial_console_init) //放入.con_initcall.init代碼段
在tty_io.c中
/*
* Initialize the console device. This is called *early*, so
* we can't necessarily depend on lots of kernel help here.
* Just do some early initializations, and do the complex setup
* later.
*/
void __init
console_init(void)
//在start_kernel中被調用
{
initcall_t *call;
/* Setup the default TTY line discipline. */
tty_ldisc_begin();
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
call =
__con_initcall_start;
while (call <
__con_initcall_end) {
(*call)();
call++;
}
}