diff options
| author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2013-05-28 09:15:57 +0200 | 
|---|---|---|
| committer | Felipe Balbi <balbi@ti.com> | 2013-06-10 17:58:08 +0300 | 
| commit | f466c6353819326873fa48a02c6f2d7c903240d6 (patch) | |
| tree | b478bb450e13710092e5808cd2c6b5335951cebc | |
| parent | 02832e56f88a981474ee4c7c141f46fc1b4454f4 (diff) | |
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 <andrzej.p@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
| -rw-r--r-- | drivers/usb/gadget/Kconfig | 3 | ||||
| -rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
| -rw-r--r-- | drivers/usb/gadget/ether.c | 1 | ||||
| -rw-r--r-- | drivers/usb/gadget/f_rndis.c | 203 | ||||
| -rw-r--r-- | drivers/usb/gadget/g_ffs.c | 1 | ||||
| -rw-r--r-- | drivers/usb/gadget/multi.c | 1 | ||||
| -rw-r--r-- | drivers/usb/gadget/u_rndis.h | 32 | 
7 files changed, 215 insertions, 28 deletions
| 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 <linux/slab.h>  #include <linux/kernel.h> +#include <linux/module.h>  #include <linux/device.h>  #include <linux/etherdevice.h>  #include <linux/atomic.h>  #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 <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_RNDIS_H +#define U_RNDIS_H + +#include <linux/usb/composite.h> + +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 */ | 
