linux serial构架分析及驱动开发(2)

原文地址:http://blog.csdn.net/sirzjp/article/details/6163429

上节介绍了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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SATA (Serial ATA) 是一种高速传输数据的接口标准,它已经成为现代计算机中硬盘驱动器的主要接口标准之一。Linux 内核中的 SATA 驱动程序主要用于支持 SATA 硬盘控制器。 SATA 驱动程序的源代码主要包括以下几个部分: 1. 驱动程序注册和初始化 在 Linux 内核启动时,SATA 驱动程序会自动注册到内核中,以便能够识别并支持 SATA 硬盘控制器。驱动程序的初始化主要包括以下几个步骤: - 检查硬件设备是否存在并可用 - 分配内存空间和初始化数据结构 - 注册中断处理函数 - 设置硬件设备的工作模式和参数 2. 块设备驱动 SATA 硬盘控制器对应的块设备驱动程序主要实现以下几个功能: - 探测硬盘的容量和参数 - 支持读写操作 - 支持块设备的缓存和缓存管理 块设备驱动程序的核心代码包括请求队列管理、命令和数据传输、块设备缓存管理等模块。 3. 中断处理程序 当 SATA 硬盘控制器发生中断时,SATA 驱动程序会调用相应的中断处理程序来处理中断事件。中断处理程序主要实现以下几个功能: - 读取和处理硬件设备的状态信息 - 处理传输命令和数据的完成情况 - 唤醒等待中的进程 4. 控制器驱动程序 SATA 控制器驱动程序主要用于管理和控制 SATA 控制器的工作,包括以下几个功能: - 初始化和配置控制器 - 管理控制器的中断和 DMA 控制 - 处理控制器的错误和异常情况 控制器驱动程序的核心代码主要包括控制器的初始化、中断处理、DMA 控制等模块。 总的来说,SATA 驱动程序的源代码主要涉及硬件设备的初始化和配置、块设备驱动、中断处理程序和控制器驱动程序等几个方面。在理解和分析 SATA 驱动程序的源代码时,需要掌握相关的硬件知识和 Linux 内核编程技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值