From f466c6353819326873fa48a02c6f2d7c903240d6 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:57 +0200 Subject: [PATCH] usb: gadget: f_rndis: convert to new function interface with backward compatibility Converting rndis to the new function interface requires converting the USB rndis' function code and its users. This patch converts the f_rndis.c to the new function interface. The file is now compiled into a separate usb_f_rndis.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/ether.c | 1 + drivers/usb/gadget/f_rndis.c | 203 ++++++++++++++++++++++++++++++----- drivers/usb/gadget/g_ffs.c | 1 + drivers/usb/gadget/multi.c | 1 + drivers/usb/gadget/u_rndis.h | 32 ++++++ 7 files changed, 215 insertions(+), 28 deletions(-) create mode 100644 drivers/usb/gadget/u_rndis.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3a72b9dbf7f0..22c86089d647 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -526,6 +526,9 @@ config USB_F_EEM config USB_F_SUBSET tristate +config USB_F_RNDIS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1bfad55b9678..b41776065f27 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -58,6 +58,8 @@ usb_f_eem-y := f_eem.o obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o usb_f_ecm_subset-y := f_subset.o obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o +usb_f_rndis-y := f_rndis.o +obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o # # USB gadget drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 9e96d5583e4c..4d7290a48fe7 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -106,6 +106,7 @@ static inline bool has_rndis(void) #include "u_ecm.h" #include "u_gether.h" #ifdef USB_ETH_RNDIS +#define USB_FRNDIS_INCLUDED #include "f_rndis.c" #include "rndis.h" #endif diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 36e8c44d8e5e..437198b6d8fa 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -17,15 +17,16 @@ #include #include +#include #include #include #include #include "u_ether.h" +#include "u_rndis.h" #include "rndis.h" - /* * This function is an RNDIS Ethernet port -- a Microsoft protocol that's * been promoted instead of the standard CDC Ethernet. The published RNDIS @@ -655,6 +656,13 @@ static void rndis_close(struct gether *geth) /*-------------------------------------------------------------------------*/ +/* Some controllers can't support RNDIS ... */ +static inline bool can_support_rndis(struct usb_configuration *c) +{ + /* everything else is *presumably* fine */ + return true; +} + /* ethernet function driver setup/binding */ static int @@ -665,6 +673,45 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; +#ifndef USB_FRNDIS_INCLUDED + struct f_rndis_opts *rndis_opts; + + if (!can_support_rndis(c)) + return -EINVAL; + + rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() + * configurations are bound in sequence with list_for_each_entry, + * in each configuration its functions are bound in sequence + * with list_for_each_entry, so we assume no race condition + * with regard to rndis_opts->bound access + */ + if (!rndis_opts->bound) { + gether_set_gadget(rndis_opts->net, cdev->gadget); + status = gether_register_netdev(rndis_opts->net); + if (status) + return status; + rndis_opts->bound = true; + } +#endif + + if (rndis_string_defs[0].id == 0) { + /* ... and setup RNDIS itself */ + status = rndis_init(); + if (status < 0) + return status; + + status = usb_string_ids_tab(c->cdev, rndis_string_defs); + if (status) + return status; + + rndis_control_intf.iInterface = rndis_string_defs[0].id; + rndis_data_intf.iInterface = rndis_string_defs[1].id; + rndis_iad_descriptor.iFunction = rndis_string_defs[2].id; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -741,10 +788,12 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; +#ifdef USB_FRNDIS_INCLUDED status = rndis_register(rndis_response_available, rndis); if (status < 0) goto fail; rndis->config = status; +#endif rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); rndis_set_host_mac(rndis->config, rndis->ethaddr); @@ -787,8 +836,10 @@ fail: return status; } +#ifdef USB_FRNDIS_INCLUDED + static void -rndis_unbind(struct usb_configuration *c, struct usb_function *f) +rndis_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); @@ -804,13 +855,6 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis); } -/* Some controllers can't support RNDIS ... */ -static inline bool can_support_rndis(struct usb_configuration *c) -{ - /* everything else is *presumably* fine */ - return true; -} - int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], u32 vendorID, const char *manufacturer, struct eth_dev *dev) @@ -818,24 +862,6 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], struct f_rndis *rndis; int status; - if (!can_support_rndis(c) || !ethaddr) - return -EINVAL; - - if (rndis_string_defs[0].id == 0) { - /* ... and setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; - - status = usb_string_ids_tab(c->cdev, rndis_string_defs); - if (status) - return status; - - rndis_control_intf.iInterface = rndis_string_defs[0].id; - rndis_data_intf.iInterface = rndis_string_defs[1].id; - rndis_iad_descriptor.iFunction = rndis_string_defs[2].id; - } - /* allocate and initialize one new instance */ status = -ENOMEM; rndis = kzalloc(sizeof *rndis, GFP_KERNEL); @@ -859,7 +885,7 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], rndis->port.func.strings = rndis_strings; /* descriptors are per-instance copies */ rndis->port.func.bind = rndis_bind; - rndis->port.func.unbind = rndis_unbind; + rndis->port.func.unbind = rndis_old_unbind; rndis->port.func.set_alt = rndis_set_alt; rndis->port.func.setup = rndis_setup; rndis->port.func.disable = rndis_disable; @@ -872,3 +898,124 @@ fail: } return status; } + +#else + +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) +{ + struct f_rndis_opts *opts; + + opts = container_of(f, struct f_rndis_opts, func_inst); + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + opts->borrowed_net = opts->bound = true; + opts->net = net; +} +EXPORT_SYMBOL(rndis_borrow_net); + +static void rndis_free_inst(struct usb_function_instance *f) +{ + struct f_rndis_opts *opts; + + opts = container_of(f, struct f_rndis_opts, func_inst); + if (!opts->borrowed_net) { + if (opts->bound) + gether_cleanup(netdev_priv(opts->net)); + else + free_netdev(opts->net); + } + kfree(opts); +} + +static struct usb_function_instance *rndis_alloc_inst(void) +{ + struct f_rndis_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.free_func_inst = rndis_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + return &opts->func_inst; +} + +static void rndis_free(struct usb_function *f) +{ + struct f_rndis *rndis; + + rndis = func_to_rndis(f); + rndis_deregister(rndis->config); + kfree(rndis); +} + +static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + + rndis_exit(); + rndis_string_defs[0].id = 0; + usb_free_all_descriptors(f); + + kfree(rndis->notify_req->buf); + usb_ep_free_request(rndis->notify, rndis->notify_req); +} + +static struct usb_function *rndis_alloc(struct usb_function_instance *fi) +{ + struct f_rndis *rndis; + struct f_rndis_opts *opts; + int status; + + /* allocate and initialize one new instance */ + rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); + if (!rndis) { + rndis_exit(); + return ERR_PTR(-ENOMEM); + } + + opts = container_of(fi, struct f_rndis_opts, func_inst); + + gether_get_host_addr_u8(opts->net, rndis->ethaddr); + rndis->vendorID = opts->vendor_id; + rndis->manufacturer = opts->manufacturer; + + rndis->port.ioport = netdev_priv(opts->net); + /* RNDIS activates when the host changes this filter */ + rndis->port.cdc_filter = 0; + + /* RNDIS has special (and complex) framing */ + rndis->port.header_len = sizeof(struct rndis_packet_msg_type); + rndis->port.wrap = rndis_add_header; + rndis->port.unwrap = rndis_rm_hdr; + + rndis->port.func.name = "rndis"; + rndis->port.func.strings = rndis_strings; + /* descriptors are per-instance copies */ + rndis->port.func.bind = rndis_bind; + rndis->port.func.unbind = rndis_unbind; + rndis->port.func.set_alt = rndis_set_alt; + rndis->port.func.setup = rndis_setup; + rndis->port.func.disable = rndis_disable; + rndis->port.func.free_func = rndis_free; + + status = rndis_register(rndis_response_available, rndis); + if (status < 0) { + kfree(rndis); + return ERR_PTR(status); + } + rndis->config = status; + + return &rndis->port.func; +} + +DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); + +#endif diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 3d290e5106af..5327c82472ed 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -33,6 +33,7 @@ #define USB_FSUBSET_INCLUDED # include "f_subset.c" # ifdef USB_ETH_RNDIS +# define USB_FRNDIS_INCLUDED # include "f_rndis.c" # include "rndis.h" # endif diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 656c99983f62..032b96a51ce4 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -46,6 +46,7 @@ MODULE_LICENSE("GPL"); #define USBF_ECM_INCLUDED #include "f_ecm.c" #ifdef USB_ETH_RNDIS +# define USB_FRNDIS_INCLUDED # include "f_rndis.c" # include "rndis.h" #endif diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h new file mode 100644 index 000000000000..d274df56ce75 --- /dev/null +++ b/drivers/usb/gadget/u_rndis.h @@ -0,0 +1,32 @@ +/* + * u_rndis.h + * + * Utility definitions for the subset function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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_RNDIS_H +#define U_RNDIS_H + +#include + +struct f_rndis_opts { + struct usb_function_instance func_inst; + u32 vendor_id; + const char *manufacturer; + struct net_device *net; + bool bound; + bool borrowed_net; +}; + +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); + +#endif /* U_RNDIS_H */ -- 2.39.2