Linux USB Printer Gadget Driver

通用

如果您正在使用Linux作为嵌入式操作系统编写打印机固件,则可能使用此驱动程序。这个驱动程序与在Linux主机系统上使用打印机没有任何关系。

您将需要一个USB设备控制器和Linux驱动程序,它接受一个使用Linux USB gadget API的gadget/“设备类”驱动程序。加载USB设备控制器驱动后,再加载printer gadget驱动。这将向USB设备端口连接的USB主机提供printer接口。

这个驱动程序是为在用户模式下运行的printer固件而设计的。用户模式printer固件将使用设备文件从内核模式printer gadget驱动程序读取和写入数据。当USB HOST发送获取打印机(printer)状态的设备请求时,打印机返回一个打印机状态字节。用户空间固件可以使用设备文件/dev/g_printer读取或写入这个状态字节。支持阻塞和非阻塞读写调用。

如何使用这个驱动程序

加载USB设备控制器驱动和打印机gadget驱动。以Netchip 2280 USB设备控制器驱动为例:

modprobe net2280
modprobe g_printer

可以在加载打印机gadget时使用如下命令行参数(例如:modprobe g_printer idVendor=0x0525 idProduct=0xa4a8):

idVendor

  这是设备描述符中使用的Vendor ID。默认是Netchip厂商id 0x0525。在发布产品之前,您必须更改为您自己的供应商id。如果您计划发布产品,但还没有供应商ID,请参阅www.usb.org了解如何获得供应商ID的详细信息。
idProduct

  这是设备描述符中使用的Product ID。默认值是0xa4a8,您应该将其更改为一个不被任何其他USB产品使用的ID(如果您有任何USB产品)。从0x0001开始为产品编号是个好主意。

bcdDevice

  这是您产品的版本号。把你的固件版本放在这里是个好主意。

iManufacturer

  包含供应商名称的字符串

iProduct

  包含产品名称的字符串。

iSerialNum

  包含序列号的字符串。这应该为每个单位的产品而改变。

iPNPstring

  用于此打印机的PNP ID字符串。您将希望在命令行或硬编码上设置用于打印机产品的PNP ID字符串。

qlen

  每个端点要使用的8k缓冲区的数量。默认值是10,您应该针对您的产品进行优化。您可能还想为您的产品调整每个缓冲区的大小。

使用示例代码

此示例代码与标准输出对话,而不是与打印引擎对话。

编译下面的测试代码:

  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

将打印机设置为 Selected/On-line:

# prn_example -selected

将打印机设置为Not Selected/Off-line:

# prn_example -not_selected

将纸张状态设置为纸用完:

# prn_example -paper_out

将纸张状态设置为已载纸:

# prn_example -paper_loaded

将错误状态设置为打印机OK:

# prn_example -no_error

设置error状态为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);
}

 

posted @ 2021-08-23 18:39  王楼小子  阅读(344)  评论(0编辑  收藏  举报