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 对话,而不是打印引擎。

要编译以下测试代码

  1. 将其保存到一个名为 prn_example.c 的文件中

  2. 使用以下命令编译代码

    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);
}