linux UART驱动 三

来源:互联网 发布:sns是什么软件 编辑:程序博客网 时间:2024/06/05 01:57

当我们打开一个串口(UART)设备的时候,实际上调用的是 tty_open 函数

static int tty_open(struct inode *inode, struct file *filp){int ret;lock_kernel();ret = __tty_open(inode, filp);unlock_kernel();return ret;}static int __tty_open(struct inode *inode, struct file *filp){struct tty_struct *tty = NULL;int noctty, retval;struct tty_driver *driver;int index;dev_t device = inode->i_rdev;unsigned saved_flags = filp->f_flags;nonseekable_open(inode, filp);retry_open:noctty = filp->f_flags & O_NOCTTY;index  = -1;retval = 0;mutex_lock(&tty_mutex);if (device == MKDEV(TTYAUX_MAJOR, 0)) {tty = get_current_tty();if (!tty) {mutex_unlock(&tty_mutex);return -ENXIO;}driver = tty_driver_kref_get(tty->driver);index = tty->index;filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block *//* noctty = 1; *//* FIXME: Should we take a driver reference ? */tty_kref_put(tty);goto got_driver;}#ifdef CONFIG_VTif (device == MKDEV(TTY_MAJOR, 0)) {extern struct tty_driver *console_driver;driver = tty_driver_kref_get(console_driver);index = fg_console;noctty = 1;goto got_driver;}#endifif (device == MKDEV(TTYAUX_MAJOR, 1)) {struct tty_driver *console_driver = console_device(&index);if (console_driver) {driver = tty_driver_kref_get(console_driver);if (driver) {/* Don't let /dev/console block */filp->f_flags |= O_NONBLOCK;noctty = 1;goto got_driver;}}mutex_unlock(&tty_mutex);return -ENODEV;}/*以s3c2440 uart设备为例,static struct uart_driver s3c24xx_uart_drv = {.owner= THIS_MODULE,.dev_name= "s3c2410_serial",.nr= CONFIG_SERIAL_SAMSUNG_UARTS,.cons= S3C24XX_SERIAL_CONSOLE,.driver_name= S3C24XX_SERIAL_NAME,.major= S3C24XX_SERIAL_MAJOR,   //204.minor= S3C24XX_SERIAL_MINOR,   //64};所以前面的条件都不成立,从这里开始*///描述Adriver = get_tty_driver(device, &index);if (!driver) {mutex_unlock(&tty_mutex);return -ENODEV;}got_driver:if (!tty) {/* check whether we're reopening an existing tty *///描述Btty = tty_driver_lookup_tty(driver, inode, index);if (IS_ERR(tty)) {mutex_unlock(&tty_mutex);return PTR_ERR(tty);}}if (tty) {retval = tty_reopen(tty);if (retval)tty = ERR_PTR(retval);} else//描述Ctty = tty_init_dev(driver, index, 0);mutex_unlock(&tty_mutex);tty_driver_kref_put(driver);if (IS_ERR(tty))return PTR_ERR(tty);filp->private_data = tty;file_move(filp, &tty->tty_files);check_tty_count(tty, "tty_open");if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&    tty->driver->subtype == PTY_TYPE_MASTER)noctty = 1;#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG "opening %s...", tty->name);#endifif (!retval) {//描述Dif (tty->ops->open)retval = tty->ops->open(tty, filp);elseretval = -ENODEV;}filp->f_flags = saved_flags;if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&!capable(CAP_SYS_ADMIN))retval = -EBUSY;if (retval) {#ifdef TTY_DEBUG_HANGUPprintk(KERN_DEBUG "error %d in opening %s...", retval,       tty->name);#endiftty_release_dev(filp);if (retval != -ERESTARTSYS)return retval;if (signal_pending(current))return retval;schedule();/* * Need to reset f_op in case a hangup happened. */if (filp->f_op == &hung_up_tty_fops)filp->f_op = &tty_fops;goto retry_open;}mutex_lock(&tty_mutex);spin_lock_irq(¤t->sighand->siglock);if (!noctty &&    current->signal->leader &&    !current->signal->tty &&    tty->session == NULL)__proc_set_tty(current, tty);spin_unlock_irq(¤t->sighand->siglock);mutex_unlock(&tty_mutex);return 0;}

描述A:

static struct tty_driver *get_tty_driver(dev_t device, int *index){struct tty_driver *p;list_for_each_entry(p, &tty_drivers, tty_drivers) {dev_t base = MKDEV(p->major, p->minor_start);if (device < base || device >= base + p->num)continue;*index = device - base;return tty_driver_kref_get(p);}return NULL;}

前面在函数 uart_register_driver -> tty_register_driver 中,将 tty_driver 挂载到
tty_drivers链表中,这里则是通过遍历 来查找曾经注册过的 tty_driver


描述B:
static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,struct inode *inode, int idx){struct tty_struct *tty;if (driver->ops->lookup)return driver->ops->lookup(driver, inode, idx);tty = driver->ttys[idx];return tty;}

前面在函数 uart_register_driver 中将 driver->ops 设置为 uart_ops
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.proc_fops= &uart_proc_fops,#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};

在这里 driver->ops->lookup 为空,所以执行下一条语句
tty = driver->ttys[idx];
而 driver->ttys 是在 uart_register_driver -> tty_register_driver 中初始化的,
void **p = NULL;if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);if (!p)return -ENOMEM;}if (p) {driver->ttys = (struct tty_struct **)p;driver->termios = (struct ktermios **)(p + driver->num);} 
所以 tty = driver->ttys[idx] = NULL;


