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 對話,而不是列印引擎。
編譯下面的測試程式碼
將其儲存到名為 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);
}