Re: Printer Gadget / "Device Class" Driver

David Brownell <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Thanks for refreshing that, Craig.  I was cleaning out some mail
and found previous versions.  Maybe it's finally time to merge.  :)


On Monday 23 April 2007, Craig W. Nadler wrote:
> diff -Nru a/Documentation/usb/gadget_printer.txt b/Documentation/usb/gadget_printer.txt
> --- a/Documentation/usb/gadget_printer.txt      1969-12-31 19:00:00.000000000 -0500
> +++ b/Documentation/usb/gadget_printer.txt      2007-03-05 14:42:48.000000000 -0500

> +GENERAL
> +=======
> +
> ...
> +
> +This driver is structured for printer firmware that runs in user mode. The
> +user mode printer firmware will read and write data from the kernel mode
> +printer gadget driver using a device file. The printer returns a printer status
> +byte when the USB HOST sends a device request to get the printer status.  The
> +user space firmware can read or write this status byte using a device file.
> +All the read and write calls for this printer gadget driver are non-blocking.
> +If there is no room to write more data or no more data to read the system calls
> +with return 0.

It's more conventional to require O_NONBLOCK and support a bunch
of other conventions.  ISTR that a nonblocking read without data
should report -EAGAIN, not zero ... one expects poll() and select()
to work too, also SIGIO and friends.


> +HOWTO USE THIS DRIVER
> +=====================
> +
> +To create the device files for the USB printer gadget
> +
> +mknod /dev/glp0 c 300 0
> +mknod /dev/glp0_status c 300 1

That part should be automated by udev/mdev ... is there any
reason the old manual style mknod would be required?  And
where does major 300 come from?


> +
> +EXAMPLE CODE
> +============
> +
> +     The following is an example of how to read data from the printer gadget
> +from a user space application.
> +
> +
> +********************************* CUT HERE **********************************
> +#include <stdio.h>
> +#include <fcntl.h>
> +
> +#define PRINTER_DATA_FILE      "/dev/glp0"
> +#define BUF_SIZE               512
> +
> +main ()
> +{
> +       int fd = open(PRINTER_DATA_FILE, O_RDWR);

Presumably the printer status reported will be on the
order of "not ready" until the read() below is posted?


> +       if (fd < 0) {
> +               printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE);
> +               close (fd);
> +               return;
> +       }
> +       while (1) {
> +               static char buf[BUF_SIZE];
> +               int bytes_read;
> +
> +               bytes_read = read (fd, buf, BUF_SIZE);
> +               if (bytes_read) {
> +                       fwrite (buf, 1, bytes_read, stdout); 
> +                       fflush (stdout); 
> +               } else {
> +                       usleep (10000);

Much better would be just blocking the read, or a poll() or select()...
waiting ten seconds between reads is excessive!  I'd expect to be
able to spit several short jobs at the printer without delays.


> +               }
> +       }
> +
> +       close(fd);
> +}
> +********************************* CUT HERE **********************************
> +
> +
> +
> +
> +
> +     The following is an example of how to write data to the printer gadget
> +from a user space application.
> +
> +********************************* CUT HERE **********************************
> +#include <stdio.h>
> +#include <fcntl.h>
> +
> +#define PRINTER_DATA_FILE      "/dev/glp0"
> +#define BUF_SIZE               512
> +
> +main ()
> +{
> +       int fd = open(PRINTER_DATA_FILE, O_RDWR);
> +
> +       if (fd < 0) {
> +               printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE);
> +               close (fd);
> +               return;
> +       }
> +       while (1) {
> +               static char buf[BUF_SIZE];
> +               int bytes_read = fread (buf, 1, BUF_SIZE, stdin); 
> +
> +               if (!bytes_read) break;
> +
> +               while (bytes_read) {
> +                       bytes_read -= write (fd, buf, bytes_read);
> +               }
> +       }
> +
> +       close(fd);
> +}
> +********************************* CUT HERE **********************************
> +
> +
> +
> +
> +
> +     The following is an example of how to get the printer status from a user
> +space application. See the USB Printer Class Spec. for the get printer status
> +device request.
> +
> +********************************* CUT HERE **********************************
> +#include <stdio.h>
> +#include <fcntl.h>
> +
> +#define PRINTER_DATA_FILE      "/dev/glp0_status"
> +
> +main ()
> +{
> +       char buf;
> +       int bytes_read;
> +       int fd = open(PRINTER_DATA_FILE, O_RDWR);
> +
> +       if (fd < 0) {
> +               printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE);
> +               close (fd);
> +               return;
> +       }
> +
> +       bytes_read = read (fd, &buf, 1);
> +       if (bytes_read == 1) {
> +               fwrite (&buf, 1, 1, stdout); 
> +               fflush (stdout); 
> +       }
> +
> +       close(fd);
> +}
> +********************************* CUT HERE **********************************
> +
> +
> +
> +
> +
> +     The following is an example of how to set the printer status from a user
> +space application.  See the USB Printer Class Spec. for the get printer status
> +device request.
> +
> +********************************* CUT HERE **********************************
> +#include <stdio.h>
> +#include <fcntl.h>
> +
> +#define PRINTER_DATA_FILE      "/dev/glp0_status"
> +
> +main ()
> +{
> +       char buf;
> +       int bytes_read;
> +       int fd = open(PRINTER_DATA_FILE, O_RDWR);
> +
> +       if (fd < 0) {
> +               printf("Error %d opening %s\n", fd, PRINTER_DATA_FILE);
> +               close (fd);
> +               return;
> +       }
> +
> +       bytes_read = fread (&buf, 1, 1, stdin);
> +       if (bytes_read == 1)
> +               write (fd, &buf, 1);
> +
> +       close(fd);
> +}
> +********************************* CUT HERE **********************************


