1
2                       Linux USB Printer Gadget Driver
3                                 06/04/2007
4
5              Copyright (C) 2007 Craig W. Nadler <craig@nadler.us>
6
7
8
9GENERAL
10=======
11
12This driver may be used if you are writing printer firmware using Linux as
13the embedded OS. This driver has nothing to do with using a printer with
14your Linux host system.
15
16You will need a USB device controller and a Linux driver for it that accepts
17a gadget / "device class" driver using the Linux USB Gadget API. After the
18USB device controller driver is loaded then load the printer gadget driver.
19This will present a printer interface to the USB Host that your USB Device
20port is connected to.
21
22This driver is structured for printer firmware that runs in user mode. The
23user mode printer firmware will read and write data from the kernel mode
24printer gadget driver using a device file. The printer returns a printer status
25byte when the USB HOST sends a device request to get the printer status.  The
26user space firmware can read or write this status byte using a device file
27/dev/g_printer . Both blocking and non-blocking read/write calls are supported.
28
29
30
31
32HOWTO USE THIS DRIVER
33=====================
34
35To load the USB device controller driver and the printer gadget driver. The
36following example uses the Netchip 2280 USB device controller driver:
37
38modprobe net2280
39modprobe g_printer
40
41
42The follow command line parameter can be used when loading the printer gadget
43(ex: modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 ):
44
45idVendor - This is the Vendor ID used in the device descriptor. The default is
46	the Netchip vendor id 0x0525. YOU MUST CHANGE TO YOUR OWN VENDOR ID
47	BEFORE RELEASING A PRODUCT. If you plan to release a product and don't
48	already have a Vendor ID please see www.usb.org for details on how to
49	get one.
50
51idProduct - This is the Product ID used in the device descriptor. The default
52	is 0xa4a8, you should change this to an ID that's not used by any of
53	your other USB products if you have any. It would be a good idea to
54	start numbering your products starting with say 0x0001.
55
56bcdDevice - This is the version number of your product. It would be a good idea
57	to put your firmware version here.
58
59iManufacturer - A string containing the name of the Vendor.
60
61iProduct - A string containing the Product Name.
62
63iSerialNum - A string containing the Serial Number. This should be changed for
64	each unit of your product.
65
66iPNPstring -  The PNP ID string used for this printer. You will want to set
67	either on the command line or hard code the PNP ID string used for
68	your printer product.
69
70qlen - The number of 8k buffers to use per endpoint. The default is 10, you
71	should tune this for your product. You may also want to tune the
72	size of each buffer for your product.
73
74
75
76
77USING THE EXAMPLE CODE
78======================
79
80This example code talks to stdout, instead of a print engine.
81
82To compile the test code below:
83
841) save it to a file called prn_example.c
852) compile the code with the follow command:
86	 gcc prn_example.c -o prn_example
87
88
89
90To read printer data from the host to stdout:
91
92	# prn_example -read_data
93
94
95To write printer data from a file (data_file) to the host:
96
97	# cat data_file | prn_example -write_data
98
99
100To get the current printer status for the gadget driver:
101
102	# prn_example -get_status
103
104	Printer status is:
105	     Printer is NOT Selected
106	     Paper is Out
107	     Printer OK
108
109
110To set printer to Selected/On-line:
111
112	# prn_example -selected
113
114
115To set printer to Not Selected/Off-line:
116
117	# prn_example -not_selected
118
119
120To set paper status to paper out:
121
122	# prn_example -paper_out
123
124
125To set paper status to paper loaded:
126
127	# prn_example -paper_loaded
128
129
130To set error status to printer OK:
131
132	# prn_example -no_error
133
134
135To set error status to ERROR:
136
137	# prn_example -error
138
139
140
141
142EXAMPLE CODE
143============
144
145
146#include <stdio.h>
147#include <stdlib.h>
148#include <fcntl.h>
149#include <linux/poll.h>
150#include <sys/ioctl.h>
151#include <linux/usb/g_printer.h>
152
153#define PRINTER_FILE			"/dev/g_printer"
154#define BUF_SIZE			512
155
156
157/*
158 * 'usage()' - Show program usage.
159 */
160
161static void
162usage(const char *option)		/* I - Option string or NULL */
163{
164	if (option) {
165		fprintf(stderr,"prn_example: Unknown option \"%s\"!\n",
166				option);
167	}
168
169	fputs("\n", stderr);
170	fputs("Usage: prn_example -[options]\n", stderr);
171	fputs("Options:\n", stderr);
172	fputs("\n", stderr);
173	fputs("-get_status    Get the current printer status.\n", stderr);
174	fputs("-selected      Set the selected status to selected.\n", stderr);
175	fputs("-not_selected  Set the selected status to NOT selected.\n",
176			stderr);
177	fputs("-error         Set the error status to error.\n", stderr);
178	fputs("-no_error      Set the error status to NO error.\n", stderr);
179	fputs("-paper_out     Set the paper status to paper out.\n", stderr);
180	fputs("-paper_loaded  Set the paper status to paper loaded.\n",
181			stderr);
182	fputs("-read_data     Read printer data from driver.\n", stderr);
183	fputs("-write_data    Write printer sata to driver.\n", stderr);
184	fputs("-NB_read_data  (Non-Blocking) Read printer data from driver.\n",
185			stderr);
186	fputs("\n\n", stderr);
187
188	exit(1);
189}
190
191
192static int
193read_printer_data()
194{
195	struct pollfd	fd[1];
196
197	/* Open device file for printer gadget. */
198	fd[0].fd = open(PRINTER_FILE, O_RDWR);
199	if (fd[0].fd < 0) {
200		printf("Error %d opening %s\n", fd[0].fd, PRINTER_FILE);
201		close(fd[0].fd);
202		return(-1);
203	}
204
205	fd[0].events = POLLIN | POLLRDNORM;
206
207	while (1) {
208		static char buf[BUF_SIZE];
209		int bytes_read;
210		int retval;
211
212		/* Wait for up to 1 second for data. */
213		retval = poll(fd, 1, 1000);
214
215		if (retval && (fd[0].revents & POLLRDNORM)) {
216
217			/* Read data from printer gadget driver. */
218			bytes_read = read(fd[0].fd, buf, BUF_SIZE);
219
220			if (bytes_read < 0) {
221				printf("Error %d reading from %s\n",
222						fd[0].fd, PRINTER_FILE);
223				close(fd[0].fd);
224				return(-1);
225			} else if (bytes_read > 0) {
226				/* Write data to standard OUTPUT (stdout). */
227				fwrite(buf, 1, bytes_read, stdout);
228				fflush(stdout);
229			}
230
231		}
232
233	}
234
235	/* Close the device file. */
236	close(fd[0].fd);
237
238	return 0;
239}
240
241
242static int
243write_printer_data()
244{
245	struct pollfd	fd[1];
246
247	/* Open device file for printer gadget. */
248	fd[0].fd = open (PRINTER_FILE, O_RDWR);
249	if (fd[0].fd < 0) {
250		printf("Error %d opening %s\n", fd[0].fd, PRINTER_FILE);
251		close(fd[0].fd);
252		return(-1);
253	}
254
255	fd[0].events = POLLOUT | POLLWRNORM;
256
257	while (1) {
258		int retval;
259		static char buf[BUF_SIZE];
260		/* Read data from standard INPUT (stdin). */
261		int bytes_read = fread(buf, 1, BUF_SIZE, stdin);
262
263		if (!bytes_read) {
264			break;
265		}
266
267		while (bytes_read) {
268
269			/* Wait for up to 1 second to sent data. */
270			retval = poll(fd, 1, 1000);
271
272			/* Write data to printer gadget driver. */
273			if (retval && (fd[0].revents & POLLWRNORM)) {
274				retval = write(fd[0].fd, buf, bytes_read);
275				if (retval < 0) {
276					printf("Error %d writing to %s\n",
277							fd[0].fd,
278							PRINTER_FILE);
279					close(fd[0].fd);
280					return(-1);
281				} else {
282					bytes_read -= retval;
283				}
284
285			}
286
287		}
288
289	}
290
291	/* Wait until the data has been sent. */
292	fsync(fd[0].fd);
293
294	/* Close the device file. */
295	close(fd[0].fd);
296
297	return 0;
298}
299
300
301static int
302read_NB_printer_data()
303{
304	int		fd;
305	static char	buf[BUF_SIZE];
306	int		bytes_read;
307
308	/* Open device file for printer gadget. */
309	fd = open(PRINTER_FILE, O_RDWR|O_NONBLOCK);
310	if (fd < 0) {
311		printf("Error %d opening %s\n", fd, PRINTER_FILE);
312		close(fd);
313		return(-1);
314	}
315
316	while (1) {
317		/* Read data from printer gadget driver. */
318		bytes_read = read(fd, buf, BUF_SIZE);
319		if (bytes_read <= 0) {
320			break;
321		}
322
323		/* Write data to standard OUTPUT (stdout). */
324		fwrite(buf, 1, bytes_read, stdout);
325		fflush(stdout);
326	}
327
328	/* Close the device file. */
329	close(fd);
330
331	return 0;
332}
333
334
335static int
336get_printer_status()
337{
338	int	retval;
339	int	fd;
340
341	/* Open device file for printer gadget. */
342	fd = open(PRINTER_FILE, O_RDWR);
343	if (fd < 0) {
344		printf("Error %d opening %s\n", fd, PRINTER_FILE);
345		close(fd);
346		return(-1);
347	}
348
349	/* Make the IOCTL call. */
350	retval = ioctl(fd, GADGET_GET_PRINTER_STATUS);
351	if (retval < 0) {
352		fprintf(stderr, "ERROR: Failed to set printer status\n");
353		return(-1);
354	}
355
356	/* Close the device file. */
357	close(fd);
358
359	return(retval);
360}
361
362
363static int
364set_printer_status(unsigned char buf, int clear_printer_status_bit)
365{
366	int	retval;
367	int	fd;
368
369	retval = get_printer_status();
370	if (retval < 0) {
371		fprintf(stderr, "ERROR: Failed to get printer status\n");
372		return(-1);
373	}
374
375	/* Open device file for printer gadget. */
376	fd = open(PRINTER_FILE, O_RDWR);
377
378	if (fd < 0) {
379		printf("Error %d opening %s\n", fd, PRINTER_FILE);
380		close(fd);
381		return(-1);
382	}
383
384	if (clear_printer_status_bit) {
385		retval &= ~buf;
386	} else {
387		retval |= buf;
388	}
389
390	/* Make the IOCTL call. */
391	if (ioctl(fd, GADGET_SET_PRINTER_STATUS, (unsigned char)retval)) {
392		fprintf(stderr, "ERROR: Failed to set printer status\n");
393		return(-1);
394	}
395
396	/* Close the device file. */
397	close(fd);
398
399	return 0;
400}
401
402
403static int
404display_printer_status()
405{
406	char	printer_status;
407
408	printer_status = get_printer_status();
409	if (printer_status < 0) {
410		fprintf(stderr, "ERROR: Failed to get printer status\n");
411		return(-1);
412	}
413
414	printf("Printer status is:\n");
415	if (printer_status & PRINTER_SELECTED) {
416		printf("     Printer is Selected\n");
417	} else {
418		printf("     Printer is NOT Selected\n");
419	}
420	if (printer_status & PRINTER_PAPER_EMPTY) {
421		printf("     Paper is Out\n");
422	} else {
423		printf("     Paper is Loaded\n");
424	}
425	if (printer_status & PRINTER_NOT_ERROR) {
426		printf("     Printer OK\n");
427	} else {
428		printf("     Printer ERROR\n");
429	}
430
431	return(0);
432}
433
434
435int
436main(int  argc, char *argv[])
437{
438	int	i;		/* Looping var */
439	int	retval = 0;
440
441	/* No Args */
442	if (argc == 1) {
443		usage(0);
444		exit(0);
445	}
446
447	for (i = 1; i < argc && !retval; i ++) {
448
449		if (argv[i][0] != '-') {
450			continue;
451		}
452
453		if (!strcmp(argv[i], "-get_status")) {
454			if (display_printer_status()) {
455				retval = 1;
456			}
457
458		} else if (!strcmp(argv[i], "-paper_loaded")) {
459			if (set_printer_status(PRINTER_PAPER_EMPTY, 1)) {
460				retval = 1;
461			}
462
463		} else if (!strcmp(argv[i], "-paper_out")) {
464			if (set_printer_status(PRINTER_PAPER_EMPTY, 0)) {
465				retval = 1;
466			}
467
468		} else if (!strcmp(argv[i], "-selected")) {
469			if (set_printer_status(PRINTER_SELECTED, 0)) {
470				retval = 1;
471			}
472
473		} else if (!strcmp(argv[i], "-not_selected")) {
474			if (set_printer_status(PRINTER_SELECTED, 1)) {
475				retval = 1;
476			}
477
478		} else if (!strcmp(argv[i], "-error")) {
479			if (set_printer_status(PRINTER_NOT_ERROR, 1)) {
480				retval = 1;
481			}
482
483		} else if (!strcmp(argv[i], "-no_error")) {
484			if (set_printer_status(PRINTER_NOT_ERROR, 0)) {
485				retval = 1;
486			}
487
488		} else if (!strcmp(argv[i], "-read_data")) {
489			if (read_printer_data()) {
490				retval = 1;
491			}
492
493		} else if (!strcmp(argv[i], "-write_data")) {
494			if (write_printer_data()) {
495				retval = 1;
496			}
497
498		} else if (!strcmp(argv[i], "-NB_read_data")) {
499			if (read_NB_printer_data()) {
500				retval = 1;
501			}
502
503		} else {
504			usage(argv[i]);
505			retval = 1;
506		}
507	}
508
509	exit(retval);
510}
511