linux serial构架分析及驱动开发(2)
上节介绍了serial驱动核心提供的重要数据结构,这一节将介绍serial核心提供给驱动开发的核心函数uart_register_driver向内核注册serial驱动(具体操作就是向内核注册一个tty_driver)以及uart_unregister_driver注销serial驱动。uart_add_one_port用于为串口驱动添加一个串口端口(在总线型设备驱动中,通常用于探测函数probe中),uart_remove_one_port用于删除一个已经添加到驱动中的串口端口(通常在卸载函数中调用)。
//uart_register_driver就是初始化一个tty_driver并把其加入tty core层,并对uart_state做一些初始化,起始就是写一个tty类驱动。
/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
*
* Register a uart driver with the core driver. We in turn register
* with the tty layer, and initialise the core driver per-port state.
*
* We have a proc file in /proc/tty/driver which is named after the
* normal driver.
*
* drv->port should be NULL, and the per-port structures should be
* registered using uart_add_one_port after this call has succeeded.
*/
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL; //定义一个tty_driver驱动指针
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); //为串口设备管理分配空间即uart_state数组
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; //tty设备类型
normal->subtype = SERIAL_TYPE_NORMAL; //tty设备子类型
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;
//TTY_DRIVER_DYNAMIC_DEV是不会在初始化的时候去注册device.也就是说在/dev/下没有动态生成结点
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv; //便于查找uart_driver
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); //把normal加入到tty core中
out:
if (retval < 0) {
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
//uart_unregister_driver完成与上面代码相反的操作
/**
* uart_unregister_driver - remove a driver from the uart core layer
* @drv: low level driver structure
*
* Remove all references to a driver from the core driver. The low
* level driver must have removed all its ports via the
* uart_remove_one_port() if it registered them with uart_add_one_port().
* (ie, drv->port == NULL)
*/
void uart_unregister_driver(struct uart_driver *drv)
{
struct tty_driver *p = drv->tty_driver;
tty_unregister_driver(p);
put_tty_driver(p);
kfree(drv->state);
drv->tty_driver = NULL;
}
//uart_add_one_port 向uart_driver加入一个可操作的端口,注意serial驱动的核心在端口的操作函数上
//即const struct uart_ops *ops的成员函数上,每个端口对应一个uart_state结构
/**
* uart_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @port: uart port structure to use for this port.
*
* This allows the driver to register its own uart_port structure
* with the core driver. The main purpose is to allow the low
* level uart drivers to expand uart_port, rather than having yet
* more levels of structures.
*/
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) //port->line 指在uart_state数组中的索引
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; //两者指向同一个uart_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); //注册tty设备
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;
}
//端口为配置的情况下调用端口的自动配置函数,否则直接报告端口信息以及modem控制设置等操作
static void uart_configure_port(struct uart_driver *drv, struct uart_state *state,
struct uart_port *port)
{
unsigned int flags;
/*
* If there isn't a port here, don't do anything further.
*/
if (!port->iobase && !port->mapbase && !port->membase) //设备不存在
return;
/*
* Now do the auto configuration stuff. Note that config_port
* is expected to claim the resources and map the port for us.
*/
flags = UART_CONFIG_TYPE;
if (port->flags & UPF_AUTO_IRQ)
flags |= UART_CONFIG_IRQ;
if (port->flags & UPF_BOOT_AUTOCONF) {
port->type = PORT_UNKNOWN;
port->ops->config_port(port, flags); //调用设备的自动配置函数,在后面的serial驱动例子中可以看看具体看什么
}
if (port->type != PORT_UNKNOWN) {
unsigned long flags;
uart_report_port(drv, port); //输出端口的相关信息
/* Power up port for set_mctrl() */
uart_change_pm(state, 0); //改变端口的电源状态
/*
* Ensure that the modem control lines are de-activated.
* keep the DTR setting that is set in uart_set_options()
* We probably don't need a spinlock around this, but
*/
spin_lock_irqsave(&port->lock, flags);
port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR); //设置串口modem控制
spin_unlock_irqrestore(&port->lock, flags);
/*
* If this driver supports console, and it hasn't been
* successfully registered yet, try to re-register it.
* It may be that the port was not available.
*/
if (port->cons && !(port->cons->flags & CON_ENABLED))
register_console(port->cons);
/*
* Power down all ports by default, except the
* console if we have one.
*/
if (!uart_console(port))
uart_change_pm(state, 3);
}
}
//uart_remove_one_port完成uart_add_one_port相反操作,删除一个已加入的串口端口
/**
* uart_remove_one_port - detach a driver defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @port: uart port structure for this port
*
* This unhooks (and hangs up) the specified port structure from the
* core driver. No further calls will be made to the low-level code
* for this port.
*/
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state = drv->state + port->line;
struct uart_info *info;
BUG_ON(in_interrupt());
if (state->port != port) //删除端口与驱动对应端口不匹配
printk(KERN_ALERT "Removing wrong port: %p != %p/n",
state->port, port);
mutex_lock(&port_mutex);
/*
* Mark the port "dead" - this prevents any opens from
* succeeding while we shut down the port.
*/
mutex_lock(&state->mutex);
port->flags |= UPF_DEAD; //设置端口不可用标志
mutex_unlock(&state->mutex);
/*
* Remove the devices from the tty layer
*/
tty_unregister_device(drv->tty_driver, port->line); //端口对应设备从内核注销掉
info = state->info;
if (info && info->port.tty)
tty_vhangup(info->port.tty);//处理tty挂起相关操作do_tty_hungup函数
/*
* All users of this port should now be disconnected from
* this driver, and the port shut down. We should be the
* only thread fiddling with this port from now on.
*/
state->info = NULL;
/*
* Free the port IO and memory resources, if any.
*/
if (port->type != PORT_UNKNOWN)
port->ops->release_port(port); //释放端口资源
/*
* Indicate that there isn't a port here anymore.
*/
port->type = PORT_UNKNOWN;
/*
* Kill the tasklet, and free resources.
*/
if (info) {
tasklet_kill(&info->tlet);//下半部机制确保tasklet不再被调用
kfree(info);
}
state->port = NULL;
mutex_unlock(&port_mutex);
return 0;
}
更多推荐
所有评论(0)