Linux中终端设备驱动
2012-05-04 11:06
363 查看
核心层
1、在tty_io.c文件(drivers/char/ )
在tty_write()函数中,调用了线路规程:
其中在 tty_struct 结构中,有成员 tty_ldsic:
在源码中,搜索 “tty_ldisc_ops” ,就可以,找到所有的 线路规程; 典型的 /drviers/char/n_tty.c文件针对 N_TTY线路规程实现了 tty_disc结构体中的成员。
其中的 write 方法实现如下:
通过上面的调用流程后,就通过指针,线路规程这一层,调用了tty驱动着一层。
尽管一个特定的UAR设备驱动完全可以按照 前面的方法就行设计, 即 定义tty_driver并实现 tty_operation 其中的成员函数,但是linux已经在一个文件 serial_core.c 中实现了 UART 设备的通用 tyy驱动层(姑且称为串口驱动层)。这样子,UART驱动的主要任务就 演变成 实现 serial-core.c中定义的一组 uart_xxx皆苦并非 tty_xxx接口。
1、在serial_core.h中 ,定义了 uart_driver结构
而注册函数演变成 uart_register_driver( ). ,在注册uart driver 的函数 ,实际上也调用了
tty_register_driver ();
uart_port 结构如下 :
在serial_core.c 中,提供了下面一个函数来 添加一个端口: uart_add_one_port()函数。在uart_register_dirver之后,uart_add_one_port 一个重要作用就是 封装了 tty_register_device( );
uart_ops定义了一系列的操作,如果说,tty_operation对于串口还比较抽象的话,那么 uart_ops则直接面向 了 串口的UART 。这非常类似于面向对象中的 基类和派生类的关系。
在 serial_console.c 文件中 定义了 tty_operation的事例,
这些函数会借助 uar_ops结构体中的成员来完成具体的操作。
比如::tty_operations的 uart_send_xchar()成员函数利用了 uart_ops结构体的star_tx来进行初始化。
在使用了串口核心层tty接口后,一个串口的要完成的主要工作是
1\ 定义uart_diver uart_ops uart_port
2\ uart_register_diver() uart_add_one_port,注册驱动并添加端口。
3\ 根据具体的datasheet 实现 uart_ops中的成员。这些函数成了实现UART驱动的主体工作。
下面根据这三部来分析 S3C6410的串口驱动:
1\ S3C6410具有4路高速串口。具体见开发板硬件手册。
在samsung.c文件,在官方下的内核改文件已经添加了进来。
但是 在同一目录下的 S3C6400。.C 文件并不存在。
-->
被添加的 uart_port uart_ops成员 定义在 samsung.c文件中
从源代码中发现:
1、在tty_io.c文件(drivers/char/ )
static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_write, .poll = tty_poll, .unlocked_ioctl = tty_ioctl, .compat_ioctl = tty_compat_ioctl, .open = tty_open, .release = tty_release, .fasync = tty_fasync, };
static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct tty_struct *tty; struct inode *inode = file->f_path.dentry->d_inode; ssize_t ret; struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_write")) return -EIO; if (!tty || !tty->ops->write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; /* Short term debug to catch buggy drivers */ if (tty->ops->write_room == NULL) printk(KERN_ERR "tty driver %s lacks a write_room method.\n", tty->driver->name); ld = tty_ldisc_ref_wait(tty); if (!ld->ops->write) ret = -EIO; else ret = do_tty_write(ld->ops->write, tty, file, buf, count); tty_ldisc_deref(ld); return ret; }
在tty_write()函数中,调用了线路规程:
其中在 tty_struct 结构中,有成员 tty_ldsic:
struct tty_struct { int magic; struct kref kref; struct tty_driver *driver; const struct tty_operations *ops; int index; /* The ldisc objects are protected by tty_ldisc_lock at the moment */ struct tty_ldisc ldisc; struct mutex termios_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ struct ktermios *termios, *termios_locked; struct termiox *termiox; /* May be NULL for unsupported */ char name[64]; struct pid *pgrp; /* Protected by ctrl lock */ struct pid *session; unsigned long flags; int count; struct winsize winsize; /* termios mutex */ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; unsigned char low_latency:1, warned:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ struct tty_struct *link; struct fasync_struct *fasync; struct tty_bufhead buf; /* Locked internally */ int alt_speed; /* For magic substitution of 38400 bps */ wait_queue_head_t write_wait; wait_queue_head_t read_wait; struct work_struct hangup_work; void *disc_data; void *driver_data; struct list_head tty_files; #define N_TTY_BUF_SIZE 4096 /* * The following is data for the N_TTY line discipline. For * historical reasons, this is included in the tty structure. * Mostly locked by the BKL. */ unsigned int column; unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char closing:1; unsigned char echo_overrun:1; unsigned short minimum_to_wake; unsigned long overrun_time; int num_overrun; unsigned long process_char_map[256/(8*sizeof(unsigned long))]; char *read_buf; int read_head; int read_tail; int read_cnt; unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; unsigned char *echo_buf; unsigned int echo_pos; unsigned int echo_cnt; int canon_data; unsigned long canon_head; unsigned int canon_column; struct mutex atomic_read_lock; struct mutex atomic_write_lock; struct mutex output_lock; struct mutex echo_lock; unsigned char *write_buf; int write_cnt; spinlock_t read_lock; /* If the tty has a pending do_SAK, queue it here - akpm */ struct work_struct SAK_work; struct tty_port *port; };
在源码中,搜索 “tty_ldisc_ops” ,就可以,找到所有的 线路规程; 典型的 /drviers/char/n_tty.c文件针对 N_TTY线路规程实现了 tty_disc结构体中的成员。
struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", .open = n_tty_open, .close = n_tty_close, .flush_buffer = n_tty_flush_buffer, .chars_in_buffer = n_tty_chars_in_buffer, .read = n_tty_read, .write = n_tty_write, .ioctl = n_tty_ioctl, .set_termios = n_tty_set_termios, .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup };
其中的 write 方法实现如下:
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) { const unsigned char *b = buf; DECLARE_WAITQUEUE(wait, current); int c; ssize_t retval = 0; /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) { retval = tty_check_change(tty); if (retval) return retval; } /* Write out any echoed characters that are still pending */ process_echoes(tty); add_wait_queue(&tty->write_wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { retval = -ERESTARTSYS; break; } if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) { retval = -EIO; break; } if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { while (nr > 0) { ssize_t num = process_output_block(tty, b, nr); if (num < 0) { if (num == -EAGAIN) break; retval = num; goto break_out; } b += num; nr -= num; if (nr == 0) break; c = *b; if (process_output(c, tty) < 0) break; b++; nr--; } if (tty->ops->flush_chars) tty->ops->flush_chars(tty); //刷新缓冲区并丢弃任何剩下的数据。 } else { while (nr > 0) { c = tty->ops->write(tty, b, nr); //跳转到tty 驱动着一层。 if (c < 0) { retval = c; goto break_out; } if (!c) break; b += c; nr -= c; } } if (!nr) break; if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; break; } schedule(); } break_out: __set_current_state(TASK_RUNNING); remove_wait_queue(&tty->write_wait, &wait); if (b - buf != nr && tty->fasync) set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); return (b - buf) ? b - buf : retval; }
通过上面的调用流程后,就通过指针,线路规程这一层,调用了tty驱动着一层。
尽管一个特定的UAR设备驱动完全可以按照 前面的方法就行设计, 即 定义tty_driver并实现 tty_operation 其中的成员函数,但是linux已经在一个文件 serial_core.c 中实现了 UART 设备的通用 tyy驱动层(姑且称为串口驱动层)。这样子,UART驱动的主要任务就 演变成 实现 serial-core.c中定义的一组 uart_xxx皆苦并非 tty_xxx接口。
1、在serial_core.h中 ,定义了 uart_driver结构
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; };
而注册函数演变成 uart_register_driver( ). ,在注册uart driver 的函数 ,实际上也调用了
tty_register_driver ();
int uart_register_driver(struct uart_driver *drv) { struct tty_driver *normal = NULL; int i, retval; BUG_ON(drv->state); /* * Maybe we should be using a slab cache for this, especially if * we have a large number of ports to handle. */ drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); retval = -ENOMEM; if (!drv->state) goto out; normal = alloc_tty_driver(drv->nr); if (!normal) goto out; drv->tty_driver = normal; normal->owner = drv->owner; 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; tty_set_operations(normal, &uart_ops); /* * Initialise the UART state(s). */ for (i = 0; i < drv->nr; i++) { struct uart_state *state = drv->state + i; state->close_delay = 500; /* .5 seconds */ state->closing_wait = 30000; /* 30 seconds */ mutex_init(&state->mutex); tty_port_init(&state->info.port); init_waitqueue_head(&state->info.delta_msr_wait); tasklet_init(&state->info.tlet, uart_tasklet_action, (unsigned long)state); } retval = tty_register_driver(normal); out: if (retval < 0) { put_tty_driver(normal); kfree(drv->state); } return retval; }
uart_port 结构如下 :
struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int (*serial_in)(struct uart_port *, int); void (*serial_out)(struct uart_port *, int, int); unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ unsigned int fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ unsigned char iotype; /* io access style */ unsigned char unused1; #define UPIO_PORT (0) #define UPIO_HUB6 (1) #define UPIO_MEM (2) #define UPIO_MEM32 (3) #define UPIO_AU (4) /* Au1x00 type IO */ #define UPIO_TSI (5) /* Tsi108/109 type IO */ #define UPIO_DWAPB (6) /* DesignWare APB UART */ #define UPIO_RM9000 (7) /* RM9000 type IO */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ struct uart_info *info; /* pointer to parent info */ struct uart_icount icount; /* statistics */ struct console *cons; /* struct console, if any */ #ifdef CONFIG_SERIAL_CORE_CONSOLE unsigned long sysrq; /* sysrq timeout */ #endif upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1)) #define UPF_SAK ((__force upf_t) (1 << 2)) #define UPF_SPD_MASK ((__force upf_t) (0x1030)) #define UPF_SPD_HI ((__force upf_t) (0x0010)) #define UPF_SPD_VHI ((__force upf_t) (0x0020)) #define UPF_SPD_CUST ((__force upf_t) (0x0030)) #define UPF_SPD_SHI ((__force upf_t) (0x1000)) #define UPF_SPD_WARP ((__force upf_t) (0x1010)) #define UPF_SKIP_TEST ((__force upf_t) (1 << 6)) #define UPF_AUTO_IRQ ((__force upf_t) (1 << 7)) #define UPF_HARDPPS_CD ((__force upf_t) (1 << 11)) #define UPF_LOW_LATENCY ((__force upf_t) (1 << 13)) #define UPF_BUGGY_UART ((__force upf_t) (1 << 14)) #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) #define UPF_FIXED_PORT ((__force upf_t) (1 << 29)) #define UPF_DEAD ((__force upf_t) (1 << 30)) #define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) #define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ const struct uart_ops *ops; unsigned int custom_divisor; unsigned int line; /* port index */ resource_size_t mapbase; /* for ioremap */ struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; unsigned char unused[2]; void *private_data; /* generic platform data pointer */ };
在serial_core.c 中,提供了下面一个函数来 添加一个端口: uart_add_one_port()函数。在uart_register_dirver之后,uart_add_one_port 一个重要作用就是 封装了 tty_register_device( );
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state; int ret = 0; struct device *tty_dev; BUG_ON(in_interrupt()); if (port->line >= drv->nr) return -EINVAL; state = drv->state + port->line; mutex_lock(&port_mutex); mutex_lock(&state->mutex); if (state->port) { ret = -EINVAL; goto out; } state->port = port; state->pm_state = -1; port->cons = drv->cons; port->info = &state->info; /* * If this port is a console, then the spinlock is already * initialised. */ if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) { spin_lock_init(&port->lock); lockdep_set_class(&port->lock, &port_lock_key); } uart_configure_port(drv, state, port); /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev); if (likely(!IS_ERR(tty_dev))) { device_init_wakeup(tty_dev, 1); device_set_wakeup_enable(tty_dev, 0); } else printk(KERN_ERR "Cannot register tty device on line %d\n", port->line); /* * Ensure UPF_DEAD is not set. */ port->flags &= ~UPF_DEAD; out: mutex_unlock(&state->mutex); mutex_unlock(&port_mutex); return ret; }
uart_ops定义了一系列的操作,如果说,tty_operation对于串口还比较抽象的话,那么 uart_ops则直接面向 了 串口的UART 。这非常类似于面向对象中的 基类和派生类的关系。
struct uart_ops { unsigned int (*tx_empty)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int mctrl); unsigned int (*get_mctrl)(struct uart_port *); void (*stop_tx)(struct uart_port *); void (*start_tx)(struct uart_port *); void (*send_xchar)(struct uart_port *, char ch); void (*stop_rx)(struct uart_port *); void (*enable_ms)(struct uart_port *); void (*break_ctl)(struct uart_port *, int ctl); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); void (*set_ldisc)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); int (*set_wake)(struct uart_port *, unsigned int state); void (*wake_peer)(struct uart_port *); /* * Return a string describing the type of the port */ const char *(*type)(struct uart_port *); /* * Release IO and memory resources used by the port. * This includes iounmap if necessary. */ void (*release_port)(struct uart_port *); /* * Request IO and memory resources used by the port. * This includes iomapping the port if necessary. */ int (*request_port)(struct uart_port *); void (*config_port)(struct uart_port *, int); int (*verify_port)(struct uart_port *, struct serial_struct *); int (*ioctl)(struct uart_port *, unsigned int, unsigned long); #ifdef CONFIG_CONSOLE_POLL void (*poll_put_char)(struct uart_port *, unsigned char); int (*poll_get_char)(struct uart_port *); #endif };
在 serial_console.c 文件中 定义了 tty_operation的事例,
static const struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, .write = uart_write, .put_char = uart_put_char, .flush_chars = uart_flush_chars, .write_room = uart_write_room, .chars_in_buffer= uart_chars_in_buffer, .flush_buffer = uart_flush_buffer, .ioctl = uart_ioctl, .throttle = uart_throttle, .unthrottle = uart_unthrottle, .send_xchar = uart_send_xchar, .set_termios = uart_set_termios, .set_ldisc = uart_set_ldisc, .stop = uart_stop, .start = uart_start, .hangup = uart_hangup, .break_ctl = uart_break_ctl, .wait_until_sent= uart_wait_until_sent, #ifdef CONFIG_PROC_FS .read_proc = uart_read_proc, #endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset, #ifdef CONFIG_CONSOLE_POLL .poll_init = uart_poll_init, .poll_get_char = uart_poll_get_char, .poll_put_char = uart_poll_put_char, #endif };
这些函数会借助 uar_ops结构体中的成员来完成具体的操作。
比如::tty_operations的 uart_send_xchar()成员函数利用了 uart_ops结构体的star_tx来进行初始化。
static void uart_send_xchar(struct tty_struct *tty, char ch) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; unsigned long flags; if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { port->x_char = ch; if (ch) { spin_lock_irqsave(&port->lock, flags); port->ops->start_tx(port); spin_unlock_irqrestore(&port->lock, flags); } } }
在使用了串口核心层tty接口后,一个串口的要完成的主要工作是
1\ 定义uart_diver uart_ops uart_port
2\ uart_register_diver() uart_add_one_port,注册驱动并添加端口。
3\ 根据具体的datasheet 实现 uart_ops中的成员。这些函数成了实现UART驱动的主体工作。
下面根据这三部来分析 S3C6410的串口驱动:
1\ S3C6410具有4路高速串口。具体见开发板硬件手册。
在samsung.c文件,在官方下的内核改文件已经添加了进来。
static int __init s3c24xx_serial_modinit(void) { int ret; ret = uart_register_driver(&s3c24xx_uart_drv); if (ret < 0) { printk(KERN_ERR "failed to register UART driver\n"); return -1; } return 0; }
但是 在同一目录下的 S3C6400。.C 文件并不存在。
static int __init s3c6400_serial_init(void) { return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf); }
-->
static struct platform_driver s3c6400_serial_drv = { .probe = s3c6400_serial_probe, .remove = s3c24xx_serial_remove, .driver = { .name = "s3c6400-uart", .owner = THIS_MODULE, }, }
static int s3c6400_serial_probe(struct platform_device *dev) { dbg("s3c6400_serial_probe: dev=%p\n", dev); return s3c24xx_serial_probe(dev, &s3c6400_uart_inf); }
int s3c24xx_serial_probe(struct platform_device *dev, struct s3c24xx_uart_info *info) { struct s3c24xx_uart_port *ourport; int ret; dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); ourport = &s3c24xx_serial_ports[probe_index]; probe_index++; dbg("%s: initialising port %p...\n", __func__, ourport); ret = s3c24xx_serial_init_port(ourport, info, dev); if (ret < 0) goto probe_err; dbg("%s: adding port\n", __func__); uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); platform_set_drvdata(dev, &ourport->port); ret = device_create_file(&dev->dev, &dev_attr_clock_source); if (ret < 0) printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); ret = s3c24xx_serial_cpufreq_register(ourport); if (ret < 0) dev_err(&dev->dev, "failed to add cpufreq notifier\n"); return 0; probe_err: return ret; }
被添加的 uart_port uart_ops成员 定义在 samsung.c文件中
static struct uart_ops s3c24xx_serial_ops = { .pm = s3c24xx_serial_pm, .tx_empty = s3c24xx_serial_tx_empty, .get_mctrl = s3c24xx_serial_get_mctrl, .set_mctrl = s3c24xx_serial_set_mctrl, .stop_tx = s3c24xx_serial_stop_tx, .start_tx = s3c24xx_serial_start_tx, .stop_rx = s3c24xx_serial_stop_rx, .enable_ms = s3c24xx_serial_enable_ms, .break_ctl = s3c24xx_serial_break_ctl, .startup = s3c24xx_serial_startup, .shutdown = s3c24xx_serial_shutdown, .set_termios = s3c24xx_serial_set_termios, .type = s3c24xx_serial_type, .release_port = s3c24xx_serial_release_port, .request_port = s3c24xx_serial_request_port, .config_port = s3c24xx_serial_config_port, .verify_port = s3c24xx_serial_verify_port, };
从源代码中发现:
tatic struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { [0] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX0, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 0, } }, [1] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX1, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 1, } }, #if CONFIG_SERIAL_SAMSUNG_UARTS > 2 [2] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX2, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 2, } }, #endif #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 [3] = { .port = { .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), .iotype = UPIO_MEM, .irq = IRQ_S3CUART_RX3, .uartclk = 0, .fifosize = 16, .ops = &s3c24xx_serial_ops, .flags = UPF_BOOT_AUTOCONF, .line = 3, } } #endif };
相关文章推荐
- Linux终端设备驱动(三)
- Linux终端设备驱动(三)
- Linux终端tty设备及驱动
- Linux终端设备驱动(四)
- Linux终端设备驱动(五)
- ARM-Linux S5PV210 UART驱动(2)---- 终端设备驱动
- Linux tty 终端设备驱动
- linux终端设备uart驱动分析
- Linux 终端设备驱动
- 乾坤合一:Linux设备驱动之终端设备驱动
- linux终端设备uart驱动分析
- 第十四章 Linux终端设备驱动
- Linux终端设备驱动 ----UART的驱动
- Linux终端tty设备驱动
- Linux终端设备驱动
- Linux终端设备驱动
- linux终端tty设备驱动模型分析
- Linux终端设备驱动
- Linux终端设备驱动(五)
- linux串口终端设备驱动分析