From 8f900a9a6e2691441ad763952d640ac44220e5dc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 3 Dec 2012 20:07:05 +0100 Subject: usb: gadget: consider link speed for bMaxPower The USB 2.0 specification says that bMaxPower is the maximum power consumption expressed in 2 mA units and the USB 3.0 specification says that it is expressed in 8 mA units. This patch renames bMaxPower to MaxPower and the various /2 and *2 are removed. Before reporting the config descriptor, the proper value is computer based on the speed, all in-tree users are updated. MaxPower is also increased to u16 so we can store the nokia gadget value which is larger than the max value allowed for u8. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/nokia.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/gadget/nokia.c') diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 661600abace8..1475d663b527 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -133,7 +133,7 @@ static struct usb_configuration nokia_config_500ma_driver = { .bConfigurationValue = 1, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = 250, /* 500mA */ + .MaxPower = 500, }; static struct usb_configuration nokia_config_100ma_driver = { @@ -141,7 +141,7 @@ static struct usb_configuration nokia_config_100ma_driver = { .bConfigurationValue = 2, /* .iConfiguration = DYNAMIC */ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 50, /* 100 mA */ + .MaxPower = 100, }; static int __init nokia_bind(struct usb_composite_dev *cdev) -- cgit v1.2.3 From 3249ca22c088c286d6227d8fae9c85a43a8ce9f6 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 23 Dec 2012 21:10:04 +0100 Subject: usb: gadget: u_serial: convert into a module Every user of u_serial has now to select the U_SERIAL symbol instead of including the file. There is one limition with this: ports and and gs_tty_driver are global variables in u_serial. Since all users share them, there can be only one user loaded at a time i.e. either g_serial or g_nokia. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 9 +++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/acm_ms.c | 1 - drivers/usb/gadget/cdc2.c | 1 - drivers/usb/gadget/dbgp.c | 4 +--- drivers/usb/gadget/multi.c | 1 - drivers/usb/gadget/nokia.c | 1 - drivers/usb/gadget/serial.c | 1 - drivers/usb/gadget/u_serial.c | 13 ++++++++++++- 9 files changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/usb/gadget/nokia.c') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 0ef5549b6544..8aefbbddf2a7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -503,6 +503,9 @@ config USB_LIBCOMPOSITE config USB_F_SS_LB tristate +config USB_U_SERIAL + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -754,6 +757,7 @@ config USB_GADGET_TARGET config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" + select USB_U_SERIAL select USB_LIBCOMPOSITE help The Serial Gadget talks to the Linux-USB generic serial driver. @@ -807,6 +811,7 @@ config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET select USB_LIBCOMPOSITE + select USB_U_SERIAL help This driver provides two functions in one configuration: a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link. @@ -822,6 +827,7 @@ config USB_G_NOKIA tristate "Nokia composite gadget" depends on PHONET select USB_LIBCOMPOSITE + select USB_U_SERIAL help The Nokia composite gadget provides support for acm, obex and phonet in only one composite gadget driver. @@ -833,6 +839,7 @@ config USB_G_ACM_MS tristate "CDC Composite Device (ACM and mass storage)" depends on BLOCK select USB_LIBCOMPOSITE + select USB_U_SERIAL help This driver provides two functions in one configuration: a mass storage, and a CDC ACM (serial port) link. @@ -845,6 +852,7 @@ config USB_G_MULTI depends on BLOCK && NET select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS select USB_LIBCOMPOSITE + select USB_U_SERIAL help The Multifunction Composite Gadget provides Ethernet (RNDIS and/or CDC Ethernet), mass storage and ACM serial link @@ -920,6 +928,7 @@ config USB_G_DBGP_PRINTK config USB_G_DBGP_SERIAL depends on USB_G_DBGP + select USB_U_SERIAL bool "serial" help Userland can interact using /dev/ttyGSxxx. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e412befa7a35..b88ee775de6b 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -78,3 +78,4 @@ obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o # USB Functions f_ss_lb-y := f_loopback.o f_sourcesink.o obj-$(CONFIG_USB_F_SS_LB) += f_ss_lb.o +obj-$(CONFIG_USB_U_SERIAL) += u_serial.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 5a7f289805ff..35cbe7283514 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -41,7 +41,6 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "u_serial.c" #include "f_acm.c" #include "f_mass_storage.c" diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 1e4bb77f00bb..379df679ef30 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -43,7 +43,6 @@ USB_GADGET_COMPOSITE_OPTIONS(); * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "u_serial.c" #include "f_acm.c" #include "f_ecm.c" #include "u_ether.c" diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 87d165028162..41eb98df5644 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -13,9 +13,7 @@ #include #include -#ifdef CONFIG_USB_G_DBGP_SERIAL -#include "u_serial.c" -#endif +#include "u_serial.h" #define DRIVER_VENDOR_ID 0x0525 /* NetChip */ #define DRIVER_PRODUCT_ID 0xc0de /* undefined */ diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 88472bf7dbb7..00667911100d 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -42,7 +42,6 @@ MODULE_LICENSE("GPL"); */ #include "f_mass_storage.c" -#include "u_serial.c" #include "f_acm.c" #include "f_ecm.c" diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 1475d663b527..48f0d5c372a8 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -37,7 +37,6 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "u_serial.c" #include "f_acm.c" #include "f_ecm.c" #include "f_obex.c" diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index bc23905800d3..4816f494fc4d 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -39,7 +39,6 @@ #include "f_acm.c" #include "f_obex.c" #include "f_serial.c" -#include "u_serial.c" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index d0f95482f40e..1662d839a1d6 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "u_serial.h" @@ -309,6 +310,7 @@ gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags) return req; } +EXPORT_SYMBOL_GPL(gs_alloc_req); /* * gs_free_req @@ -320,6 +322,7 @@ void gs_free_req(struct usb_ep *ep, struct usb_request *req) kfree(req->buf); usb_ep_free_request(ep, req); } +EXPORT_SYMBOL_GPL(gs_free_req); /* * gs_send_packet @@ -1081,6 +1084,9 @@ int gserial_setup(struct usb_gadget *g, unsigned count) if (count == 0 || count > N_PORTS) return -EINVAL; + if (gs_tty_driver) + return -EBUSY; + gs_tty_driver = alloc_tty_driver(count); if (!gs_tty_driver) return -ENOMEM; @@ -1153,6 +1159,7 @@ fail: gs_tty_driver = NULL; return status; } +EXPORT_SYMBOL_GPL(gserial_setup); static int gs_closed(struct gs_port *port) { @@ -1213,6 +1220,7 @@ void gserial_cleanup(void) pr_debug("%s: cleaned up ttyGS* support\n", __func__); } +EXPORT_SYMBOL_GPL(gserial_cleanup); /** * gserial_connect - notify TTY I/O glue that USB link is active @@ -1292,7 +1300,7 @@ fail_out: gser->in->driver_data = NULL; return status; } - +EXPORT_SYMBOL_GPL(gserial_connect); /** * gserial_disconnect - notify TTY I/O glue that USB link is inactive * @gser: the function, on which gserial_connect() was called @@ -1347,3 +1355,6 @@ void gserial_disconnect(struct gserial *gser) spin_unlock_irqrestore(&port->port_lock, flags); } +EXPORT_SYMBOL_GPL(gserial_disconnect); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 19b10a8828a6cdd5a4e7e37babd5084d35641f87 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 23 Dec 2012 21:10:06 +0100 Subject: usb: gadget: allocate & giveback serial ports instead hard code them This patch removes gserial_setup() and gserial_cleanup() and adds gserial_alloc_line() and gserial_free_line() to replace them. The initial setup of u_serial happens now on module load time. A maximum of four TTY ports can be requested which is the current limit. In theory we could extend this limit, the hard limit is the number of available endpoints. alloc_tty_driver() is now called at module init time with the max available ports. The per-line footprint here is on 32bit is 3 * size of pointer + 60 bytes (for cdevs). The remaining memory (struct gs_port) is allocated once a port is requested. With this change it is possible to load g_multi and g_serial at the same time. GS0 receives the module that is loaded first, GS1 is received by the next module and so on. With the configfs interface the port number can be exported and the device node is more predictable. Nothing changes for g_serial and friends as long as one module is used. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/acm_ms.c | 10 +- drivers/usb/gadget/cdc2.c | 9 +- drivers/usb/gadget/dbgp.c | 10 +- drivers/usb/gadget/f_acm.c | 3 - drivers/usb/gadget/f_obex.c | 4 - drivers/usb/gadget/f_serial.c | 4 - drivers/usb/gadget/multi.c | 12 +- drivers/usb/gadget/nokia.c | 37 +++-- drivers/usb/gadget/serial.c | 31 +++-- drivers/usb/gadget/u_serial.c | 310 ++++++++++++++++++++---------------------- drivers/usb/gadget/u_serial.h | 8 +- 11 files changed, 229 insertions(+), 209 deletions(-) (limited to 'drivers/usb/gadget/nokia.c') diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index 35cbe7283514..eb0fbdae5ccb 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -111,6 +111,7 @@ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); static struct fsg_common fsg_common; /*-------------------------------------------------------------------------*/ +static unsigned char tty_line; /* * We _always_ have both ACM and mass storage functions. @@ -125,7 +126,7 @@ static int __init acm_ms_do_config(struct usb_configuration *c) } - status = acm_bind_config(c, 0); + status = acm_bind_config(c, tty_line); if (status < 0) return status; @@ -152,7 +153,7 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) void *retp; /* set up serial link layer */ - status = gserial_setup(cdev->gadget, 1); + status = gserial_alloc_line(&tty_line); if (status < 0) return status; @@ -188,14 +189,13 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev) fail1: fsg_common_put(&fsg_common); fail0: - gserial_cleanup(); + gserial_free_line(tty_line); return status; } static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) { - gserial_cleanup(); - + gserial_free_line(tty_line); return 0; } diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 379df679ef30..16911a5aeb03 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -108,6 +108,7 @@ static u8 hostaddr[ETH_ALEN]; /*-------------------------------------------------------------------------*/ +static unsigned char tty_line; /* * We _always_ have both CDC ECM and CDC ACM functions. */ @@ -124,7 +125,7 @@ static int __init cdc_do_config(struct usb_configuration *c) if (status < 0) return status; - status = acm_bind_config(c, 0); + status = acm_bind_config(c, tty_line); if (status < 0) return status; @@ -157,7 +158,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return status; /* set up serial link layer */ - status = gserial_setup(cdev->gadget, 1); + status = gserial_alloc_line(&tty_line); if (status < 0) goto fail0; @@ -183,7 +184,7 @@ static int __init cdc_bind(struct usb_composite_dev *cdev) return 0; fail1: - gserial_cleanup(); + gserial_free_line(tty_line); fail0: gether_cleanup(); return status; @@ -191,7 +192,7 @@ fail0: static int __exit cdc_unbind(struct usb_composite_dev *cdev) { - gserial_cleanup(); + gserial_free_line(tty_line); gether_cleanup(); return 0; } diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 41eb98df5644..986fc511a2ed 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -231,6 +231,10 @@ static void dbgp_unbind(struct usb_gadget *gadget) gadget->ep0->driver_data = NULL; } +#ifdef CONFIG_USB_G_DBGP_SERIAL +static unsigned char tty_line; +#endif + static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) { int stp; @@ -268,7 +272,7 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) dbgp.serial->in->desc = &i_desc; dbgp.serial->out->desc = &o_desc; - if (gserial_setup(gadget, 1) < 0) { + if (gserial_alloc_line(&tty_line)) { stp = 3; goto fail_3; } @@ -377,7 +381,7 @@ static int dbgp_setup(struct usb_gadget *gadget, #ifdef CONFIG_USB_G_DBGP_PRINTK err = dbgp_enable_ep(); #else - err = gserial_connect(dbgp.serial, 0); + err = gserial_connect(dbgp.serial, tty_line); #endif if (err < 0) goto fail; @@ -420,7 +424,7 @@ static void __exit dbgp_exit(void) { usb_gadget_unregister_driver(&dbgp_driver); #ifdef CONFIG_USB_G_DBGP_SERIAL - gserial_cleanup(); + gserial_free_line(tty_line); #endif } diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index d4a7c1912105..3178fa70916e 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -719,9 +719,6 @@ acm_unbind(struct usb_configuration *c, struct usb_function *f) * * Returns zero on success, else negative errno. * - * Caller must have called @gserial_setup() with enough ports to - * handle all the ones it binds. Caller is also responsible - * for calling @gserial_cleanup() before module unload. */ int acm_bind_config(struct usb_configuration *c, u8 port_num) { diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index d8dd8782768c..36a004563b82 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -406,10 +406,6 @@ static inline bool can_support_obex(struct usb_configuration *c) * Context: single threaded during gadget setup * * Returns zero on success, else negative errno. - * - * Caller must have called @gserial_setup() with enough ports to - * handle all the ones it binds. Caller is also responsible - * for calling @gserial_cleanup() before module unload. */ int __init obex_bind_config(struct usb_configuration *c, u8 port_num) { diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index 98fa7795df5f..da33cfb3031d 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -260,10 +260,6 @@ gser_unbind(struct usb_configuration *c, struct usb_function *f) * Context: single threaded during gadget setup * * Returns zero on success, else negative errno. - * - * Caller must have called @gserial_setup() with enough ports to - * handle all the ones it binds. Caller is also responsible - * for calling @gserial_cleanup() before module unload. */ int __init gser_bind_config(struct usb_configuration *c, u8 port_num) { diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 00667911100d..9ca0ac0334e7 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -136,6 +136,7 @@ static struct fsg_common fsg_common; static u8 hostaddr[ETH_ALEN]; +static unsigned char tty_line; /********** RNDIS **********/ @@ -154,7 +155,7 @@ static __init int rndis_do_config(struct usb_configuration *c) if (ret < 0) return ret; - ret = acm_bind_config(c, 0); + ret = acm_bind_config(c, tty_line); if (ret < 0) return ret; @@ -205,7 +206,7 @@ static __init int cdc_do_config(struct usb_configuration *c) if (ret < 0) return ret; - ret = acm_bind_config(c, 0); + ret = acm_bind_config(c, tty_line); if (ret < 0) return ret; @@ -242,7 +243,6 @@ static int cdc_config_register(struct usb_composite_dev *cdev) /****************************** Gadget Bind ******************************/ - static int __ref multi_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; @@ -260,7 +260,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) return status; /* set up serial link layer */ - status = gserial_setup(cdev->gadget, 1); + status = gserial_alloc_line(&tty_line); if (status < 0) goto fail0; @@ -300,7 +300,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev) fail2: fsg_common_put(&fsg_common); fail1: - gserial_cleanup(); + gserial_free_line(tty_line); fail0: gether_cleanup(); return status; @@ -308,7 +308,7 @@ fail0: static int __exit multi_unbind(struct usb_composite_dev *cdev) { - gserial_cleanup(); + gserial_free_line(tty_line); gether_cleanup(); return 0; } diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 48f0d5c372a8..084d0d7cb8fa 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -100,6 +100,15 @@ MODULE_LICENSE("GPL"); static u8 hostaddr[ETH_ALEN]; +enum { + TTY_PORT_OBEX0, + TTY_PORT_OBEX1, + TTY_PORT_ACM, + TTY_PORTS_MAX, +}; + +static unsigned char tty_lines[TTY_PORTS_MAX]; + static int __init nokia_bind_config(struct usb_configuration *c) { int status = 0; @@ -108,15 +117,15 @@ static int __init nokia_bind_config(struct usb_configuration *c) if (status) printk(KERN_DEBUG "could not bind phonet config\n"); - status = obex_bind_config(c, 0); + status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX0]); if (status) printk(KERN_DEBUG "could not bind obex config %d\n", 0); - status = obex_bind_config(c, 1); + status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX1]); if (status) printk(KERN_DEBUG "could not bind obex config %d\n", 0); - status = acm_bind_config(c, 2); + status = acm_bind_config(c, tty_lines[TTY_PORT_ACM]); if (status) printk(KERN_DEBUG "could not bind acm config\n"); @@ -147,14 +156,17 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; int status; + int cur_line; status = gphonet_setup(cdev->gadget); if (status < 0) goto err_phonet; - status = gserial_setup(cdev->gadget, 3); - if (status < 0) - goto err_serial; + for (cur_line = 0; cur_line < TTY_PORTS_MAX; cur_line++) { + status = gserial_alloc_line(&tty_lines[cur_line]); + if (status) + goto err_ether; + } status = gether_setup(cdev->gadget, hostaddr); if (status < 0) @@ -191,8 +203,10 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) err_usb: gether_cleanup(); err_ether: - gserial_cleanup(); -err_serial: + cur_line--; + while (cur_line >= 0) + gserial_free_line(tty_lines[cur_line--]); + gphonet_cleanup(); err_phonet: return status; @@ -200,8 +214,13 @@ err_phonet: static int __exit nokia_unbind(struct usb_composite_dev *cdev) { + int i; + gphonet_cleanup(); - gserial_cleanup(); + + for (i = 0; i < TTY_PORTS_MAX; i++) + gserial_free_line(tty_lines[i]); + gether_cleanup(); return 0; diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index 4816f494fc4d..a883562f6a89 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -127,6 +127,7 @@ module_param(n_ports, uint, 0); MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ +static unsigned char tty_lines[MAX_U_SERIAL_PORTS]; static int __init serial_bind_acm_config(struct usb_configuration *c) { @@ -134,7 +135,7 @@ static int __init serial_bind_acm_config(struct usb_configuration *c) int status = 0; for (i = 0; i < n_ports && status == 0; i++) - status = acm_bind_config(c, i); + status = acm_bind_config(c, tty_lines[i]); return status; } @@ -144,7 +145,7 @@ static int __init serial_bind_obex_config(struct usb_configuration *c) int status = 0; for (i = 0; i < n_ports && status == 0; i++) - status = obex_bind_config(c, i); + status = obex_bind_config(c, tty_lines[i]); return status; } @@ -154,7 +155,7 @@ static int __init serial_bind_gser_config(struct usb_configuration *c) int status = 0; for (i = 0; i < n_ports && status == 0; i++) - status = gser_bind_config(c, i); + status = gser_bind_config(c, tty_lines[i]); return status; } @@ -165,13 +166,25 @@ static struct usb_configuration serial_config_driver = { .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; +static int gs_unbind(struct usb_composite_dev *cdev) +{ + int i; + + for (i = 0; i < n_ports; i++) + gserial_free_line(tty_lines[i]); + return 0; +} + static int __init gs_bind(struct usb_composite_dev *cdev) { int status; + int cur_line; - status = gserial_setup(cdev->gadget, n_ports); - if (status < 0) - return status; + for (cur_line = 0; cur_line < n_ports; cur_line++) { + status = gserial_alloc_line(&tty_lines[cur_line]); + if (status) + goto fail; + } /* Allocate string descriptor numbers ... note that string * contents can be overridden by the composite_dev glue. @@ -209,7 +222,9 @@ static int __init gs_bind(struct usb_composite_dev *cdev) return 0; fail: - gserial_cleanup(); + cur_line--; + while (cur_line >= 0) + gserial_free_line(tty_lines[cur_line--]); return status; } @@ -219,6 +234,7 @@ static __refdata struct usb_composite_driver gserial_driver = { .strings = dev_strings, .max_speed = USB_SPEED_SUPER, .bind = gs_bind, + .unbind = gs_unbind, }; static int __init init(void) @@ -254,6 +270,5 @@ module_init(init); static void __exit cleanup(void) { usb_composite_unregister(&gserial_driver); - gserial_cleanup(); } module_exit(cleanup); diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 1662d839a1d6..ea360fda2e14 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -36,11 +36,12 @@ * "serial port" functionality through the USB gadget stack. Each such * port is exposed through a /dev/ttyGS* node. * - * After initialization (gserial_setup), these TTY port devices stay - * available until they are removed (gserial_cleanup). Each one may be - * connected to a USB function (gserial_connect), or disconnected (with - * gserial_disconnect) when the USB host issues a config change event. - * Data can only flow when the port is connected to the host. + * After this module has been loaded, the individual TTY port can be requested + * (gserial_alloc_line()) and it will stay available until they are removed + * (gserial_free_line()). Each one may be connected to a USB function + * (gserial_connect), or disconnected (with gserial_disconnect) when the USB + * host issues a config change event. Data can only flow when the port is + * connected to the host. * * A given TTY port can be made available in multiple configurations. * For example, each one might expose a ttyGS0 node which provides a @@ -120,13 +121,10 @@ struct gs_port { struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ }; -/* increase N_PORTS if you need more */ -#define N_PORTS 4 static struct portmaster { struct mutex lock; /* protect open/close */ struct gs_port *port; -} ports[N_PORTS]; -static unsigned n_ports; +} ports[MAX_U_SERIAL_PORTS]; #define GS_CLOSE_TIMEOUT 15 /* seconds */ @@ -1033,10 +1031,19 @@ static int gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) { struct gs_port *port; + int ret = 0; + + mutex_lock(&ports[port_num].lock); + if (ports[port_num].port) { + ret = -EBUSY; + goto out; + } port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); - if (port == NULL) - return -ENOMEM; + if (port == NULL) { + ret = -ENOMEM; + goto out; + } tty_port_init(&port->port); spin_lock_init(&port->port_lock); @@ -1052,114 +1059,10 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) port->port_line_coding = *coding; ports[port_num].port = port; - - return 0; -} - -/** - * gserial_setup - initialize TTY driver for one or more ports - * @g: gadget to associate with these ports - * @count: how many ports to support - * Context: may sleep - * - * The TTY stack needs to know in advance how many devices it should - * plan to manage. Use this call to set up the ports you will be - * exporting through USB. Later, connect them to functions based - * on what configuration is activated by the USB host; and disconnect - * them as appropriate. - * - * An example would be a two-configuration device in which both - * configurations expose port 0, but through different functions. - * One configuration could even expose port 1 while the other - * one doesn't. - * - * Returns negative errno or zero. - */ -int gserial_setup(struct usb_gadget *g, unsigned count) -{ - unsigned i; - struct usb_cdc_line_coding coding; - int status; - - if (count == 0 || count > N_PORTS) - return -EINVAL; - - if (gs_tty_driver) - return -EBUSY; - - gs_tty_driver = alloc_tty_driver(count); - if (!gs_tty_driver) - return -ENOMEM; - - gs_tty_driver->driver_name = "g_serial"; - gs_tty_driver->name = PREFIX; - /* uses dynamically assigned dev_t values */ - - gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - gs_tty_driver->init_termios = tty_std_termios; - - /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on - * MS-Windows. Otherwise, most of these flags shouldn't affect - * anything unless we were to actually hook up to a serial line. - */ - gs_tty_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - gs_tty_driver->init_termios.c_ispeed = 9600; - gs_tty_driver->init_termios.c_ospeed = 9600; - - coding.dwDTERate = cpu_to_le32(9600); - coding.bCharFormat = 8; - coding.bParityType = USB_CDC_NO_PARITY; - coding.bDataBits = USB_CDC_1_STOP_BITS; - - tty_set_operations(gs_tty_driver, &gs_tty_ops); - - /* make devices be openable */ - for (i = 0; i < count; i++) { - mutex_init(&ports[i].lock); - status = gs_port_alloc(i, &coding); - if (status) { - count = i; - goto fail; - } - } - n_ports = count; - - /* export the driver ... */ - status = tty_register_driver(gs_tty_driver); - if (status) { - pr_err("%s: cannot register, err %d\n", - __func__, status); - goto fail; - } - - /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - for (i = 0; i < count; i++) { - struct device *tty_dev; - - tty_dev = tty_port_register_device(&ports[i].port->port, - gs_tty_driver, i, &g->dev); - if (IS_ERR(tty_dev)) - pr_warning("%s: no classdev for port %d, err %ld\n", - __func__, i, PTR_ERR(tty_dev)); - } - - pr_debug("%s: registered %d ttyGS* device%s\n", __func__, - count, (count == 1) ? "" : "s"); - - return status; -fail: - while (count--) { - tty_port_destroy(&ports[count].port->port); - kfree(ports[count].port); - } - put_tty_driver(gs_tty_driver); - gs_tty_driver = NULL; - return status; +out: + mutex_unlock(&ports[port_num].lock); + return ret; } -EXPORT_SYMBOL_GPL(gserial_setup); static int gs_closed(struct gs_port *port) { @@ -1171,56 +1074,77 @@ static int gs_closed(struct gs_port *port) return cond; } -/** - * gserial_cleanup - remove TTY-over-USB driver and devices - * Context: may sleep - * - * This is called to free all resources allocated by @gserial_setup(). - * Accordingly, it may need to wait until some open /dev/ files have - * closed. - * - * The caller must have issued @gserial_disconnect() for any ports - * that had previously been connected, so that there is never any - * I/O pending when it's called. - */ -void gserial_cleanup(void) +static void gserial_free_port(struct gs_port *port) +{ + tasklet_kill(&port->push); + /* wait for old opens to finish */ + wait_event(port->port.close_wait, gs_closed(port)); + WARN_ON(port->port_usb != NULL); + tty_port_destroy(&port->port); + kfree(port); +} + +void gserial_free_line(unsigned char port_num) { - unsigned i; struct gs_port *port; - if (!gs_tty_driver) + mutex_lock(&ports[port_num].lock); + if (WARN_ON(!ports[port_num].port)) { + mutex_unlock(&ports[port_num].lock); return; + } + port = ports[port_num].port; + ports[port_num].port = NULL; + mutex_unlock(&ports[port_num].lock); - /* start sysfs and /dev/ttyGS* node removal */ - for (i = 0; i < n_ports; i++) - tty_unregister_device(gs_tty_driver, i); - - for (i = 0; i < n_ports; i++) { - /* prevent new opens */ - mutex_lock(&ports[i].lock); - port = ports[i].port; - ports[i].port = NULL; - mutex_unlock(&ports[i].lock); - - tasklet_kill(&port->push); + gserial_free_port(port); + tty_unregister_device(gs_tty_driver, port_num); +} +EXPORT_SYMBOL_GPL(gserial_free_line); - /* wait for old opens to finish */ - wait_event(port->port.close_wait, gs_closed(port)); +int gserial_alloc_line(unsigned char *line_num) +{ + struct usb_cdc_line_coding coding; + struct device *tty_dev; + int ret; + int port_num; - WARN_ON(port->port_usb != NULL); + coding.dwDTERate = cpu_to_le32(9600); + coding.bCharFormat = 8; + coding.bParityType = USB_CDC_NO_PARITY; + coding.bDataBits = USB_CDC_1_STOP_BITS; - tty_port_destroy(&port->port); - kfree(port); + for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) { + ret = gs_port_alloc(port_num, &coding); + if (ret == -EBUSY) + continue; + if (ret) + return ret; + break; } - n_ports = 0; + if (ret) + return ret; - tty_unregister_driver(gs_tty_driver); - put_tty_driver(gs_tty_driver); - gs_tty_driver = NULL; + /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */ - pr_debug("%s: cleaned up ttyGS* support\n", __func__); + tty_dev = tty_port_register_device(&ports[port_num].port->port, + gs_tty_driver, port_num, NULL); + if (IS_ERR(tty_dev)) { + struct gs_port *port; + pr_err("%s: failed to register tty for port %d, err %ld\n", + __func__, port_num, PTR_ERR(tty_dev)); + + ret = PTR_ERR(tty_dev); + port = ports[port_num].port; + ports[port_num].port = NULL; + gserial_free_port(port); + goto err; + } + *line_num = port_num; +err: + return ret; } -EXPORT_SYMBOL_GPL(gserial_cleanup); +EXPORT_SYMBOL_GPL(gserial_alloc_line); /** * gserial_connect - notify TTY I/O glue that USB link is active @@ -1237,8 +1161,8 @@ EXPORT_SYMBOL_GPL(gserial_cleanup); * * Caller needs to have set up the endpoints and USB function in @dev * before calling this, as well as the appropriate (speed-specific) - * endpoint descriptors, and also have set up the TTY driver by calling - * @gserial_setup(). + * endpoint descriptors, and also have allocate @port_num by calling + * @gserial_alloc_line(). * * Returns negative errno or zero. * On success, ep->driver_data will be overwritten. @@ -1249,11 +1173,18 @@ int gserial_connect(struct gserial *gser, u8 port_num) unsigned long flags; int status; - if (!gs_tty_driver || port_num >= n_ports) + if (port_num >= MAX_U_SERIAL_PORTS) return -ENXIO; - /* we "know" gserial_cleanup() hasn't been called */ port = ports[port_num].port; + if (!port) { + pr_err("serial line %d not allocated.\n", port_num); + return -EINVAL; + } + if (port->port_usb) { + pr_err("serial line %d is in use.\n", port_num); + return -EBUSY; + } /* activate the endpoints */ status = usb_ep_enable(gser->in); @@ -1357,4 +1288,63 @@ void gserial_disconnect(struct gserial *gser) } EXPORT_SYMBOL_GPL(gserial_disconnect); +int userial_init(void) +{ + unsigned i; + int status; + + gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS); + if (!gs_tty_driver) + return -ENOMEM; + + gs_tty_driver->driver_name = "g_serial"; + gs_tty_driver->name = PREFIX; + /* uses dynamically assigned dev_t values */ + + gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gs_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + gs_tty_driver->init_termios = tty_std_termios; + + /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on + * MS-Windows. Otherwise, most of these flags shouldn't affect + * anything unless we were to actually hook up to a serial line. + */ + gs_tty_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + gs_tty_driver->init_termios.c_ispeed = 9600; + gs_tty_driver->init_termios.c_ospeed = 9600; + + tty_set_operations(gs_tty_driver, &gs_tty_ops); + for (i = 0; i < MAX_U_SERIAL_PORTS; i++) + mutex_init(&ports[i].lock); + + /* export the driver ... */ + status = tty_register_driver(gs_tty_driver); + if (status) { + pr_err("%s: cannot register, err %d\n", + __func__, status); + goto fail; + } + + pr_debug("%s: registered %d ttyGS* device%s\n", __func__, + MAX_U_SERIAL_PORTS, + (MAX_U_SERIAL_PORTS == 1) ? "" : "s"); + + return status; +fail: + put_tty_driver(gs_tty_driver); + gs_tty_driver = NULL; + return status; +} +module_init(userial_init); + +static void userial_cleanup(void) +{ + tty_unregister_driver(gs_tty_driver); + put_tty_driver(gs_tty_driver); + gs_tty_driver = NULL; +} +module_exit(userial_cleanup); + MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index 9b0fe6450fbf..f06ee9a5db51 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -15,6 +15,8 @@ #include #include +#define MAX_U_SERIAL_PORTS 4 + /* * One non-multiplexed "serial" I/O port ... there can be several of these * on any given USB peripheral device, if it provides enough endpoints. @@ -49,9 +51,9 @@ struct gserial { struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags); void gs_free_req(struct usb_ep *, struct usb_request *req); -/* port setup/teardown is handled by gadget driver */ -int gserial_setup(struct usb_gadget *g, unsigned n_ports); -void gserial_cleanup(void); +/* management of individual TTY ports */ +int gserial_alloc_line(unsigned char *port_line); +void gserial_free_line(unsigned char port_line); /* connect/disconnect is handled by individual functions */ int gserial_connect(struct gserial *, u8 port_num); -- cgit v1.2.3 From ff47f59467388104d369a441aa6996d257343775 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 23 Dec 2012 21:10:07 +0100 Subject: usb: gadget: f_acm: convert to new function interface with backwards compatibility This patch converts f_acm into a module which uses the new function interface. It also converts one of its users that is g_serial to make use of it. The other users of it (g_nokia for instance) are still using the old include file system and should not notice the change at all. So they can be converter later independently. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 4 ++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/acm_ms.c | 2 +- drivers/usb/gadget/cdc2.c | 2 +- drivers/usb/gadget/f_acm.c | 141 ++++++++++++++++++++++++++++++++---------- drivers/usb/gadget/multi.c | 2 +- drivers/usb/gadget/nokia.c | 1 + drivers/usb/gadget/serial.c | 83 +++++++++++++++++++------ drivers/usb/gadget/u_serial.h | 5 ++ 9 files changed, 186 insertions(+), 55 deletions(-) (limited to 'drivers/usb/gadget/nokia.c') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 8aefbbddf2a7..b61c72fc305a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -500,6 +500,9 @@ config USB_LIBCOMPOSITE tristate depends on USB_GADGET +config USB_F_ACM + tristate + config USB_F_SS_LB tristate @@ -758,6 +761,7 @@ config USB_GADGET_TARGET config USB_G_SERIAL tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" select USB_U_SERIAL + select USB_F_ACM select USB_LIBCOMPOSITE help The Serial Gadget talks to the Linux-USB generic serial driver. diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b88ee775de6b..97a13c349cc5 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o # USB Functions +obj-$(CONFIG_USB_F_ACM) += f_acm.o f_ss_lb-y := f_loopback.o f_sourcesink.o obj-$(CONFIG_USB_F_SS_LB) += f_ss_lb.o obj-$(CONFIG_USB_U_SERIAL) += u_serial.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c index eb0fbdae5ccb..4105a06582f8 100644 --- a/drivers/usb/gadget/acm_ms.c +++ b/drivers/usb/gadget/acm_ms.c @@ -40,7 +40,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ - +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_mass_storage.c" diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 16911a5aeb03..13b17f0b2da1 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -42,7 +42,7 @@ USB_GADGET_COMPOSITE_OPTIONS(); * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ - +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_ecm.c" #include "u_ether.c" diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 3178fa70916e..3ea7dc89b43d 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include "u_serial.h" #include "gadget_chips.h" @@ -608,6 +610,22 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; + /* REVISIT might want instance-specific strings to help + * distinguish instances ... + */ + + /* maybe allocate device-global string IDs, and patch descriptors */ + if (acm_string_defs[0].id == 0) { + status = usb_string_ids_tab(c->cdev, acm_string_defs); + if (status < 0) + return status; + acm_control_interface_desc.iInterface = + acm_string_defs[ACM_CTRL_IDX].id; + acm_data_interface_desc.iInterface = + acm_string_defs[ACM_DATA_IDX].id; + acm_iad_descriptor.iFunction = acm_string_defs[ACM_IAD_IDX].id; + } + /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) @@ -700,14 +718,41 @@ fail: return status; } +static struct f_acm *acm_alloc_basic_func(void) +{ + struct f_acm *acm; + + acm = kzalloc(sizeof(*acm), GFP_KERNEL); + if (!acm) + return NULL; + + spin_lock_init(&acm->lock); + + acm->port.connect = acm_connect; + acm->port.disconnect = acm_disconnect; + acm->port.send_break = acm_send_break; + + acm->port.func.name = "acm"; + acm->port.func.strings = acm_strings; + /* descriptors are per-instance copies */ + acm->port.func.bind = acm_bind; + acm->port.func.set_alt = acm_set_alt; + acm->port.func.setup = acm_setup; + acm->port.func.disable = acm_disable; + + return acm; +} + +#ifdef USB_FACM_INCLUDED static void -acm_unbind(struct usb_configuration *c, struct usb_function *f) +acm_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_acm *acm = func_to_acm(f); acm_string_defs[0].id = 0; usb_free_all_descriptors(f); - gs_free_req(acm->notify, acm->notify_req); + if (acm->notify_req) + gs_free_req(acm->notify, acm->notify_req); kfree(acm); } @@ -725,46 +770,74 @@ int acm_bind_config(struct usb_configuration *c, u8 port_num) struct f_acm *acm; int status; - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ - - /* maybe allocate device-global string IDs, and patch descriptors */ - if (acm_string_defs[0].id == 0) { - status = usb_string_ids_tab(c->cdev, acm_string_defs); - if (status < 0) - return status; - acm_control_interface_desc.iInterface = - acm_string_defs[ACM_CTRL_IDX].id; - acm_data_interface_desc.iInterface = - acm_string_defs[ACM_DATA_IDX].id; - acm_iad_descriptor.iFunction = acm_string_defs[ACM_IAD_IDX].id; - } - /* allocate and initialize one new instance */ - acm = kzalloc(sizeof *acm, GFP_KERNEL); + acm = acm_alloc_basic_func(); if (!acm) return -ENOMEM; - spin_lock_init(&acm->lock); - acm->port_num = port_num; - - acm->port.connect = acm_connect; - acm->port.disconnect = acm_disconnect; - acm->port.send_break = acm_send_break; - - acm->port.func.name = "acm"; - acm->port.func.strings = acm_strings; - /* descriptors are per-instance copies */ - acm->port.func.bind = acm_bind; - acm->port.func.unbind = acm_unbind; - acm->port.func.set_alt = acm_set_alt; - acm->port.func.setup = acm_setup; - acm->port.func.disable = acm_disable; + acm->port.func.unbind = acm_old_unbind; status = usb_add_function(c, &acm->port.func); if (status) kfree(acm); return status; } + +#else + +static void acm_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + + acm_string_defs[0].id = 0; + usb_free_all_descriptors(f); + if (acm->notify_req) + gs_free_req(acm->notify, acm->notify_req); +} + +static void acm_free_func(struct usb_function *f) +{ + struct f_acm *acm = func_to_acm(f); + + kfree(acm); +} + +static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) +{ + struct f_serial_opts *opts; + struct f_acm *acm; + + acm = acm_alloc_basic_func(); + if (!acm) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_serial_opts, func_inst); + acm->port_num = opts->port_num; + acm->port.func.unbind = acm_unbind; + acm->port.func.free_func = acm_free_func; + + return &acm->port.func; +} + +static void acm_free_instance(struct usb_function_instance *fi) +{ + struct f_serial_opts *opts; + + opts = container_of(fi, struct f_serial_opts, func_inst); + kfree(opts); +} + +static struct usb_function_instance *acm_alloc_instance(void) +{ + struct f_serial_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + opts->func_inst.free_func_inst = acm_free_instance; + return &opts->func_inst; +} +DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); +MODULE_LICENSE("GPL"); +#endif diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 9ca0ac0334e7..24b0f5630d44 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -41,7 +41,7 @@ MODULE_LICENSE("GPL"); * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "f_mass_storage.c" - +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_ecm.c" diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 084d0d7cb8fa..def37403989a 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -37,6 +37,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ +#define USB_FACM_INCLUDED #include "f_acm.c" #include "f_ecm.c" #include "f_obex.c" diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index a883562f6a89..68d7bb06ebcb 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -36,7 +36,6 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ -#include "f_acm.c" #include "f_obex.c" #include "f_serial.c" @@ -129,16 +128,6 @@ MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); /*-------------------------------------------------------------------------*/ static unsigned char tty_lines[MAX_U_SERIAL_PORTS]; -static int __init serial_bind_acm_config(struct usb_configuration *c) -{ - unsigned i; - int status = 0; - - for (i = 0; i < n_ports && status == 0; i++) - status = acm_bind_config(c, tty_lines[i]); - return status; -} - static int __init serial_bind_obex_config(struct usb_configuration *c) { unsigned i; @@ -166,13 +155,58 @@ static struct usb_configuration serial_config_driver = { .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; -static int gs_unbind(struct usb_composite_dev *cdev) +static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS]; +static struct usb_function *f_serial[MAX_U_SERIAL_PORTS]; + +static int serial_register_ports(struct usb_composite_dev *cdev, + struct usb_configuration *c, const char *f_name) { int i; + int ret; + + ret = usb_add_config_only(cdev, c); + if (ret) + goto out; + + for (i = 0; i < n_ports; i++) { + struct f_serial_opts *opts; + + fi_serial[i] = usb_get_function_instance(f_name); + if (IS_ERR(fi_serial[i])) { + ret = PTR_ERR(fi_serial[i]); + goto fail; + } + opts = container_of(fi_serial[i], struct f_serial_opts, func_inst); + opts->port_num = tty_lines[i]; + + f_serial[i] = usb_get_function(fi_serial[i]); + if (IS_ERR(f_serial[i])) { + ret = PTR_ERR(f_serial[i]); + goto err_get_func; + } + + ret = usb_add_function(c, f_serial[i]); + if (ret) + goto err_add_func; + } - for (i = 0; i < n_ports; i++) - gserial_free_line(tty_lines[i]); return 0; + +err_add_func: + usb_put_function(f_serial[i]); +err_get_func: + usb_put_function_instance(fi_serial[i]); + +fail: + i--; + while (i >= 0) { + usb_remove_function(c, f_serial[i]); + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + i--; + } +out: + return ret; } static int __init gs_bind(struct usb_composite_dev *cdev) @@ -204,10 +238,11 @@ static int __init gs_bind(struct usb_composite_dev *cdev) } /* register our configuration */ - if (use_acm) - status = usb_add_config(cdev, &serial_config_driver, - serial_bind_acm_config); - else if (use_obex) + if (use_acm) { + status = serial_register_ports(cdev, &serial_config_driver, + "acm"); + usb_ep_autoconfig_reset(cdev->gadget); + } else if (use_obex) status = usb_add_config(cdev, &serial_config_driver, serial_bind_obex_config); else @@ -228,6 +263,18 @@ fail: return status; } +static int gs_unbind(struct usb_composite_dev *cdev) +{ + int i; + + for (i = 0; i < n_ports; i++) { + usb_put_function(f_serial[i]); + usb_put_function_instance(fi_serial[i]); + gserial_free_line(tty_lines[i]); + } + return 0; +} + static __refdata struct usb_composite_driver gserial_driver = { .name = "g_serial", .dev = &device_desc, diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index f06ee9a5db51..66ce73a00509 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -17,6 +17,11 @@ #define MAX_U_SERIAL_PORTS 4 +struct f_serial_opts { + struct usb_function_instance func_inst; + u8 port_num; +}; + /* * One non-multiplexed "serial" I/O port ... there can be several of these * on any given USB peripheral device, if it provides enough endpoints. -- cgit v1.2.3