usb: gadget: f_printer: convert to new function interface with backward compatibility
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Tue, 3 Mar 2015 09:52:28 +0000 (10:52 +0100)
committerFelipe Balbi <balbi@ti.com>
Tue, 10 Mar 2015 20:33:40 +0000 (15:33 -0500)
In order to add configfs support, a usb function must be converted to use
the new interface. This patch converts the function to the new interface
and provides backward compatiblity layer, which can be removed after
all its users are converted to use the new interface.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/gadget/Kconfig
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_printer.c
drivers/usb/gadget/function/u_printer.h [new file with mode: 0644]
drivers/usb/gadget/legacy/printer.c

index b454d05be5838e08ecdda4fd60514ad0f444edc9..9d507cf98f94f59fb7f8404ee9f267986f59992d 100644 (file)
@@ -196,6 +196,9 @@ config USB_F_MIDI
 config USB_F_HID
        tristate
 
+config USB_F_PRINTER
+       tristate
+
 choice
        tristate "USB Gadget Drivers"
        default USB_ETH
index f71b1aaa0edfcaf78fe464170f508009140cc375..bd7def576955d825bf1651508fd715c964bd33c2 100644 (file)
@@ -42,3 +42,5 @@ usb_f_midi-y                  := f_midi.o
 obj-$(CONFIG_USB_F_MIDI)       += usb_f_midi.o
 usb_f_hid-y                    := f_hid.o
 obj-$(CONFIG_USB_F_HID)                += usb_f_hid.o
+usb_f_printer-y                        := f_printer.o
+obj-$(CONFIG_USB_F_PRINTER)    += usb_f_printer.o
index 0847972b96866e49739f1954dd87a1c6da35a0ef..93f4d4e61fefc29f08f7e9fb340f0a0e5e27ac6b 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mutex.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/idr.h>
 #include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/interrupt.h>
@@ -46,6 +47,8 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/g_printer.h>
 
+#include "u_printer.h"
+
 #define PNP_STRING_LEN         1024
 #define PRINTER_MINORS         4
 #define GET_DEVICE_ID          0
 
 static int major, minors;
 static struct class *usb_gadget_class;
+#ifndef USBF_PRINTER_INCLUDED
+static DEFINE_IDA(printer_ida);
+static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */
+#endif
 
 /*-------------------------------------------------------------------------*/
 
@@ -999,7 +1006,7 @@ unknown:
        return value;
 }
 
