您的位置 首页 > 数码极客

(linux如何注册一个中断)linux如何删除一行

上一章我们介绍了串口的数据结构,本章我们介绍串口相关的操作接口,并介绍串口的注册与注销接口,本章主要包括如下几个方面的内容:

  1. 串口控制器、串口的注册与注销
  2. 串口子系统操作接口的介绍说明
  3. 串口驱动开发流程说明
  4. 串口子系统架构总结

串口控制器、串口的注册与注销

既然串口子系统是对tty子系统的封装,因此其注册与注销也同样包括tty_driver、tty_port两部分。在之前阅读tty子

系统时,我们可以发现真正实现读写tty端口的操作全部是由tty_driver数据结构中的操作接口实现的,而tty_port中仅仅是实现具体tty端口的使能与去使能等操作。而在串口子系统中,为了减少开发量,同时又要对接tty子系统,串口子系统内部为tty_driver注册了通用的操作接口,而把不同串口控制器操作接口的操作又移至uart_port->ops接口中,从而使串口驱动的开发只需要关注uart_potr->ops即可,无需再同时关注tty_driver->ops、tty_port->ops。

串口控制器驱动注册与注销

针对串口控制器驱动,其注册接口为uart_register_driver,而针对uart_driver的注册,开发人员主要需要设置

uart_driver的driver_name、dev_name、nr、cons,其中dev_name、nr表示串口的名称前缀、串口个数,而cons则表示该串口控制器是否支持控制台功能,若支持控制台则设置该变量。

如下就是uart_driver的注册接口,其实现的功能较简单,主要功能如下:

  1. 调用alloc_tty_driver申请tty_driver类型的内存,并设置tty_driver的各成员变量,同时设置其ops成员为uart_ops;
  2. 根据串口的个数,为每一个串口申请对应的uart_state类型的内存空间(也就完成了对应tty_port的创建),用于存储每一个串口的资源相关的信息以及串口对应tty_port信息,并为tty_port设置其ops成员为uart_port_ops;

我们知道tty_port中包含接收数据的缓存,但却没有发送数据的缓存,而uart_state中则包含了发送数据的缓存(即环形缓存区),因此借助uart_state、tty_port则实现了串口收发数据的缓存(在我们之前实现的虚拟串口中也定义了数据发送的buff,只不过是使用kfifo实现的)。


从上面的代码我们可以看到,针对所有的uart_driver,其tty_driver成员的ops都设置为uart_ops接口,而uart_ops的定义如下,下面简要说明下其实现的功能:

  1. open、close用于开启和关闭串口,通过调用struct uart_ops的startup、stop_rx、stop_tx,实现串口的开启与关闭操作;
  2. write接口实现将数据从串口发送出去(当然在uart_write中先将数据写入环形缓冲区中,再调用uart_port->ops->start_tx接口,实现数据的发送);
  3. uart_put_char接口主要将一个字节的数据拷贝到该串口的发送缓存中;
  4. Flush_chars接口的作用是将串口发送缓冲中的数据发送出去(调用uart_port->ops->start_tx接口,实现真正的数据发送);
  5. uart_write_room、uart_chars_in_buffer主要用于返回发送缓存中可用空间、已使用空间的字节数等;
  6. uart_flush_buffer接口主要用于flush该串口的发送缓存,并唤醒写等待队列;
  7. uart_throttle、uart_unthrottle主要是实现流控(包括硬件流控和软件流控,其中软件流控则通过定义一个特殊字符作为流控约束的标志位);
  8. uart_set_termios接口主要是termios设置,包括输入、输出参数设置、控制参数设置等,如上面说的软件流控中start、stop标志字符的设置,还包括串口波特率的设置、CRTSCTS、字节宽度的设置等等;
  9. uart_stop、uart_start、uart_hangup则主要串口停止、启动、挂起相关的接口,内部也是调用uart_port->ops->stop、start停止或启动一个发送操作;而uart_hangup则主要是挂起操作;
  10. uart_tiocmget、uart_tiocmset则主要是modem ctrl相关的操作接口。


在主要的接口中,也设置了tty_port->ops成员,我们看下uart定义的tty_port操作接口函数,这几个接口也捎带说明下:

  1. 针对activate,在tty子系统中,主要在tty_port_open中调用,实现tty端口的启用,但针对uart子系统而言,在其uart_tty_driver->open接口中,并没有调用tty_port_open接口,取而代之的是uart_startup、uart_ops->start,因此只需要实现uart_ops->start接口即可,此处定义的uart_port_activate并不会被调用,没有意义。。。
  2. 针对shutdown接口而言,在tty子系统中,主要是在tty_port_close接口中调用,而针对uart_tty_driver->close接口中,也没有调用uart_port_ops->shutdown接口(即uart_port_shutdown)

因此此处设置的activate、shutdown,没有任何意义,没有必要设置这两个变量


串口的注册接口

