linux用户空间并口编程

本文介绍了如何在Linux环境下进行用户空间的并口编程,通过QEMU虚拟并口的重定向实现与PC设备交互。详细讲解了使用`/dev/port`直接访问IO端口以及利用`/dev/parport`设备的ioctl和write接口进行数据传输的方法,并提供了相应的测试代码示例。

qemu虚拟并口重定向

qemu可以使用参数 “-parallel dev” 将虚拟并口重定向至PC上的设备。比如重定向至一个打开的伪终端: -parallel /dev/pts/2
或者重定向至PC上的文件: -parallel file:/path/to/file


以下测试使用qemu运行并将并口重定向至文件 pptest.txt

使用/dev/port

linux内核提供 /dev/port 设备以供用户空间以常规文件读写方式直接访问IO端口。比如要读取地址为100的16位端口,只需seek到文件的100字节处并读取2字节数据,写端口数据亦然。
以并口为例,以下代码发送一串字符至地址为0x378的并口:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define PORT_FILE	"/dev/port"
#define PORT_ADDR	0x378
#define PORT_DATA	(PORT_ADDR + 0)
#define PORT_STAT	(PORT_ADDR + 1)
#define PORT_CTRL	(PORT_ADDR + 2)

static int fd = -1;

static unsigned char io_read(unsigned int port)
{
	unsigned char data;
	lseek(fd, port, SEEK_SET);
	read(fd, &data, 1);
	return data;
}

static void io_write(unsigned int port, unsigned char data)
{
	lseek(fd, port, SEEK_SET);
	write(fd, &data, 1);
}

int main(void)
{
	char msg[32] = "pptest: use /dev/port\r\n";
	int i;

	fd = open(PORT_FILE, O_RDWR);
	if (fd < 0) {
		fprintf(stderr, "open %s failed: %s\n", PORT_FILE, strerror(errno));
		return -1;
	}
	fprintf(stdout, "opened %s\n", PORT_FILE);
	fprintf(stdout, "status 0x%02x\n", io_read(PORT_STAT));

	for (i = 0; i < strlen(msg); i++) {
		io_write(PORT_DATA, (unsigned char)msg[i]);
		io_write(PORT_CTRL, 0x0D); /* strobe */
		io_write(PORT_CTRL, 0x0C);
	}
	fprintf(stdout, "send message done\n");

	close(fd);
	return 0;
}

生成测试程式 pptest_io :
gcc -static -o pptest_io pptest_io.c
在qemu中运行:

# ./pptest_io 
opened /dev/port
status 0xd9
send message done
# 

查看输出结果:

$ cat pptest.txt 
pptest: use /dev/port
$

使用/dev/parport

linux内核还提供 /dev/parport 设备以简化用户空间并口编程,相关模块为:

配置模块说明
CONFIG_PARPORTparport.ko提供通用的基本接口
CONFIG_PARPORT_PCparport_pc.ko提供PC标准并口驱动
CONFIG_PPDEVppdev.ko提供用户空间并口设备驱动,即/dev/parport

/dev/parport 提供了一组 ioctl 接口以供用户配置并口或直接访问IO端口,也支持使用 read/write 函数收发数据。
以下代码分别使用 ioctlwrite 的方式来发送字符串:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <asm/ioctl.h>
#include <linux/ppdev.h>

#define DEVICE	"/dev/parport0"

int main(void)
{
	char msg1[32] = "pptest: use /dev/parport0\r\n";
	char msg2[32] = "pptest: call write\r\n";
	unsigned char stat, ctrl, ctrl_s;
	int fd, i;

	/* open and claim port */
	fd = open(DEVICE, O_RDWR);
	if (fd < 0) {
		fprintf(stderr, "open %s failed: %s\n", DEVICE, strerror(errno));
		return -1;
	}
	fprintf(stdout, "%s opened\n", DEVICE);
	if (ioctl(fd, PPCLAIM) < 0) {
		fprintf(stderr, "PPCLAIM failed: %s\n", strerror(errno));
		close(fd);
		return -1;
	}
	fprintf(stdout, "port claimed\n");

	/* read status and initial control */
	ioctl(fd, PPRSTATUS, &stat);
	ioctl(fd, PPRCONTROL, &ctrl);
	fprintf(stdout, "status 0x%02x\n", stat);
	fprintf(stdout, "control 0x%02x\n", ctrl);
	ctrl = ctrl & ~0x01;
	ctrl_s = ctrl | 0x01;

	/* use 'ioctl' to send data */
	for (i = 0; i < strlen(msg1); i++) {
		ioctl(fd, PPWDATA, &msg1[i]);
		ioctl(fd, PPWCONTROL, &ctrl_s); /* strobe */
		ioctl(fd, PPWCONTROL, &ctrl);
	}
	fprintf(stdout, "send message 1 done\n");

	/* use 'write' to send data */
	if (write(fd, msg2, strlen(msg2)) != strlen(msg2))
		fprintf(stderr, "failed to write: %s\n", strerror(errno));
	else
		fprintf(stdout, "send message 2 done\n");

	ioctl(fd, PPRELEASE);
	fprintf(stdout, "port released\n");
	close(fd);
	return 0;
}

生成测试程式 pptest_dev :
gcc -static -o pptest_dev pptest_dev.c
在qemu中运行:

# insmod /lib/modules/3.12.74/parport.ko 
# insmod /lib/modules/3.12.74/parport_pc.ko 
parport_pc 00:04: reported by Plug and Play ACPI
parport0: PC-style at 0x378, irq 7 [PCSPP,TRISTATE]
# 
# insmod /lib/modules/3.12.74/ppdev.ko 
ppdev: user-space parallel port driver
# 
# ./pptest_dev 
/dev/parport0 opened
port claimed
status 0xd8
control 0x0c
send message 1 done
send message 2 done
port released
#

查看输出结果:

$ cat pptest.txt 
pptest: use /dev/parport0
pptest: call write
$
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值