描述C:
tty = tty_init_dev(driver, index, 0);

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok){struct tty_struct *tty;int retval;/* Check if pty master is being opened multiple times */if (driver->subtype == PTY_TYPE_MASTER &&(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)return ERR_PTR(-EIO);/* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released.  (Except that the termios * and locked termios may be retained.) */if (!try_module_get(driver->owner))return ERR_PTR(-ENODEV);/*struct tty_struct *alloc_tty_struct(void){return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);}分配内存*/tty = alloc_tty_struct();if (!tty)goto fail_no_mem;//描述1initialize_tty_struct(tty, driver, idx);//描述2retval = tty_driver_install_tty(driver, tty);if (retval < 0) {free_tty_struct(tty);module_put(driver->owner);return ERR_PTR(retval);}/* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up.  No need * to decrement the use counts, as release_tty doesn't care. *///描述3retval = tty_ldisc_setup(tty, tty->link);if (retval)goto release_mem_out;return tty;fail_no_mem:module_put(driver->owner);return ERR_PTR(-ENOMEM);/* call the tty release_tty routine to clean out this slot */release_mem_out:if (printk_ratelimit())printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx);release_tty(tty, idx);return ERR_PTR(retval);}
描述1
initialize_tty_struct(tty, driver, idx);
void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx){memset(tty, 0, sizeof(struct tty_struct));kref_init(&tty->kref);tty->magic = TTY_MAGIC;tty_ldisc_init(tty);tty->session = NULL;tty->pgrp = NULL;tty->overrun_time = jiffies;tty->buf.head = tty->buf.tail = NULL;tty_buffer_init(tty);mutex_init(&tty->termios_mutex);mutex_init(&tty->ldisc_mutex);init_waitqueue_head(&tty->write_wait);init_waitqueue_head(&tty->read_wait);INIT_WORK(&tty->hangup_work, do_tty_hangup);mutex_init(&tty->atomic_read_lock);mutex_init(&tty->atomic_write_lock);mutex_init(&tty->output_lock);mutex_init(&tty->echo_lock);spin_lock_init(&tty->read_lock);spin_lock_init(&tty->ctrl_lock);INIT_LIST_HEAD(&tty->tty_files);INIT_WORK(&tty->SAK_work, do_SAK_work);tty->driver = driver;tty->ops = driver->ops;tty->index = idx;tty_line_name(driver, idx, tty->name);}
该函数完成对 struct tty_struct 结构中成员的初始化工作。需要注意的有几点:
1> tty_ldisc_init(tty); 该函数是对 struct tty_struct 结构中成员 struct tty_ldisc
的初始化
void tty_ldisc_init(struct tty_struct *tty){struct tty_ldisc *ld = tty_ldisc_get(N_TTY);if (IS_ERR(ld))panic("n_tty: init_tty");tty_ldisc_assign(tty, ld);}static struct tty_ldisc *tty_ldisc_get(int disc){struct tty_ldisc *ld;struct tty_ldisc_ops *ldops;if (disc < N_TTY || disc >= NR_LDISCS)return ERR_PTR(-EINVAL);/* * 获取操作函数集 */ldops = get_ldops(disc);if (IS_ERR(ldops)) {request_module("tty-ldisc-%d", disc);ldops = get_ldops(disc);if (IS_ERR(ldops))return ERR_CAST(ldops);}/* * 分配内存 */ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);if (ld == NULL) {put_ldops(ldops);return ERR_PTR(-ENOMEM);}ld->ops = ldops;atomic_set(&ld->users, 1);return ld;}static struct tty_ldisc_ops *get_ldops(int disc){unsigned long flags;struct tty_ldisc_ops *ldops, *ret;spin_lock_irqsave(&tty_ldisc_lock, flags);ret = ERR_PTR(-EINVAL);/* * tty_ldiscs是一个全局变量,它的值是什么呢? */ldops = tty_ldiscs[disc];if (ldops) {ret = ERR_PTR(-EAGAIN);if (try_module_get(ldops->owner)) {ldops->refcount++;ret = ldops;}}spin_unlock_irqrestore(&tty_ldisc_lock, flags);return ret;}
看一下 tty_ldiscs 变量的定义
static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
看一下对它的赋值
console_init();->tty_ldisc_begin();->tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc){unsigned long flags;int ret = 0;if (disc < N_TTY || disc >= NR_LDISCS)return -EINVAL;spin_lock_irqsave(&tty_ldisc_lock, flags);/* * 将新的值注册进tty_ldiscs中 */tty_ldiscs[disc] = new_ldisc;new_ldisc->num = disc;new_ldisc->refcount = 0;spin_unlock_irqrestore(&tty_ldisc_lock, flags);return ret;}
tty_ldisc_N_TTY 是一个全局变量
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};

