1. 程式人生 > >LDD3: tiny_serial模組編譯和測試

LDD3: tiny_serial模組編譯和測試

UART驅動層是在tty驅動層上對常用的非同步通訊串列埠做了封裝,簡化了串列埠驅動的編寫。從測試結果來看,在2.6.323.12.74核心下編譯和執行遇到的問題基本相同,相比tty驅動容易移植得多,這就是封裝的好處。

編譯

error: ‘struct uart_port’ has no member named ‘info’ info 資訊已改成使用 "struct uart_state *state"

	struct circ_buf *xmit = &port->state->xmit;
warning: passing argument 1 of ‘tty_insert_flip_char’ from incompatible pointer type
note: expected ‘struct tty_port *’ but argument is of type ‘struct tty_struct *’

flip buffer介面改成 tty_port, 可以從 state 資訊得到:

static void tiny_timer(unsigned long data)
{
	struct uart_port *port;
	struct tty_port *tport;
	...
	tport = &port->state->port;
	...
	tty_insert_flip_char(tport, TINY_DATA_CHARACTER, 0);
	tty_flip_buffer_push(tport);
	...
}

2.6.32核心則使用 tty = port->state->port.tty

warning: passing argument 2 of ‘uart_get_baud_rate’ from incompatible pointer type
note: expected ‘struct ktermios *’ but argument is of type ‘struct termios *’

set_termios 函式引數改為 struct ktermios *

static void tiny_set_termios(struct uart_port *port,
			     struct ktermios *new, struct ktermios *
old)
warning: initialization from incompatible pointer type
warning: (near initialization for ‘tiny_ops.stop_tx’)
warning: (near initialization for ‘tiny_ops.start_tx’)

start/stop_tx 函式引數發生了變化:

static void tiny_stop_tx(struct uart_port *port)
{
}

static void tiny_start_tx(struct uart_port *port)
{
}

static void tiny_tx_chars(struct uart_port *port)
{
		...
		tiny_stop_tx(port);
		...
}

測試

安裝並執行:

/ # insmod /lib/modules/3.12.74/tiny_serial.ko 
Tiny serial driver loaded
/ # 
/ # cat /proc/tty/drivers | grep tiny
ttytiny              /dev/ttytiny  240       1 serial
/ # 
/ # cat /proc/tty/driver/ttytiny 
serinfo:1.0 driver revision:
0: uart:tinytty port:00000000 irq:0
/ # 
/ # ls /sys/class/tty/ | grep tiny
ttytiny0
/ # 
/ # cat /sys/class/tty/ttytiny0/dev 
240:1
/ # 
/ # mknod /dev/ttytiny0 c 240 1
/ # 
/ # stty -F /dev/ttytiny0 -a
stty: /dev/ttytiny0: Input/output error

任何對裝置的操作都返回 -EIO 錯誤。 檢查原始碼,發現這個錯誤是由於 uart_porttype 引數未設定引起的(預設為0, 即 PORT_UNKNOWN),如下:

static int uart_port_startup(...)
{
	if (uport->type == PORT_UNKNOWN)
		return 1;
	...
}

static int uart_startup(...)
{
	...
	set_bit(TTY_IO_ERROR, &tty->flags);
	retval = uart_port_startup(tty, state, init_hw);
	if (!retval) {
		clear_bit(TTY_IO_ERROR, &tty->flags);
	} else if (retval > 0)
		retval = 0;
	return retval;
}

static int uart_open(struct tty_struct *tty, struct file *filp)
{
	...
	retval = uart_startup(tty, state, 0);
	...
}

如果 type 為UNKNOWN,則設定標誌位 TTY_IO_ERROR,這將導致後續的操作返回I/O錯誤,比如寫操作:

static ssize_t tty_write(struct file *file, const char __user *buf,
						size_t count, loff_t *ppos)
{
	...
	if (!tty || !tty->ops->write ||
		(test_bit(TTY_IO_ERROR, &tty->flags)))
			return -EIO;
	...
}

type 的值定義在標頭檔案”serial.h",即UART控制晶片的型別:

#define PORT_UNKNOWN	0
#define PORT_8250	1
#define PORT_16450	2
#define PORT_16550	3
...

經測試,把 type 設定為任一個已知型別或任意非零值,即可避免I/O錯誤,比如:

static struct uart_port tiny_port = {
	.ops		= &tiny_ops,
	.type		= 1000,
};

重新編譯並安裝,出現新的錯誤:

/ # stty -F /dev/ttytiny0 -a
------------[ cut here ]------------
kernel BUG at kernel/timer.c:912!
Internal error: Oops - BUG: 0 [#1] SMP ARM
Modules linked in: tiny_serial(O)
CPU: 1 PID: 595 Comm: stty Tainted: G           O 3.12.74 #6
task: bf988000 ti: bf026000 task.ti: bf026000
PC is at add_timer+0x14/0x18
LR is at tiny_startup+0x3c/0x7c [tiny_serial]
...

這個問題是由於 timer 沒有初始化導致,修改如下:

static int tiny_startup(struct uart_port *port)
{
	if (!timer) {
		timer = kmalloc(sizeof(*timer), GFP_KERNEL);
		...
		init_timer(timer);
	}

重新編譯執行,新的錯誤出現:

/ # stty -F /dev/ttytiny0 -a
 - data bits = 8
------------[ cut here ]------------
WARNING: CPU: 1 PID: 595 at drivers/tty/serial/serial_core.c:398 uart_get_baud_rate+0x100/0x158()
Modules linked in: tiny_serial(O)
...
Division by zero in kernel.
CPU: 1 PID: 595 Comm: stty Tainted: G        W  O 3.12.74 #6
...
[<801947f4>] (Ldiv0+0x8/0x10) from [<801dffa8>] (uart_get_divisor+0x20/0x40)
[<801dffa8>] (uart_get_divisor+0x20/0x40) from [<801e17c8>] (uart_change_speed+0x70/0x84)
...

由於 uart_portuartclk 引數未設定(預設為0),因此呼叫 uart_get_baud_rate 的最小和最大值均為0,導致該函式列印一個警告並返回 baud=0,並導致接下來的 uart_get_divisor 除0錯誤。應設定 uartclk 為有效值,例如:

static struct uart_port tiny_port = {
	.ops		= &tiny_ops,
	.type		= 1000,
	.uartclk        = 1843200,
};

修改後的執行結果:

預設 pr_debug 資訊不顯示,可以通過 make EXTRA_CFLAGS+="-DDEBUG" 來開啟

/ # stty -F /dev/ttytiny0 -a
 - data bits = 8
 - parity = none
 - stop bits = 1
 - RTS/CTS is disabled
speed 9600 baud;stty: /dev/ttytiny0: No such file or directory
 line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
echoctl echoke -flusho -extproc
/ # 
/ # echo "abcd" > /dev/ttytiny0 
 - data bits = 8
 - parity = none
 - stop bits = 1
 - RTS/CTS is disabled
wrote 61
wrote 62wrote 63
wrote 64wrote  d
wrote  a/ # 
/ #