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 主機發送裝置請求以獲取印表機狀態時,印表機返回一個印表機狀態位元組。使用者空間韌體可以使用裝置檔案 /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);
}