mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02:00
USB fixes for 7.2-rc5
Here are some small USB fixes and new device quirks and ids for 7.2-rc5. Included in here are: - usb storage quirk added - new usb serial device ids added - usb-serial device name leak and other bug fixes - small xhci driver fixes - normal batch of typec driver fixes for reported issues - usb-atm much-reported-by-syzbot fix for firmware download races - sysfs BOS device removal race fix - lots of usb gadget driver fixes for reported issues - other small USB driver fixes for other reported problems. All of these have been in linux-next this past week, many of them much longer. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCamYE0A8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+yk0twCdFi0ivPtWtEzmP7lUNRCzmyT3MvoAoI7o6lJm E5+ID/c4mWfX/DjoPFhU =uUBv -----END PGP SIGNATURE----- Merge tag 'usb-7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB fixes from Greg KH: "Here are some small USB fixes and new device quirks and ids: - usb storage quirk added - new usb serial device ids added - usb-serial device name leak and other bug fixes - small xhci driver fixes - normal batch of typec driver fixes for reported issues - usb-atm much-reported-by-syzbot fix for firmware download races - sysfs BOS device removal race fix - lots of usb gadget driver fixes for reported issues - other small USB driver fixes for other reported problems All of these have been in linux-next this past week, many of them much longer" * tag 'usb-7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (30 commits) usb: typec: ucsi: Correct teardown ordering in ucsi_init() error path USB: serial: io_edgeport: cap received transmit credits USB: serial: option: add TDTECH MT5710-CN USB: serial: io_ti: reject oversized boot-mode firmware USB: serial: mxuport: validate firmware header size usb: atm: ueagle-atm: reject descriptors that confuse probe and disconnect usb: typec: ucsi: yoga_c630: Remove redundant duplicate altmode handling usb: typec: ucsi: Add duplicate detection to nvidia registration path usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback usb: chipidea: fix usage_count leak when autosuspend_delay is negative usb: core: sysfs: add lock to bos_descriptors_read() usb: musb: omap2430: Do not put borrowed of_node in probe usb: core: port: Deattach Type-C connector on component unbind USB: storage: add NO_ATA_1X quirk for Longmai USB Key USB: serial: ftdi_sio: add support for E+H FXA291 USB: serial: keyspan_pda: fix data loss on receive throttling usb: gadget: printer: fix infinite loop in printer_read() usb: gadget: f_midi: cancel pending IN work before freeing the midi object usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown ...
This commit is contained in:
commit
79ba9db003
|
|
@ -1250,6 +1250,7 @@ static void ci_hdrc_remove(struct platform_device *pdev)
|
|||
usb_role_switch_unregister(ci->role_switch);
|
||||
|
||||
if (ci->supports_runtime_pm) {
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
|
|
|||
|
|
@ -740,6 +740,8 @@ static void connector_unbind(struct device *dev, struct device *connector, void
|
|||
|
||||
sysfs_remove_link(&connector->kobj, dev_name(dev));
|
||||
sysfs_remove_link(&dev->kobj, "connector");
|
||||
if (port_dev->child)
|
||||
typec_deattach(port_dev->connector, &port_dev->child->dev);
|
||||
port_dev->connector = NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -899,10 +899,15 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
|
|||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
struct usb_host_bos *bos = udev->bos;
|
||||
struct usb_host_bos *bos;
|
||||
struct usb_bos_descriptor *desc;
|
||||
size_t desclen, n = 0;
|
||||
int rc;
|
||||
|
||||
rc = usb_lock_device_interruptible(udev);
|
||||
if (rc < 0)
|
||||
return -EINTR;
|
||||
bos = udev->bos;
|
||||
if (bos) {
|
||||
desc = bos->desc;
|
||||
desclen = le16_to_cpu(desc->wTotalLength);
|
||||
|
|
@ -911,6 +916,7 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
|
|||
memcpy(buf, (void *) desc + off, n);
|
||||
}
|
||||
}
|
||||
usb_unlock_device(udev);
|
||||
return n;
|
||||
}
|
||||
static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
|
||||
|
|
|
|||
|
|
@ -1309,6 +1309,7 @@ static void f_midi_free(struct usb_function *f)
|
|||
opts = container_of(f->fi, struct f_midi_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
if (!--midi->free_ref) {
|
||||
cancel_work_sync(&midi->work);
|
||||
kfree(midi->id);
|
||||
kfifo_free(&midi->in_req_fifo);
|
||||
kfree(midi);
|
||||
|
|
|
|||
|
|
@ -1189,6 +1189,10 @@ static int ncm_unwrap_ntb(struct gether *port,
|
|||
frame_max = ncm_opts->max_segment_size;
|
||||
|
||||
parse_ntb:
|
||||
if (to_process < (int)opts->nth_size) {
|
||||
INFO(port->func.config->cdev, "Packet too small for headers\n");
|
||||
goto err;
|
||||
}
|
||||
tmp = (__le16 *)ntb_ptr;
|
||||
|
||||
/* dwSignature */
|
||||
|
|
@ -1209,8 +1213,12 @@ static int ncm_unwrap_ntb(struct gether *port,
|
|||
tmp++; /* skip wSequence */
|
||||
|
||||
block_len = get_ncm(&tmp, opts->block_length);
|
||||
if (block_len == 0)
|
||||
block_len = to_process;
|
||||
|
||||
/* (d)wBlockLength */
|
||||
if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) {
|
||||
if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max) ||
|
||||
(block_len > to_process)) {
|
||||
INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len);
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -1273,7 +1281,7 @@ static int ncm_unwrap_ntb(struct gether *port,
|
|||
index = index2;
|
||||
/* wDatagramIndex[0] */
|
||||
if ((index < opts->nth_size) ||
|
||||
(index > block_len - opts->dpe_size)) {
|
||||
(index > block_len)) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad index: %#X\n", index);
|
||||
goto err;
|
||||
|
|
@ -1285,7 +1293,8 @@ static int ncm_unwrap_ntb(struct gether *port,
|
|||
* ethernet hdr + crc or larger than max frame size
|
||||
*/
|
||||
if ((dg_len < 14 + crc_len) ||
|
||||
(dg_len > frame_max)) {
|
||||
(dg_len > frame_max) ||
|
||||
(dg_len > block_len - index)) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad dgram length: %#X\n", dg_len);
|
||||
goto err;
|
||||
|
|
@ -1310,7 +1319,7 @@ static int ncm_unwrap_ntb(struct gether *port,
|
|||
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
|
||||
|
||||
/* wDatagramIndex[1] */
|
||||
if (index2 > block_len - opts->dpe_size) {
|
||||
if (index2 > block_len) {
|
||||
INFO(port->func.config->cdev,
|
||||
"Bad index: %#X\n", index2);
|
||||
goto err;
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
|||
{
|
||||
struct printer_dev *dev = fd->private_data;
|
||||
unsigned long flags;
|
||||
size_t size;
|
||||
size_t size, not_copied, copied;
|
||||
size_t bytes_copied;
|
||||
struct usb_request *req;
|
||||
/* This is a pointer to the current USB rx request. */
|
||||
|
|
@ -524,10 +524,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
|||
else
|
||||
size = len;
|
||||
|
||||
size -= copy_to_user(buf, current_rx_buf, size);
|
||||
bytes_copied += size;
|
||||
len -= size;
|
||||
buf += size;
|
||||
not_copied = copy_to_user(buf, current_rx_buf, size);
|
||||
copied = size - not_copied;
|
||||
|
||||
bytes_copied += copied;
|
||||
len -= copied;
|
||||
buf += copied;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
|
|
@ -542,6 +544,17 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
|||
if (dev->interface < 0)
|
||||
goto out_disabled;
|
||||
|
||||
if (!copied) {
|
||||
dev->current_rx_req = current_rx_req;
|
||||
dev->current_rx_bytes = current_rx_bytes;
|
||||
dev->current_rx_buf = current_rx_buf;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
mutex_unlock(&dev->lock_printer_io);
|
||||
return bytes_copied ? bytes_copied : -EFAULT;
|
||||
}
|
||||
|
||||
size = copied;
|
||||
|
||||
/* If we not returning all the data left in this RX request
|
||||
* buffer then adjust the amount of data left in the buffer.
|
||||
* Othewise if we are done with this RX request buffer then
|
||||
|
|
|
|||
|
|
@ -2363,31 +2363,158 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
struct guas_setup_wq {
|
||||
struct work_struct work;
|
||||
struct f_uas *fu;
|
||||
unsigned int alt;
|
||||
};
|
||||
static void tcm_cleanup_old_alt(struct f_uas *fu)
|
||||
{
|
||||
if (fu->flags & USBG_IS_UAS)
|
||||
uasp_cleanup_old_alt(fu);
|
||||
else if (fu->flags & USBG_IS_BOT)
|
||||
bot_cleanup_old_alt(fu);
|
||||
fu->flags = 0;
|
||||
}
|
||||
|
||||
static void tcm_delayed_set_alt_done(struct f_uas *fu)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
|
||||
fu->delayed_set_alt_cancel = false;
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
}
|
||||
|
||||
static bool tcm_delayed_set_alt_cancelled(struct f_uas *fu)
|
||||
{
|
||||
bool cancelled;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
cancelled = fu->delayed_set_alt_cancel;
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
static bool tcm_complete_delayed_status(struct f_uas *fu)
|
||||
{
|
||||
struct usb_composite_dev *cdev = fu->function.config->cdev;
|
||||
struct usb_request *req = cdev->req;
|
||||
unsigned long cdev_flags;
|
||||
bool cancelled;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&cdev->lock, cdev_flags);
|
||||
spin_lock(&fu->delayed_set_alt_lock);
|
||||
cancelled = fu->delayed_set_alt_cancel;
|
||||
if (!cancelled) {
|
||||
fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
|
||||
fu->delayed_set_alt_cancel = false;
|
||||
}
|
||||
spin_unlock(&fu->delayed_set_alt_lock);
|
||||
|
||||
if (cancelled) {
|
||||
spin_unlock_irqrestore(&cdev->lock, cdev_flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cdev->delayed_status == 0) {
|
||||
WARN(cdev, "%s: Unexpected call\n", __func__);
|
||||
} else if (--cdev->delayed_status == 0) {
|
||||
req->length = 0;
|
||||
req->context = cdev;
|
||||
ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
|
||||
if (ret == 0) {
|
||||
cdev->setup_pending = true;
|
||||
} else {
|
||||
req->status = 0;
|
||||
req->complete(cdev->gadget->ep0, req);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&cdev->lock, cdev_flags);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tcm_cancel_delayed_set_alt(struct f_uas *fu)
|
||||
{
|
||||
bool cleanup = false;
|
||||
bool cancel = false;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
switch (fu->delayed_set_alt_state) {
|
||||
case USBG_DELAYED_SET_ALT_IDLE:
|
||||
cleanup = true;
|
||||
break;
|
||||
case USBG_DELAYED_SET_ALT_QUEUED:
|
||||
case USBG_DELAYED_SET_ALT_RUNNING:
|
||||
fu->delayed_set_alt_cancel = true;
|
||||
cancel = true;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
|
||||
if (cancel && cancel_work(&fu->delayed_set_alt)) {
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
if (fu->delayed_set_alt_state == USBG_DELAYED_SET_ALT_QUEUED) {
|
||||
fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
|
||||
fu->delayed_set_alt_cancel = false;
|
||||
cleanup = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
}
|
||||
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
static void tcm_cancel_delayed_set_alt_sync(struct f_uas *fu)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE)
|
||||
fu->delayed_set_alt_cancel = true;
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
|
||||
cancel_work_sync(&fu->delayed_set_alt);
|
||||
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE;
|
||||
fu->delayed_set_alt_cancel = false;
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
}
|
||||
|
||||
static void tcm_delayed_set_alt(struct work_struct *wq)
|
||||
{
|
||||
struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq,
|
||||
work);
|
||||
struct f_uas *fu = work->fu;
|
||||
int alt = work->alt;
|
||||
struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt);
|
||||
unsigned long flags;
|
||||
unsigned int alt;
|
||||
|
||||
kfree(work);
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_QUEUED) {
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
return;
|
||||
}
|
||||
fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_RUNNING;
|
||||
alt = fu->delayed_alt;
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
|
||||
if (fu->flags & USBG_IS_BOT)
|
||||
bot_cleanup_old_alt(fu);
|
||||
if (fu->flags & USBG_IS_UAS)
|
||||
uasp_cleanup_old_alt(fu);
|
||||
tcm_cleanup_old_alt(fu);
|
||||
|
||||
if (tcm_delayed_set_alt_cancelled(fu))
|
||||
goto out_done;
|
||||
|
||||
if (alt == USB_G_ALT_INT_BBB)
|
||||
bot_set_alt(fu);
|
||||
else if (alt == USB_G_ALT_INT_UAS)
|
||||
uasp_set_alt(fu);
|
||||
usb_composite_setup_continue(fu->function.config->cdev);
|
||||
|
||||
if (tcm_complete_delayed_status(fu))
|
||||
return;
|
||||
|
||||
tcm_cleanup_old_alt(fu);
|
||||
out_done:
|
||||
tcm_delayed_set_alt_done(fu);
|
||||
}
|
||||
|
||||
static int tcm_get_alt(struct usb_function *f, unsigned intf)
|
||||
|
|
@ -2413,15 +2540,20 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
|||
return -EOPNOTSUPP;
|
||||
|
||||
if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) {
|
||||
struct guas_setup_wq *work;
|
||||
unsigned long flags;
|
||||
|
||||
work = kmalloc_obj(*work, GFP_ATOMIC);
|
||||
if (!work)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&work->work, tcm_delayed_set_alt);
|
||||
work->fu = fu;
|
||||
work->alt = alt;
|
||||
schedule_work(&work->work);
|
||||
spin_lock_irqsave(&fu->delayed_set_alt_lock, flags);
|
||||
if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) {
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock,
|
||||
flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
fu->delayed_alt = alt;
|
||||
fu->delayed_set_alt_cancel = false;
|
||||
fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_QUEUED;
|
||||
spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags);
|
||||
|
||||
schedule_work(&fu->delayed_set_alt);
|
||||
return USB_GADGET_DELAYED_STATUS;
|
||||
}
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -2431,11 +2563,8 @@ static void tcm_disable(struct usb_function *f)
|
|||
{
|
||||
struct f_uas *fu = to_f_uas(f);
|
||||
|
||||
if (fu->flags & USBG_IS_UAS)
|
||||
uasp_cleanup_old_alt(fu);
|
||||
else if (fu->flags & USBG_IS_BOT)
|
||||
bot_cleanup_old_alt(fu);
|
||||
fu->flags = 0;
|
||||
if (tcm_cancel_delayed_set_alt(fu))
|
||||
tcm_cleanup_old_alt(fu);
|
||||
}
|
||||
|
||||
static int tcm_setup(struct usb_function *f,
|
||||
|
|
@ -2583,11 +2712,16 @@ static void tcm_free(struct usb_function *f)
|
|||
{
|
||||
struct f_uas *tcm = to_f_uas(f);
|
||||
|
||||
tcm_cancel_delayed_set_alt_sync(tcm);
|
||||
kfree(tcm);
|
||||
}
|
||||
|
||||
static void tcm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_uas *fu = to_f_uas(f);
|
||||
|
||||
tcm_cancel_delayed_set_alt_sync(fu);
|
||||
tcm_cleanup_old_alt(fu);
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
|
|
@ -2620,6 +2754,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
|
|||
fu->function.disable = tcm_disable;
|
||||
fu->function.free_func = tcm_free;
|
||||
fu->tpg = tpg_instances[i].tpg;
|
||||
INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt);
|
||||
spin_lock_init(&fu->delayed_set_alt_lock);
|
||||
|
||||
hash_init(fu->stream_hash);
|
||||
mutex_unlock(&tpg_instances_lock);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#define __TARGET_USB_GADGET_H__
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/spinlock.h>
|
||||
/* #include <linux/usb/uas.h> */
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/usb/composite.h>
|
||||
|
|
@ -29,6 +30,12 @@ enum {
|
|||
|
||||
#define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS
|
||||
|
||||
enum {
|
||||
USBG_DELAYED_SET_ALT_IDLE = 0,
|
||||
USBG_DELAYED_SET_ALT_QUEUED,
|
||||
USBG_DELAYED_SET_ALT_RUNNING,
|
||||
};
|
||||
|
||||
struct tcm_usbg_nexus {
|
||||
struct se_session *tvn_se_sess;
|
||||
};
|
||||
|
|
@ -132,6 +139,12 @@ struct f_uas {
|
|||
#define USBG_BOT_CMD_PEND (1 << 4)
|
||||
#define USBG_BOT_WEDGED (1 << 5)
|
||||
|
||||
struct work_struct delayed_set_alt;
|
||||
spinlock_t delayed_set_alt_lock; /* protects delayed_set_alt_* */
|
||||
unsigned int delayed_alt;
|
||||
unsigned int delayed_set_alt_state;
|
||||
bool delayed_set_alt_cancel;
|
||||
|
||||
struct usbg_cdb cmd[USBG_NUM_CMDS];
|
||||
struct usb_ep *ep_in;
|
||||
struct usb_ep *ep_out;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,8 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
|
|||
return usb_ep_set_halt(cdev->gadget->ep0);
|
||||
|
||||
req->length = min_t(unsigned int, uvc->event_length, data->length);
|
||||
if (req->length > sizeof(data->data))
|
||||
req->length = sizeof(data->data);
|
||||
req->zero = data->length < uvc->event_length;
|
||||
|
||||
memcpy(req->buf, data->data, req->length);
|
||||
|
|
|
|||
|
|
@ -585,9 +585,29 @@ static int bdc_probe(struct platform_device *pdev)
|
|||
static void bdc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bdc *bdc;
|
||||
unsigned long flags;
|
||||
u32 temp;
|
||||
|
||||
bdc = platform_get_drvdata(pdev);
|
||||
dev_dbg(bdc->dev, "%s ()\n", __func__);
|
||||
/*
|
||||
* Disable the device interrupt source before freeing the IRQ:
|
||||
* clear BDC_GIE so the controller stops asserting interrupts,
|
||||
* then free_irq drains any in-flight handler.
|
||||
*/
|
||||
spin_lock_irqsave(&bdc->lock, flags);
|
||||
temp = bdc_readl(bdc->regs, BDC_BDCSC);
|
||||
temp &= ~BDC_GIE;
|
||||
bdc_writel(bdc->regs, BDC_BDCSC, temp);
|
||||
spin_unlock_irqrestore(&bdc->lock, flags);
|
||||
free_irq(bdc->irq, bdc);
|
||||
/*
|
||||
* Drain func_wake_notify after free_irq: the IRQ handler arms this
|
||||
* delayed_work via bdc_sr_uspc -> handle_link_state_change ->
|
||||
* schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so
|
||||
* the IRQ must be released first to prevent re-arm after cancel.
|
||||
*/
|
||||
cancel_delayed_work_sync(&bdc->func_wake_notify);
|
||||
bdc_udc_exit(bdc);
|
||||
bdc_hw_exit(bdc);
|
||||
bdc_phy_exit(bdc);
|
||||
|
|
|
|||
|
|
@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc)
|
|||
|
||||
|
||||
bdc->gadget.name = BRCM_BDC_NAME;
|
||||
ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
|
||||
IRQF_SHARED, BRCM_BDC_NAME, bdc);
|
||||
ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED,
|
||||
BRCM_BDC_NAME, bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev,
|
||||
"failed to request irq #%d %d\n",
|
||||
|
|
@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc)
|
|||
ret = bdc_init_ep(bdc);
|
||||
if (ret) {
|
||||
dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
|
||||
return ret;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
|
||||
|
|
@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc)
|
|||
err1:
|
||||
usb_del_gadget_udc(&bdc->gadget);
|
||||
err0:
|
||||
free_irq(bdc->irq, bdc);
|
||||
bdc_free_ep(bdc);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -278,6 +278,7 @@ struct dummy {
|
|||
unsigned ints_enabled:1;
|
||||
unsigned udc_suspended:1;
|
||||
unsigned pullup:1;
|
||||
unsigned fifo_req_busy:1;
|
||||
|
||||
/*
|
||||
* HOST side support
|
||||
|
|
@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_to_dummy(struct device *dev)
|
|||
|
||||
/* DEVICE/GADGET SIDE UTILITY ROUTINES */
|
||||
|
||||
/*
|
||||
* Give back a gadget request with dum->lock dropped around the callback.
|
||||
* If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag
|
||||
* was set in dummy_queue() when the shared request was taken and must stay
|
||||
* set until its completion callback has returned; list_del_init() alone
|
||||
* makes the request look idle while the callback is still running.
|
||||
* Caller holds dum->lock and has already done list_del_init() + status.
|
||||
*/
|
||||
static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
|
||||
struct dummy_request *req)
|
||||
{
|
||||
bool fifo = req == &dum->fifo_req;
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
usb_gadget_giveback_request(_ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
if (fifo)
|
||||
dum->fifo_req_busy = 0;
|
||||
}
|
||||
|
||||
/* called with spinlock held */
|
||||
static void nuke(struct dummy *dum, struct dummy_ep *ep)
|
||||
{
|
||||
|
|
@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, struct dummy_ep *ep)
|
|||
list_del_init(&req->queue);
|
||||
req->req.status = -ESHUTDOWN;
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
dummy_giveback(dum, &ep->ep, req);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
|
|||
|
||||
/* implement an emulated single-request FIFO */
|
||||
if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
|
||||
list_empty(&dum->fifo_req.queue) &&
|
||||
!dum->fifo_req_busy &&
|
||||
list_empty(&ep->queue) &&
|
||||
_req->length <= FIFO_SIZE) {
|
||||
req = &dum->fifo_req;
|
||||
dum->fifo_req_busy = 1;
|
||||
req->req = *_req;
|
||||
req->req.buf = dum->fifo_buf;
|
||||
memcpy(dum->fifo_buf, _req->buf, _req->length);
|
||||
|
|
@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
|||
dev_dbg(udc_dev(dum),
|
||||
"dequeued req %p from %s, len %d buf %p\n",
|
||||
req, _ep->name, _req->length, _req->buf);
|
||||
spin_unlock(&dum->lock);
|
||||
usb_gadget_giveback_request(_ep, _req);
|
||||
spin_lock(&dum->lock);
|
||||
dummy_giveback(dum, _ep, req);
|
||||
}
|
||||
spin_unlock_irqrestore(&dum->lock, flags);
|
||||
return retval;
|
||||
|
|
@ -1523,9 +1541,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
|
|||
if (req->req.status != -EINPROGRESS) {
|
||||
list_del_init(&req->queue);
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
dummy_giveback(dum, &ep->ep, req);
|
||||
|
||||
/* requests might have been unlinked... */
|
||||
rescan = 1;
|
||||
|
|
@ -1910,9 +1926,7 @@ static enum hrtimer_restart dummy_timer(struct hrtimer *t)
|
|||
dev_dbg(udc_dev(dum), "stale req = %p\n",
|
||||
req);
|
||||
|
||||
spin_unlock(&dum->lock);
|
||||
usb_gadget_giveback_request(&ep->ep, &req->req);
|
||||
spin_lock(&dum->lock);
|
||||
dummy_giveback(dum, &ep->ep, req);
|
||||
ep->already_seen = 0;
|
||||
goto restart;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ __acquires(ep->udc->lock)
|
|||
usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
|
||||
|
||||
if (status && (status != -ESHUTDOWN))
|
||||
dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n",
|
||||
dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n",
|
||||
ep->ep.name, &req->req, status,
|
||||
req->req.actual, req->req.length);
|
||||
|
||||
|
|
@ -286,7 +286,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
|||
timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
|
||||
while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&udc->gadget.dev, "udc reset timeout!\n");
|
||||
dev_err(udc->dev, "udc reset timeout!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
cpu_relax();
|
||||
|
|
@ -309,7 +309,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
|
|||
tmp &= USB_EP_LIST_ADDRESS_MASK;
|
||||
fsl_writel(tmp, &dr_regs->endpointlistaddr);
|
||||
|
||||
dev_vdbg(&udc->gadget.dev,
|
||||
dev_vdbg(udc->dev,
|
||||
"vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n",
|
||||
udc->ep_qh, (int)tmp,
|
||||
fsl_readl(&dr_regs->endpointlistaddr));
|
||||
|
|
@ -500,7 +500,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
|
|||
tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
|
||||
break;
|
||||
default:
|
||||
dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type);
|
||||
dev_vdbg(udc->dev, "error ep type is %d\n", ep_type);
|
||||
return;
|
||||
}
|
||||
if (zlt)
|
||||
|
|
@ -613,7 +613,7 @@ static int fsl_ep_enable(struct usb_ep *_ep,
|
|||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
retval = 0;
|
||||
|
||||
dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n",
|
||||
dev_vdbg(udc->dev, "enabled %s (ep%d%s) maxpacket %d\n",
|
||||
ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f,
|
||||
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
|
||||
max);
|
||||
|
|
@ -634,13 +634,8 @@ static int fsl_ep_disable(struct usb_ep *_ep)
|
|||
int ep_num;
|
||||
|
||||
ep = container_of(_ep, struct fsl_ep, ep);
|
||||
if (!_ep || !ep->ep.desc) {
|
||||
/*
|
||||
* dev_vdbg(&udc->gadget.dev, "%s not enabled\n",
|
||||
* _ep ? ep->ep.name : NULL);
|
||||
*/
|
||||
if (!_ep || !ep->ep.desc)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* disable ep on controller */
|
||||
ep_num = ep_index(ep);
|
||||
|
|
@ -664,7 +659,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)
|
|||
ep->stopped = 1;
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
||||
dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name);
|
||||
dev_vdbg(udc->dev, "disabled %s OK\n", _ep->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -724,9 +719,6 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
|
|||
{
|
||||
u32 temp, bitmask, tmp_stat;
|
||||
|
||||
/* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr);
|
||||
dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */
|
||||
|
||||
bitmask = ep_is_in(ep)
|
||||
? (1 << (ep_index(ep) + 16))
|
||||
: (1 << (ep_index(ep)));
|
||||
|
|
@ -813,7 +805,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
|
|||
*is_last = 0;
|
||||
|
||||
if ((*is_last) == 0)
|
||||
dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n");
|
||||
dev_vdbg(udc_controller->dev, "multi-dtd request!\n");
|
||||
/* Fill in the transfer size; set active bit */
|
||||
swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
|
||||
|
||||
|
|
@ -825,7 +817,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
|
|||
|
||||
mb();
|
||||
|
||||
dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma);
|
||||
dev_vdbg(udc_controller->dev, "length = %d address= 0x%x\n", *length, (int)*dma);
|
||||
|
||||
return dtd;
|
||||
}
|
||||
|
|
@ -876,11 +868,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
|
|||
/* catch various bogus parameters */
|
||||
if (!_req || !req->req.complete || !req->req.buf
|
||||
|| !list_empty(&req->queue)) {
|
||||
dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__);
|
||||
dev_vdbg(udc->dev, "%s, bad params\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (unlikely(!ep->ep.desc)) {
|
||||
dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__);
|
||||
dev_vdbg(udc->dev, "%s, bad ep\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
|
||||
|
|
@ -1040,7 +1032,7 @@ static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
|
|||
udc->ep0_dir = 0;
|
||||
}
|
||||
out:
|
||||
dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name,
|
||||
dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name,
|
||||
value ? "set" : "clear", status);
|
||||
|
||||
return status;
|
||||
|
|
@ -1109,7 +1101,7 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep)
|
|||
/* Wait until flush complete */
|
||||
while (fsl_readl(&dr_regs->endptflush)) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&udc_controller->gadget.dev,
|
||||
dev_err(udc_controller->dev,
|
||||
"ep flush timeout\n");
|
||||
return;
|
||||
}
|
||||
|
|
@ -1182,7 +1174,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
|
|||
|
||||
udc = container_of(gadget, struct fsl_udc, gadget);
|
||||
spin_lock_irqsave(&udc->lock, flags);
|
||||
dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active));
|
||||
dev_vdbg(udc->dev, "VBUS %s\n", str_on_off(is_active));
|
||||
udc->vbus_active = (is_active != 0);
|
||||
if (can_pullup(udc))
|
||||
fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
|
||||
|
|
@ -1548,7 +1540,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
|
|||
udc->ep0_state = WAIT_FOR_SETUP;
|
||||
break;
|
||||
case WAIT_FOR_SETUP:
|
||||
dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n");
|
||||
dev_err(udc->dev, "Unexpected ep0 packets\n");
|
||||
break;
|
||||
default:
|
||||
ep0stall(udc);
|
||||
|
|
@ -1617,7 +1609,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
|
|||
errors = hc32_to_cpu(curr_td->size_ioc_sts);
|
||||
if (errors & DTD_ERROR_MASK) {
|
||||
if (errors & DTD_STATUS_HALTED) {
|
||||
dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe);
|
||||
dev_err(udc->dev, "dTD error %08x QH=%d\n", errors, pipe);
|
||||
/* Clear the errors and Halt condition */
|
||||
tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
|
||||
tmp &= ~errors;
|
||||
|
|
@ -1628,26 +1620,26 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
|
|||
break;
|
||||
}
|
||||
if (errors & DTD_STATUS_DATA_BUFF_ERR) {
|
||||
dev_vdbg(&udc->gadget.dev, "Transfer overflow\n");
|
||||
dev_vdbg(udc->dev, "Transfer overflow\n");
|
||||
status = -EPROTO;
|
||||
break;
|
||||
} else if (errors & DTD_STATUS_TRANSACTION_ERR) {
|
||||
dev_vdbg(&udc->gadget.dev, "ISO error\n");
|
||||
dev_vdbg(udc->dev, "ISO error\n");
|
||||
status = -EILSEQ;
|
||||
break;
|
||||
} else
|
||||
dev_err(&udc->gadget.dev,
|
||||
dev_err(udc->dev,
|
||||
"Unknown error has occurred (0x%x)!\n",
|
||||
errors);
|
||||
|
||||
} else if (hc32_to_cpu(curr_td->size_ioc_sts)
|
||||
& DTD_STATUS_ACTIVE) {
|
||||
dev_vdbg(&udc->gadget.dev, "Request not complete\n");
|
||||
dev_vdbg(udc->dev, "Request not complete\n");
|
||||
status = REQ_UNCOMPLETE;
|
||||
return status;
|
||||
} else if (remaining_length) {
|
||||
if (direction) {
|
||||
dev_vdbg(&udc->gadget.dev,
|
||||
dev_vdbg(udc->dev,
|
||||
"Transmit dTD remaining length not zero\n");
|
||||
status = -EPROTO;
|
||||
break;
|
||||
|
|
@ -1655,8 +1647,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
dev_vdbg(&udc->gadget.dev,
|
||||
"dTD transmitted successful\n");
|
||||
dev_vdbg(udc->dev, "dTD transmitted successful\n");
|
||||
}
|
||||
|
||||
if (j != curr_req->dtd_count - 1)
|
||||
|
|
@ -1699,7 +1690,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)
|
|||
|
||||
/* If the ep is configured */
|
||||
if (!curr_ep->ep.name) {
|
||||
dev_warn(&udc->gadget.dev, "Invalid EP?\n");
|
||||
dev_warn(udc->dev, "Invalid EP?\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1708,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)
|
|||
queue) {
|
||||
status = process_ep_req(udc, i, curr_req);
|
||||
|
||||
dev_vdbg(&udc->gadget.dev,
|
||||
dev_vdbg(udc->dev,
|
||||
"status of process_ep_req= %d, ep = %d\n",
|
||||
status, ep_num);
|
||||
if (status == REQ_UNCOMPLETE)
|
||||
|
|
@ -1829,7 +1820,7 @@ static void reset_irq(struct fsl_udc *udc)
|
|||
while (fsl_readl(&dr_regs->endpointprime)) {
|
||||
/* Wait until all endptprime bits cleared */
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&udc->gadget.dev, "Timeout for reset\n");
|
||||
dev_err(udc->dev, "Timeout for reset\n");
|
||||
break;
|
||||
}
|
||||
cpu_relax();
|
||||
|
|
@ -1839,7 +1830,7 @@ static void reset_irq(struct fsl_udc *udc)
|
|||
fsl_writel(0xffffffff, &dr_regs->endptflush);
|
||||
|
||||
if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
|
||||
dev_vdbg(&udc->gadget.dev, "Bus reset\n");
|
||||
dev_vdbg(udc->dev, "Bus reset\n");
|
||||
/* Bus is reseting */
|
||||
udc->bus_reset = 1;
|
||||
/* Reset all the queues, include XD, dTD, EP queue
|
||||
|
|
@ -1847,7 +1838,7 @@ static void reset_irq(struct fsl_udc *udc)
|
|||
reset_queues(udc, true);
|
||||
udc->usb_state = USB_STATE_DEFAULT;
|
||||
} else {
|
||||
dev_vdbg(&udc->gadget.dev, "Controller reset\n");
|
||||
dev_vdbg(udc->dev, "Controller reset\n");
|
||||
/* initialize usb hw reg except for regs for EP, not
|
||||
* touch usbintr reg */
|
||||
dr_controller_setup(udc);
|
||||
|
|
@ -1881,7 +1872,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
|
|||
/* Clear notification bits */
|
||||
fsl_writel(irq_src, &dr_regs->usbsts);
|
||||
|
||||
/* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */
|
||||
/* dev_vdbg(udc->dev, "irq_src [0x%8x]", irq_src); */
|
||||
|
||||
/* Need to resume? */
|
||||
if (udc->usb_state == USB_STATE_SUSPENDED)
|
||||
|
|
@ -1890,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
|
|||
|
||||
/* USB Interrupt */
|
||||
if (irq_src & USB_STS_INT) {
|
||||
dev_vdbg(&udc->gadget.dev, "Packet int\n");
|
||||
dev_vdbg(udc->dev, "Packet int\n");
|
||||
/* Setup package, we only support ep0 as control ep */
|
||||
if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
|
||||
tripwire_handler(udc, 0,
|
||||
|
|
@ -1919,7 +1910,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
|
|||
|
||||
/* Reset Received */
|
||||
if (irq_src & USB_STS_RESET) {
|
||||
dev_vdbg(&udc->gadget.dev, "reset int\n");
|
||||
dev_vdbg(udc->dev, "reset int\n");
|
||||
reset_irq(udc);
|
||||
status = IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -1931,7 +1922,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
|
|||
}
|
||||
|
||||
if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
|
||||
dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src);
|
||||
dev_vdbg(udc->dev, "Error IRQ %x\n", irq_src);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&udc->lock, flags);
|
||||
|
|
@ -1967,7 +1958,7 @@ static int fsl_udc_start(struct usb_gadget *g,
|
|||
udc_controller->transceiver->otg,
|
||||
&udc_controller->gadget);
|
||||
if (retval < 0) {
|
||||
dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n");
|
||||
dev_err(udc_controller->dev, "can't bind to transceiver\n");
|
||||
udc_controller->driver = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -2252,7 +2243,7 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
|||
|
||||
udc->eps = kzalloc_objs(struct fsl_ep, udc->max_ep);
|
||||
if (!udc->eps) {
|
||||
dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n");
|
||||
dev_err(udc->dev, "kmalloc udc endpoint status failed\n");
|
||||
goto eps_alloc_failed;
|
||||
}
|
||||
|
||||
|
|
@ -2267,7 +2258,7 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
|||
udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
|
||||
&udc->ep_qh_dma, GFP_KERNEL);
|
||||
if (!udc->ep_qh) {
|
||||
dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n");
|
||||
dev_err(udc->dev, "malloc QHs for udc failed\n");
|
||||
goto ep_queue_alloc_failed;
|
||||
}
|
||||
|
||||
|
|
@ -2278,14 +2269,14 @@ static int struct_udc_setup(struct fsl_udc *udc,
|
|||
udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
|
||||
struct fsl_req, req);
|
||||
if (!udc->status_req) {
|
||||
dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n");
|
||||
dev_err(udc->dev, "kzalloc for udc status request failed\n");
|
||||
goto udc_status_alloc_failed;
|
||||
}
|
||||
|
||||
/* allocate a small amount of memory to get valid address */
|
||||
udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
|
||||
if (!udc->status_req->req.buf) {
|
||||
dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n");
|
||||
dev_err(udc->dev, "kzalloc for udc request buffer failed\n");
|
||||
goto udc_req_buf_alloc_failed;
|
||||
}
|
||||
|
||||
|
|
@ -2373,6 +2364,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
|||
if (udc_controller == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
udc_controller->dev = &pdev->dev;
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
udc_controller->pdata = pdata;
|
||||
spin_lock_init(&udc_controller->lock);
|
||||
|
|
@ -2382,7 +2374,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
|||
if (pdata->operating_mode == FSL_USB2_DR_OTG) {
|
||||
udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
|
||||
dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n");
|
||||
dev_err(&pdev->dev, "Can't find OTG driver!\n");
|
||||
ret = -ENODEV;
|
||||
goto err_kfree;
|
||||
}
|
||||
|
|
@ -2398,7 +2390,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
|||
if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
driver_name)) {
|
||||
dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name);
|
||||
dev_err(&pdev->dev, "failed to request mem region\n");
|
||||
ret = -EBUSY;
|
||||
goto err_kfree;
|
||||
}
|
||||
|
|
@ -2429,7 +2421,7 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
|||
/* Read Device Controller Capability Parameters register */
|
||||
dccparams = fsl_readl(&dr_regs->dccparams);
|
||||
if (!(dccparams & DCCPARAMS_DC)) {
|
||||
dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n");
|
||||
dev_err(&pdev->dev, "This SOC doesn't support device role\n");
|
||||
ret = -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
|
|
@ -2447,14 +2439,14 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
|||
ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
|
||||
driver_name, udc_controller);
|
||||
if (ret != 0) {
|
||||
dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n",
|
||||
dev_err(&pdev->dev, "cannot request irq %d err %d\n",
|
||||
udc_controller->irq, ret);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
/* Initialize the udc structure including QH member and other member */
|
||||
if (struct_udc_setup(udc_controller, pdev)) {
|
||||
dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n");
|
||||
dev_err(&pdev->dev, "Can't initialize udc data structure\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
|
@ -2474,7 +2466,6 @@ static int fsl_udc_probe(struct platform_device *pdev)
|
|||
udc_controller->gadget.name = driver_name;
|
||||
|
||||
/* Setup gadget.dev and register with kernel */
|
||||
dev_set_name(&udc_controller->gadget.dev, "gadget");
|
||||
udc_controller->gadget.dev.of_node = pdev->dev.of_node;
|
||||
|
||||
if (!IS_ERR_OR_NULL(udc_controller->transceiver))
|
||||
|
|
@ -2548,12 +2539,6 @@ static void fsl_udc_remove(struct platform_device *pdev)
|
|||
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
|
||||
if (!udc_controller) {
|
||||
dev_err(&pdev->dev,
|
||||
"Driver still in use but removing anyhow\n");
|
||||
return;
|
||||
}
|
||||
|
||||
udc_controller->done = &done;
|
||||
usb_del_gadget_udc(&udc_controller->gadget);
|
||||
|
||||
|
|
|
|||
|
|
@ -470,6 +470,7 @@ struct fsl_ep {
|
|||
#define EP_DIR_OUT 0
|
||||
|
||||
struct fsl_udc {
|
||||
struct device *dev;
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
struct fsl_usb2_platform_data *pdata;
|
||||
|
|
|
|||
|
|
@ -2374,9 +2374,10 @@ static int pxa_udc_probe(struct platform_device *pdev)
|
|||
struct pxa_udc *udc = &memory;
|
||||
int retval = 0;
|
||||
|
||||
udc->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_ASIS);
|
||||
udc->gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_ASIS);
|
||||
if (IS_ERR(udc->gpiod))
|
||||
return PTR_ERR(udc->gpiod);
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(udc->gpiod),
|
||||
"Couldn't find or request D+ gpio\n");
|
||||
|
||||
udc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(udc->regs))
|
||||
|
|
@ -2395,11 +2396,6 @@ static int pxa_udc_probe(struct platform_device *pdev)
|
|||
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
}
|
||||
|
||||
if (IS_ERR(udc->gpiod)) {
|
||||
dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
|
||||
PTR_ERR(udc->gpiod));
|
||||
return PTR_ERR(udc->gpiod);
|
||||
}
|
||||
if (udc->gpiod)
|
||||
gpiod_direction_output(udc->gpiod, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev)
|
|||
/* device struct setup */
|
||||
dev->gadget.ops = &udc_ops;
|
||||
|
||||
dev_set_name(&dev->gadget.dev, "gadget");
|
||||
dev->gadget.name = name;
|
||||
dev->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
||||
|
|
|
|||
|
|
@ -448,6 +448,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) {
|
||||
xhci->quirks |= XHCI_LPM_SUPPORT;
|
||||
xhci->quirks |= XHCI_TRB_OVERFETCH;
|
||||
xhci->dma_mask_bits = 36;
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
|
||||
|
|
|
|||
|
|
@ -5466,6 +5466,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
|
|||
if (xhci->hci_version > 0x100)
|
||||
xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
|
||||
|
||||
xhci->dma_mask_bits = 64;
|
||||
xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS);
|
||||
xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
|
||||
/* xhci-plat or xhci-pci might have set max_interrupters already */
|
||||
|
|
@ -5515,12 +5516,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
|
|||
if (xhci->quirks & XHCI_NO_64BIT_SUPPORT)
|
||||
xhci->hcc_params &= ~BIT(0);
|
||||
|
||||
/* Set dma_mask and coherent_dma_mask to 64-bits,
|
||||
* if xHC supports 64-bit addressing */
|
||||
/*
|
||||
* Set dma_mask and coherent_dma_mask to 64-bits if xHC supports
|
||||
* 64-bit addressing, unless a controller-specific quirk callback
|
||||
* limits the usable address width.
|
||||
*/
|
||||
if ((xhci->hcc_params & HCC_64BIT_ADDR) &&
|
||||
!dma_set_mask(dev, DMA_BIT_MASK(64))) {
|
||||
xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
|
||||
dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
|
||||
!dma_set_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits))) {
|
||||
xhci_dbg(xhci, "Enabling %u-bit DMA addresses.\n",
|
||||
xhci->dma_mask_bits);
|
||||
dma_set_coherent_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits));
|
||||
} else {
|
||||
/*
|
||||
* This is to avoid error in cases where a 32-bit USB
|
||||
|
|
|
|||
|
|
@ -1526,6 +1526,7 @@ struct xhci_hcd {
|
|||
/* imod_interval in ns (I * 250ns) */
|
||||
u32 imod_interval;
|
||||
u32 page_size;
|
||||
unsigned int dma_mask_bits;
|
||||
/* MSI-X/MSI vectors */
|
||||
int nvecs;
|
||||
/* optional clocks */
|
||||
|
|
|
|||
|
|
@ -454,7 +454,6 @@ static int omap2430_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "failed to register musb device\n");
|
||||
goto err_disable_rpm;
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -464,7 +463,6 @@ static int omap2430_probe(struct platform_device *pdev)
|
|||
if (!IS_ERR(glue->control_otghs))
|
||||
put_device(glue->control_otghs);
|
||||
err_put_musb:
|
||||
of_node_put(np);
|
||||
platform_device_put(musb);
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -1072,6 +1072,8 @@ static const struct usb_device_id id_table_combined[] = {
|
|||
{ USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) },
|
||||
/* Abacus Electrics */
|
||||
{ USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) },
|
||||
/* Endress+Hauser AG devices */
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_EH_FXA291_PID) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -313,6 +313,11 @@
|
|||
#define FTDI_ELV_UDF77_PID 0xFB5E /* USB DCF Funkuhr (UDF 77) */
|
||||
#define FTDI_ELV_UIO88_PID 0xFB5F /* USB-I/O Interface (UIO 88) */
|
||||
|
||||
/*
|
||||
* Endress+Hauser AG product ids (FTDI_VID)
|
||||
*/
|
||||
#define FTDI_EH_FXA291_PID 0xE510
|
||||
|
||||
/*
|
||||
* EVER Eco Pro UPS (http://www.ever.com.pl/)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -646,7 +646,8 @@ static void edge_interrupt_callback(struct urb *urb)
|
|||
if (edge_port && edge_port->open) {
|
||||
spin_lock_irqsave(&edge_port->ep_lock,
|
||||
flags);
|
||||
edge_port->txCredits += txCredits;
|
||||
edge_port->txCredits = min(edge_port->txCredits + txCredits,
|
||||
edge_port->maxTxCredits);
|
||||
spin_unlock_irqrestore(&edge_port->ep_lock,
|
||||
flags);
|
||||
dev_dbg(dev, "%s - txcredits for port%d = %d\n",
|
||||
|
|
|
|||
|
|
@ -1464,6 +1464,12 @@ static int do_boot_mode(struct edgeport_serial *serial,
|
|||
/* Allocate a 15.5k buffer + 3 byte header */
|
||||
buffer_size = (((1024 * 16) - 512) +
|
||||
sizeof(struct ti_i2c_image_header));
|
||||
if (fw->size - 4 > buffer_size) {
|
||||
dev_err(dev, "%s - firmware image is too large\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buffer = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ struct keyspan_pda_private {
|
|||
struct work_struct unthrottle_work;
|
||||
struct usb_serial *serial;
|
||||
struct usb_serial_port *port;
|
||||
bool throttled;
|
||||
bool throttle_req;
|
||||
};
|
||||
|
||||
static int keyspan_pda_write_start(struct usb_serial_port *port);
|
||||
|
|
@ -148,6 +150,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
|
|||
int retval;
|
||||
int status = urb->status;
|
||||
struct keyspan_pda_private *priv;
|
||||
bool throttled = false;
|
||||
unsigned long flags;
|
||||
|
||||
priv = usb_get_serial_port_data(port);
|
||||
|
|
@ -209,16 +212,24 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
|
|||
}
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&port->dev,
|
||||
"%s - usb_submit_urb failed with result %d\n",
|
||||
__func__, retval);
|
||||
spin_lock_irqsave(&port->lock, flags);
|
||||
if (priv->throttle_req) {
|
||||
priv->throttled = true;
|
||||
throttled = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&port->lock, flags);
|
||||
|
||||
if (!throttled) {
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval);
|
||||
}
|
||||
}
|
||||
|
||||
static void keyspan_pda_rx_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
/*
|
||||
* Stop receiving characters. We just turn off the URB request, and
|
||||
|
|
@ -228,16 +239,29 @@ static void keyspan_pda_rx_throttle(struct tty_struct *tty)
|
|||
* send an XOFF, although it might make sense to foist that off upon
|
||||
* the device too.
|
||||
*/
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
spin_lock_irq(&port->lock);
|
||||
priv->throttle_req = true;
|
||||
spin_unlock_irq(&port->lock);
|
||||
}
|
||||
|
||||
static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct usb_serial_port *port = tty->driver_data;
|
||||
struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
|
||||
bool throttled;
|
||||
int ret;
|
||||
|
||||
/* just restart the receive interrupt URB */
|
||||
if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
|
||||
dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
|
||||
spin_lock_irq(&port->lock);
|
||||
throttled = priv->throttled;
|
||||
priv->throttled = false;
|
||||
priv->throttle_req = false;
|
||||
spin_unlock_irq(&port->lock);
|
||||
|
||||
if (throttled) {
|
||||
ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
if (ret)
|
||||
dev_err(&port->dev, "failed to submit in urb: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
|
||||
|
|
@ -577,6 +601,8 @@ static int keyspan_pda_open(struct tty_struct *tty,
|
|||
|
||||
spin_lock_irq(&port->lock);
|
||||
priv->tx_room = rc;
|
||||
priv->throttled = false;
|
||||
priv->throttle_req = false;
|
||||
spin_unlock_irq(&port->lock);
|
||||
|
||||
rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
|
||||
|
|
|
|||
|
|
@ -1080,6 +1080,13 @@ static int mxuport_probe(struct usb_serial *serial,
|
|||
/* Use the firmware already in the device */
|
||||
err = 0;
|
||||
} else {
|
||||
if (fw_p->size <= VER_ADDR_3) {
|
||||
dev_err(&serial->interface->dev,
|
||||
"Firmware %s is too short\n", buf);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
local_ver = ((fw_p->data[VER_ADDR_1] << 16) |
|
||||
(fw_p->data[VER_ADDR_2] << 8) |
|
||||
fw_p->data[VER_ADDR_3]);
|
||||
|
|
|
|||
|
|
@ -2496,6 +2496,7 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = RSVD(5) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */
|
||||
.driver_info = RSVD(5) },
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) }, /* TDTECH MT5710-CN */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
|
||||
|
|
|
|||
|
|
@ -395,6 +395,13 @@ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110,
|
|||
USB_SC_DEVICE, USB_PR_CB, NULL,
|
||||
US_FL_MAX_SECTORS_MIN),
|
||||
|
||||
/* Reported by Ai Chao <aichao@kylinos.cn> */
|
||||
UNUSUAL_DEV( 0x04b4, 0xb708, 0x0000, 0xffff,
|
||||
"Longmai Technologies",
|
||||
"USB Key",
|
||||
USB_SC_SCSI, USB_PR_BULK, NULL,
|
||||
US_FL_NO_ATA_1X),
|
||||
|
||||
/*
|
||||
* Reported by Simon Levitt <simon@whattf.com>
|
||||
* This entry needs Sub and Proto fields
|
||||
|
|
|
|||
|
|
@ -529,6 +529,129 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con,
|
||||
u8 recipient, u16 svid,
|
||||
u32 existing_vdo, u32 new_vdo,
|
||||
int offset)
|
||||
{
|
||||
static const char * const recipient_names[] = {
|
||||
[UCSI_RECIPIENT_CON] = "port",
|
||||
[UCSI_RECIPIENT_SOP] = "partner",
|
||||
[UCSI_RECIPIENT_SOP_P] = "plug",
|
||||
[UCSI_RECIPIENT_SOP_PP] = "cable plug prime",
|
||||
};
|
||||
|
||||
dev_warn(con->ucsi->dev,
|
||||
"con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n",
|
||||
con->num, recipient_names[recipient], svid, offset);
|
||||
|
||||
if (existing_vdo != new_vdo)
|
||||
dev_warn(con->ucsi->dev,
|
||||
"con%d: VDO mismatch: 0x%08x vs 0x%08x\n",
|
||||
con->num, existing_vdo, new_vdo);
|
||||
}
|
||||
|
||||
/* Count altmodes in @altmodes that advertise @svid. */
|
||||
static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid)
|
||||
{
|
||||
int count = 0;
|
||||
int k;
|
||||
|
||||
for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
|
||||
if (!altmodes[k])
|
||||
break;
|
||||
if (altmodes[k]->svid == svid)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if an altmode is a duplicate. Some firmware implementations
|
||||
* incorrectly return the same altmode multiple times, causing sysfs errors.
|
||||
* Returns true if the altmode should be skipped.
|
||||
*
|
||||
* The matching rules differ by recipient:
|
||||
*
|
||||
* - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
|
||||
* Two altmodes with identical SVID and VDO are byte-for-byte duplicates
|
||||
* and the second has no observable function. Drop them.
|
||||
*
|
||||
* - UCSI_RECIPIENT_SOP (partner):
|
||||
* The typec class binds each partner altmode to a port altmode of the
|
||||
* same SVID via altmode_match()/device_find_child(), which returns the
|
||||
* first port altmode with a matching SVID. If the partner advertises
|
||||
* more altmodes for SVID X than the port advertises, the surplus
|
||||
* partner altmode(s) collapse onto an already-paired port altmode and
|
||||
* trigger a "duplicate filename .../partner" sysfs error during
|
||||
* typec_altmode_create_links(). Use the port-side altmode count for
|
||||
* SVID X as the authoritative cap and reject any partner altmode that
|
||||
* would exceed it. This preserves legitimate multi-Mode partner
|
||||
* altmodes (e.g. vendor SVIDs that the port really does advertise
|
||||
* twice) while filtering the firmware-generated duplicates that have
|
||||
* no port counterpart.
|
||||
*/
|
||||
static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient,
|
||||
const struct ucsi_altmode *alt_batch, int batch_idx,
|
||||
u16 svid, u32 vdo, int offset)
|
||||
{
|
||||
struct typec_altmode **altmodes;
|
||||
int port_count, partner_count;
|
||||
int k;
|
||||
|
||||
/* Check for duplicates within the current batch first */
|
||||
for (k = 0; k < batch_idx; k++) {
|
||||
if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) {
|
||||
ucsi_dump_duplicate_altmode(con, recipient, svid,
|
||||
vdo, vdo, offset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (recipient) {
|
||||
case UCSI_RECIPIENT_SOP:
|
||||
/*
|
||||
* Cap partner altmodes per SVID by the port-side count:
|
||||
* any further partner altmode for that SVID would alias an
|
||||
* already-paired port altmode and break typec sysfs.
|
||||
*/
|
||||
port_count = ucsi_altmode_count_svid(con->port_altmode, svid);
|
||||
partner_count = ucsi_altmode_count_svid(con->partner_altmode,
|
||||
svid);
|
||||
if (port_count && partner_count >= port_count) {
|
||||
ucsi_dump_duplicate_altmode(con, recipient, svid,
|
||||
con->partner_altmode[partner_count - 1]->vdo,
|
||||
vdo, offset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case UCSI_RECIPIENT_CON:
|
||||
altmodes = con->port_altmode;
|
||||
break;
|
||||
case UCSI_RECIPIENT_SOP_P:
|
||||
altmodes = con->plug_altmode;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* CON and SOP_P: drop only exact SVID+VDO duplicates. */
|
||||
for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
|
||||
if (!altmodes[k])
|
||||
break;
|
||||
|
||||
if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo)
|
||||
continue;
|
||||
|
||||
ucsi_dump_duplicate_altmode(con, recipient, svid,
|
||||
altmodes[k]->vdo, vdo, offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
|
||||
{
|
||||
|
|
@ -583,19 +706,25 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
|
|||
|
||||
/* now register altmodes */
|
||||
for (i = 0; i < max_altmodes; i++) {
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
if (multi_dp) {
|
||||
desc.svid = updated[i].svid;
|
||||
desc.vdo = updated[i].mid;
|
||||
} else {
|
||||
desc.svid = orig[i].svid;
|
||||
desc.vdo = orig[i].mid;
|
||||
}
|
||||
desc.roles = TYPEC_PORT_DRD;
|
||||
struct ucsi_altmode *altmode_array = multi_dp ? updated : orig;
|
||||
|
||||
if (!desc.svid)
|
||||
if (!altmode_array[i].svid)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check for duplicates in current array and already
|
||||
* registered altmodes. Skip if duplicate found.
|
||||
*/
|
||||
if (ucsi_altmode_is_duplicate(con, recipient, altmode_array, i,
|
||||
altmode_array[i].svid,
|
||||
altmode_array[i].mid, i))
|
||||
continue;
|
||||
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
desc.svid = altmode_array[i].svid;
|
||||
desc.vdo = altmode_array[i].mid;
|
||||
desc.roles = TYPEC_PORT_DRD;
|
||||
|
||||
ret = ucsi_register_altmode(con, &desc, recipient);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -653,6 +782,15 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
|
|||
if (!alt[j].svid)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check for duplicates in current batch and already
|
||||
* registered altmodes. Skip if duplicate found.
|
||||
*/
|
||||
if (ucsi_altmode_is_duplicate(con, recipient, alt, j,
|
||||
alt[j].svid, alt[j].mid,
|
||||
i - num + j))
|
||||
continue;
|
||||
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
desc.vdo = alt[j].mid;
|
||||
desc.svid = alt[j].svid;
|
||||
|
|
@ -1845,6 +1983,42 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ucsi_unregister_port(struct ucsi_connector *con)
|
||||
{
|
||||
struct ucsi_work *uwork;
|
||||
|
||||
if (con->wq) {
|
||||
mutex_lock(&con->lock);
|
||||
ucsi_unregister_partner(con);
|
||||
/*
|
||||
* queue delayed items immediately so they can execute
|
||||
* and free themselves before the wq is destroyed
|
||||
*/
|
||||
list_for_each_entry(uwork, &con->partner_tasks, node) {
|
||||
if (cancel_delayed_work(&uwork->work))
|
||||
queue_delayed_work(con->wq, &uwork->work, 0);
|
||||
}
|
||||
mutex_unlock(&con->lock);
|
||||
|
||||
destroy_workqueue(con->wq);
|
||||
con->wq = NULL;
|
||||
} else {
|
||||
ucsi_unregister_partner(con);
|
||||
}
|
||||
|
||||
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
|
||||
ucsi_unregister_port_psy(con);
|
||||
|
||||
usb_power_delivery_unregister_capabilities(con->port_sink_caps);
|
||||
con->port_sink_caps = NULL;
|
||||
usb_power_delivery_unregister_capabilities(con->port_source_caps);
|
||||
con->port_source_caps = NULL;
|
||||
usb_power_delivery_unregister(con->pd);
|
||||
con->pd = NULL;
|
||||
typec_unregister_port(con->port);
|
||||
con->port = NULL;
|
||||
}
|
||||
|
||||
static u64 ucsi_get_supported_notifications(struct ucsi *ucsi)
|
||||
{
|
||||
u16 features = ucsi->cap.features;
|
||||
|
|
@ -1968,25 +2142,11 @@ static int ucsi_init(struct ucsi *ucsi)
|
|||
return 0;
|
||||
|
||||
err_unregister:
|
||||
for (con = connector; con->port; con++)
|
||||
ucsi_unregister_port(con);
|
||||
for (i = 0; i < ucsi->cap.num_connectors; i++)
|
||||
lockdep_unregister_key(&connector[i].lock_key);
|
||||
|
||||
for (con = connector; con->port; con++) {
|
||||
if (con->wq)
|
||||
destroy_workqueue(con->wq);
|
||||
ucsi_unregister_partner(con);
|
||||
ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
|
||||
ucsi_unregister_port_psy(con);
|
||||
|
||||
usb_power_delivery_unregister_capabilities(con->port_sink_caps);
|
||||
con->port_sink_caps = NULL;
|
||||
usb_power_delivery_unregister_capabilities(con->port_source_caps);
|
||||
con->port_source_caps = NULL;
|
||||
usb_power_delivery_unregister(con->pd);
|
||||
con->pd = NULL;
|
||||
typec_unregister_port(con->port);
|
||||
con->port = NULL;
|
||||
}
|
||||
kfree(connector);
|
||||
err_reset:
|
||||
memset(&ucsi->cap, 0, sizeof(ucsi->cap));
|
||||
|
|
@ -2214,33 +2374,7 @@ void ucsi_unregister(struct ucsi *ucsi)
|
|||
|
||||
for (i = 0; i < ucsi->cap.num_connectors; i++) {
|
||||
cancel_work_sync(&ucsi->connector[i].work);
|
||||
|
||||
if (ucsi->connector[i].wq) {
|
||||
struct ucsi_work *uwork;
|
||||
|
||||
mutex_lock(&ucsi->connector[i].lock);
|
||||
/*
|
||||
* queue delayed items immediately so they can execute
|
||||
* and free themselves before the wq is destroyed
|
||||
*/
|
||||
list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node)
|
||||
mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0);
|
||||
mutex_unlock(&ucsi->connector[i].lock);
|
||||
destroy_workqueue(ucsi->connector[i].wq);
|
||||
}
|
||||
|
||||
ucsi_unregister_partner(&ucsi->connector[i]);
|
||||
ucsi_unregister_altmodes(&ucsi->connector[i],
|
||||
UCSI_RECIPIENT_CON);
|
||||
ucsi_unregister_port_psy(&ucsi->connector[i]);
|
||||
|
||||
usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps);
|
||||
ucsi->connector[i].port_sink_caps = NULL;
|
||||
usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps);
|
||||
ucsi->connector[i].port_source_caps = NULL;
|
||||
usb_power_delivery_unregister(ucsi->connector[i].pd);
|
||||
ucsi->connector[i].pd = NULL;
|
||||
typec_unregister_port(ucsi->connector[i].port);
|
||||
ucsi_unregister_port(&ucsi->connector[i]);
|
||||
lockdep_unregister_key(&ucsi->connector[i].lock_key);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,28 +139,6 @@ static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi,
|
||||
u8 recipient,
|
||||
struct ucsi_altmode *orig,
|
||||
struct ucsi_altmode *updated)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (orig[0].svid == 0 || recipient != UCSI_RECIPIENT_SOP)
|
||||
return false;
|
||||
|
||||
/* EC is nice and repeats altmodes again and again. Ignore copies. */
|
||||
for (i = 1; i < UCSI_MAX_ALTMODES; i++) {
|
||||
if (orig[i].svid == orig[0].svid) {
|
||||
dev_dbg(ucsi->dev, "Found duplicate altmodes, starting from %d\n", i);
|
||||
memset(&orig[i], 0, (UCSI_MAX_ALTMODES - i) * sizeof(*orig));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void yoga_c630_ucsi_update_connector(struct ucsi_connector *con)
|
||||
{
|
||||
if (con->num == 1)
|
||||
|
|
@ -174,7 +152,6 @@ static const struct ucsi_operations yoga_c630_ucsi_ops = {
|
|||
.read_message_in = yoga_c630_ucsi_read_message_in,
|
||||
.sync_control = yoga_c630_ucsi_sync_control,
|
||||
.async_control = yoga_c630_ucsi_async_control,
|
||||
.update_altmodes = yoga_c630_ucsi_update_altmodes,
|
||||
.update_connector = yoga_c630_ucsi_update_connector,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user