Merge "usb: gadget: Add DPL support using IPA over BAM2BAM"
This commit is contained in:
commit
03a382981c
4 changed files with 421 additions and 235 deletions
|
@ -26,20 +26,18 @@
|
|||
#define RMNET_NOTIFY_INTERVAL 5
|
||||
#define RMNET_MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification)
|
||||
|
||||
|
||||
#define ACM_CTRL_DTR (1 << 0)
|
||||
|
||||
/* TODO: use separate structures for data and
|
||||
* control paths
|
||||
*/
|
||||
struct f_rmnet {
|
||||
struct usb_function func;
|
||||
enum qti_port_type qti_port_type;
|
||||
enum ipa_func_type func_type;
|
||||
struct grmnet port;
|
||||
int ifc_id;
|
||||
atomic_t online;
|
||||
atomic_t ctrl_online;
|
||||
struct usb_composite_dev *cdev;
|
||||
|
||||
struct gadget_ipa_port ipa_port;
|
||||
spinlock_t lock;
|
||||
|
||||
/* usb eps*/
|
||||
|
@ -47,11 +45,9 @@ struct f_rmnet {
|
|||
struct usb_request *notify_req;
|
||||
|
||||
/* control info */
|
||||
struct gadget_ipa_port ipa_port;
|
||||
struct list_head cpkt_resp_q;
|
||||
unsigned long notify_count;
|
||||
unsigned long cpkts_len;
|
||||
} *rmnet_port;
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor rmnet_interface_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
|
@ -213,6 +209,70 @@ static struct usb_gadget_strings *rmnet_strings[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_interface_descriptor dpl_data_intf_desc = {
|
||||
.bLength = sizeof(dpl_data_intf_desc),
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0xff,
|
||||
.bInterfaceProtocol = 0xff,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor dpl_hs_data_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor dpl_ss_data_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor dpl_data_ep_comp_desc = {
|
||||
.bLength = sizeof(dpl_data_ep_comp_desc),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
.bMaxBurst = 1,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = 0,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *dpl_hs_data_only_desc[] = {
|
||||
(struct usb_descriptor_header *) &dpl_data_intf_desc,
|
||||
(struct usb_descriptor_header *) &dpl_hs_data_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *dpl_ss_data_only_desc[] = {
|
||||
(struct usb_descriptor_header *) &dpl_data_intf_desc,
|
||||
(struct usb_descriptor_header *) &dpl_ss_data_desc,
|
||||
(struct usb_descriptor_header *) &dpl_data_ep_comp_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* string descriptors: */
|
||||
|
||||
static struct usb_string dpl_string_defs[] = {
|
||||
[0].s = "QDSS DATA",
|
||||
{}, /* end of list */
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings dpl_string_table = {
|
||||
.language = 0x0409,
|
||||
.strings = dpl_string_defs,
|
||||
};
|
||||
|
||||
static struct usb_gadget_strings *dpl_strings[] = {
|
||||
&dpl_string_table,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void frmnet_ctrl_response_available(struct f_rmnet *dev);
|
||||
|
||||
/* ------- misc functions --------------------*/
|
||||
|
@ -227,6 +287,24 @@ static inline struct f_rmnet *port_to_rmnet(struct grmnet *r)
|
|||
return container_of(r, struct f_rmnet, port);
|
||||
}
|
||||
|
||||
int name_to_prot(struct f_rmnet *dev, const char *name)
|
||||
{
|
||||
if (!name)
|
||||
goto error;
|
||||
|
||||
if (!strncasecmp("rmnet", name, MAX_INST_NAME_LEN)) {
|
||||
dev->qti_port_type = QTI_PORT_RMNET;
|
||||
dev->func_type = USB_IPA_FUNC_RMNET;
|
||||
} else if (!strncasecmp("dpl", name, MAX_INST_NAME_LEN)) {
|
||||
dev->qti_port_type = QTI_PORT_DPL;
|
||||
dev->func_type = USB_IPA_FUNC_DPL;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct usb_request *
|
||||
frmnet_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags)
|
||||
{
|
||||
|
@ -279,51 +357,57 @@ static void rmnet_free_ctrl_pkt(struct rmnet_ctrl_pkt *pkt)
|
|||
|
||||
/* -------------------------------------------*/
|
||||
|
||||
static int gport_rmnet_connect(struct f_rmnet *dev, unsigned intf)
|
||||
static int gport_rmnet_connect(struct f_rmnet *dev)
|
||||
{
|
||||
int ret;
|
||||
int src_connection_idx = 0, dst_connection_idx = 0;
|
||||
struct usb_gadget *gadget = dev->cdev->gadget;
|
||||
enum usb_ctrl usb_bam_type;
|
||||
int bam_pipe_num = (dev->qti_port_type == QTI_PORT_DPL) ? 1 : 0;
|
||||
|
||||
ret = gqti_ctrl_connect(&dev->port, QTI_PORT_RMNET, dev->ifc_id);
|
||||
ret = gqti_ctrl_connect(&dev->port, dev->qti_port_type, dev->ifc_id);
|
||||
if (ret) {
|
||||
pr_err("%s: gqti_ctrl_connect failed: err:%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dev->qti_port_type == QTI_PORT_DPL)
|
||||
dev->port.send_encap_cmd(QTI_PORT_DPL, NULL, 0);
|
||||
dev->ipa_port.cdev = dev->cdev;
|
||||
ipa_data_port_select(USB_IPA_FUNC_RMNET);
|
||||
ipa_data_port_select(dev->func_type);
|
||||
usb_bam_type = usb_bam_get_bam_type(gadget->name);
|
||||
src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
|
||||
IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
|
||||
QTI_PORT_RMNET);
|
||||
dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
|
||||
IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
|
||||
QTI_PORT_RMNET);
|
||||
|
||||
if (dev->ipa_port.in) {
|
||||
dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
|
||||
IPA_P_BAM, PEER_PERIPHERAL_TO_USB,
|
||||
USB_BAM_DEVICE, bam_pipe_num);
|
||||
}
|
||||
if (dev->ipa_port.out) {
|
||||
src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
|
||||
IPA_P_BAM, USB_TO_PEER_PERIPHERAL,
|
||||
USB_BAM_DEVICE, bam_pipe_num);
|
||||
}
|
||||
if (dst_connection_idx < 0 || src_connection_idx < 0) {
|
||||
pr_err("%s: usb_bam_get_connection_idx failed\n",
|
||||
__func__);
|
||||
gqti_ctrl_disconnect(&dev->port, QTI_PORT_RMNET);
|
||||
gqti_ctrl_disconnect(&dev->port, dev->qti_port_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = ipa_data_connect(&dev->ipa_port, USB_IPA_FUNC_RMNET,
|
||||
ret = ipa_data_connect(&dev->ipa_port, dev->func_type,
|
||||
src_connection_idx, dst_connection_idx);
|
||||
if (ret) {
|
||||
pr_err("%s: ipa_data_connect failed: err:%d\n",
|
||||
__func__, ret);
|
||||
gqti_ctrl_disconnect(&dev->port, QTI_PORT_RMNET);
|
||||
gqti_ctrl_disconnect(&dev->port, dev->qti_port_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gport_rmnet_disconnect(struct f_rmnet *dev)
|
||||
{
|
||||
gqti_ctrl_disconnect(&dev->port, QTI_PORT_RMNET);
|
||||
ipa_data_disconnect(&dev->ipa_port, USB_IPA_FUNC_RMNET);
|
||||
gqti_ctrl_disconnect(&dev->port, dev->qti_port_type);
|
||||
ipa_data_disconnect(&dev->ipa_port, dev->func_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -333,24 +417,25 @@ static void frmnet_free(struct usb_function *f)
|
|||
|
||||
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);
|
||||
struct usb_gadget *gadget = c->cdev->gadget;
|
||||
|
||||
pr_debug("%s: start unbinding\n", __func__);
|
||||
if (gadget_is_superspeed(c->cdev->gadget))
|
||||
pr_debug("%s: start unbinding\nclear_desc\n", __func__);
|
||||
if (gadget_is_superspeed(gadget) && f->ss_descriptors)
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
if (gadget_is_dualspeed(c->cdev->gadget))
|
||||
|
||||
if (gadget_is_dualspeed(gadget) && f->hs_descriptors)
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
usb_free_descriptors(f->fs_descriptors);
|
||||
|
||||
frmnet_free_req(dev->notify, dev->notify_req);
|
||||
if (f->fs_descriptors)
|
||||
usb_free_descriptors(f->fs_descriptors);
|
||||
|
||||
kfree(f->name);
|
||||
if (dev->notify_req)
|
||||
frmnet_free_req(dev->notify, dev->notify_req);
|
||||
}
|
||||
|
||||
static void frmnet_purge_responses(struct f_rmnet *dev)
|
||||
|
@ -384,11 +469,11 @@ static void frmnet_suspend(struct usb_function *f)
|
|||
pr_debug("%s: dev: %p remote_wakeup: %d\n",
|
||||
__func__, dev, remote_wakeup_allowed);
|
||||
|
||||
usb_ep_fifo_flush(dev->notify);
|
||||
frmnet_purge_responses(dev);
|
||||
|
||||
ipa_data_suspend(&dev->ipa_port, USB_IPA_FUNC_RMNET,
|
||||
remote_wakeup_allowed);
|
||||
if (dev->notify) {
|
||||
usb_ep_fifo_flush(dev->notify);
|
||||
frmnet_purge_responses(dev);
|
||||
}
|
||||
ipa_data_suspend(&dev->ipa_port, dev->func_type, remote_wakeup_allowed);
|
||||
}
|
||||
|
||||
static void frmnet_resume(struct usb_function *f)
|
||||
|
@ -404,8 +489,7 @@ static void frmnet_resume(struct usb_function *f)
|
|||
pr_debug("%s: dev: %p remote_wakeup: %d\n",
|
||||
__func__, dev, remote_wakeup_allowed);
|
||||
|
||||
ipa_data_resume(&dev->ipa_port, USB_IPA_FUNC_RMNET,
|
||||
remote_wakeup_allowed);
|
||||
ipa_data_resume(&dev->ipa_port, dev->func_type, remote_wakeup_allowed);
|
||||
}
|
||||
|
||||
static void frmnet_disable(struct usb_function *f)
|
||||
|
@ -413,15 +497,13 @@ static void frmnet_disable(struct usb_function *f)
|
|||
struct f_rmnet *dev = func_to_rmnet(f);
|
||||
|
||||
pr_debug("%s: Disabling\n", __func__);
|
||||
usb_ep_disable(dev->notify);
|
||||
dev->notify->driver_data = NULL;
|
||||
|
||||
atomic_set(&dev->online, 0);
|
||||
if (dev->notify) {
|
||||
usb_ep_disable(dev->notify);
|
||||
dev->notify->driver_data = NULL;
|
||||
frmnet_purge_responses(dev);
|
||||
}
|
||||
|
||||
frmnet_purge_responses(dev);
|
||||
|
||||
msm_ep_unconfig(dev->ipa_port.out);
|
||||
msm_ep_unconfig(dev->ipa_port.in);
|
||||
gport_rmnet_disconnect(dev);
|
||||
}
|
||||
|
||||
|
@ -430,64 +512,78 @@ frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
{
|
||||
struct f_rmnet *dev = func_to_rmnet(f);
|
||||
struct usb_composite_dev *cdev = f->config->cdev;
|
||||
int ret;
|
||||
struct list_head *cpkt;
|
||||
int ret = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
|
||||
if (ret) {
|
||||
dev->notify->desc = NULL;
|
||||
ERROR(cdev, "config_ep_by_speed failes for ep %s, result %d\n",
|
||||
dev->notify->name, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = usb_ep_enable(dev->notify);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s: usb ep#%s enable failed, err#%d\n",
|
||||
__func__, dev->notify->name, ret);
|
||||
dev->notify->desc = NULL;
|
||||
return ret;
|
||||
}
|
||||
dev->notify->driver_data = dev;
|
||||
|
||||
if (!dev->ipa_port.in->desc || !dev->ipa_port.out->desc) {
|
||||
if (config_ep_by_speed(cdev->gadget, f, dev->ipa_port.in) ||
|
||||
config_ep_by_speed(cdev->gadget, f, dev->ipa_port.out)) {
|
||||
pr_err("%s(): config_ep_by_speed failed.\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto err_disable_ep;
|
||||
if (dev->notify) {
|
||||
if (dev->notify->driver_data) {
|
||||
pr_debug("%s: reset port\n", __func__);
|
||||
usb_ep_disable(dev->notify);
|
||||
}
|
||||
dev->ipa_port.cdev = dev->cdev;
|
||||
|
||||
ret = config_ep_by_speed(cdev->gadget, f, dev->notify);
|
||||
if (ret) {
|
||||
dev->notify->desc = NULL;
|
||||
ERROR(cdev,
|
||||
"config_ep_by_speed failed for ep %s, result %d\n",
|
||||
dev->notify->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = usb_ep_enable(dev->notify);
|
||||
if (ret) {
|
||||
pr_err("%s: usb ep#%s enable failed, err#%d\n",
|
||||
__func__, dev->notify->name, ret);
|
||||
dev->notify->desc = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev->notify->driver_data = dev;
|
||||
}
|
||||
|
||||
ret = gport_rmnet_connect(dev, intf);
|
||||
if (dev->ipa_port.in && !dev->ipa_port.in->desc
|
||||
&& config_ep_by_speed(cdev->gadget, f, dev->ipa_port.in)) {
|
||||
pr_err("%s(): config_ep_by_speed failed.\n",
|
||||
__func__);
|
||||
dev->ipa_port.in->desc = NULL;
|
||||
ret = -EINVAL;
|
||||
goto err_disable_ep;
|
||||
}
|
||||
|
||||
if (dev->ipa_port.out && !dev->ipa_port.out->desc
|
||||
&& config_ep_by_speed(cdev->gadget, f, dev->ipa_port.out)) {
|
||||
pr_err("%s(): config_ep_by_speed failed.\n",
|
||||
__func__);
|
||||
dev->ipa_port.out->desc = NULL;
|
||||
ret = -EINVAL;
|
||||
goto err_disable_ep;
|
||||
}
|
||||
|
||||
ret = gport_rmnet_connect(dev);
|
||||
if (ret) {
|
||||
pr_err("%s(): gport_rmnet_connect fail with err:%d\n",
|
||||
__func__, ret);
|
||||
__func__, ret);
|
||||
goto err_disable_ep;
|
||||
}
|
||||
|
||||
atomic_set(&dev->online, 1);
|
||||
|
||||
/*
|
||||
* In case notifications were aborted, but there are pending control
|
||||
* packets in the response queue, re-add the notifications.
|
||||
*/
|
||||
list_for_each(cpkt, &dev->cpkt_resp_q)
|
||||
frmnet_ctrl_response_available(dev);
|
||||
* In case notifications were aborted, but there are
|
||||
* pending control packets in the response queue,
|
||||
* re-add the notifications.
|
||||
*/
|
||||
if (dev->qti_port_type == QTI_PORT_RMNET) {
|
||||
struct list_head *cpkt;
|
||||
|
||||
list_for_each(cpkt, &dev->cpkt_resp_q)
|
||||
frmnet_ctrl_response_available(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
err_disable_ep:
|
||||
dev->ipa_port.in->desc = NULL;
|
||||
dev->ipa_port.out->desc = NULL;
|
||||
usb_ep_disable(dev->notify);
|
||||
if (dev->notify && dev->notify->driver_data)
|
||||
usb_ep_disable(dev->notify);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -813,108 +909,119 @@ invalid:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int ipa_update_function_bind_params(struct f_rmnet *dev,
|
||||
struct usb_composite_dev *cdev, struct ipa_function_bind_info *info)
|
||||
{
|
||||
struct f_rmnet *dev = func_to_rmnet(f);
|
||||
struct usb_ep *ep;
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
int ret = -ENODEV;
|
||||
struct usb_ep *ep;
|
||||
struct usb_function *f = &dev->func;
|
||||
int status;
|
||||
|
||||
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;
|
||||
/* maybe allocate device-global string IDs */
|
||||
if (info->string_defs[0].id != 0)
|
||||
goto skip_string_id_alloc;
|
||||
|
||||
if (info->data_str_idx >= 0 && info->data_desc) {
|
||||
/* data interface label */
|
||||
status = usb_string_id(cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
info->string_defs[info->data_str_idx].id = status;
|
||||
info->data_desc->iInterface = status;
|
||||
}
|
||||
|
||||
skip_string_id_alloc:
|
||||
if (info->data_desc)
|
||||
info->data_desc->bInterfaceNumber = dev->ifc_id;
|
||||
|
||||
if (info->fs_in_desc) {
|
||||
ep = usb_ep_autoconfig(cdev->gadget, info->fs_in_desc);
|
||||
if (!ep) {
|
||||
pr_err("%s: usb epin autoconfig failed\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
rmnet_string_defs[0].id = ret;
|
||||
dev->ipa_port.in = ep;
|
||||
ep->driver_data = cdev;
|
||||
}
|
||||
|
||||
pr_debug("%s: start binding\n", __func__);
|
||||
dev->ifc_id = usb_interface_id(c, f);
|
||||
if (dev->ifc_id < 0) {
|
||||
pr_err("%s: unable to allocate ifc id, err:%d\n",
|
||||
__func__, dev->ifc_id);
|
||||
return dev->ifc_id;
|
||||
if (info->fs_out_desc) {
|
||||
ep = usb_ep_autoconfig(cdev->gadget, info->fs_out_desc);
|
||||
if (!ep) {
|
||||
pr_err("%s: usb epout autoconfig failed\n",
|
||||
__func__);
|
||||
status = -ENODEV;
|
||||
goto ep_auto_out_fail;
|
||||
}
|
||||
dev->ipa_port.out = ep;
|
||||
ep->driver_data = cdev;
|
||||
}
|
||||
rmnet_interface_desc.bInterfaceNumber = dev->ifc_id;
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_in_desc);
|
||||
if (!ep) {
|
||||
pr_err("%s: usb epin autoconfig failed\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
dev->ipa_port.in = ep;
|
||||
ep->driver_data = cdev;
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_out_desc);
|
||||
if (!ep) {
|
||||
pr_err("%s: usb epout autoconfig failed\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto ep_auto_out_fail;
|
||||
}
|
||||
dev->ipa_port.out = ep;
|
||||
ep->driver_data = cdev;
|
||||
|
||||
ep = usb_ep_autoconfig(cdev->gadget, &rmnet_fs_notify_desc);
|
||||
if (!ep) {
|
||||
pr_err("%s: usb epnotify autoconfig failed\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto ep_auto_notify_fail;
|
||||
}
|
||||
dev->notify = ep;
|
||||
ep->driver_data = cdev;
|
||||
|
||||
dev->notify_req = frmnet_alloc_req(ep,
|
||||
if (info->fs_notify_desc) {
|
||||
ep = usb_ep_autoconfig(cdev->gadget, info->fs_notify_desc);
|
||||
if (!ep) {
|
||||
pr_err("%s: usb epnotify autoconfig failed\n",
|
||||
__func__);
|
||||
status = -ENODEV;
|
||||
goto ep_auto_notify_fail;
|
||||
}
|
||||
dev->notify = ep;
|
||||
ep->driver_data = cdev;
|
||||
dev->notify_req = frmnet_alloc_req(ep,
|
||||
sizeof(struct usb_cdc_notification),
|
||||
GFP_KERNEL);
|
||||
if (IS_ERR(dev->notify_req)) {
|
||||
pr_err("%s: unable to allocate memory for notify req\n",
|
||||
if (IS_ERR(dev->notify_req)) {
|
||||
pr_err("%s: unable to allocate memory for notify req\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto ep_notify_alloc_fail;
|
||||
status = -ENOMEM;
|
||||
goto ep_notify_alloc_fail;
|
||||
}
|
||||
|
||||
dev->notify_req->complete = frmnet_notify_complete;
|
||||
dev->notify_req->context = dev;
|
||||
}
|
||||
|
||||
dev->notify_req->complete = frmnet_notify_complete;
|
||||
dev->notify_req->context = dev;
|
||||
|
||||
ret = -ENOMEM;
|
||||
f->fs_descriptors = usb_copy_descriptors(rmnet_fs_function);
|
||||
|
||||
status = -ENOMEM;
|
||||
f->fs_descriptors = usb_copy_descriptors(info->fs_desc_hdr);
|
||||
if (!f->fs_descriptors) {
|
||||
pr_err("%s: no descriptors,usb_copy descriptors(fs)failed\n",
|
||||
pr_err("%s: no descriptors, usb_copy descriptors(fs)failed\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (gadget_is_dualspeed(cdev->gadget)) {
|
||||
rmnet_hs_in_desc.bEndpointAddress =
|
||||
rmnet_fs_in_desc.bEndpointAddress;
|
||||
rmnet_hs_out_desc.bEndpointAddress =
|
||||
rmnet_fs_out_desc.bEndpointAddress;
|
||||
rmnet_hs_notify_desc.bEndpointAddress =
|
||||
rmnet_fs_notify_desc.bEndpointAddress;
|
||||
if (info->fs_in_desc && info->hs_in_desc)
|
||||
info->hs_in_desc->bEndpointAddress =
|
||||
info->fs_in_desc->bEndpointAddress;
|
||||
if (info->fs_out_desc && info->hs_out_desc)
|
||||
info->hs_out_desc->bEndpointAddress =
|
||||
info->fs_out_desc->bEndpointAddress;
|
||||
if (info->fs_notify_desc && info->hs_notify_desc)
|
||||
info->hs_notify_desc->bEndpointAddress =
|
||||
info->fs_notify_desc->bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->hs_descriptors = usb_copy_descriptors(rmnet_hs_function);
|
||||
|
||||
f->hs_descriptors = usb_copy_descriptors(info->hs_desc_hdr);
|
||||
if (!f->hs_descriptors) {
|
||||
pr_err("%s: no hs_descriptors,usb_copy descriptors(hs)failed\n",
|
||||
__func__);
|
||||
pr_err("%s: no hs_descriptors, usb_copy descriptors(hs)failed\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (gadget_is_superspeed(cdev->gadget)) {
|
||||
rmnet_ss_in_desc.bEndpointAddress =
|
||||
rmnet_fs_in_desc.bEndpointAddress;
|
||||
rmnet_ss_out_desc.bEndpointAddress =
|
||||
rmnet_fs_out_desc.bEndpointAddress;
|
||||
rmnet_ss_notify_desc.bEndpointAddress =
|
||||
rmnet_fs_notify_desc.bEndpointAddress;
|
||||
if (info->fs_in_desc && info->ss_in_desc)
|
||||
info->ss_in_desc->bEndpointAddress =
|
||||
info->fs_in_desc->bEndpointAddress;
|
||||
|
||||
if (info->fs_out_desc && info->ss_out_desc)
|
||||
info->ss_out_desc->bEndpointAddress =
|
||||
info->fs_out_desc->bEndpointAddress;
|
||||
if (info->fs_notify_desc && info->ss_notify_desc)
|
||||
info->ss_notify_desc->bEndpointAddress =
|
||||
info->fs_notify_desc->bEndpointAddress;
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
f->ss_descriptors = usb_copy_descriptors(rmnet_ss_function);
|
||||
|
||||
f->ss_descriptors = usb_copy_descriptors(info->ss_desc_hdr);
|
||||
if (!f->ss_descriptors) {
|
||||
pr_err("%s: no ss_descriptors,usb_copy descriptors(ss)failed\n",
|
||||
__func__);
|
||||
|
@ -922,57 +1029,95 @@ static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
}
|
||||
}
|
||||
|
||||
pr_debug("%s: RmNet %s Speed, IN:%s OUT:%s\n",
|
||||
__func__, gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
|
||||
dev->ipa_port.in->name, dev->ipa_port.out->name);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (f->ss_descriptors)
|
||||
if (gadget_is_superspeed(cdev->gadget) && f->ss_descriptors)
|
||||
usb_free_descriptors(f->ss_descriptors);
|
||||
if (f->hs_descriptors)
|
||||
if (gadget_is_dualspeed(cdev->gadget) && f->hs_descriptors)
|
||||
usb_free_descriptors(f->hs_descriptors);
|
||||
if (f->fs_descriptors)
|
||||
usb_free_descriptors(f->fs_descriptors);
|
||||
if (dev->notify_req)
|
||||
frmnet_free_req(dev->notify, dev->notify_req);
|
||||
ep_notify_alloc_fail:
|
||||
dev->notify->driver_data = NULL;
|
||||
dev->notify = NULL;
|
||||
dev->notify->driver_data = NULL;
|
||||
dev->notify = NULL;
|
||||
ep_auto_notify_fail:
|
||||
dev->ipa_port.out->driver_data = NULL;
|
||||
dev->ipa_port.out = NULL;
|
||||
dev->ipa_port.out->driver_data = NULL;
|
||||
dev->ipa_port.out = NULL;
|
||||
ep_auto_out_fail:
|
||||
dev->ipa_port.in->driver_data = NULL;
|
||||
dev->ipa_port.in = NULL;
|
||||
dev->ipa_port.in->driver_data = NULL;
|
||||
dev->ipa_port.in = NULL;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int frmnet_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_rmnet *dev = func_to_rmnet(f);
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
int ret = -ENODEV;
|
||||
struct ipa_function_bind_info info = {0};
|
||||
|
||||
pr_debug("%s: start binding\n", __func__);
|
||||
dev->ifc_id = usb_interface_id(c, f);
|
||||
if (dev->ifc_id < 0) {
|
||||
pr_err("%s: unable to allocate ifc id, err:%d\n",
|
||||
__func__, dev->ifc_id);
|
||||
return dev->ifc_id;
|
||||
}
|
||||
|
||||
info.data_str_idx = 0;
|
||||
if (dev->qti_port_type == QTI_PORT_RMNET) {
|
||||
info.string_defs = rmnet_string_defs;
|
||||
info.data_desc = &rmnet_interface_desc;
|
||||
info.fs_in_desc = &rmnet_fs_in_desc;
|
||||
info.fs_out_desc = &rmnet_fs_out_desc;
|
||||
info.fs_notify_desc = &rmnet_fs_notify_desc;
|
||||
info.hs_in_desc = &rmnet_hs_in_desc;
|
||||
info.hs_out_desc = &rmnet_hs_out_desc;
|
||||
info.hs_notify_desc = &rmnet_hs_notify_desc;
|
||||
info.ss_in_desc = &rmnet_ss_in_desc;
|
||||
info.ss_out_desc = &rmnet_ss_out_desc;
|
||||
info.ss_notify_desc = &rmnet_ss_notify_desc;
|
||||
info.fs_desc_hdr = rmnet_fs_function;
|
||||
info.hs_desc_hdr = rmnet_hs_function;
|
||||
info.ss_desc_hdr = rmnet_ss_function;
|
||||
} else {
|
||||
info.string_defs = dpl_string_defs;
|
||||
info.data_desc = &dpl_data_intf_desc;
|
||||
info.fs_in_desc = &dpl_hs_data_desc;
|
||||
info.hs_in_desc = &dpl_hs_data_desc;
|
||||
info.ss_in_desc = &dpl_ss_data_desc;
|
||||
info.fs_desc_hdr = dpl_hs_data_only_desc;
|
||||
info.hs_desc_hdr = dpl_hs_data_only_desc;
|
||||
info.ss_desc_hdr = dpl_ss_data_only_desc;
|
||||
}
|
||||
|
||||
ret = ipa_update_function_bind_params(dev, cdev, &info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct usb_function *frmnet_bind_config(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_rmnet_opts *opts;
|
||||
int status;
|
||||
struct f_rmnet_opts *opts;
|
||||
struct f_rmnet *dev;
|
||||
struct usb_function *f;
|
||||
unsigned long flags;
|
||||
|
||||
/* 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);
|
||||
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 ERR_PTR(-ENOMEM);
|
||||
if (dev->qti_port_type == QTI_PORT_RMNET) {
|
||||
f->name = "rmnet";
|
||||
f->strings = rmnet_strings;
|
||||
} else {
|
||||
f->name = "dpl";
|
||||
f->strings = dpl_strings;
|
||||
}
|
||||
|
||||
f->strings = rmnet_strings;
|
||||
f->bind = frmnet_bind;
|
||||
f->unbind = frmnet_unbind;
|
||||
f->disable = frmnet_disable;
|
||||
|
@ -1004,21 +1149,53 @@ 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);
|
||||
ipa_data_free(opts->dev->func_type);
|
||||
kfree(opts->dev);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static int rmnet_set_inst_name(struct usb_function_instance *fi,
|
||||
const char *name)
|
||||
{
|
||||
int name_len;
|
||||
int ret;
|
||||
int name_len, ret = 0;
|
||||
struct f_rmnet *dev;
|
||||
struct f_rmnet_opts *opts = container_of(fi,
|
||||
struct f_rmnet_opts, func_inst);
|
||||
|
||||
name_len = strlen(name) + 1;
|
||||
if (name_len > MAX_INST_NAME_LEN)
|
||||
return -ENAMETOOLONG;
|
||||
|
||||
ret = ipa_data_setup(USB_IPA_FUNC_RMNET);
|
||||
dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
/* Update qti->qti_port_type */
|
||||
ret = name_to_prot(dev, name);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to find prot for %s instance\n",
|
||||
__func__, name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (dev->qti_port_type >= QTI_NUM_PORTS ||
|
||||
dev->func_type >= USB_IPA_NUM_FUNCS) {
|
||||
pr_err("%s: invalid prot\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&dev->cpkt_resp_q);
|
||||
ret = ipa_data_setup(dev->func_type);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
opts->dev = dev;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1062,14 +1239,6 @@ static struct usb_function_instance *rmnet_alloc_inst(void)
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -204,7 +204,6 @@ int gqti_ctrl_connect(void *gr, enum qti_port_type qport, unsigned intf)
|
|||
{
|
||||
struct qti_ctrl_port *port;
|
||||
struct grmnet *g_rmnet = NULL;
|
||||
struct gqdss *g_dpl = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
pr_debug("%s: port type:%d gadget:%p\n", __func__, qport, gr);
|
||||
|
@ -224,17 +223,13 @@ int gqti_ctrl_connect(void *gr, enum qti_port_type qport, unsigned intf)
|
|||
port->ep_type = DATA_EP_TYPE_HSUSB;
|
||||
port->intf = intf;
|
||||
|
||||
if (gr && port->port_type == QTI_PORT_RMNET) {
|
||||
if (gr) {
|
||||
port->port_usb = gr;
|
||||
g_rmnet = (struct grmnet *)gr;
|
||||
g_rmnet->send_encap_cmd = gqti_ctrl_send_cpkt_tomodem;
|
||||
g_rmnet->notify_modem = gqti_ctrl_notify_modem;
|
||||
} else if (gr && port->port_type == QTI_PORT_DPL) {
|
||||
port->port_usb = gr;
|
||||
g_dpl = (struct gqdss *)gr;
|
||||
g_dpl->send_encap_cmd = gqti_ctrl_send_cpkt_tomodem;
|
||||
g_dpl->notify_modem = gqti_ctrl_notify_modem;
|
||||
atomic_set(&port->line_state, 1);
|
||||
if (port->port_type == QTI_PORT_DPL)
|
||||
atomic_set(&port->line_state, 1);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
pr_err("%s(): Port is used without port type.\n", __func__);
|
||||
|
@ -263,7 +258,6 @@ void gqti_ctrl_disconnect(void *gr, enum qti_port_type qport)
|
|||
unsigned long flags;
|
||||
struct rmnet_ctrl_pkt *cpkt;
|
||||
struct grmnet *g_rmnet = NULL;
|
||||
struct gqdss *g_dpl = NULL;
|
||||
|
||||
pr_debug("%s: gadget:%p\n", __func__, gr);
|
||||
|
||||
|
@ -287,14 +281,10 @@ void gqti_ctrl_disconnect(void *gr, enum qti_port_type qport)
|
|||
port->ipa_cons_idx = -1;
|
||||
port->port_usb = NULL;
|
||||
|
||||
if (gr && port->port_type == QTI_PORT_RMNET) {
|
||||
if (gr) {
|
||||
g_rmnet = (struct grmnet *)gr;
|
||||
g_rmnet->send_encap_cmd = NULL;
|
||||
g_rmnet->notify_modem = NULL;
|
||||
} else if (gr && port->port_type == QTI_PORT_DPL) {
|
||||
g_dpl = (struct gqdss *)gr;
|
||||
g_dpl->send_encap_cmd = NULL;
|
||||
g_dpl->notify_modem = NULL;
|
||||
} else {
|
||||
pr_err("%s(): unrecognized gadget type(%d).\n",
|
||||
__func__, port->port_type);
|
||||
|
|
|
@ -837,13 +837,16 @@ void ipa_data_suspend(struct gadget_ipa_port *gp, enum ipa_func_type func,
|
|||
* the BAM disconnect API. This lets us restore this info when
|
||||
* the USB bus is resumed.
|
||||
*/
|
||||
gp->in_ep_desc_backup = gp->in->desc;
|
||||
gp->out_ep_desc_backup = gp->out->desc;
|
||||
|
||||
pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p",
|
||||
gp->in_ep_desc_backup,
|
||||
gp->out_ep_desc_backup);
|
||||
|
||||
if (gp->in) {
|
||||
gp->in_ep_desc_backup = gp->in->desc;
|
||||
pr_debug("in_ep_desc_backup = %p\n",
|
||||
gp->in_ep_desc_backup);
|
||||
}
|
||||
if (gp->out) {
|
||||
gp->out_ep_desc_backup = gp->out->desc;
|
||||
pr_debug("out_ep_desc_backup = %p\n",
|
||||
gp->out_ep_desc_backup);
|
||||
}
|
||||
ipa_data_disconnect(gp, func);
|
||||
return;
|
||||
}
|
||||
|
@ -919,8 +922,8 @@ void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func,
|
|||
struct ipa_data_ch_info *port;
|
||||
unsigned long flags;
|
||||
struct usb_gadget *gadget = NULL;
|
||||
u8 src_connection_idx;
|
||||
u8 dst_connection_idx;
|
||||
u8 src_connection_idx = 0;
|
||||
u8 dst_connection_idx = 0;
|
||||
enum usb_ctrl usb_bam_type;
|
||||
|
||||
pr_debug("dev:%p port number:%d\n", gp, func);
|
||||
|
@ -944,20 +947,25 @@ void ipa_data_resume(struct gadget_ipa_port *gp, enum ipa_func_type func,
|
|||
gadget = gp->cdev->gadget;
|
||||
/* resume with remote wakeup disabled */
|
||||
if (!remote_wakeup_enabled) {
|
||||
/* Restore endpoint descriptors info. */
|
||||
gp->in->desc = gp->in_ep_desc_backup;
|
||||
gp->out->desc = gp->out_ep_desc_backup;
|
||||
|
||||
pr_debug("in_ep_desc_backup = %p, out_ep_desc_backup = %p",
|
||||
gp->in_ep_desc_backup,
|
||||
gp->out_ep_desc_backup);
|
||||
int bam_pipe_num = (func == USB_IPA_FUNC_DPL) ? 1 : 0;
|
||||
usb_bam_type = usb_bam_get_bam_type(gadget->name);
|
||||
src_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
|
||||
IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
|
||||
0);
|
||||
dst_connection_idx = usb_bam_get_connection_idx(usb_bam_type,
|
||||
IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
|
||||
0);
|
||||
/* Restore endpoint descriptors info. */
|
||||
if (gp->in) {
|
||||
gp->in->desc = gp->in_ep_desc_backup;
|
||||
pr_debug("in_ep_desc_backup = %p\n",
|
||||
gp->in_ep_desc_backup);
|
||||
dst_connection_idx = usb_bam_get_connection_idx(
|
||||
usb_bam_type, IPA_P_BAM, PEER_PERIPHERAL_TO_USB,
|
||||
USB_BAM_DEVICE, bam_pipe_num);
|
||||
}
|
||||
if (gp->out) {
|
||||
gp->out->desc = gp->out_ep_desc_backup;
|
||||
pr_debug("out_ep_desc_backup = %p\n",
|
||||
gp->out_ep_desc_backup);
|
||||
src_connection_idx = usb_bam_get_connection_idx(
|
||||
usb_bam_type, IPA_P_BAM, USB_TO_PEER_PERIPHERAL,
|
||||
USB_BAM_DEVICE, bam_pipe_num);
|
||||
}
|
||||
ipa_data_connect(gp, func,
|
||||
src_connection_idx, dst_connection_idx);
|
||||
return;
|
||||
|
|
|
@ -47,6 +47,25 @@ struct gadget_ipa_port {
|
|||
|
||||
};
|
||||
|
||||
struct ipa_function_bind_info {
|
||||
struct usb_string *string_defs;
|
||||
int data_str_idx;
|
||||
struct usb_interface_descriptor *data_desc;
|
||||
struct usb_endpoint_descriptor *fs_in_desc;
|
||||
struct usb_endpoint_descriptor *fs_out_desc;
|
||||
struct usb_endpoint_descriptor *fs_notify_desc;
|
||||
struct usb_endpoint_descriptor *hs_in_desc;
|
||||
struct usb_endpoint_descriptor *hs_out_desc;
|
||||
struct usb_endpoint_descriptor *hs_notify_desc;
|
||||
struct usb_endpoint_descriptor *ss_in_desc;
|
||||
struct usb_endpoint_descriptor *ss_out_desc;
|
||||
struct usb_endpoint_descriptor *ss_notify_desc;
|
||||
|
||||
struct usb_descriptor_header **fs_desc_hdr;
|
||||
struct usb_descriptor_header **hs_desc_hdr;
|
||||
struct usb_descriptor_header **ss_desc_hdr;
|
||||
};
|
||||
|
||||
/* for configfs support */
|
||||
#define MAX_INST_NAME_LEN 40
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue