aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/most/hdm-usb/hdm_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/most/hdm-usb/hdm_usb.c')
-rw-r--r--drivers/staging/most/hdm-usb/hdm_usb.c230
1 files changed, 85 insertions, 145 deletions
diff --git a/drivers/staging/most/hdm-usb/hdm_usb.c b/drivers/staging/most/hdm-usb/hdm_usb.c
index 26c9adb29308..d6db0bd65be0 100644
--- a/drivers/staging/most/hdm-usb/hdm_usb.c
+++ b/drivers/staging/most/hdm-usb/hdm_usb.c
@@ -97,9 +97,7 @@ struct clear_hold_work {
* @cap: channel capabilities
* @conf: channel configuration
* @dci: direct communication interface of hardware
- * @hw_addr: MAC address of hardware
* @ep_address: endpoint address table
- * @link_stat: link status of hardware
* @description: device description
* @suffix: suffix for channel name
* @channel_lock: synchronize channel access
@@ -117,9 +115,7 @@ struct most_dev {
struct most_channel_capability *cap;
struct most_channel_config *conf;
struct most_dci_obj *dci;
- u8 hw_addr[6];
u8 *ep_address;
- u16 link_stat;
char description[MAX_STRING_LEN];
char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
@@ -186,28 +182,9 @@ static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
5 * HZ);
}
-/**
- * free_anchored_buffers - free device's anchored items
- * @mdev: the device
- * @channel: channel ID
- * @status: status of MBO termination
- */
-static void free_anchored_buffers(struct most_dev *mdev, unsigned int channel,
- enum mbo_status_flags status)
+static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep)
{
- struct mbo *mbo;
- struct urb *urb;
-
- while ((urb = usb_get_from_anchor(&mdev->busy_urbs[channel]))) {
- mbo = urb->context;
- usb_kill_urb(urb);
- if (mbo && mbo->complete) {
- mbo->status = status;
- mbo->processed_length = 0;
- mbo->complete(mbo);
- }
- usb_free_urb(urb);
- }
+ return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1);
}
/**
@@ -278,7 +255,7 @@ static int hdm_poison_channel(struct most_interface *iface, int channel)
cancel_work_sync(&mdev->clear_work[channel].ws);
mutex_lock(&mdev->io_mutex);
- free_anchored_buffers(mdev, channel, MBO_E_CLOSE);
+ usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
if (mdev->padding_active[channel])
mdev->padding_active[channel] = false;
@@ -377,33 +354,27 @@ static void hdm_write_completion(struct urb *urb)
unsigned long flags;
spin_lock_irqsave(lock, flags);
- if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
- !mdev->is_channel_healthy[channel]) {
- spin_unlock_irqrestore(lock, flags);
- return;
- }
- if (unlikely(urb->status && urb->status != -ESHUTDOWN)) {
- mbo->processed_length = 0;
+ mbo->processed_length = 0;
+ mbo->status = MBO_E_INVAL;
+ if (likely(mdev->is_channel_healthy[channel])) {
switch (urb->status) {
+ case 0:
+ case -ESHUTDOWN:
+ mbo->processed_length = urb->actual_length;
+ mbo->status = MBO_SUCCESS;
+ break;
case -EPIPE:
dev_warn(dev, "Broken OUT pipe detected\n");
mdev->is_channel_healthy[channel] = false;
- spin_unlock_irqrestore(lock, flags);
mdev->clear_work[channel].pipe = urb->pipe;
schedule_work(&mdev->clear_work[channel].ws);
- return;
+ break;
case -ENODEV:
case -EPROTO:
mbo->status = MBO_E_CLOSE;
break;
- default:
- mbo->status = MBO_E_INVAL;
- break;
}
- } else {
- mbo->status = MBO_SUCCESS;
- mbo->processed_length = urb->actual_length;
}
spin_unlock_irqrestore(lock, flags);
@@ -531,40 +502,35 @@ static void hdm_read_completion(struct urb *urb)
unsigned long flags;
spin_lock_irqsave(lock, flags);
- if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
- !mdev->is_channel_healthy[channel]) {
- spin_unlock_irqrestore(lock, flags);
- return;
- }
- if (unlikely(urb->status && urb->status != -ESHUTDOWN)) {
- mbo->processed_length = 0;
+ mbo->processed_length = 0;
+ mbo->status = MBO_E_INVAL;
+ if (likely(mdev->is_channel_healthy[channel])) {
switch (urb->status) {
+ case 0:
+ case -ESHUTDOWN:
+ mbo->processed_length = urb->actual_length;
+ mbo->status = MBO_SUCCESS;
+ if (mdev->padding_active[channel] &&
+ hdm_remove_padding(mdev, channel, mbo)) {
+ mbo->processed_length = 0;
+ mbo->status = MBO_E_INVAL;
+ }
+ break;
case -EPIPE:
dev_warn(dev, "Broken IN pipe detected\n");
mdev->is_channel_healthy[channel] = false;
- spin_unlock_irqrestore(lock, flags);
mdev->clear_work[channel].pipe = urb->pipe;
schedule_work(&mdev->clear_work[channel].ws);
- return;
+ break;
case -ENODEV:
case -EPROTO:
mbo->status = MBO_E_CLOSE;
break;
case -EOVERFLOW:
dev_warn(dev, "Babble on IN pipe detected\n");
- default:
- mbo->status = MBO_E_INVAL;
break;
}
- } else {
- mbo->processed_length = urb->actual_length;
- mbo->status = MBO_SUCCESS;
- if (mdev->padding_active[channel] &&
- hdm_remove_padding(mdev, channel, mbo)) {
- mbo->processed_length = 0;
- mbo->status = MBO_E_INVAL;
- }
}
spin_unlock_irqrestore(lock, flags);
@@ -668,6 +634,15 @@ _error:
* @iface: interface
* @channel: channel ID
* @conf: structure that holds the configuration information
+ *
+ * The attached network interface controller (NIC) supports a padding mode
+ * to avoid short packets on USB, hence increasing the performance due to a
+ * lower interrupt load. This mode is default for synchronous data and can
+ * be switched on for isochronous data. In case padding is active the
+ * driver needs to know the frame size of the payload in order to calculate
+ * the number of bytes it needs to pad when transmitting or to cut off when
+ * receiving data.
+ *
*/
static int hdm_configure_channel(struct most_interface *iface, int channel,
struct most_channel_config *conf)
@@ -701,6 +676,11 @@ static int hdm_configure_channel(struct most_interface *iface, int channel,
!(conf->data_type == MOST_CH_ISOC &&
conf->packets_per_xact != 0xFF)) {
mdev->padding_active[channel] = false;
+ /*
+ * Since the NIC's padding mode is not going to be
+ * used, we can skip the frame size calculations and
+ * move directly on to exit.
+ */
goto exit;
}
@@ -734,56 +714,12 @@ static int hdm_configure_channel(struct most_interface *iface, int channel,
- conf->buffer_size;
exit:
mdev->conf[channel] = *conf;
- return 0;
-}
+ if (conf->data_type == MOST_CH_ASYNC) {
+ u16 ep = mdev->ep_address[channel];
-/**
- * hdm_update_netinfo - retrieve latest networking information
- * @mdev: device interface
- *
- * This triggers the USB vendor requests to read the hardware address and
- * the current link status of the attached device.
- */
-static int hdm_update_netinfo(struct most_dev *mdev)
-{
- struct usb_device *usb_device = mdev->usb_device;
- struct device *dev = &usb_device->dev;
- u16 hi, mi, lo, link;
-
- if (!is_valid_ether_addr(mdev->hw_addr)) {
- if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi) < 0) {
- dev_err(dev, "Vendor request \"hw_addr_hi\" failed\n");
- return -EFAULT;
- }
-
- if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi) < 0) {
- dev_err(dev, "Vendor request \"hw_addr_mid\" failed\n");
- return -EFAULT;
- }
-
- if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo) < 0) {
- dev_err(dev, "Vendor request \"hw_addr_low\" failed\n");
- return -EFAULT;
- }
-
- mutex_lock(&mdev->io_mutex);
- mdev->hw_addr[0] = hi >> 8;
- mdev->hw_addr[1] = hi;
- mdev->hw_addr[2] = mi >> 8;
- mdev->hw_addr[3] = mi;
- mdev->hw_addr[4] = lo >> 8;
- mdev->hw_addr[5] = lo;
- mutex_unlock(&mdev->io_mutex);
- }
-
- if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link) < 0) {
- dev_err(dev, "Vendor request \"link status\" failed\n");
- return -EFAULT;
+ if (start_sync_ep(mdev->usb_device, ep) < 0)
+ dev_warn(dev, "sync for ep%02x failed", ep);
}
-
- mutex_lock(&mdev->io_mutex);
- mdev->link_stat = link;
- mutex_unlock(&mdev->io_mutex);
return 0;
}
@@ -807,7 +743,7 @@ static void hdm_request_netinfo(struct most_interface *iface, int channel)
}
/**
- * link_stat_timer_handler - add work to link_stat work queue
+ * link_stat_timer_handler - schedule work obtaining mac address and link status
* @data: pointer to USB device instance
*
* The handler runs in interrupt context. That's why we need to defer the
@@ -823,33 +759,47 @@ static void link_stat_timer_handler(unsigned long data)
}
/**
- * wq_netinfo - work queue function
+ * wq_netinfo - work queue function to deliver latest networking information
* @wq_obj: object that holds data for our deferred work to do
*
* This retrieves the network interface status of the USB INIC
- * and compares it with the current status. If the status has
- * changed, it updates the status of the core.
*/
static void wq_netinfo(struct work_struct *wq_obj)
{
struct most_dev *mdev = to_mdev_from_work(wq_obj);
- int i, prev_link_stat = mdev->link_stat;
- u8 prev_hw_addr[6];
+ struct usb_device *usb_device = mdev->usb_device;
+ struct device *dev = &usb_device->dev;
+ u16 hi, mi, lo, link;
+ u8 hw_addr[6];
- for (i = 0; i < 6; i++)
- prev_hw_addr[i] = mdev->hw_addr[i];
+ if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi) < 0) {
+ dev_err(dev, "Vendor request 'hw_addr_hi' failed\n");
+ return;
+ }
+
+ if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi) < 0) {
+ dev_err(dev, "Vendor request 'hw_addr_mid' failed\n");
+ return;
+ }
- if (hdm_update_netinfo(mdev) < 0)
+ if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo) < 0) {
+ dev_err(dev, "Vendor request 'hw_addr_low' failed\n");
+ return;
+ }
+
+ if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link) < 0) {
+ dev_err(dev, "Vendor request 'link status' failed\n");
return;
- if (prev_link_stat != mdev->link_stat ||
- prev_hw_addr[0] != mdev->hw_addr[0] ||
- prev_hw_addr[1] != mdev->hw_addr[1] ||
- prev_hw_addr[2] != mdev->hw_addr[2] ||
- prev_hw_addr[3] != mdev->hw_addr[3] ||
- prev_hw_addr[4] != mdev->hw_addr[4] ||
- prev_hw_addr[5] != mdev->hw_addr[5])
- most_deliver_netinfo(&mdev->iface, mdev->link_stat,
- &mdev->hw_addr[0]);
+ }
+
+ hw_addr[0] = hi >> 8;
+ hw_addr[1] = hi;
+ hw_addr[2] = mi >> 8;
+ hw_addr[3] = mi;
+ hw_addr[4] = lo >> 8;
+ hw_addr[5] = lo;
+
+ most_deliver_netinfo(&mdev->iface, link, hw_addr);
}
/**
@@ -867,7 +817,7 @@ static void wq_clear_halt(struct work_struct *wq_obj)
mutex_lock(&mdev->io_mutex);
most_stop_enqueue(&mdev->iface, channel);
- free_anchored_buffers(mdev, channel, MBO_E_INVAL);
+ usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
if (usb_clear_halt(mdev->usb_device, pipe))
dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
@@ -1053,6 +1003,7 @@ static ssize_t store_value(struct most_dci_obj *dci_obj,
u16 val;
u16 reg_addr;
const char *name = attr->attr.name;
+ struct usb_device *usb_dev = dci_obj->usb_device;
int err = kstrtou16(buf, 16, &val);
if (err)
@@ -1063,18 +1014,15 @@ static ssize_t store_value(struct most_dci_obj *dci_obj,
return count;
}
- if (!strcmp(name, "arb_value")) {
- reg_addr = dci_obj->reg_addr;
- } else if (!strcmp(name, "sync_ep")) {
- u16 ep = val;
-
- reg_addr = DRCI_REG_BASE + DRCI_COMMAND + ep * 16;
- val = 1;
- } else if (get_static_reg_addr(ro_regs, name, &reg_addr)) {
+ if (!strcmp(name, "arb_value"))
+ err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val);
+ else if (!strcmp(name, "sync_ep"))
+ err = start_sync_ep(usb_dev, val);
+ else if (!get_static_reg_addr(ro_regs, name, &reg_addr))
+ err = drci_wr_reg(usb_dev, reg_addr, val);
+ else
return -EFAULT;
- }
- err = drci_wr_reg(dci_obj->usb_device, reg_addr, val);
if (err < 0)
return err;
@@ -1186,7 +1134,6 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
struct most_channel_capability *tmp_cap;
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
- int err;
if (!mdev)
goto exit_ENOMEM;
@@ -1262,13 +1209,6 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
tmp_cap++;
init_usb_anchor(&mdev->busy_urbs[i]);
spin_lock_init(&mdev->channel_lock[i]);
- err = drci_wr_reg(usb_dev,
- DRCI_REG_BASE + DRCI_COMMAND +
- ep_desc->bEndpointAddress * 16,
- 1);
- if (err < 0)
- dev_warn(dev, "DCI Sync for EP %02x failed",
- ep_desc->bEndpointAddress);
}
dev_notice(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n",
le16_to_cpu(usb_dev->descriptor.idVendor),