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>
This commit is contained in:
parent
5f7a2a2a57
commit
a6fb576c5b
5 changed files with 201 additions and 80 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 &rndis->func;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
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");
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue