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);
}
retval = tty_register_driver(normal);
out:
if (retval < 0) {
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
从上面代码可以看出.uart_driver中很多数据结构其实就是tty_driver中的.将数据转换为tty_driver之后,注册tty_driver.然后初始化uart_driver->state的存储空间.
这样,就会注册uart_driver->nr个设备节点.主设备号为uart_driver-> major. 开始的次设备号为uart_driver-> minor.
值得注意的是.在这里将tty_driver的操作集统一设为了uart_ops.其次,在tty_driver-> driver_state保存了这个uart_driver.这样做是为了在用户空间对设备文件的操作时,很容易转到对应的uart_driver.
在前面提到.在对uart设备文件过程中.会将操作转换到对应的port上,这个port跟uart_driver是怎么关联起来的呢?这就是uart_add_ont_port()的主要工作了.
顾名思义,这个函数是在uart_driver增加一个port.代码如下:
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_can_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_port->line就是对uart设备文件序号.它对应的也就是uart_driver->state数组中的uart_port->line项.
它主要初始化对应uart_driver->state项.接着调用uart_configure_port()进行port的自动配置.然后注册tty_device.如果用户空间运行了udev或者已经配置好了hotplug.就会在/dev下自动生成设备文件了.
操作流程图如下所示:
六:设备节点的open操作
在用户空间执行open操作的时候,就会执行uart_ops->open. 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,
.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,
};
对应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;
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 info 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 info->tty will always contain something reasonable.
/
state = uart_get(drv, line);
if (IS_ERR(state)) {
retval = PTR_ERR(state);
goto fail;
}
/
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;
tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
state->info->tty = tty;
/
If the port is in the middle of closing, bail out now.
/
if (tty_hung_up_p(filp)) {
retval = -EAGAIN;
state->count—;
mutex_unlock(&state->mutex);
goto fail;
}
/
Make sure the device is in D0 state.
/
if (state->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(&state->mutex);
/
If this is the first open to succeed, adjust things to suit.
/
if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {
state->info->flags |= UIF_NORMAL_ACTIVE;
uart_update_termios(state);
}
fail:
return retval;
}
在这里函数里,继续完成操作的设备文件所对应state初始化.现在用户空间open这个设备了.即要对这个文件进行操作了.那uart_port也要开始工作了.即调用uart_startup()使其进入工作状态.当然,也需要初始化uart_port所对应的环形缓冲区circ_buf.即state->info-> xmit.
特别要注意,在这里将tty->driver_data = state;这是因为以后的操作只有port相关了,不需要去了解uart_driver的相关信息.
跟踪看一下里面调用的两个重要的子函数. uart_get()和uart_startup().先分析uart_get().代码如下:
static struct uart_state uart_get(struct uart_driver drv, int line)
{
struct uart_state state;
int ret = 0;
state = drv->state + line;
if (mutex_lock_interruptible(&state->mutex)) {
ret = -ERESTARTSYS;
goto err;
}
state->count++;
if (!state->port || state->port->flags & UPF_DEAD) {
ret = -ENXIO;
goto err_unlock;
}
if (!state->info) {
state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL);
if (state->info) {
init_waitqueue_head(&state->info->open_wait);
init_waitqueue_head(&state->info->delta_msr_wait);
/
Link the info into the other structures.
/
state->port->info = state->info;
tasklet_init(&state->info->tlet, uart_tasklet_action,
(unsigned long)state);
} else {
ret = -ENOMEM;
goto err_unlock;
}
}
return state;
err_unlock:
state->count—;
mutex_unlock(&state->mutex);
err:
return ERR_PTR(ret);
}
从代码中可以看出.这里注要是操作是初始化state->info.注意port->info就是state->info的一个副本.即port直接通过port->info可以找到它要操作的缓存区.
uart_startup()代码如下:
static int uart_startup(struct uart_state state, int init_hw)
{
struct uart_info info = state->info;
struct uart_port port = state->port;
unsigned long page;
int retval = 0;
if (info->flags & UIF_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, &info->tty->flags);
if (port->type == PORT_UNKNOWN)
return 0;
/
Initialise and allocate the transmit and temporary
buffer.
/
if (!info->xmit.buf) {
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
info->xmit.buf = (unsigned char ) page;
uart_circ_clear(&info->xmit);
}
retval = port->ops->startup(port);
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 (info->tty->termios->c_cflag & CBAUD)
uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
}
if (info->flags & UIF_CTS_FLOW) {
spin_lock_irq(&port->lock);
if (!(port->ops->get_mctrl(port) & TIOCM_CTS))
info->tty->hw_stopped = 1;
spin_unlock_irq(&port->lock);
}
info->flags |= UIF_INITIALIZED;
clear_bit(TTY_IO_ERROR, &info->tty->flags);
}
if (retval && capable(CAP_SYS_ADMIN))
retval = 0;
return retval;
}
在这里,注要完成对环形缓冲,即info->xmit的初始化.然后调用port->ops->startup( )将这个port带入到工作状态.其它的是一个可调参数的设置,就不详细讲解了.
七:设备节点的write操作
Write操作对应的操作接口为uart_write( ).代码如下:
static int
uart_write(struct tty_struct tty, const unsigned char buf, int count)
{
struct uart_state state = tty->driver_data;
struct uart_port port;
struct circ_buf circ;
unsigned long flags;
int c, ret = 0;
/
还没有评论,来说两句吧...