串口的注册接口为uart_add_one_port,该接口的定义如下,主要功能说明如下:

  1. 完成uart_state与uart_port的互相关联;
  2. 调用uart_configure_port,配置该串口(如mem、io资源的申请等);
  3. 调用tty_port_register_device_attr接口,完成tty_port与tty_driver的关联,并调用device_register完成tty_port对应device的注册,同时该动作也完成了tty_port对应device与tty_class的关联,同时也完成tty对应字符设备的创建(通过向应用层发送kobject add uevent,而udevd、mdev在接收到该uevent后,根据设备节点至,通过调用mknod接口完成设备文件inode的创建,也就完成了字符设备文件的创建),同时也为该tty端口对应的device创建了uart相关的属性信息(即tty_dev_attr_groups)


在uart port的注册时,为其对应的device创建了属性文件(即tty_dev_attr_groups),我们看下tty_dev_attr_groups的定义。包含的属性如下所示,包括串口类型、index、端口的iobase、中断、flag、发送fifo大小、uart clk、iomem_base等,当我们需要获取一个串口详细信息时,可进入串口对应的sysfs文件下对应文件中查看(我之前实现的虚拟串口也创一个文件属性,用于模拟向虚拟串口发送,当时是调用sysfs_create_group实现的,其实tty_port_register_device_attr接口即可实现tty port注册+创建属性文件,哎,我没看这个接口啊…)。


串口子系统操作接口的介绍说明

串口子系统提供的操作接口包含uart_open、uart_close、uart_write、uart_put_char uart_flush_chars、uart_write_room、uart_chars_in_buffer、uart_flush_buffer、uart_ioctl、uart_throttle、uart_unthrottle等接口,在上面也做了简要说明,此处则对应uart_open、uart_write这两个接口进行一个说明

uart_open接口

一般针对tty_driver->open接口而言,基本上就是调用tty_port_open接口,增加tty_port的引用计数、完成tty_port与tty_struct的关联,并调用tty_port->ops->active接口,使能该tty端口;而在串口的open函数中,虽然没有调用tty_port_open接口,但也实现了其中的tty_port的引用计数、完成tty_port与tty_struct的关联,也调用tty_port_block_til_ready等待该串口可用。因没有调用tty_port->ops->active接口,uart_open则通过调用uart_startup使能一个串口。

下面看下该接口的实现,该接口实现如下流程图(省略了异常判断),主要功能如下:

  1. 增加tty_port的引用计数(即tty_port->count++);
  2. 完成tty_struct与uart_state的关联、uart_port与uart_state的关联;
  3. 完成tty_port与tty_struct的关联(在此之前tty_struct已完成tty_port),至此完成tty_port与tty_struct的相互关联
  4. 调用uart_startup,用于启动一个串口,在uart_startup接口中,通过间接调用uart_port->ops->startup、uart_port->ops->set_termios完成一个端口的开启,并完成termios相关参数的设置(如波特率等)。
  5. 最后调用tty_port_block_til_ready,用于等待一个tty端口准备好(若该串口正处于关闭状态,则等该串口关闭完成后,则返回失败;若该串口是通过非阻塞模式打开,则返回打开成功;若该串口为阻塞打开,则必须等待已打开该串口的进程关闭该串口后,方能返回)


而tty_port_block_til_ready的作用如下:

  1. 若该端口处于hangup或者处于关闭状态中,则将该进程加入到close_wait等待队列,待hangup结束后,则返回EAGAIN;待close完成被唤醒后,则返回ERESTARTSYS;
  2. 若非上述情况,且是非阻塞,则设置port的标签为ASYNC_NORMAL_ACTIVE,并返回;
  3. 若非1中情况,且是第一次打开,则设置port的标签为ASYNC_NORMAL_ACTIVE,并返回
  4. ;若为阻塞,且不是上述1.2.3中情况,则当前进程等待,并加入open_wait,待上一个进程关闭后,本进程被唤醒,并继续执行打开操作。

uart_write接口

该接口的实现比较简单,代码如下:

  1. 将待发送的数据写入环形缓冲区;
  2. 调用uart_start,启动一次发送操作,而uart_start接口则主要调用uart_port->ops->start_tx接口,由具体串口设备的start_tx接口启动一次发送(如在该接口中使能tx中断,实现tx中断触发,而在tx中断处理函数中完成一次发送)


串口驱动开发流程说明

  1. 在module_init标识的初始化函数中,调用uart_register_driver,完成uart_driver的注册,主要设置串口字符设备文件名称的前缀、串口个数的设置等;
  2. 创建一个platform_driver,在platform_driver的probe接口中,获取待注册串口信息的资源信息,并定义struct uart_ops中各函数,并调用uart_add_one_port完成uart port的注册;
  3. 为每一个待注册的串口设备,创建一个platform device,并传递该串口相关的信息(若内核支持设备树,开发人员无需事先该步,只需在设备树中增加相应串口信息即可)。

基本上完成以上几步,即完成了一个串口驱动,其实最主要的就是实现struct uart_ops中各成员函数指针。下一章我们来完成一个虚拟的串口设备及其驱动。

串口子系统架构总结

串口子系统针对所有串口,完成了统一的tty_driver的变量的实现,而将每一个串口需要实现的内容全部移至uart_port、uart_pos中,而无需实现tty_driver的操作接口、tty_port的操作接口(均由串口子系统内部实现)。相对于tty子系统而言,确实方便了一些。

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“linux如何注册一个中断,linux如何删除一行,linux如何注册服务,Linux如何复制一行”边界阅读