s3c6410的UART设备驱动(3)

本文详细解析了UART驱动在Linux内核中的注册与卸载过程,重点介绍了uart_add_one_port和uart_remove_one_port函数的工作原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇说到了第二部分,但没说完,这一篇接着说第二部分,如下这部分:

在模块初始化是调用uart_register_driver和uart_add_one_port注册UART驱动并添加端口,在模块卸载时调用uart_unregister_driver和uart_remove_one_port以注销UART驱动并移除端口。

1、先来看uart_add_one_port函数,源码如下:

/**
 * 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 s3c24xx_serial_probe(struct platform_device *dev,struct s3c24xx_uart_info *info)函数中被调用,

和平台设备有关的函数。

调用如下:

samsung.c

int s3c24xx_serial_probe(struct platform_device *dev,
struct s3c24xx_uart_info *info)
{
struct s3c24xx_uart_port *ourport;
int ret;
。。。
ourport = &s3c24xx_serial_ports[probe_index];
probe_index++;


ret = s3c24xx_serial_init_port(ourport, info, dev);
。。。
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);//ourport->port=struct uart_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__);
。。。

return 0;
}


s3c2410.c

static int s3c2410_serial_probe(struct platform_device *dev)
{
return s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
}

static struct platform_driver s3c2410_serial_driver = {
.probe = s3c2410_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
.name = "s3c2410-uart",
.owner = THIS_MODULE,
},
};

static int __init s3c2410_serial_init(void)
{
return s3c24xx_serial_init(&s3c2410_serial_driver, &s3c2410_uart_inf);
}
static void __exit s3c2410_serial_exit(void)
{
platform_driver_unregister(&s3c2410_serial_driver);
}
module_init(s3c2410_serial_init);
module_exit(s3c2410_serial_exit);


int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state;
int ret = 0;
struct device *tty_dev;
........
if (port->line >= drv->nr)
return -EINVAL;

state = drv->state + port->line;//可能有多个port,这里获取对应的port口的state

mutex_lock(&port_mutex);
mutex_lock(&state->mutex);
if (state->port) {
ret = -EINVAL;
goto out;
}

state->port = port;

/*

对state结构体中的成员port赋值,部分源码如下:

struct uart_state {
........
struct uart_info*info;
struct uart_port*port;
struct mutex mutex;
};

*/

state->pm_state = -1;
port->cons = drv->cons;

if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);

}

uart_configure_port(drv, state, port);

看函数名就知道了干什么的。源码如下:先说下 struct uart_port *port结构体有什么用?在我看来,一个此种结构体的实例,就表示有一个串口可供我们使用。在列出在第一篇中列出的这个结构体的实例(其中的一部分),如下所示:

static struct s3c24xx_uart_ports s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = { //struct uart_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,
}
}看这些都是什么?是不是和一个具体的串口很相关,如中断等等。

在看一下uart_configure_port函数

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 = 0;
if (port->flags & UPF_AUTO_IRQ)
flags |= UART_CONFIG_IRQ;
if (port->flags & UPF_BOOT_AUTOCONF) {//UPF_BOOT_AUTOCONF
if (!(port->flags & UPF_FIXED_TYPE)) {
port->type = PORT_UNKNOWN;
flags |= UART_CONFIG_TYPE;
}

port->ops->config_port(port, flags);//s3c24xx_serial_config_port=request mem region
}

有关的一些标志位,通过port->ops->config_port(port, flags)这个函数进行。是在下面这个结构中的函数,和具体硬件有关。这个结构体在第一篇中,也有说明。

/*
 * This structure describes all the operations that can be
 * done on the physical hardware.
 */
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void(*set_mctrl)(struct uart_port *, unsigned int mctrl);
......
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);
.....
/*
* 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 *);
........
};

static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
。。。
.config_port = s3c24xx_serial_config_port,
。。
};

static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
。。。
.fifosize = 16,
.ops =
&s3c24xx_serial_ops,
。。。
}
},
};

static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
{
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);//s3c2400_uart_inf
if (flags & UART_CONFIG_TYPE &&s3c24xx_serial_request_port(port) == 0)
port->type = info->type; //s3c24xx_serial_request_port=request memory
}

static int s3c24xx_serial_request_port(struct uart_port *port)
{
const char *name = s3c24xx_serial_portname(port);
return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
}


if (port->type != PORT_UNKNOWN) {
unsigned long flags;
/*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);
spin_unlock_irqrestore(&port->lock, flags);
/*
* 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);
这个函数以前也说过,主要和sysfs文件系统有关-添加注册port->dev   //device_create 创建一个设备并注册到内核驱动架构
return ret;
}

对应的uart_remove_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
 *
 
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
{
......
/*
* 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);

/*
* 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.
*/
}

看这些注释,就可以明白个大概。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值