Hmm, I'd much rather have one integrated program than all those small examples.

That may well be my personal taste -- unit test cases are also handy! -- but
I'd sure rather see something at the level of "collect print job and report
status" as an example.  (Right, that may not be meaningful here; my point is
more about a meaningful highlevel task, vs. a toolkit.)


> diff -Nru a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> --- a/drivers/usb/gadget/Kconfig        2007-02-20 01:34:32.000000000 -0500
> +++ b/drivers/usb/gadget/Kconfig        2007-03-05 14:42:48.000000000 -0500
> @@ -418,6 +418,17 @@
>           Say "y" to link the driver statically, or "m" to build a
>           dynamically linked module called "g_midi".
>  
> +config USB_G_PRINTER
> +       tristate "Printer Gadget"
> +       help
> +         The Printer Gadget channels data between it's USB printer interface

... "between its" ... 

What's missing in this description is:  where's the printer hardware.
I think you mean to say that the gadget channels data between the host
and a userspace program driving the print engine.


> +         and a device file. A user space program can be used to read and
> +         write this device file to receive or send printer data. If you'd
> +         perfer to handle the printer data in Kernel space there is a

"prefer".


> +         compile time option for using an API instead of device files.

Hmm, the docs didn't mention that in-kernel API.

I've seen maybe three different printer gadget drivers at various times.
This is the only one with an in-kernel option.  (Of the two others, one
has been turned into a large-format printer from HP, and I forget about
the other one.)


> +
> +         Say "y" to link the driver statically, or "m" to build a
> +         dynamically linked module called "g_printer".
>  
>  # put drivers that need isochronous transfer support (for audio
>  # or video class gadget drivers), or specific hardware, here.
> diff -Nru a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
> --- a/drivers/usb/gadget/Makefile       2007-02-20 01:34:32.000000000 -0500
> +++ b/drivers/usb/gadget/Makefile       2007-03-05 14:47:18.000000000 -0500
> @@ -19,6 +19,8 @@
>  gadgetfs-objs                  := inode.o
>  g_file_storage-objs            := file_storage.o usbstring.o config.o \
>                                         epautoconf.o
> +g_printer-objs                 := printer.o usbstring.o config.o \
> +                                       epautoconf.o
>  
>  ifeq ($(CONFIG_USB_ETH_RNDIS),y)
>         g_ether-objs            += rndis.o
> @@ -29,5 +31,6 @@
>  obj-$(CONFIG_USB_GADGETFS)     += gadgetfs.o
>  obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
>  obj-$(CONFIG_USB_G_SERIAL)     += g_serial.o
> +obj-$(CONFIG_USB_G_PRINTER)    += g_printer.o
>  obj-$(CONFIG_USB_MIDI_GADGET)  += g_midi.o
>  

OK, so far so good.  I'll stop my review right there, at least for now,
since it's late and the rest would require more time.

I did a refresh against 2.6.21-rc7, and quilt told me this file has
LOTS of end-of-line whitespace to remove, though.  :)

- Dave


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.