usb: gadget: make g_printer use composite
authorSebastian Andrzej Siewior <sebastian@breakpoint.cc>
Sat, 4 Feb 2012 17:55:20 +0000 (18:55 +0100)
committerFelipe Balbi <balbi@ti.com>
Fri, 4 May 2012 12:53:04 +0000 (15:53 +0300)
This patch converts the g_printer to make use of the compoiste framework
for descriptor parsing instead of its own implementation of it.
This gadget contains now one function which is the printer gadget.
Compile tested only.

Signed-off-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/printer.c

index daef2741f452abb4f1d1965f5062d49bb23aa5ba..f1f9290a2f47d11771108cb99ec53a6b6239117a 100644 (file)
@@ -51,6 +51,7 @@
  * the runtime footprint, and giving us at least some parts of what
  * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
  */
+#include "composite.c"
 #include "usbstring.c"
 #include "config.c"
 #include "epautoconf.c"
@@ -75,8 +76,6 @@ struct printer_dev {
        /* lock buffer lists during read/write calls */
        struct mutex            lock_printer_io;
        struct usb_gadget       *gadget;
-       struct usb_request      *req;           /* for control responses */
-       u8                      config;
        s8                      interface;
        struct usb_ep           *in_ep, *out_ep;
 
@@ -100,6 +99,7 @@ struct printer_dev {
        struct device           *pdev;
        u8                      printer_cdev_open;
        wait_queue_head_t       wait;
+       struct usb_function     function;
 };
 
 static struct printer_dev usb_printer_gadget;
@@ -120,26 +120,6 @@ static struct printer_dev usb_printer_gadget;
  * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
  */
 
-static ushort idVendor;
-module_param(idVendor, ushort, S_IRUGO);
-MODULE_PARM_DESC(idVendor, "USB Vendor ID");
-
-static ushort idProduct;
-module_param(idProduct, ushort, S_IRUGO);
-MODULE_PARM_DESC(idProduct, "USB Product ID");
-
-static ushort bcdDevice;
-module_param(bcdDevice, ushort, S_IRUGO);
-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
-
-static char *iManufacturer;
-module_param(iManufacturer, charp, S_IRUGO);
-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
-
-static char *iProduct;
-module_param(iProduct, charp, S_IRUGO);
-MODULE_PARM_DESC(iProduct, "USB Product string");
-
 static char *iSerialNum;
 module_param(iSerialNum, charp, S_IRUGO);
 MODULE_PARM_DESC(iSerialNum, "1");
@@ -156,39 +136,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
 
 /*-------------------------------------------------------------------------*/
 
-#define xprintk(d, level, fmt, args...) \
-       printk(level "%s: " fmt, DRIVER_DESC, ## args)
-
-#ifdef DEBUG
-#define DBG(dev, fmt, args...) \
-       xprintk(dev, KERN_DEBUG, fmt, ## args)
-#else
-#define DBG(dev, fmt, args...) \
-       do { } while (0)
-#endif /* DEBUG */
-
-#ifdef VERBOSE
-#define VDBG(dev, fmt, args...) \
-       xprintk(dev, KERN_DEBUG, fmt, ## args)
-#else
-#define VDBG(dev, fmt, args...) \
-       do { } while (0)
-#endif /* VERBOSE */
-
-#define ERROR(dev, fmt, args...) \
-       xprintk(dev, KERN_ERR, fmt, ## args)
-#define WARNING(dev, fmt, args...) \
-       xprintk(dev, KERN_WARNING, fmt, ## args)
-#define INFO(dev, fmt, args...) \
-       xprintk(dev, KERN_INFO, fmt, ## args)
-
-/*-------------------------------------------------------------------------*/
-
-/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
- * ep0 implementation:  descriptors, config management, setup().
- * also optional class-specific notification interrupt transfer.
- */
-
 /*
  * DESCRIPTORS ... most are static, but strings and (full) configuration
  * descriptors are built on demand.
@@ -221,24 +168,6 @@ static struct usb_device_descriptor device_desc = {
        .bNumConfigurations =   1
 };
 
-static struct usb_otg_descriptor otg_desc = {
-       .bLength =              sizeof otg_desc,
-       .bDescriptorType =      USB_DT_OTG,
-       .bmAttributes =         USB_OTG_SRP
-};
-
-static struct usb_config_descriptor config_desc = {
-       .bLength =              sizeof config_desc,
-       .bDescriptorType =      USB_DT_CONFIG,
-
-       /* compute wTotalLength on the fly */
-       .bNumInterfaces =       1,
-       .bConfigurationValue =  DEV_CONFIG_VALUE,
-       .iConfiguration =       0,
-       .bmAttributes =         USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
-       .bMaxPower =            CONFIG_USB_GADGET_VBUS_DRAW / 2,
-};
-
 static struct usb_interface_descriptor intf_desc = {
        .bLength =              sizeof intf_desc,
        .bDescriptorType =      USB_DT_INTERFACE,
@@ -264,8 +193,7 @@ static struct usb_endpoint_descriptor fs_ep_out_desc = {
        .bmAttributes =         USB_ENDPOINT_XFER_BULK
 };
 
-static const struct usb_descriptor_header *fs_printer_function [11] = {
-       (struct usb_descriptor_header *) &otg_desc,
+static struct usb_descriptor_header *fs_printer_function[] = {
        (struct usb_descriptor_header *) &intf_desc,
        (struct usb_descriptor_header *) &fs_ep_in_desc,
        (struct usb_descriptor_header *) &fs_ep_out_desc,
@@ -299,14 +227,24 @@ static struct usb_qualifier_descriptor dev_qualifier = {
        .bNumConfigurations =   1
 };
 
-static const struct usb_descriptor_header *hs_printer_function [11] = {
-       (struct usb_descriptor_header *) &otg_desc,
+static struct usb_descriptor_header *hs_printer_function[] = {
        (struct usb_descriptor_header *) &intf_desc,
        (struct usb_descriptor_header *) &hs_ep_in_desc,
        (struct usb_descriptor_header *) &hs_ep_out_desc,
        NULL
 };
 
+static struct usb_otg_descriptor otg_descriptor = {
+       .bLength =              sizeof otg_descriptor,
+       .bDescriptorType =      USB_DT_OTG,
+       .bmAttributes =         USB_OTG_SRP,
+};
+
+static const struct usb_descriptor_header *otg_desc[] = {
+       (struct usb_descriptor_header *) &otg_descriptor,
+       NULL,
+};
+
 /* maxpacket and other transfer characteristics vary by speed. */
 #define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs))
 
@@ -328,11 +266,16 @@ static struct usb_string          strings [] = {
        {  }            /* end of list */
 };
 
-static struct usb_gadget_strings       stringtab = {
+static struct usb_gadget_strings       stringtab_dev = {
        .language       = 0x0409,       /* en-us */
        .strings        = strings,
 };
 
+static struct usb_gadget_strings *dev_strings[] = {
+       &stringtab_dev,
+       NULL,
+};
+
 /*-------------------------------------------------------------------------*/
 
 static struct usb_request *
@@ -922,78 +865,8 @@ static void printer_reset_interface(struct printer_dev *dev)
        dev->interface = -1;
 }
 
-/* change our operational config.  must agree with the code
- * that returns config descriptors, and altsetting code.
- */
-static int
-printer_set_config(struct printer_dev *dev, unsigned number)
-{
-       int                     result = 0;
-       struct usb_gadget       *gadget = dev->gadget;
-
-       switch (number) {
-       case DEV_CONFIG_VALUE:
-               result = 0;
-               break;
-       default:
-               result = -EINVAL;
-               /* FALL THROUGH */
-       case 0:
-               break;
-       }
-
-       if (result) {
-               usb_gadget_vbus_draw(dev->gadget,
-                               dev->gadget->is_otg ? 8 : 100);
-       } else {
-               unsigned power;
-
-               power = 2 * config_desc.bMaxPower;
-               usb_gadget_vbus_draw(dev->gadget, power);
-
-               dev->config = number;
-               INFO(dev, "%s config #%d: %d mA, %s\n",
-                    usb_speed_string(gadget->speed),
-                    number, power, driver_desc);
-       }
-       return result;
-}
-
-static int
-config_buf(enum usb_device_speed speed, u8 *buf, u8 type, unsigned index,
-               int is_otg)
-{
-       int                                     len;
-       const struct usb_descriptor_header      **function;
-       int                                     hs = (speed == USB_SPEED_HIGH);
-
-       if (type == USB_DT_OTHER_SPEED_CONFIG)
-               hs = !hs;
-
-       if (hs) {
-               function = hs_printer_function;
-       } else {
-               function = fs_printer_function;
-       }
-
-       if (index >= device_desc.bNumConfigurations)
-               return -EINVAL;
-
-       /* for now, don't advertise srp-only devices */
-       if (!is_otg)
-               function++;
-
-       len = usb_gadget_config_buf(&config_desc, buf, USB_DESC_BUFSIZE,
-                       function);
-       if (len < 0)
-               return len;
-       ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
-       return len;
-}
-
 /* Change our operational Interface. */
-static int
-set_interface(struct printer_dev *dev, unsigned number)
+static int set_interface(struct printer_dev *dev, unsigned number)
 {
        int                     result = 0;
 
@@ -1024,14 +897,6 @@ set_interface(struct printer_dev *dev, unsigned number)
        return result;
 }
 
-static void printer_setup_complete(struct usb_ep *ep, struct usb_request *req)
-{
-       if (req->status || req->actual != req->length)
-               DBG((struct printer_dev *) ep->driver_data,
-                               "setup complete --> %d, %d/%d\n",
-                               req->status, req->actual, req->length);
-}
-
 static void printer_soft_reset(struct printer_dev *dev)
 {
        struct usb_request      *req;
@@ -1088,11 +953,12 @@ static void printer_soft_reset(struct printer_dev *dev)
  * The setup() callback implements all the ep0 functionality that's not
  * handled lower down.
  */
-static int
-printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+static int printer_func_setup(struct usb_function *f,
+               const struct usb_ctrlrequest *ctrl)
 {
-       struct printer_dev      *dev = get_gadget_data(gadget);
-       struct usb_request      *req = dev->req;
+       struct printer_dev *dev = container_of(f, struct printer_dev, function);
+       struct usb_composite_dev *cdev = f->config->cdev;
+       struct usb_request      *req = cdev->req;
        int                     value = -EOPNOTSUPP;
        u16                     wIndex = le16_to_cpu(ctrl->wIndex);
        u16                     wValue = le16_to_cpu(ctrl->wValue);
@@ -1101,100 +967,7 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
        DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
                ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
 
-       req->complete = printer_setup_complete;
-
        switch (ctrl->bRequestType&USB_TYPE_MASK) {
-
-       case USB_TYPE_STANDARD:
-               switch (ctrl->bRequest) {
-
-               case USB_REQ_GET_DESCRIPTOR:
-                       if (ctrl->bRequestType != USB_DIR_IN)
-                               break;
-                       switch (wValue >> 8) {
-
-                       case USB_DT_DEVICE:
-                               device_desc.bMaxPacketSize0 =
-                                       gadget->ep0->maxpacket;
-                               value = min(wLength, (u16) sizeof device_desc);
-                               memcpy(req->buf, &device_desc, value);
-                               break;
-                       case USB_DT_DEVICE_QUALIFIER:
-                               if (!gadget_is_dualspeed(gadget))
-                                       break;
-                               /*
-                                * assumes ep0 uses the same value for both
-                                * speeds
-                                */
-                               dev_qualifier.bMaxPacketSize0 =
-                                       gadget->ep0->maxpacket;
-                               value = min(wLength,
-                                               (u16) sizeof dev_qualifier);
-                               memcpy(req->buf, &dev_qualifier, value);
-                               break;
-
-                       case USB_DT_OTHER_SPEED_CONFIG:
-                               if (!gadget_is_dualspeed(gadget))
-                                       break;
-                               /* FALLTHROUGH */
-                       case USB_DT_CONFIG:
-                               value = config_buf(gadget->speed, req->buf,
-                                               wValue >> 8,
-                                               wValue & 0xff,
-                                               gadget->is_otg);
-                               if (value >= 0)
-                                       value = min(wLength, (u16) value);
-                               break;
-
-                       case USB_DT_STRING:
-                               value = usb_gadget_get_string(&stringtab,
-                                               wValue & 0xff, req->buf);
-                               if (value >= 0)
-                                       value = min(wLength, (u16) value);
-                               break;
-                       }
-                       break;
-
-               case USB_REQ_SET_CONFIGURATION:
-                       if (ctrl->bRequestType != 0)
-                               break;
-                       if (gadget->a_hnp_support)
-                               DBG(dev, "HNP available\n");
-                       else if (gadget->a_alt_hnp_support)
-                               DBG(dev, "HNP needs a different root port\n");
-                       value = printer_set_config(dev, wValue);
-                       if (!value)
-                               value = set_interface(dev, PRINTER_INTERFACE);
-                       break;
-               case USB_REQ_GET_CONFIGURATION:
-                       if (ctrl->bRequestType != USB_DIR_IN)
-                               break;
-                       *(u8 *)req->buf = dev->config;
-                       value = min(wLength, (u16) 1);
-                       break;
-
-               case USB_REQ_SET_INTERFACE:
-                       if (ctrl->bRequestType != USB_RECIP_INTERFACE ||
-                                       !dev->config)
-                               break;
-
-                       value = set_interface(dev, PRINTER_INTERFACE);
-                       break;
-               case USB_REQ_GET_INTERFACE:
-                       if (ctrl->bRequestType !=
-                                       (USB_DIR_IN|USB_RECIP_INTERFACE)
-                                       || !dev->config)
-                               break;
-
-                       *(u8 *)req->buf = dev->interface;
-                       value = min(wLength, (u16) 1);
-                       break;
-
-               default:
-                       goto unknown;
-               }
-               break;
-
        case USB_TYPE_CLASS:
                switch (ctrl->bRequest) {
                case 0: /* Get the IEEE-1284 PNP String */
@@ -1240,44 +1013,50 @@ unknown:
                        wValue, wIndex, wLength);
                break;
        }
-
-       /* respond with data transfer before status phase? */
-       if (value >= 0) {
-               req->length = value;
-               req->zero = value < wLength;
-               value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
-               if (value < 0) {
-                       DBG(dev, "ep_queue --> %d\n", value);
-                       req->status = 0;
-                       printer_setup_complete(gadget->ep0, req);
-               }
-       }
-
        /* host either stalls (value < 0) or reports success */
        return value;
 }
 
-static void
-printer_disconnect(struct usb_gadget *gadget)
+static int __init printer_func_bind(struct usb_configuration *c,
+               struct usb_function *f)
+{
+       return 0;
+}
+
+static void printer_func_unbind(struct usb_configuration *c,
+               struct usb_function *f)
+{
+}
+
+static int printer_func_set_alt(struct usb_function *f,
+               unsigned intf, unsigned alt)
 {
-       struct printer_dev      *dev = get_gadget_data(gadget);
+       struct printer_dev *dev = container_of(f, struct printer_dev, function);
+       int ret = -ENOTSUPP;
+
+       if (!alt)
+               ret = set_interface(dev, PRINTER_INTERFACE);
+       return ret;
+}
+
+static void printer_func_disable(struct usb_function *f)
+{
+       struct printer_dev *dev = container_of(f, struct printer_dev, function);
        unsigned long           flags;
 
        DBG(dev, "%s\n", __func__);
 
        spin_lock_irqsave(&dev->lock, flags);
-
        printer_reset_interface(dev);
-
        spin_unlock_irqrestore(&dev->lock, flags);
 }
 
-static void
-printer_unbind(struct usb_gadget *gadget)
+static void printer_cfg_unbind(struct usb_configuration *c)
 {
-       struct printer_dev      *dev = get_gadget_data(gadget);
+       struct printer_dev      *dev;
        struct usb_request      *req;
 
+       dev = &usb_printer_gadget;
 
        DBG(dev, "%s\n", __func__);
 
@@ -1315,18 +1094,18 @@ printer_unbind(struct usb_gadget *gadget)
                list_del(&req->list);
                printer_req_free(dev->out_ep, req);
        }
-
-       if (dev->req) {
-               printer_req_free(gadget->ep0, dev->req);
-               dev->req = NULL;
-       }
-
-       set_gadget_data(gadget, NULL);
 }
 
-static int __init
-printer_bind(struct usb_gadget *gadget)
+static struct usb_configuration printer_cfg_driver = {
+       .label                  = "printer",
+       .unbind                 = printer_cfg_unbind,
+       .bConfigurationValue    = 1,
+       .bmAttributes           = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+};
+
+static int __init printer_bind_config(struct usb_configuration *c)
 {
+       struct usb_gadget       *gadget = c->cdev->gadget;
        struct printer_dev      *dev;
        struct usb_ep           *in_ep, *out_ep;
        int                     status = -ENOMEM;
@@ -1337,6 +1116,14 @@ printer_bind(struct usb_gadget *gadget)
 
        dev = &usb_printer_gadget;
 
+       dev->function.name = shortname;
+       dev->function.descriptors = fs_printer_function;
+       dev->function.hs_descriptors = hs_printer_function;
+       dev->function.bind = printer_func_bind;
+       dev->function.setup = printer_func_setup;
+       dev->function.unbind = printer_func_unbind;
+       dev->function.set_alt = printer_func_set_alt;
+       dev->function.disable = printer_func_disable;
 
        /* Setup the sysfs files for the printer gadget. */
        dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
@@ -1372,29 +1159,6 @@ printer_bind(struct usb_gadget *gadget)
                init_utsname()->sysname, init_utsname()->release,
                gadget->name);
 
-       device_desc.idVendor =
-               cpu_to_le16(PRINTER_VENDOR_NUM);
-       device_desc.idProduct =
-               cpu_to_le16(PRINTER_PRODUCT_NUM);
-
-       /* support optional vendor/distro customization */
-       if (idVendor) {
-               if (!idProduct) {
-                       dev_err(&gadget->dev, "idVendor needs idProduct!\n");
-                       return -ENODEV;
-               }
-               device_desc.idVendor = cpu_to_le16(idVendor);
-               device_desc.idProduct = cpu_to_le16(idProduct);
-               if (bcdDevice)
-                       device_desc.bcdDevice = cpu_to_le16(bcdDevice);
-       }
-
-       if (iManufacturer)
-               strlcpy(manufacturer, iManufacturer, sizeof manufacturer);
-
-       if (iProduct)
-               strlcpy(product_desc, iProduct, sizeof product_desc);
-
        if (iSerialNum)
                strlcpy(serial_num, iSerialNum, sizeof serial_num);
 
@@ -1428,8 +1192,9 @@ autoconf_fail:
        usb_gadget_set_selfpowered(gadget);
 
        if (gadget->is_otg) {
-               otg_desc.bmAttributes |= USB_OTG_HNP,
-               config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+               otg_descriptor.bmAttributes |= USB_OTG_HNP;
+               printer_cfg_driver.descriptors = otg_desc;
+               printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
        }
 
        spin_lock_init(&dev->lock);
@@ -1443,7 +1208,6 @@ autoconf_fail:
        init_waitqueue_head(&dev->tx_wait);
        init_waitqueue_head(&dev->tx_flush_wait);
 
-       dev->config = 0;
        dev->interface = -1;
        dev->printer_cdev_open = 0;
        dev->printer_status = PRINTER_NOT_ERROR;
@@ -1454,14 +1218,6 @@ autoconf_fail:
        dev->in_ep = in_ep;
        dev->out_ep = out_ep;
 
-       /* preallocate control message data and buffer */
-       dev->req = printer_req_alloc(gadget->ep0, USB_DESC_BUFSIZE,
-                       GFP_KERNEL);
-       if (!dev->req) {
-               status = -ENOMEM;
-               goto fail;
-       }
-
        for (i = 0; i < QLEN; i++) {
                req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
                if (!req) {
@@ -1490,45 +1246,37 @@ autoconf_fail:
                list_add(&req->list, &dev->rx_reqs);
        }
 
-       dev->req->complete = printer_setup_complete;
-
        /* finish hookup to lower layer ... */
        dev->gadget = gadget;
-       set_gadget_data(gadget, dev);
-       gadget->ep0->driver_data = dev;
 
        INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
        INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, out_ep->name,
                        in_ep->name);
-
        return 0;
 
 fail:
-       printer_unbind(gadget);
+       printer_cfg_unbind(c);
        return status;
 }
 
-/*-------------------------------------------------------------------------*/
+static int printer_unbind(struct usb_composite_dev *cdev)
+{
+       return 0;
+}
 
-static struct usb_gadget_driver printer_driver = {
-       .max_speed      = USB_SPEED_HIGH,
+static int __init printer_bind(struct usb_composite_dev *cdev)
+{
+       return usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
+}
 
-       .function       = (char *) driver_desc,
+static struct usb_composite_driver printer_driver = {
+       .name           = shortname,
+       .dev            = &device_desc,
+       .strings        = dev_strings,
+       .max_speed      = USB_SPEED_HIGH,
        .unbind         = printer_unbind,
-
-       .setup          = printer_setup,
-       .disconnect     = printer_disconnect,
-
-       .driver         = {
-               .name           = (char *) shortname,
-               .owner          = THIS_MODULE,
-       },
 };
 
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_AUTHOR("Craig Nadler");
-MODULE_LICENSE("GPL");
-
 static int __init
 init(void)
 {
@@ -1537,23 +1285,23 @@ init(void)
        usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
        if (IS_ERR(usb_gadget_class)) {
                status = PTR_ERR(usb_gadget_class);
-               ERROR(dev, "unable to create usb_gadget class %d\n", status);
+               pr_err("unable to create usb_gadget class %d\n", status);
                return status;
        }
 
        status = alloc_chrdev_region(&g_printer_devno, 0, 1,
                        "USB printer gadget");
        if (status) {
-               ERROR(dev, "alloc_chrdev_region %d\n", status);
+               pr_err("alloc_chrdev_region %d\n", status);
                class_destroy(usb_gadget_class);
                return status;
        }
 
-       status = usb_gadget_probe_driver(&printer_driver, printer_bind);
+       status = usb_composite_probe(&printer_driver, printer_bind);
        if (status) {
                class_destroy(usb_gadget_class);
                unregister_chrdev_region(g_printer_devno, 1);
-               DBG(dev, "usb_gadget_probe_driver %x\n", status);
+               pr_err("usb_gadget_probe_driver %x\n", status);
        }
 
        return status;
@@ -1563,15 +1311,14 @@ module_init(init);
 static void __exit
 cleanup(void)
 {
-       int status;
-
        mutex_lock(&usb_printer_gadget.lock_printer_io);
-       status = usb_gadget_unregister_driver(&printer_driver);
-       if (status)
-               ERROR(dev, "usb_gadget_unregister_driver %x\n", status);
-
+       usb_composite_unregister(&printer_driver);
        unregister_chrdev_region(g_printer_devno, 1);
        class_destroy(usb_gadget_class);
        mutex_unlock(&usb_printer_gadget.lock_printer_io);
 }
 module_exit(cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Craig Nadler");
+MODULE_LICENSE("GPL");
This page took 0.034328 seconds and 5 git commands to generate.