LDD3: tiny_serial模組編譯和測試
UART驅動層是在tty驅動層上對常用的非同步通訊串列埠做了封裝,簡化了串列埠驅動的編寫。從測試結果來看,在2.6.32和3.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_port 的 type 引數未設定引起的(預設為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_port 的 uartclk 引數未設定(預設為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/ #
/ #