summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorChandana Kishori Chiluveru <cchiluve@codeaurora.org>2016-09-28 11:00:23 +0530
committerChandana Kishori Chiluveru <cchiluve@codeaurora.org>2016-10-03 14:31:00 +0530
commita6fb576c5bfc2390ee1297ddc73504d326c38b80 (patch)
treea97433312c9f1a339b42ec706ab3a28b28258131 /drivers/usb
parent5f7a2a2a57726b9ed2744e816228ff48ded7271f (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/Kconfig11
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_qc_rndis.c236
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.c14
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.h18
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);