-static int __init printer_func_bind(struct usb_configuration *c,
+static int printer_func_bind(struct usb_configuration *c,
                struct usb_function *f)
 {
        struct usb_gadget *gadget = c->cdev->gadget;
@@ -1111,6 +1118,7 @@ fail_tx_reqs:
 
 }
 
+#ifdef USBF_PRINTER_INCLUDED
 static void printer_func_unbind(struct usb_configuration *c,
                struct usb_function *f)
 {
@@ -1155,6 +1163,7 @@ static void printer_func_unbind(struct usb_configuration *c,
        usb_free_all_descriptors(f);
        kfree(dev);
 }
+#endif
 
 static int printer_func_set_alt(struct usb_function *f,
                unsigned intf, unsigned alt)
@@ -1180,6 +1189,7 @@ static void printer_func_disable(struct usb_function *f)
        spin_unlock_irqrestore(&dev->lock, flags);
 }
 
+#ifdef USBF_PRINTER_INCLUDED
 static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str,
                                 char *pnp_string, unsigned q_len, int minor)
 {
@@ -1240,6 +1250,179 @@ static int f_printer_bind_config(struct usb_configuration *c, char *pnp_str,
        INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
        return 0;
 }
+#else
+static inline int gprinter_get_minor(void)
+{
+       return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
+}
+
+static inline void gprinter_put_minor(int minor)
+{
+       ida_simple_remove(&printer_ida, minor);
+}
+
+static int gprinter_setup(int);
+static void gprinter_cleanup(void);
+
+static void gprinter_free_inst(struct usb_function_instance *f)
+{
+       struct f_printer_opts *opts;
+
+       opts = container_of(f, struct f_printer_opts, func_inst);
+
+       mutex_lock(&printer_ida_lock);
+
+       gprinter_put_minor(opts->minor);
+       if (idr_is_empty(&printer_ida.idr))
+               gprinter_cleanup();
+
+       mutex_unlock(&printer_ida_lock);
+
+       kfree(opts);
+}
+
+static struct usb_function_instance *gprinter_alloc_inst(void)
+{
+       struct f_printer_opts *opts;
+       struct usb_function_instance *ret;
+       int status = 0;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       opts->func_inst.free_func_inst = gprinter_free_inst;
+       ret = &opts->func_inst;
+
+       mutex_lock(&printer_ida_lock);
+
+       if (idr_is_empty(&printer_ida.idr)) {
+               status = gprinter_setup(PRINTER_MINORS);
+               if (status) {
+                       ret = ERR_PTR(status);
+                       kfree(opts);
+                       goto unlock;
+               }
+       }
+
+       opts->minor = gprinter_get_minor();
+       if (opts->minor < 0) {
+               ret = ERR_PTR(opts->minor);
+               kfree(opts);
+               if (idr_is_empty(&printer_ida.idr))
+                       gprinter_cleanup();
+       }
+
+unlock:
+       mutex_unlock(&printer_ida_lock);
+       return ret;
+}
+
+static void gprinter_free(struct usb_function *f)
+{
+       struct printer_dev *dev = func_to_printer(f);
+
+       kfree(dev);
+}
+
+static void printer_func_unbind(struct usb_configuration *c,
+               struct usb_function *f)
+{
+       struct printer_dev      *dev;
+       struct usb_request      *req;
+
+       dev = func_to_printer(f);
+
+       device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
+
+       /* Remove Character Device */
+       cdev_del(&dev->printer_cdev);
+
+       /* we must already have been disconnected ... no i/o may be active */
+       WARN_ON(!list_empty(&dev->tx_reqs_active));
+       WARN_ON(!list_empty(&dev->rx_reqs_active));
+
+       /* Free all memory for this driver. */
+       while (!list_empty(&dev->tx_reqs)) {
+               req = container_of(dev->tx_reqs.next, struct usb_request,
+                               list);
+               list_del(&req->list);
+               printer_req_free(dev->in_ep, req);
+       }
+
+       if (dev->current_rx_req != NULL)
+               printer_req_free(dev->out_ep, dev->current_rx_req);
+
+       while (!list_empty(&dev->rx_reqs)) {
+               req = container_of(dev->rx_reqs.next,
+                               struct usb_request, list);
+               list_del(&req->list);
+               printer_req_free(dev->out_ep, req);
+       }
+
+       while (!list_empty(&dev->rx_buffers)) {
+               req = container_of(dev->rx_buffers.next,
+                               struct usb_request, list);
+               list_del(&req->list);
+               printer_req_free(dev->out_ep, req);
+       }
+       usb_free_all_descriptors(f);
+}
+
+static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
+{
+       struct printer_dev      *dev;
+       struct f_printer_opts   *opts;
+
+       opts = container_of(fi, struct f_printer_opts, func_inst);
+
+       if (opts->minor >= minors)
+               return ERR_PTR(-ENOENT);
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return ERR_PTR(-ENOMEM);
+
+       dev->minor = opts->minor;
+       dev->pnp_string = opts->pnp_string;
+       dev->q_len = opts->q_len;
+
+       dev->function.name = "printer";
+       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;
+       dev->function.req_match = gprinter_req_match;
+       dev->function.free_func = gprinter_free;
+
+       INIT_LIST_HEAD(&dev->tx_reqs);
+       INIT_LIST_HEAD(&dev->rx_reqs);
+       INIT_LIST_HEAD(&dev->rx_buffers);
+       INIT_LIST_HEAD(&dev->tx_reqs_active);
+       INIT_LIST_HEAD(&dev->rx_reqs_active);
+
+       spin_lock_init(&dev->lock);
+       mutex_init(&dev->lock_printer_io);
+       init_waitqueue_head(&dev->rx_wait);
+       init_waitqueue_head(&dev->tx_wait);
+       init_waitqueue_head(&dev->tx_flush_wait);
+
+       dev->interface = -1;
+       dev->printer_cdev_open = 0;
+       dev->printer_status = PRINTER_NOT_ERROR;
+       dev->current_rx_req = NULL;
+       dev->current_rx_bytes = 0;
+       dev->current_rx_buf = NULL;
+
+       return &dev->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Craig Nadler");
+
+#endif
 
 static int gprinter_setup(int count)
 {
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h
new file mode 100644 (file)
index 0000000..b2338ca
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * u_printer.h
+ *
+ * Utility definitions for the printer function
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_PRINTER_H
+#define U_PRINTER_H
+
+#include <linux/usb/composite.h>
+
+#define PNP_STRING_LEN                 1024
+
+struct f_printer_opts {
+       struct usb_function_instance    func_inst;
+       int                             minor;
+       char                            pnp_string[PNP_STRING_LEN];
+       unsigned                        q_len;
+};
+
+#endif /* U_PRINTER_H */
index 4d926d08df02950132aae5c863f5162cb2de4513..770b5041323eff0744b5b206b3c57903482ad531 100644 (file)
@@ -33,6 +33,7 @@ static const char driver_desc [] = DRIVER_DESC;
  * This will be changed when f_printer is converted
  * to the new function interface.
  */
+#define USBF_PRINTER_INCLUDED
 #include "f_printer.c"
 
 /*-------------------------------------------------------------------------*/