编写Linux并行接口字符设备驱动
By Claudia Salzberg Rodriguez, Gordon Fischer, Steven Smolski
KEY:Linux 驱动程序 并口
INTRO
本实验项目是为一个只有很简单功能的并行端口控制器( parallel port controller)写Linux驱动。现在的PC并口控制器一般是集成在南桥芯片的超级IO中。本实验驱动是对编写字符类型设备驱动一很好的示范。实验代码不太实用,但你可以自己动手改善它。因为我是在设备寄存器一级写代码,所以这些代码也适用于PowerPC平台。
我们并口驱动模块(没有特指的话一般指可动态加载模块)实现了标准的open()、 close()和最重要的ioctl()系统调用函数,展示了设备驱动结构和工作原理。我们没有现实read()或write(),因为本实验适合用ioctl()替代之。
我们开始先大致描述一下如何与并口通信,然后再分析最终的字符驱动模块的结构。我们使用ioctl()接口分别单独访问设备控制器的各个寄存器,最后创建一个应用程序来演示调用驱动程序。
Parallel Port Hardware
本项目使用X86作为硬件环境,不过最终驱动模块很容易被移植到PowerPC。现在并口在嵌入式 PowerPC 平台虽然还很常见,但在桌面PC已经越来越少见了,比如在第四第五代PC。
与并口通信时,C代码我们分别使用函数inb()和outb()。我也可以使用独立于具体体系(如x86和PPC)的函数readb()writeb(),这是两个在io.h的宏定义。
X86系统的并口一般集成在系统板上的Superio设备上,或者也可以以独立的扩展卡(PCI)方式添加到主机。那么底层配置方面,并口使用了什么地址与主机联系呢?又有没有使用中断信号呢?你可以进入BIOS设备查看并口占用了哪些系统I/O地址空间。一般X86系统里,并口可能使用三个基址:0x278, 0x378, 0x3bc,使用IRQ7。并口有三个8位的寄存器(一个占用一个I/O空间地址,所以并口只占三个I/O地址空间),比如以下使用0x378作基址的情况:
[*]
低电平有效(Active low)
KEMIN:都说并行端口只是一个端口,不是设备控制器,怎么还会有寄存器的概念?那并口所连接的设备控制寄存器的角色是什么?
- The data register contains the 8 bits to write out to the pins on the connector.
- The status register contains the input signals from the connector.
- The control register sends specific control signals to the connector.
并口的连接头是25-pin D-shell (DB-25),以下是连接头的针脚与信号的映射表:
注意:并口对静电和电流是敏感的。请不要用你们的板载并口作实验,除非你对硬件很熟悉或不担心烧掉主板。建议使用独立的并口卡作实验。
改装连接头(看逻辑图
)
For input operations, we will jumper D7 (pin 9) to Acknowledge (pin 10) and D6 (pin 8) to Busy (pin 11) with 470 ohm resistors. To monitor output, we drive LEDs with data pins D0 through D4 by using a 470 ohm current limiting resistor. We can do this by using an old printer cable or a 25-pin male D-Shell connector from a local electronics store.
datasheet
一位优秀的寄存器级别的程序员(KEMIN:其实就是汇编程序员)总是非常熟悉他手上硬件的细节。包括很迅速地查找出某一个并口设备的 datasheet
(KEMIN:注意是设备[控制器
]的 datasheet,不是并口的datasheet,并口本身不是设备)。datasheet包含了诸如控制器各寄存器功能及设备电流上限等一些设备操作信息。程序员在操作前必须仔细阅读设备[控制器
]的datasheet。
Parallel Port Software
本项目实验的驱动源码主要是三个:parll.c、parll.h和Make文件。
1. Setting Up the File Operations (fops) 创建文件操作数据结构(fops)
上面已经说了,驱动模块实现了标准的open()、 close()和ioctl()函数,此外还需要模块初始化代码和清理工作代码。
我们的第一步工作是创建文件操作结构(file operations structure)。这个结构在 /linux/fs.h定义,该结构有很多本驱动不实现的抽象接口函数。
通过实现这个结构的抽象接口,告知内核[具体实现函数代码](open, release, and ioctl)的位置。
parll.c-->file_operations parlport_fops
接着分别实现open() 和close(),本实验这两个函数没有实质的工作:
parll.c-->parlport_open()
实现 ioctl()函数。注意下面的声明代码必须放在源码文件parll.c的前面:
parll.c-->parlport_ioctl()
ioctl()函数设计的目的是给用户实现自定义的I/O操作(KEMIN:也就是标准I/O操作(像读或写)不能满足的操作)。本项目简单的演示了通过并口实现三个I/O命令(command)访问控制器寄存器数据:
* The DATA_OUT command sends a value to the data register,
* the GET_STATUS command reads from the status register,
* the CTRL_OUT command is available to set the control signals to the port.
虽然根据[美化原则](better methodology),读写设备操作的特定细节应该分别隐藏于read() 和write()两个接口函数内,但是本实验主要目的是演示I/O,而不是数据封装,所以选择使用ioctl。
代码中使用的三个I/O宏命令在头文件 parll.h中定义。parlport_ioctl() 的开始处有对输入命令的合法检测的逻辑。由于应用程序会引
用同一个头文件,所以这些命令的定义是一致的。
2. Setting Up the Module Initialization Routine 创建模块初始化函数
初始化代码负责把驱动模块关联入操作系统,复杂一些驱动类型(比如USB)可能需要一些必要的数据结构会在这个时候被初始化。因为并口驱动不需复杂的数据结构,所以以下只是简单的地把驱动模块注册入操作系统:
parll.c-->parll_init()
The register_chrdev() function takes in the requested major number (discussed in Section 5.2 and later in Chapter 10; if 0, the kernel assigns one to the module). Recall that the major number is kept in the inode structure, which is pointed to by the dentry structure, which is pointed to by a file struct. The second parameter is the name of the device as it will appear in /proc/devices. The third parameter is
the file operations structure that was just shown.
Upon successfully registering, our init routine calls request_region() with the base address of the parallel port and the length (in bytes) of the range of registers we are interested in.
The init_module() function returns a negative number upon failure.
3. Setting Up the Module Cleanup Routine 创建模块清理函数
The cleanup_module() function is responsible for unregistering the module and releasing the I/O range that we requested earlier:
parll.c-->parll_cleanup( )
Finally, we include the required init and cleanup entry points.
parll.c
4. Inserting the Module 安装驱动模块
We can now insert our module into the kernel, as in the previous projects, by using
Looking at /var/log/messages shows us our init() routine output as before, but make specific note of the major number returned.
In previous projects, we simply inserted and removed our module from the kernel. We now need to associate our module with the filesystem with the mknod command. From the command line, enter the following:
The parameters:
*c. Create a character special file (as opposed to block)
*/dev/parll. The path to our device (for the open call)
*XXX. The major number returned at init time (from /var/log/messages)
*0. The minor number of our device (not used in this example)
For example, if you saw a major number of 254 in /var/log/messages, the command would look like this:
5. Application Code 应用程序代码
Here, we created a simple application that opens our module and starts a binary count on the D0 through D7 output pins.
Compile this code with gcc app.c. The executable output defaults to a.out:
app.c
如果把连接头改装成如下的样子,那么忙碌位(busy)和应答位(ack)也可高位有效。用户代码可以分别读取两最高有效数据位代表它们。
Figure 5.5. Built Connector
本实验项目只是演示了编写一个字符设备驱动需要的主要元素。不过有了这个演示例子,你完全可以编写自己的字符设备驱动程序了。如果要为这个驱动添加中断处理,则必须在 init_module()内调用request_irq()并传递所需IRQ和中断处理函数的函数名。
以下是对本实验驱动程序改进的一些建议:
Here are some suggested additions to the driver:
- *Make parallel port module service-timer interrupts to poll input.
- How can we multiplex 8 bits of I/O into 16, 32, 64? What is sacrificed?
- Send a character out the serial port from the write routine within the module.
- Add an interrupt routine by using the ack signal.
分享到:
相关推荐
《嵌入式Linux系统中字符...总之,该文献提供了嵌入式Linux系统中字符设备驱动程序开发的全面指南,包括开发环境的配置、驱动程序的编写、调试和发布等关键步骤,对于进行嵌入式系统开发的工程师具有很高的参考价值。
除了上述主要类型的设备驱动,书中的其他章节还涵盖了并行端口、串行端口、USB设备、PCI设备、GPIO、中断控制器等多种硬件的驱动编写,以及驱动程序的调试方法和技巧。这些内容使得读者能够处理各种复杂的硬件接口和...
本教程将详细讲解如何为一款使用8080并行接口的小尺寸液晶屏(LCD)在Linux下实现驱动程序。 首先,8080并行接口是一种常见的微处理器接口,广泛应用于各种显示设备,如LCD显示器。这种接口有8条数据线(D0-D7),...
本书主要分为几个部分,首先介绍了Linux内核的基本结构和设备驱动的基础知识,包括内核模块的加载和卸载、内核编程接口等。这部分内容是理解和编写驱动程序的基础,通过学习,读者可以了解Linux是如何组织和管理硬件...
在Linux操作系统中,字符设备驱动程序是连接硬件设备与用户空间应用程序的关键组件。它们负责处理对设备的输入输出操作,并确保数据的正确传输。本文主要介绍Linux字符设备驱动中的核心概念,包括主设备号、次设备号...
字符设备驱动,如串行和并行接口,处理的是连续的数据流;块设备驱动,如磁盘驱动,处理的是以固定大小块为单位的数据;网络设备驱动用于处理网络数据包,如网卡;杂项设备驱动则包括那些不归属于以上三类的设备,如...
基于嵌入式Linux的字符设备驱动程序设计涉及的IT知识点相当丰富,主要涵盖了嵌入式Linux系统的应用、字符设备驱动程序的开发流程、以及与特定硬件ADS8364模数转换器的接口实现等。 首先,嵌入式Linux系统是整个驱动...
该书旨在帮助读者理解和掌握如何为Linux系统编写高效的设备驱动,以便充分利用硬件资源,提高系统的稳定性和性能。下面将详细阐述书中涉及的主要知识点。 1. **Linux内核基础**:在学习设备驱动之前,了解Linux内核...
设备管理的主要任务包括设备驱动程序的编写和管理,设备的分配与释放,以及实现设备间的并行操作。设备驱动程序是操作系统与硬件之间的桥梁,它负责执行具体的I/O操作,控制硬件设备,并处理与硬件相关的异常。设备...
3. 嵌入式Linux系统中的字符设备驱动模型和驱动程序设计。 4. 驱动程序的编译、加载过程及其在Linux内核中的管理。 5. 数据在ARM和DSP之间的高效可靠传输机制。 这些内容对于理解和实施嵌入式Linux系统开发,尤其是...
在给定的“linux-adc-key驱动.rar”文件中,包含的是针对Linux 4.4内核的ADC驱动实现,特别针对RK3308处理器的ADC驱动编写和设备树配置。RK3308是一款基于ARM Cortex-A53架构的系统级芯片,常用于物联网(IoT)设备和...
2. **14**:这部分源码可能涵盖了字符设备驱动的编写,包括注册和注销字符设备(`register_chrdev()`、`unregister_chrdev()`),以及实现设备文件的操作函数,如`read()`、`write()`、`open()`、`release()`等。...
6. **用户空间接口**:为了方便用户空间应用程序控制LCD,驱动通常会提供一个字符设备或通过ioctl调用的接口。 在“255318”这个文件中,很可能包含了LCD驱动的源代码、文档或者示例,可以帮助开发者理解LCD驱动的...
论文以Atmel公司的AT91RM9200芯片的并行输入/输出控制器为例,详细介绍了如何为小型医疗设备开发字符设备驱动。这个过程通常包括设计驱动程序的架构,编写与硬件交互的底层函数,实现file_operations结构体中的函数...
#### 六、高级字符驱动操作 - **ioctl命令**:`ioctl`是一个非常强大的系统调用,可用于执行设备特定的操作。 - **阻塞I/O**:阻塞I/O是一种等待方式,当请求的数据不可用时,系统会将当前进程挂起直到数据可用为止...
对于“Linux下的并口时钟驱动程序”,我们主要关注的是如何利用并行接口(Parallel Port)来实现一个时钟显示功能,通常通过LED矩阵来显示时间。并口在早期的计算机中广泛用于打印机等外设,但在现代系统中,由于...
在Linux操作系统中,驱动程序是连接硬件设备与操作系统内核的关键组件。对于12232点阵液晶屏,驱动程序扮演着至关重要的角色,它允许Linux系统与这种特定的显示器进行通信,从而显示文本和其他图形信息。"12232点阵...
9. **字符设备驱动模型**:16c554驱动程序通常遵循Linux的字符设备模型,包括注册设备节点、实现read/write系统调用等。 10. **调试技巧**:通过dmesg、sysfs、/proc文件系统以及kernel log等工具进行驱动的调试。 ...
字符驱动主要处理串行或并行通信接口,如串口、键盘和鼠标。块驱动则针对存储设备,如硬盘、闪存等,它们处理的是连续的数据块。而网络驱动涉及网络协议栈,负责数据的接收和发送。 开发Linux驱动时,首先需要理解...
### Linux设备驱动第3版知识点概述 #### 一、引言与基础知识 - **设备驱动的角色**:在《Linux设备驱动第3版》的第一章中,作者首先介绍了设备驱动的基本概念及其在系统中的作用。设备驱动是操作系统和硬件之间的...