2> 注意下面的两条语句
tty->driver = driver;tty->ops = driver->ops;

initialize_tty_struct 函数分析完了。


描述2:
retval = tty_driver_install_tty(driver, tty);
static int tty_driver_install_tty(struct tty_driver *driver,struct tty_struct *tty){int idx = tty->index;if (driver->ops->install)return driver->ops->install(driver, tty);if (tty_init_termios(tty) == 0) {tty_driver_kref_get(driver);tty->count++;/* *这里将tty安装到driver中 */driver->ttys[idx] = tty;return 0;}return -ENOMEM;}

描述3:
retval = tty_ldisc_setup(tty, tty->link/*NULL*/);
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty){struct tty_ldisc *ld = tty->ldisc;int retval;retval = tty_ldisc_open(tty, ld);if (retval)return retval;if (o_tty) {retval = tty_ldisc_open(o_tty, o_tty->ldisc);if (retval) {tty_ldisc_close(tty, ld);return retval;}tty_ldisc_enable(o_tty);}tty_ldisc_enable(tty);return 0;}static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld){WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));if (ld->ops->open)return ld->ops->open(tty);return 0;}ld->ops->open 即 tty_ldisc_N_TTY->n_tty_open 函数static int n_tty_open(struct tty_struct *tty){if (!tty)return -EINVAL;/* These are ugly. Currently a malloc failure here can panic */if (!tty->read_buf) {/* 为 read_buf 分配内存空间*/tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);if (!tty->read_buf)return -ENOMEM;}if (!tty->echo_buf) {/* 为 echo_buf 分配内存空间*/tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);if (!tty->echo_buf)return -ENOMEM;}reset_buffer_flags(tty);tty->column = 0;/* 为uart设备设置 termios */n_tty_set_termios(tty, NULL);tty->minimum_to_wake = 1;tty->closing = 0;return 0;}

tty_init_dev 函数分析完了。


