Linux USB 打印机 Gadget 驱动程序¶
06/04/2007
版权 (C) 2007 Craig W. Nadler <craig@nadler.us>
概述¶
如果您正在使用 Linux 作为嵌入式操作系统编写打印机固件,则可以使用此驱动程序。此驱动程序与在 Linux 主机系统上使用打印机无关。
您将需要一个 USB 设备控制器和一个接受使用 Linux USB Gadget API 的 gadget /“设备类”驱动程序的 Linux 驱动程序。加载 USB 设备控制器驱动程序后,加载打印机 gadget 驱动程序。这将向连接到 USB 设备端口的 USB 主机呈现打印机接口。
此驱动程序专为在用户模式下运行的打印机固件而设计。用户模式打印机固件将使用设备文件从内核模式打印机 gadget 驱动程序读取和写入数据。当 USB HOST 发送设备请求以获取打印机状态时,打印机将返回一个打印机状态字节。用户空间固件可以使用设备文件 /dev/g_printer 读取或写入此状态字节。支持阻塞和非阻塞读取/写入调用。
如何使用此驱动程序¶
加载 USB 设备控制器驱动程序和打印机 gadget 驱动程序。以下示例使用 Netchip 2280 USB 设备控制器驱动程序
modprobe net2280
modprobe g_printer
加载打印机 gadget 时可以使用以下命令行参数(例如:modprobe g_printer idVendor=0x0525 idProduct=0xa4a8)
- idVendor
这是设备描述符中使用的供应商 ID。默认值为 Netchip 供应商 ID 0x0525。在发布产品之前,您必须更改为您自己的供应商 ID。如果您计划发布产品且尚未拥有供应商 ID,请访问 www.usb.org 了解如何获取。
- idProduct
这是设备描述符中使用的产品 ID。默认值为 0xa4a8,如果您有任何其他 USB 产品,则应将其更改为未使用的 ID。最好从例如 0x0001 开始对您的产品进行编号。
- bcdDevice
这是您的产品版本号。最好在此处放置您的固件版本。
- iManufacturer
包含供应商名称的字符串。
- iProduct
包含产品名称的字符串。
- iSerialNum
包含序列号的字符串。对于您产品的每个单元,都应更改此字符串。
- iPNPstring
用于此打印机的 PNP ID 字符串。您需要在线命令行上设置,或者硬编码用于您的打印机产品的 PNP ID 字符串。
- qlen
每个端点使用的 8k 缓冲区数量。默认值为 10,您应针对您的产品进行调整。您可能还需要针对您的产品调整每个缓冲区的大小。
使用示例代码¶
此示例代码与 stdout 对话,而不是打印引擎。
要编译以下测试代码
将其保存到一个名为 prn_example.c 的文件中
使用以下命令编译代码
gcc prn_example.c -o prn_example
要从主机读取打印机数据到 stdout
# prn_example -read_data
要将打印机数据从文件 (data_file) 写入主机
# cat data_file | prn_example -write_data
要获取 gadget 驱动程序的当前打印机状态:
# prn_example -get_status
Printer status is:
Printer is NOT Selected
Paper is Out
Printer OK
要将打印机设置为已选定/联机
# prn_example -selected
要将打印机设置为未选定/脱机
# prn_example -not_selected
要将纸张状态设置为缺纸
# prn_example -paper_out
要将纸张状态设置为已装纸
# prn_example -paper_loaded
要将错误状态设置为打印机正常
# prn_example -no_error
要将错误状态设置为错误
# prn_example -error
示例代码¶
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/poll.h>
#include <sys/ioctl.h>
#include <linux/usb/g_printer.h>
#define PRINTER_FILE "/dev/g_printer"
#define BUF_SIZE 512
/*
* 'usage()' - Show program usage.
*/
static void
usage(const char *option) /* I - Option string or NULL */
{
if (option) {
fprintf(stderr,"prn_example: Unknown option \"%s\"!\n",
option);
}
fputs("\n", stderr);
fputs("Usage: prn_example -[options]\n", stderr);
fputs("Options:\n", stderr);
fputs("\n", stderr);
fputs("-get_status Get the current printer status.\n", stderr);
fputs("-selected Set the selected status to selected.\n", stderr);
fputs("-not_selected Set the selected status to NOT selected.\n",
stderr);
fputs("-error Set the error status to error.\n", stderr);
fputs("-no_error Set the error status to NO error.\n", stderr);
fputs("-paper_out Set the paper status to paper out.\n", stderr);
fputs("-paper_loaded Set the paper status to paper loaded.\n",
stderr);
fputs("-read_data Read printer data from driver.\n", stderr);
fputs("-write_data Write printer sata to driver.\n", stderr);
fputs("-NB_read_data (Non-Blocking) Read printer data from driver.\n",
stderr);
fputs("\n\n", stderr);
exit(1);
}
static int
read_printer_data()
{
struct pollfd fd[1];
/* Open device file for printer gadget. */
fd[0].fd = open(PRINTER_FILE, O_RDWR);
if (fd[0].fd < 0) {
printf("Error %d opening %s\n", fd[0].fd, PRINTER_FILE);
close(fd[0].fd);
return(-1);
}
fd[0].events = POLLIN | POLLRDNORM;
while (1) {
static char buf[BUF_SIZE];
int bytes_read;
int retval;
/* Wait for up to 1 second for data. */
retval = poll(fd, 1, 1000);
if (retval && (fd[0].revents & POLLRDNORM)) {
/* Read data from printer gadget driver. */
bytes_read = read(fd[0].fd, buf, BUF_SIZE);
if (bytes_read < 0) {
printf("Error %d reading from %s\n",
fd[0].fd, PRINTER_FILE);
close(fd[0].fd);
return(-1);
} else if (bytes_read > 0) {
/* Write data to standard OUTPUT (stdout). */
fwrite(buf, 1, bytes_read, stdout);
fflush(stdout);
}
}
}
/* Close the device file. */
close(fd[0].fd);
return 0;
}
static int
write_printer_data()
{
struct pollfd fd[1];
/* Open device file for printer gadget. */
fd[0].fd = open (PRINTER_FILE, O_RDWR);
if (fd[0].fd < 0) {
printf("Error %d opening %s\n", fd[0].fd, PRINTER_FILE);
close(fd[0].fd);
return(-1);
}
fd[0].events = POLLOUT | POLLWRNORM;
while (1) {
int retval;
static char buf[BUF_SIZE];
/* Read data from standard INPUT (stdin). */
int bytes_read = fread(buf, 1, BUF_SIZE, stdin);
if (!bytes_read) {
break;
}
while (bytes_read) {
/* Wait for up to 1 second to sent data. */
retval = poll(fd, 1, 1000);
/* Write data to printer gadget driver. */
if (retval && (fd[0].revents & POLLWRNORM)) {
retval = write(fd[0].fd, buf, bytes_read);
if (retval < 0) {
printf("Error %d writing to %s\n",
fd[0].fd,
PRINTER_FILE);
close(fd[0].fd);
return(-1);
} else {
bytes_read -= retval;
}
}
}
}
/* Wait until the data has been sent. */
fsync(fd[0].fd);
/* Close the device file. */
close(fd[0].fd);
return 0;
}
static int
read_NB_printer_data()
{
int fd;
static char buf[BUF_SIZE];
int bytes_read;
/* Open device file for printer gadget. */
fd = open(PRINTER_FILE, O_RDWR|O_NONBLOCK);
if (fd < 0) {
printf("Error %d opening %s\n", fd, PRINTER_FILE);
close(fd);
return(-1);
}
while (1) {
/* Read data from printer gadget driver. */
bytes_read = read(fd, buf, BUF_SIZE);
if (bytes_read <= 0) {
break;
}
/* Write data to standard OUTPUT (stdout). */
fwrite(buf, 1, bytes_read, stdout);
fflush(stdout);
}
/* Close the device file. */
close(fd);
return 0;
}
static int
get_printer_status()
{
int retval;
int fd;
/* Open device file for printer gadget. */
fd = open(PRINTER_FILE, O_RDWR);
if (fd < 0) {
printf("Error %d opening %s\n", fd, PRINTER_FILE);
close(fd);
return(-1);
}
/* Make the IOCTL call. */
retval = ioctl(fd, GADGET_GET_PRINTER_STATUS);
if (retval < 0) {
fprintf(stderr, "ERROR: Failed to set printer status\n");
return(-1);
}
/* Close the device file. */
close(fd);
return(retval);
}
static int
set_printer_status(unsigned char buf, int clear_printer_status_bit)
{
int retval;
int fd;
retval = get_printer_status();
if (retval < 0) {
fprintf(stderr, "ERROR: Failed to get printer status\n");
return(-1);
}
/* Open device file for printer gadget. */
fd = open(PRINTER_FILE, O_RDWR);
if (fd < 0) {
printf("Error %d opening %s\n", fd, PRINTER_FILE);
close(fd);
return(-1);
}
if (clear_printer_status_bit) {
retval &= ~buf;
} else {
retval |= buf;
}
/* Make the IOCTL call. */
if (ioctl(fd, GADGET_SET_PRINTER_STATUS, (unsigned char)retval)) {
fprintf(stderr, "ERROR: Failed to set printer status\n");
return(-1);
}
/* Close the device file. */
close(fd);
return 0;
}
static int
display_printer_status()
{
char printer_status;
printer_status = get_printer_status();
if (printer_status < 0) {
fprintf(stderr, "ERROR: Failed to get printer status\n");
return(-1);
}
printf("Printer status is:\n");
if (printer_status & PRINTER_SELECTED) {
printf(" Printer is Selected\n");
} else {
printf(" Printer is NOT Selected\n");
}
if (printer_status & PRINTER_PAPER_EMPTY) {
printf(" Paper is Out\n");
} else {
printf(" Paper is Loaded\n");
}
if (printer_status & PRINTER_NOT_ERROR) {
printf(" Printer OK\n");
} else {
printf(" Printer ERROR\n");
}
return(0);
}
int
main(int argc, char *argv[])
{
int i; /* Looping var */
int retval = 0;
/* No Args */
if (argc == 1) {
usage(0);
exit(0);
}
for (i = 1; i < argc && !retval; i ++) {
if (argv[i][0] != '-') {
continue;
}
if (!strcmp(argv[i], "-get_status")) {
if (display_printer_status()) {
retval = 1;
}
} else if (!strcmp(argv[i], "-paper_loaded")) {
if (set_printer_status(PRINTER_PAPER_EMPTY, 1)) {
retval = 1;
}
} else if (!strcmp(argv[i], "-paper_out")) {
if (set_printer_status(PRINTER_PAPER_EMPTY, 0)) {
retval = 1;
}
} else if (!strcmp(argv[i], "-selected")) {
if (set_printer_status(PRINTER_SELECTED, 0)) {
retval = 1;
}
} else if (!strcmp(argv[i], "-not_selected")) {
if (set_printer_status(PRINTER_SELECTED, 1)) {
retval = 1;
}
} else if (!strcmp(argv[i], "-error")) {
if (set_printer_status(PRINTER_NOT_ERROR, 1)) {
retval = 1;
}
} else if (!strcmp(argv[i], "-no_error")) {
if (set_printer_status(PRINTER_NOT_ERROR, 0)) {
retval = 1;
}
} else if (!strcmp(argv[i], "-read_data")) {
if (read_printer_data()) {
retval = 1;
}
} else if (!strcmp(argv[i], "-write_data")) {
if (write_printer_data()) {
retval = 1;
}
} else if (!strcmp(argv[i], "-NB_read_data")) {
if (read_NB_printer_data()) {
retval = 1;
}
} else {
usage(argv[i]);
retval = 1;
}
}
exit(retval);
}