diff options
-rw-r--r-- | drivers/usb/gadget/Kconfig | 3 | ||||
-rw-r--r-- | drivers/usb/gadget/function/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_printer.c | 185 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_printer.h | 30 | ||||
-rw-r--r-- | drivers/usb/gadget/legacy/printer.c | 1 |
5 files changed, 220 insertions, 1 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b454d05..9d507cf 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -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 diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index f71b1aa..bd7def5 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -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 diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 0847972..93f4d4e 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -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 @@ -54,6 +57,10 @@ 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 index 0000000..b2338ca --- /dev/null +++ b/drivers/usb/gadget/function/u_printer.h @@ -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 */ diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 4d926d0..770b504 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -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" /*-------------------------------------------------------------------------*/ |