summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAjay Agarwal <ajaya@codeaurora.org>2016-10-27 19:12:50 +0530
committerAjay Agarwal <ajaya@codeaurora.org>2016-12-01 14:04:57 +0530
commitabe1a7d1574a58016dab6f337d0ae300994021d4 (patch)
tree5e55aed72a3b00e594388366445035a08102a1c0
parent52b9f4271fd006f5df246461d001ab3fece06dc1 (diff)
usb: gadget: f_rmnet: Add support for configfs
Add APIs to allocate and instanciate f_rmnet function driver using configFS. Change-Id: Iad76a7e317dd9caf846dbe870baee63a8c5ce6c1 Signed-off-by: Ajay Agarwal <ajaya@codeaurora.org>
-rw-r--r--drivers/usb/gadget/Kconfig9
-rw-r--r--drivers/usb/gadget/function/Makefile2
-rw-r--r--drivers/usb/gadget/function/f_rmnet.c177
-rw-r--r--drivers/usb/gadget/function/u_data_ipa.h6
4 files changed, 146 insertions, 48 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 9ef57e5d7d64..9d9eed2d5d68 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -178,6 +178,9 @@ config USB_F_RNDIS
config USB_F_QCRNDIS
tristate
+config USB_F_RMNET_BAM
+ tristate
+
config USB_F_MASS_STORAGE
tristate
@@ -345,6 +348,12 @@ config USB_CONFIGFS_RNDIS
XP, you'll need to download drivers from Microsoft's website; a URL
is given in comments found in that info file.
+config USB_CONFIGFS_RMNET_BAM
+ bool "RMNET"
+ depends on USB_CONFIGFS
+ depends on IPA
+ select USB_F_RMNET_BAM
+
config USB_CONFIGFS_EEM
bool "Ethernet Emulation Model (EEM)"
depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index a213cd4c8377..511909fb78f6 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -62,3 +62,5 @@ 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
+usb_f_rmnet_bam-y := f_rmnet.o u_ctrl_qti.o
+obj-$(CONFIG_USB_F_RMNET_BAM) += usb_f_rmnet_bam.o
diff --git a/drivers/usb/gadget/function/f_rmnet.c b/drivers/usb/gadget/function/f_rmnet.c
index 758414d6bcb5..0fd7e213ef99 100644
--- a/drivers/usb/gadget/function/f_rmnet.c
+++ b/drivers/usb/gadget/function/f_rmnet.c
@@ -17,9 +17,11 @@
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/usb_bam.h>
+#include <linux/module.h>
#include "u_rmnet.h"
#include "u_data_ipa.h"
+#include "configfs.h"
#define RMNET_NOTIFY_INTERVAL 5
#define RMNET_MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification)
@@ -49,7 +51,7 @@ struct f_rmnet {
struct list_head cpkt_resp_q;
unsigned long notify_count;
unsigned long cpkts_len;
-} rmnet_port;
+} *rmnet_port;
static struct usb_interface_descriptor rmnet_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
@@ -277,15 +279,6 @@ static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
/* -------------------------------------------*/
-static int rmnet_gport_setup(void)
-{
- int ret;
- ret = ipa_data_setup(USB_IPA_FUNC_RMNET);
- if (ret < 0)
- return ret;
- return 0;
-}
-
static int gport_rmnet_connect(struct f_rmnet *dev, unsigned intf)
{
int ret;
@@ -334,6 +327,16 @@ static int gport_rmnet_disconnect(struct f_rmnet *dev)
return 0;
}
+static void frmnet_free(struct usb_function *f)
+{
+ struct f_rmnet_opts *opts;
+
+ opts = container_of(f->fi, struct f_rmnet_opts, func_inst);
+ opts->refcnt--;
+ kfree(rmnet_port);
+ rmnet_port = NULL;
+}
+
static void frmnet_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rmnet *dev = func_to_rmnet(f);
@@ -426,11 +429,12 @@ static int
frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_rmnet *dev = func_to_rmnet(f);
- struct usb_composite_dev *cdev = dev->cdev;
+ struct usb_composite_dev *cdev = f->config->cdev;
int ret;
struct list_head *cpkt;
pr_debug("%s: dev: %p\n", __func__, dev);
+ dev->cdev = cdev;
if (dev->notify->driver_data) {
pr_debug("%s: reset port\n", __func__);
usb_ep_disable(dev->notify);
@@ -816,6 +820,16 @@ static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_composite_dev *cdev = c->cdev;
int ret = -ENODEV;
+ if (rmnet_string_defs[0].id == 0) {
+ ret = usb_string_id(c->cdev);
+ if (ret < 0) {
+ pr_err("%s: failed to get string id, err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ rmnet_string_defs[0].id = ret;
+ }
+
pr_debug("%s: start binding\n", __func__);
dev->ifc_id = usb_interface_id(c, f);
if (dev->ifc_id < 0) {
@@ -936,33 +950,26 @@ ep_auto_out_fail:
return ret;
}
-static int frmnet_bind_config(struct usb_configuration *c)
+static struct usb_function *frmnet_bind_config(struct usb_function_instance *fi)
{
+ struct f_rmnet_opts *opts;
int status;
struct f_rmnet *dev;
struct usb_function *f;
unsigned long flags;
- pr_debug("%s: usb config:%p\n", __func__, c);
- dev = &rmnet_port;
- if (rmnet_string_defs[0].id == 0) {
- status = usb_string_id(c->cdev);
- if (status < 0) {
- pr_err("%s: failed to get string id, err:%d\n",
- __func__, status);
- return status;
- }
- rmnet_string_defs[0].id = status;
- }
-
+ /* allocate and initialize one new instance */
+ status = -ENOMEM;
+ opts = container_of(fi, struct f_rmnet_opts, func_inst);
+ opts->refcnt++;
+ dev = opts->dev;
spin_lock_irqsave(&dev->lock, flags);
- dev->cdev = c->cdev;
f = &dev->func;
f->name = kasprintf(GFP_ATOMIC, "rmnet%d", 0);
spin_unlock_irqrestore(&dev->lock, flags);
if (!f->name) {
pr_err("%s: cannot allocate memory for name\n", __func__);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
f->strings = rmnet_strings;
@@ -973,21 +980,14 @@ static int frmnet_bind_config(struct usb_configuration *c)
f->setup = frmnet_setup;
f->suspend = frmnet_suspend;
f->resume = frmnet_resume;
+ f->free_func = frmnet_free;
dev->port.send_cpkt_response = frmnet_send_cpkt_response;
dev->port.disconnect = frmnet_disconnect;
dev->port.connect = frmnet_connect;
- status = usb_add_function(c, f);
- if (status) {
- pr_err("%s: usb add function failed: %d\n",
- __func__, status);
- kfree(f->name);
- return status;
- }
-
pr_debug("%s: complete\n", __func__);
- return status;
+ return f;
}
static int rmnet_init(void)
@@ -998,24 +998,105 @@ static int rmnet_init(void)
static void frmnet_cleanup(void)
{
gqti_ctrl_cleanup();
- kfree(&rmnet_port);
}
-static int frmnet_init_port(const char *ctrl_name, const char *data_name,
- const char *port_name)
+static void rmnet_free_inst(struct usb_function_instance *f)
+{
+ struct f_rmnet_opts *opts = container_of(f, struct f_rmnet_opts,
+ func_inst);
+ ipa_data_free(USB_IPA_FUNC_RMNET);
+ kfree(opts);
+}
+
+static int rmnet_set_inst_name(struct usb_function_instance *fi,
+ const char *name)
{
- struct f_rmnet *dev;
+ int name_len;
+ int ret;
- pr_debug("%s: ctrl port: %s data port: %s\n",
- __func__, ctrl_name, data_name);
+ name_len = strlen(name) + 1;
+ if (name_len > MAX_INST_NAME_LEN)
+ return -ENAMETOOLONG;
- dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
+ ret = ipa_data_setup(USB_IPA_FUNC_RMNET);
+ return ret;
+}
- spin_lock_init(&dev->lock);
- INIT_LIST_HEAD(&dev->cpkt_resp_q);
- rmnet_port = *dev;
+static inline struct f_rmnet_opts *to_f_rmnet_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_rmnet_opts,
+ func_inst.group);
+}
- return 0;
+static void rmnet_opts_release(struct config_item *item)
+{
+ struct f_rmnet_opts *opts = to_f_rmnet_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+};
+
+static struct configfs_item_operations rmnet_item_ops = {
+ .release = rmnet_opts_release,
+};
+
+static struct config_item_type rmnet_func_type = {
+ .ct_item_ops = &rmnet_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct usb_function_instance *rmnet_alloc_inst(void)
+{
+ struct f_rmnet_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ opts->func_inst.set_inst_name = rmnet_set_inst_name;
+ opts->func_inst.free_func_inst = rmnet_free_inst;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &rmnet_func_type);
+ return &opts->func_inst;
+}
+
+static struct usb_function *rmnet_alloc(struct usb_function_instance *fi)
+{
+ struct f_rmnet_opts *opts = container_of(fi,
+ struct f_rmnet_opts, func_inst);
+ rmnet_port = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
+ if (!rmnet_port)
+ return ERR_PTR(-ENOMEM);
+ opts->dev = rmnet_port;
+ spin_lock_init(&rmnet_port->lock);
+ INIT_LIST_HEAD(&rmnet_port->cpkt_resp_q);
+ return frmnet_bind_config(fi);
+}
+
+DECLARE_USB_FUNCTION(rmnet_bam, rmnet_alloc_inst, rmnet_alloc);
+
+static int __init usb_rmnet_init(void)
+{
+ int ret;
+
+ ret = rmnet_init();
+ if (!ret) {
+ ret = usb_function_register(&rmnet_bamusb_func);
+ if (ret) {
+ pr_err("%s: failed to register rmnet %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ return ret;
}
+
+static void __exit usb_rmnet_exit(void)
+{
+ usb_function_unregister(&rmnet_bamusb_func);
+ frmnet_cleanup();
+}
+
+module_init(usb_rmnet_init);
+module_exit(usb_rmnet_exit);
+MODULE_DESCRIPTION("USB RMNET Function Driver");
diff --git a/drivers/usb/gadget/function/u_data_ipa.h b/drivers/usb/gadget/function/u_data_ipa.h
index a6b0a51442a2..14411575af22 100644
--- a/drivers/usb/gadget/function/u_data_ipa.h
+++ b/drivers/usb/gadget/function/u_data_ipa.h
@@ -59,6 +59,12 @@ struct f_rndis_qc_opts {
int refcnt;
};
+struct f_rmnet_opts {
+ struct usb_function_instance func_inst;
+ struct f_rmnet *dev;
+ 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,