diff options
author | Chandana Kishori Chiluveru <cchiluve@codeaurora.org> | 2016-09-28 11:00:23 +0530 |
---|---|---|
committer | Chandana Kishori Chiluveru <cchiluve@codeaurora.org> | 2016-10-03 14:31:00 +0530 |
commit | a6fb576c5bfc2390ee1297ddc73504d326c38b80 (patch) | |
tree | a97433312c9f1a339b42ec706ab3a28b28258131 /drivers/usb | |
parent | 5f7a2a2a57726b9ed2744e816228ff48ded7271f (diff) |
usb: gadget: f_qc_rndis: Add support for configfs
Add APIs to allocate and instanciate f_qc_rndis function
driver using configFS.
Change-Id: I24f3dcb14c6467ab4c2d2eda464dfacda2c5b426
Signed-off-by: Chandana Kishori Chiluveru <cchiluve@codeaurora.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/Kconfig | 11 | ||||
-rw-r--r-- | drivers/usb/gadget/function/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_qc_rndis.c | 236 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_data_ipa.c | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_data_ipa.h | 18 |
5 files changed, 201 insertions, 80 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index c89ae79763c6..9ef57e5d7d64 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -175,6 +175,9 @@ config USB_F_SUBSET config USB_F_RNDIS tristate +config USB_F_QCRNDIS + tristate + config USB_F_MASS_STORAGE tristate @@ -318,6 +321,14 @@ config USB_CONFIGFS_ECM_SUBSET On hardware that can't implement the full protocol, a simple CDC subset is used, placing fewer demands on USB. +config USB_CONFIGFS_QCRNDIS + bool "RNDIS" + depends on USB_CONFIGFS + depends on RNDIS_IPA + depends on NET + select USB_U_ETHER + select USB_F_QCRNDIS + config USB_CONFIGFS_RNDIS bool "RNDIS" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 9a27deac7978..a213cd4c8377 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -60,3 +60,5 @@ usb_f_cdev-y := f_cdev.o obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o usb_f_qdss-y := f_qdss.o u_qdss.o obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o +usb_f_qcrndis-y := f_qc_rndis.o u_data_ipa.o +obj-$(CONFIG_USB_F_QCRNDIS) += usb_f_qcrndis.o diff --git a/drivers/usb/gadget/function/f_qc_rndis.c b/drivers/usb/gadget/function/f_qc_rndis.c index fe7dc76a8157..316967415aa9 100644 --- a/drivers/usb/gadget/function/f_qc_rndis.c +++ b/drivers/usb/gadget/function/f_qc_rndis.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> @@ -34,6 +35,7 @@ #include "rndis.h" #include "u_data_ipa.h" #include <linux/rndis_ipa.h> +#include "configfs.h" unsigned int rndis_dl_max_xfer_size = 9216; module_param(rndis_dl_max_xfer_size, uint, S_IRUGO | S_IWUSR); @@ -644,6 +646,7 @@ struct net_device *rndis_qc_get_net(const char *netname) static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); + struct f_rndis_qc_opts *opts; struct usb_composite_dev *cdev = f->config->cdev; u8 src_connection_idx; u8 dst_connection_idx; @@ -730,6 +733,7 @@ static int rndis_qc_set_alt(struct usb_function *f, unsigned intf, unsigned alt) net = rndis_qc_get_net("rndis0"); if (IS_ERR(net)) return PTR_ERR(net); + opts->net = net; rndis_set_param_dev(rndis->params, net, &rndis->cdc_filter); @@ -866,6 +870,31 @@ rndis_qc_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; + /* maybe allocate device-global string IDs */ + if (rndis_qc_string_defs[0].id == 0) { + + /* control interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[0].id = status; + rndis_qc_control_intf.iInterface = status; + + /* data interface label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[1].id = status; + rndis_qc_data_intf.iInterface = status; + + /* IAD iFunction label */ + status = usb_string_id(c->cdev); + if (status < 0) + return status; + rndis_qc_string_defs[2].id = status; + rndis_qc_iad_descriptor.iFunction = status; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -1024,11 +1053,18 @@ fail: return status; } +static void rndis_qc_free(struct usb_function *f) +{ + struct f_rndis_qc_opts *opts; + + opts = container_of(f->fi, struct f_rndis_qc_opts, func_inst); + opts->refcnt--; +} + static void rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis_qc *rndis = func_to_rndis_qc(f); - unsigned long flags; pr_debug("rndis_qc_unbind: free\n"); rndis_deregister(rndis->params); @@ -1051,10 +1087,6 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f) rndis_ipa_cleanup(rndis_ipa_params.private); rndis_ipa_supported = false; - spin_lock_irqsave(&rndis_lock, flags); - kfree(rndis); - _rndis_qc = NULL; - spin_unlock_irqrestore(&rndis_lock, flags); } void rndis_ipa_reset_trigger(void) @@ -1102,13 +1134,6 @@ void rndis_net_ready_notify(void) ipa_data_start_rx_tx(USB_IPA_FUNC_RNDIS); } -/* Some controllers can't support RNDIS ... */ -static inline bool can_support_rndis_qc(struct usb_configuration *c) -{ - /* everything else is *presumably* fine */ - return true; -} - /** * rndis_qc_bind_config - add RNDIS network link to a configuration * @c: the configuration to support the network link @@ -1121,62 +1146,27 @@ static inline bool can_support_rndis_qc(struct usb_configuration *c) * Caller must have called @gether_setup(). Caller is also responsible * for calling @gether_cleanup() before module unload. */ -int -rndis_qc_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]) -{ - return rndis_qc_bind_config_vendor(c, ethaddr, 0, NULL, 1, 0); -} -int -rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, - u8 max_pkt_per_xfer, - u8 pkt_alignment_factor) +static struct +usb_function *rndis_qc_bind_config_vendor(struct usb_function_instance *fi, + u32 vendorID, const char *manufacturer, + u8 max_pkt_per_xfer, u8 pkt_alignment_factor) { + struct f_rndis_qc_opts *opts = container_of(fi, + struct f_rndis_qc_opts, func_inst); struct f_rndis_qc *rndis; int status; - if (!can_support_rndis_qc(c) || !ethaddr) { - pr_debug("%s: invalid argument\n", __func__); - return -EINVAL; - } - - /* maybe allocate device-global string IDs */ - if (rndis_qc_string_defs[0].id == 0) { - - /* control interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[0].id = status; - rndis_qc_control_intf.iInterface = status; - - /* data interface label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rndis_qc_string_defs[1].id = status; - rndis_qc_data_intf.iInterface = status; - - /* IAD iFunction label */ - status = usb_string_id(c->cdev); - if (status < 0) - return status; - rnis_qc_string_defs[2].id = status; - rndis_qc_iad_descriptor.iFunction = status; - } - /* allocate and initialize one new instance */ status = -ENOMEM; - rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); - if (!rndis) { - pr_err("%s: fail allocate and initialize new instance\n", - __func__); - goto fail; - } - rndis->vendorID = vendorID; - rndis->manufacturer = manufacturer; + opts = container_of(fi, struct f_rndis_qc_opts, func_inst); + + opts->refcnt++; + rndis = opts->rndis; + + rndis->vendorID = opts->vendor_id; + rndis->manufacturer = opts->manufacturer; /* export host's Ethernet address in CDC format */ random_ether_addr(rndis_ipa_params.host_ethaddr); random_ether_addr(rndis_ipa_params.device_ethaddr); @@ -1223,27 +1213,23 @@ rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], rndis->func.disable = rndis_qc_disable; rndis->func.suspend = rndis_qc_suspend; rndis->func.resume = rndis_qc_resume; + rndis->func.free_func = rndis_qc_free; _rndis_qc = rndis; status = rndis_ipa_init(&rndis_ipa_params); if (status) { pr_err("%s: failed to init rndis_ipa\n", __func__); - goto fail; + kfree(rndis); + return ERR_PTR(status); } - status = usb_add_function(c, &rndis->func); - if (status) { - ndis_ipa_cleanup(rndis_ipa_params.private); - goto fail; - } - - return 0; + return &rndis->func; +} -fail: - kfree(rndis); - _rndis_qc = NULL; - return status; +static struct usb_function *qcrndis_alloc(struct usb_function_instance *fi) +{ + return rndis_qc_bind_config_vendor(fi, 0, NULL, 1, 0); } static int rndis_qc_open_dev(struct inode *ip, struct file *fp) @@ -1332,12 +1318,47 @@ static struct miscdevice rndis_qc_device = { .fops = &rndis_qc_fops, }; -static int rndis_qc_init(void) +static void qcrndis_free_inst(struct usb_function_instance *f) +{ + struct f_rndis_qc *rndis; + struct f_rndis_qc_opts *opts = container_of(f, + struct f_rndis_qc_opts, func_inst); + unsigned long flags; + + rndis = opts->rndis; + misc_deregister(&rndis_qc_device); + + ipa_data_free(USB_IPA_FUNC_RNDIS); + spin_lock_irqsave(&rndis_lock, flags); + kfree(rndis); + _rndis_qc = NULL; + kfree(opts->rndis); + kfree(opts); + spin_unlock_irqrestore(&rndis_lock, flags); +} + +static int qcrndis_set_inst_name(struct usb_function_instance *fi, + const char *name) { + struct f_rndis_qc_opts *opts = container_of(fi, + struct f_rndis_qc_opts, func_inst); + struct f_rndis_qc *rndis; + int name_len; int ret; - pr_info("initialize rndis QC instance\n"); + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + pr_debug("initialize rndis QC instance\n"); + rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); + if (!rndis) { + pr_err("%s: fail allocate and initialize new instance\n", + __func__); + return -ENOMEM; + } + opts->rndis = rndis; ret = misc_register(&rndis_qc_device); if (ret) pr_err("rndis QC driver failed to register\n"); @@ -1346,10 +1367,51 @@ static int rndis_qc_init(void) ret = ipa_data_setup(USB_IPA_FUNC_RNDIS); if (ret) { pr_err("bam_data_setup failed err: %d\n", ret); + kfree(rndis); return ret; } - return ret; + return 0; +} + +static inline +struct f_rndis_qc_opts *to_f_qc_rndis_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_rndis_qc_opts, + func_inst.group); +} + +static void qcrndis_attr_release(struct config_item *item) +{ + struct f_rndis_qc_opts *opts = to_f_qc_rndis_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations qcrndis_item_ops = { + .release = qcrndis_attr_release, +}; + +static struct config_item_type qcrndis_func_type = { + .ct_item_ops = &qcrndis_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct usb_function_instance *qcrndis_alloc_inst(void) +{ + struct f_rndis_qc_opts *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.set_inst_name = qcrndis_set_inst_name; + opts->func_inst.free_func_inst = qcrndis_free_inst; + + config_group_init_type_name(&opts->func_inst.group, "", + &qcrndis_func_type); + + return &opts->func_inst; } static void rndis_qc_cleanup(void) @@ -1378,3 +1440,27 @@ bool rndis_qc_get_skip_ep_config(void) { return rndis_ipa_params.skip_ep_cfg; } + +DECLARE_USB_FUNCTION_INIT(qcrndis, qcrndis_alloc_inst, qcrndis_alloc); + +static int __init usb_qcrndis_init(void) +{ + int ret; + + ret = usb_function_register(&qcrndisusb_func); + if (ret) { + pr_err("%s: failed to register diag %d\n", __func__, ret); + return ret; + } + return ret; +} + +static void __exit usb_qcrndis_exit(void) +{ + usb_function_unregister(&qcrndisusb_func); + rndis_qc_cleanup(); +} + +module_init(usb_qcrndis_init); +module_exit(usb_qcrndis_exit); +MODULE_DESCRIPTION("USB RMNET Function Driver"); diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 887773318ea6..56e7dea427ec 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -1085,6 +1085,20 @@ void ipa_data_port_select(enum ipa_func_type func) port->func_type = func; }; +void ipa_data_free(enum ipa_func_type func) +{ + pr_debug("freeing %d IPA BAM port", func); + + kfree(ipa_data_ports[func]); + ipa_data_ports[func] = NULL; + if (func == USB_IPA_FUNC_RNDIS) + kfree(rndis_data); + if (ipa_data_wq) { + destroy_workqueue(ipa_data_wq); + ipa_data_wq = NULL; + } +} + /** * ipa_data_setup() - setup BAM2BAM IPA port * diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h index 6f51a3de9618..a1c1055bd8ef 100644 --- a/drivers/usb/gadget/function/u_data_ipa.h +++ b/drivers/usb/gadget/function/u_data_ipa.h @@ -45,11 +45,24 @@ struct gadget_ipa_port { }; +/* for configfs support */ +#define MAX_INST_NAME_LEN 40 + +struct f_rndis_qc_opts { + struct usb_function_instance func_inst; + struct f_rndis_qc *rndis; + u32 vendor_id; + const char *manufacturer; + struct net_device *net; + int refcnt; +}; + void ipa_data_port_select(enum ipa_func_type func); void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func); int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func, u8 src_connection_idx, u8 dst_connection_idx); int ipa_data_setup(enum ipa_func_type func); +void ipa_data_free(enum ipa_func_type func); void ipa_data_flush_workqueue(void); void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func, @@ -69,11 +82,6 @@ void ipa_data_start_rndis_ipa(enum ipa_func_type func); void ipa_data_stop_rndis_ipa(enum ipa_func_type func); -int -rndis_qc_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], - u32 vendorID, const char *manufacturer, - u8 maxPktPerXfer, u8 pkt_alignment_factor); - void *rndis_qc_get_ipa_priv(void); void *rndis_qc_get_ipa_rx_cb(void); bool rndis_qc_get_skip_ep_config(void); |