描述D:
if (tty->ops->open)retval = tty->ops->open(tty, filp);
tty->ops->open 即 全局变量[uart_ops]里的open成员即:uart_open 函数
static int uart_open(struct tty_struct *tty, struct file *filp){struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;struct uart_state *state;struct tty_port *port;int retval, line = tty->index;BUG_ON(!kernel_locked());pr_debug("uart_open(%d) called\n", line);/* * tty->driver->num won't change, so we won't fail here with * tty->driver_data set to something non-NULL (and therefore * we won't get caught by uart_close()). */retval = -ENODEV;if (line >= tty->driver->num)goto fail;/* * We take the semaphore inside uart_get to guarantee that we won't * be re-entered while allocating the state structure, or while we * request any IRQs that the driver may need.  This also has the nice * side-effect that it delays the action of uart_hangup, so we can * guarantee that state->port.tty will always contain something * reasonable. */state = uart_get(drv, line);if (IS_ERR(state)) {retval = PTR_ERR(state);goto fail;}port = &state->port;/* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */tty->driver_data = state;state->uart_port->state = state;tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;tty->alt_speed = 0;tty_port_tty_set(port, tty);/* * If the port is in the middle of closing, bail out now. */if (tty_hung_up_p(filp)) {retval = -EAGAIN;port->count--;mutex_unlock(&port->mutex);goto fail;}/* * Make sure the device is in D0 state. */if (port->count == 1)uart_change_pm(state, 0);/* * Start up the serial port. */retval = uart_startup(state, 0);/* * If we succeeded, wait until the port is ready. */if (retval == 0)retval = uart_block_til_ready(filp, state);mutex_unlock(&port->mutex);/* * If this is the first open to succeed, adjust things to suit. */if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);uart_update_termios(state);}fail:return retval;}
这个函数其实并不复杂,只需要理清这几个变量之间的关系即可:
drv、state、port、tty
它们之间的关系请参阅后面的图片来看。
主要关注一下
retval = uart_startup(state, 0);
static int uart_startup(struct uart_state *state, int init_hw){struct uart_port *uport = state->uart_port;struct tty_port *port = &state->port;unsigned long page;int retval = 0;if (port->flags & ASYNC_INITIALIZED)return 0;/* * Set the TTY IO error marker - we will only clear this * once we have successfully opened the port.  Also set * up the tty->alt_speed kludge */set_bit(TTY_IO_ERROR, &port->tty->flags);if (uport->type == PORT_UNKNOWN)return 0;/* * Initialise and allocate the transmit and temporary * buffer. */if (!state->xmit.buf) {/* This is protected by the per port mutex *//* * 为 xmit.buf 分配内存空间 */page = get_zeroed_page(GFP_KERNEL);if (!page)return -ENOMEM;state->xmit.buf = (unsigned char *) page;uart_circ_clear(&state->xmit);}/* * 调用 uport->ops->startup 函数,即 s3c24xx_serial_startup 函数 */retval = uport->ops->startup(uport);if (retval == 0) {if (init_hw) {/* * Initialise the hardware port settings. */uart_change_speed(state, NULL);/* * Setup the RTS and DTR signals once the * port is open and ready to respond. */if (port->tty->termios->c_cflag & CBAUD)uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);}if (port->flags & ASYNC_CTS_FLOW) {spin_lock_irq(&uport->lock);if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))port->tty->hw_stopped = 1;spin_unlock_irq(&uport->lock);}set_bit(ASYNCB_INITIALIZED, &port->flags);clear_bit(TTY_IO_ERROR, &port->tty->flags);}if (retval && capable(CAP_SYS_ADMIN))retval = 0;return retval;}

终于到我们自己写的函数了
static int s3c24xx_serial_startup(struct uart_port *port){struct s3c24xx_uart_port *ourport = to_ourport(port);int ret;dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",    port->mapbase, port->membase);rx_enabled(port) = 1;/* * 注册接收中断处理函数 */ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,  s3c24xx_serial_portname(port), ourport);if (ret != 0) {printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);return ret;}ourport->rx_claimed = 1;dbg("requesting tx irq...\n");tx_enabled(port) = 1;/* * 注册发送中断处理函数 */ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,  s3c24xx_serial_portname(port), ourport);if (ret) {printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);goto err;}ourport->tx_claimed = 1;dbg("s3c24xx_serial_startup ok\n");/* the port reset code should have done the correct * register setup for the port controls */return ret; err:s3c24xx_serial_shutdown(port);return ret;}




0 0
原创粉丝点击