summaryrefslogtreecommitdiffstats
path: root/drivers/staging/most/hdm-usb/hdm_usb.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-10-05 14:50:51 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-05 14:50:51 -0700
commit41844e36206be90cd4d962ea49b0abc3612a99d0 (patch)
treece0b3a3403bc6abdb28f52779d0d7b57a51a5c86 /drivers/staging/most/hdm-usb/hdm_usb.c
parent5691f0e9a3e7855832d5fd094801bf600347c2d0 (diff)
parentfc1e2c8ea85e109acf09e74789e9b852f6eed251 (diff)
downloadop-kernel-dev-41844e36206be90cd4d962ea49b0abc3612a99d0.zip
op-kernel-dev-41844e36206be90cd4d962ea49b0abc3612a99d0.tar.gz
Merge tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging and IIO updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.9-rc1. There are a lot of patches in here, the majority due to the drivers/staging/greybus/ subsystem being merged in with full development history that went back a few years, in order to preserve the work that those developers did over time. Lots and lots of tiny cleanups happened in the tree as well, due to the Outreachy application process and lots of other developers showing up for the first time to clean code up. Along with those changes, we deleted a wireless driver, and added a raspberrypi driver (currently marked broken), and lots of new iio drivers. Overall the tree still shrunk with more lines removed than added, about 10 thousand lines removed in total. Full details are in the very long shortlog below. All of this has been in the linux-next tree with no issues. There will be some merge problems with other subsystem trees, but those are all minor problems and shouldn't be hard to work out when they happen (MAINTAINERS and some lustre build problems with the IB tree)" And furter from me asking for clarification about greybus: "Right now there is a phone from Motorola shipping with this code (a slightly older version, but the same tree), so even though Ara is not alive in the same form, the functionality is happening. We are working with the developers of that phone to merge the newer stuff in with their fork so they can use the upstream version in future versions of their phone product line. Toshiba has at least one chip shipping in their catalog that needs/uses this protocol over a Unipro link, and rumor has it that there might be more in the future. There are also other users of the greybus protocols, there is a talk next week at ELC that shows how it is being used across a network connection to control a device, and previous ELC talks have showed the protocol stack being used over USB to drive embedded Linux boards. I've also talked to some people who are starting to work to add a host controller driver to control arduinos as the greybus PHY protocols are very useful to control a serial/i2c/spio/whatever device across a random physical link, as it is a way to have a self-describing device be attached to a host without needing manual configuration. So yes, people are using it, and there is still the chance that it will show up in a phone/laptop/tablet/whatever from Google in the future as well, the tech isn't dead, even if the original large phone project happens to be" * tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (3703 commits) Staging: fbtft: Fix bug in fbtft-core staging: rtl8188eu: fix double unlock error in rtw_resume_process() staging:r8188eu: remove GEN_MLME_EXT_HANDLER macro staging:r8188eu: remove GEN_DRV_CMD_HANDLER macro staging:r8188eu: remove GEN_EVT_CODE macro staging:r8188eu: remove GEN_CMD_CODE macro staging:r8188eu: remove pkt_newalloc member of the recv_buf structure staging:r8188eu: remove rtw_handle_dualmac declaration staging:r8188eu: remove (RGTRY|BSSID)_(OFT|SZ) macros staging:r8188eu: change rtl8188e_process_phy_info function argument type Staging: fsl-mc: Remove blank lines Staging: fsl-mc: Fix unaligned * in block comments Staging: comedi: Align the * in block comments Staging : ks7010 : Fix block comments warninig Staging: vt6655: Remove explicit NULL comparison using Coccinelle staging: rtl8188eu: core: rtw_xmit: Use macros instead of constants staging: rtl8188eu: core: rtw_xmit: Move constant of the right side staging: dgnc: Fix lines longer than 80 characters Staging: dgnc: constify attribute_group structures Staging: most: hdm-dim2: constify attribute_group structures ...
Diffstat (limited to 'drivers/staging/most/hdm-usb/hdm_usb.c')
-rw-r--r--drivers/staging/most/hdm-usb/hdm_usb.c502
1 files changed, 230 insertions, 272 deletions
diff --git a/drivers/staging/most/hdm-usb/hdm_usb.c b/drivers/staging/most/hdm-usb/hdm_usb.c
index aeae071..26c9adb 100644
--- a/drivers/staging/most/hdm-usb/hdm_usb.c
+++ b/drivers/staging/most/hdm-usb/hdm_usb.c
@@ -43,8 +43,9 @@
#define USB_VENDOR_ID_SMSC 0x0424 /* VID: SMSC */
#define USB_DEV_ID_BRDG 0xC001 /* PID: USB Bridge */
-#define USB_DEV_ID_INIC 0xCF18 /* PID: USB INIC */
-#define HW_RESYNC 0x0000
+#define USB_DEV_ID_OS81118 0xCF18 /* PID: USB OS81118 */
+#define USB_DEV_ID_OS81119 0xCF19 /* PID: USB OS81119 */
+#define USB_DEV_ID_OS81210 0xCF30 /* PID: USB OS81210 */
/* DRCI Addresses */
#define DRCI_REG_NI_STATE 0x0100
#define DRCI_REG_PACKET_BW 0x0101
@@ -64,33 +65,30 @@
#define DRCI_WRITE_REQ 0xA1
/**
- * struct buf_anchor - used to create a list of pending URBs
- * @urb: pointer to USB request block
- * @clear_work_obj:
- * @list: linked list
- * @urb_completion:
- */
-struct buf_anchor {
- struct urb *urb;
- struct work_struct clear_work_obj;
- struct list_head list;
- struct completion urb_compl;
-};
-
-#define to_buf_anchor(w) container_of(w, struct buf_anchor, clear_work_obj)
-
-/**
* struct most_dci_obj - Direct Communication Interface
* @kobj:position in sysfs
* @usb_device: pointer to the usb device
+ * @reg_addr: register address for arbitrary DCI access
*/
struct most_dci_obj {
struct kobject kobj;
struct usb_device *usb_device;
+ u16 reg_addr;
};
#define to_dci_obj(p) container_of(p, struct most_dci_obj, kobj)
+struct most_dev;
+
+struct clear_hold_work {
+ struct work_struct ws;
+ struct most_dev *mdev;
+ unsigned int channel;
+ int pipe;
+};
+
+#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
+
/**
* struct most_dev - holds all usb interface specific stuff
* @parent: parent object in sysfs
@@ -104,10 +102,10 @@ struct most_dci_obj {
* @link_stat: link status of hardware
* @description: device description
* @suffix: suffix for channel name
- * @anchor_list_lock: locks list access
+ * @channel_lock: synchronize channel access
* @padding_active: indicates channel uses padding
* @is_channel_healthy: health status table of each channel
- * @anchor_list: list of anchored items
+ * @busy_urbs: list of anchored items
* @io_mutex: synchronize I/O with disconnect
* @link_stat_timer: timer for link status reports
* @poll_work_obj: work for polling link status
@@ -124,10 +122,11 @@ struct most_dev {
u16 link_stat;
char description[MAX_STRING_LEN];
char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
- spinlock_t anchor_list_lock[MAX_NUM_ENDPOINTS];
+ spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
bool padding_active[MAX_NUM_ENDPOINTS];
bool is_channel_healthy[MAX_NUM_ENDPOINTS];
- struct list_head *anchor_list;
+ struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
+ struct usb_anchor *busy_urbs;
struct mutex io_mutex;
struct timer_list link_stat_timer;
struct work_struct poll_work_obj;
@@ -191,40 +190,24 @@ static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
* 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)
+static void free_anchored_buffers(struct most_dev *mdev, unsigned int channel,
+ enum mbo_status_flags status)
{
struct mbo *mbo;
- struct buf_anchor *anchor, *tmp;
- unsigned long flags;
+ struct urb *urb;
- spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_for_each_entry_safe(anchor, tmp, &mdev->anchor_list[channel],
- list) {
- struct urb *urb = anchor->urb;
-
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
- if (likely(urb)) {
- mbo = urb->context;
- if (!irqs_disabled()) {
- usb_kill_urb(urb);
- } else {
- usb_unlink_urb(urb);
- wait_for_completion(&anchor->urb_compl);
- }
- if ((mbo) && (mbo->complete)) {
- mbo->status = MBO_E_CLOSE;
- mbo->processed_length = 0;
- mbo->complete(mbo);
- }
- usb_free_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);
}
- spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_del(&anchor->list);
- cancel_work_sync(&anchor->clear_work_obj);
- kfree(anchor);
+ usb_free_urb(urb);
}
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
}
/**
@@ -241,7 +224,7 @@ static unsigned int get_stream_frame_size(struct most_channel_config *cfg)
return frame_size;
}
switch (cfg->data_type) {
- case MOST_CH_ISOC_AVP:
+ case MOST_CH_ISOC:
frame_size = AV_PACKETS_PER_XACT * sub_size;
break;
case MOST_CH_SYNC:
@@ -274,22 +257,28 @@ static unsigned int get_stream_frame_size(struct most_channel_config *cfg)
*/
static int hdm_poison_channel(struct most_interface *iface, int channel)
{
- struct most_dev *mdev;
+ struct most_dev *mdev = to_mdev(iface);
+ unsigned long flags;
+ spinlock_t *lock; /* temp. lock */
- mdev = to_mdev(iface);
if (unlikely(!iface)) {
dev_warn(&mdev->usb_device->dev, "Poison: Bad interface.\n");
return -EIO;
}
- if (unlikely((channel < 0) || (channel >= iface->num_channels))) {
+ if (unlikely(channel < 0 || channel >= iface->num_channels)) {
dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n");
return -ECHRNG;
}
+ lock = mdev->channel_lock + channel;
+ spin_lock_irqsave(lock, flags);
mdev->is_channel_healthy[channel] = false;
+ spin_unlock_irqrestore(lock, flags);
+
+ cancel_work_sync(&mdev->clear_work[channel].ws);
mutex_lock(&mdev->io_mutex);
- free_anchored_buffers(mdev, channel);
+ free_anchored_buffers(mdev, channel, MBO_E_CLOSE);
if (mdev->padding_active[channel])
mdev->padding_active[channel] = false;
@@ -313,10 +302,10 @@ static int hdm_poison_channel(struct most_interface *iface, int channel)
static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo)
{
struct most_channel_config *conf = &mdev->conf[channel];
- unsigned int j, num_frames, frame_size;
+ unsigned int frame_size = get_stream_frame_size(conf);
+ unsigned int j, num_frames;
u16 rd_addr, wr_addr;
- frame_size = get_stream_frame_size(conf);
if (!frame_size)
return -EIO;
num_frames = mbo->buffer_length / frame_size;
@@ -350,10 +339,10 @@ static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo)
static int hdm_remove_padding(struct most_dev *mdev, int channel,
struct mbo *mbo)
{
- unsigned int j, num_frames, frame_size;
struct most_channel_config *const conf = &mdev->conf[channel];
+ unsigned int frame_size = get_stream_frame_size(conf);
+ unsigned int j, num_frames;
- frame_size = get_stream_frame_size(conf);
if (!frame_size)
return -EIO;
num_frames = mbo->processed_length / USB_MTU;
@@ -380,37 +369,29 @@ static int hdm_remove_padding(struct most_dev *mdev, int channel,
*/
static void hdm_write_completion(struct urb *urb)
{
- struct mbo *mbo;
- struct buf_anchor *anchor;
- struct most_dev *mdev;
- struct device *dev;
- unsigned int channel;
+ struct mbo *mbo = urb->context;
+ struct most_dev *mdev = to_mdev(mbo->ifp);
+ unsigned int channel = mbo->hdm_channel_id;
+ struct device *dev = &mdev->usb_device->dev;
+ spinlock_t *lock = mdev->channel_lock + channel;
unsigned long flags;
- mbo = urb->context;
- anchor = mbo->priv;
- mdev = to_mdev(mbo->ifp);
- channel = mbo->hdm_channel_id;
- dev = &mdev->usb_device->dev;
-
- if ((urb->status == -ENOENT) || (urb->status == -ECONNRESET) ||
- (!mdev->is_channel_healthy[channel])) {
- complete(&anchor->urb_compl);
+ 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 == -ENOENT ||
- urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN))) {
+ if (unlikely(urb->status && urb->status != -ESHUTDOWN)) {
mbo->processed_length = 0;
switch (urb->status) {
case -EPIPE:
dev_warn(dev, "Broken OUT pipe detected\n");
- most_stop_enqueue(&mdev->iface, channel);
- mbo->status = MBO_E_INVAL;
- usb_unlink_urb(urb);
- INIT_WORK(&anchor->clear_work_obj, wq_clear_halt);
- schedule_work(&anchor->clear_work_obj);
+ 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;
case -ENODEV:
case -EPROTO:
@@ -425,10 +406,7 @@ static void hdm_write_completion(struct urb *urb)
mbo->processed_length = urb->actual_length;
}
- spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_del(&anchor->list);
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
- kfree(anchor);
+ spin_unlock_irqrestore(lock, flags);
if (likely(mbo->complete))
mbo->complete(mbo);
@@ -545,36 +523,29 @@ static void hdm_write_completion(struct urb *urb)
*/
static void hdm_read_completion(struct urb *urb)
{
- struct mbo *mbo;
- struct buf_anchor *anchor;
- struct most_dev *mdev;
- struct device *dev;
+ struct mbo *mbo = urb->context;
+ struct most_dev *mdev = to_mdev(mbo->ifp);
+ unsigned int channel = mbo->hdm_channel_id;
+ struct device *dev = &mdev->usb_device->dev;
+ spinlock_t *lock = mdev->channel_lock + channel;
unsigned long flags;
- unsigned int channel;
-
- mbo = urb->context;
- anchor = mbo->priv;
- mdev = to_mdev(mbo->ifp);
- channel = mbo->hdm_channel_id;
- dev = &mdev->usb_device->dev;
- if ((urb->status == -ENOENT) || (urb->status == -ECONNRESET) ||
- (!mdev->is_channel_healthy[channel])) {
- complete(&anchor->urb_compl);
+ 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 == -ENOENT ||
- urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN))) {
+ if (unlikely(urb->status && urb->status != -ESHUTDOWN)) {
mbo->processed_length = 0;
switch (urb->status) {
case -EPIPE:
dev_warn(dev, "Broken IN pipe detected\n");
- mbo->status = MBO_E_INVAL;
- usb_unlink_urb(urb);
- INIT_WORK(&anchor->clear_work_obj, wq_clear_halt);
- schedule_work(&anchor->clear_work_obj);
+ 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;
case -ENODEV:
case -EPROTO:
@@ -588,21 +559,15 @@ static void hdm_read_completion(struct urb *urb)
}
} else {
mbo->processed_length = urb->actual_length;
- if (!mdev->padding_active[channel]) {
- mbo->status = MBO_SUCCESS;
- } else {
- if (hdm_remove_padding(mdev, channel, mbo)) {
- mbo->processed_length = 0;
- mbo->status = MBO_E_INVAL;
- } else {
- mbo->status = MBO_SUCCESS;
- }
+ 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_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_del(&anchor->list);
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
- kfree(anchor);
+
+ spin_unlock_irqrestore(lock, flags);
if (likely(mbo->complete))
mbo->complete(mbo);
@@ -628,18 +593,16 @@ static int hdm_enqueue(struct most_interface *iface, int channel,
struct mbo *mbo)
{
struct most_dev *mdev;
- struct buf_anchor *anchor;
struct most_channel_config *conf;
struct device *dev;
int retval = 0;
struct urb *urb;
- unsigned long flags;
unsigned long length;
void *virt_address;
if (unlikely(!iface || !mbo))
return -EIO;
- if (unlikely(iface->num_channels <= channel) || (channel < 0))
+ if (unlikely(iface->num_channels <= channel || channel < 0))
return -ECHRNG;
mdev = to_mdev(iface);
@@ -650,32 +613,15 @@ static int hdm_enqueue(struct most_interface *iface, int channel,
return -ENODEV;
urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_ATOMIC);
- if (!urb) {
- dev_err(dev, "Failed to allocate URB\n");
+ if (!urb)
return -ENOMEM;
- }
- anchor = kzalloc(sizeof(*anchor), GFP_ATOMIC);
- if (!anchor) {
- retval = -ENOMEM;
+ if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] &&
+ hdm_add_padding(mdev, channel, mbo)) {
+ retval = -EIO;
goto _error;
}
- anchor->urb = urb;
- init_completion(&anchor->urb_compl);
- mbo->priv = anchor;
-
- spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_add_tail(&anchor->list, &mdev->anchor_list[channel]);
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
-
- if ((mdev->padding_active[channel]) &&
- (conf->direction & MOST_CH_TX))
- if (hdm_add_padding(mdev, channel, mbo)) {
- retval = -EIO;
- goto _error_1;
- }
-
urb->transfer_dma = mbo->bus_address;
virt_address = mbo->virt_address;
length = mbo->buffer_length;
@@ -688,7 +634,7 @@ static int hdm_enqueue(struct most_interface *iface, int channel,
length,
hdm_write_completion,
mbo);
- if (conf->data_type != MOST_CH_ISOC_AVP)
+ if (conf->data_type != MOST_CH_ISOC)
urb->transfer_flags |= URB_ZERO_PACKET;
} else {
usb_fill_bulk_urb(urb, mdev->usb_device,
@@ -701,6 +647,8 @@ static int hdm_enqueue(struct most_interface *iface, int channel,
}
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &mdev->busy_urbs[channel]);
+
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
dev_err(dev, "URB submit failed with error %d.\n", retval);
@@ -709,10 +657,7 @@ static int hdm_enqueue(struct most_interface *iface, int channel,
return 0;
_error_1:
- spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_del(&anchor->list);
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
- kfree(anchor);
+ usb_unanchor_urb(urb);
_error:
usb_free_urb(urb);
return retval;
@@ -731,29 +676,30 @@ static int hdm_configure_channel(struct most_interface *iface, int channel,
unsigned int frame_size;
unsigned int temp_size;
unsigned int tail_space;
- struct most_dev *mdev;
- struct device *dev;
+ struct most_dev *mdev = to_mdev(iface);
+ struct device *dev = &mdev->usb_device->dev;
- mdev = to_mdev(iface);
mdev->is_channel_healthy[channel] = true;
- dev = &mdev->usb_device->dev;
+ mdev->clear_work[channel].channel = channel;
+ mdev->clear_work[channel].mdev = mdev;
+ INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt);
if (unlikely(!iface || !conf)) {
dev_err(dev, "Bad interface or config pointer.\n");
return -EINVAL;
}
- if (unlikely((channel < 0) || (channel >= iface->num_channels))) {
+ if (unlikely(channel < 0 || channel >= iface->num_channels)) {
dev_err(dev, "Channel ID out of range.\n");
return -EINVAL;
}
- if ((!conf->num_buffers) || (!conf->buffer_size)) {
+ if (!conf->num_buffers || !conf->buffer_size) {
dev_err(dev, "Misconfig: buffer size or #buffers zero.\n");
return -EINVAL;
}
- if (!(conf->data_type == MOST_CH_SYNC) &&
- !((conf->data_type == MOST_CH_ISOC_AVP) &&
- (conf->packets_per_xact != 0xFF))) {
+ if (conf->data_type != MOST_CH_SYNC &&
+ !(conf->data_type == MOST_CH_ISOC &&
+ conf->packets_per_xact != 0xFF)) {
mdev->padding_active[channel] = false;
goto exit;
}
@@ -762,7 +708,7 @@ static int hdm_configure_channel(struct most_interface *iface, int channel,
temp_size = conf->buffer_size;
frame_size = get_stream_frame_size(conf);
- if ((frame_size == 0) || (frame_size > USB_MTU)) {
+ if (frame_size == 0 || frame_size > USB_MTU) {
dev_warn(dev, "Misconfig: frame size wrong\n");
return -EINVAL;
}
@@ -807,17 +753,17 @@ static int hdm_update_netinfo(struct most_dev *mdev)
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 -1;
+ 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 -1;
+ 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 -1;
+ return -EFAULT;
}
mutex_lock(&mdev->io_mutex);
@@ -832,7 +778,7 @@ static int hdm_update_netinfo(struct most_dev *mdev)
if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link) < 0) {
dev_err(dev, "Vendor request \"link status\" failed\n");
- return -1;
+ return -EFAULT;
}
mutex_lock(&mdev->io_mutex);
@@ -886,25 +832,22 @@ static void link_stat_timer_handler(unsigned long data)
*/
static void wq_netinfo(struct work_struct *wq_obj)
{
- struct most_dev *mdev;
- int i, prev_link_stat;
+ struct most_dev *mdev = to_mdev_from_work(wq_obj);
+ int i, prev_link_stat = mdev->link_stat;
u8 prev_hw_addr[6];
- mdev = to_mdev_from_work(wq_obj);
- prev_link_stat = mdev->link_stat;
-
for (i = 0; i < 6; i++)
prev_hw_addr[i] = mdev->hw_addr[i];
if (hdm_update_netinfo(mdev) < 0)
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]))
+ 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]);
}
@@ -917,33 +860,20 @@ static void wq_netinfo(struct work_struct *wq_obj)
*/
static void wq_clear_halt(struct work_struct *wq_obj)
{
- struct buf_anchor *anchor;
- struct most_dev *mdev;
- struct mbo *mbo;
- struct urb *urb;
- unsigned int channel;
- unsigned long flags;
+ struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj);
+ struct most_dev *mdev = clear_work->mdev;
+ unsigned int channel = clear_work->channel;
+ int pipe = clear_work->pipe;
- anchor = to_buf_anchor(wq_obj);
- urb = anchor->urb;
- mbo = urb->context;
- mdev = to_mdev(mbo->ifp);
- channel = mbo->hdm_channel_id;
-
- if (usb_clear_halt(urb->dev, urb->pipe))
+ mutex_lock(&mdev->io_mutex);
+ most_stop_enqueue(&mdev->iface, channel);
+ free_anchored_buffers(mdev, channel, MBO_E_INVAL);
+ if (usb_clear_halt(mdev->usb_device, pipe))
dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
- usb_free_urb(urb);
- spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
- list_del(&anchor->list);
- spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
-
- if (likely(mbo->complete))
- mbo->complete(mbo);
- if (mdev->conf[channel].direction & MOST_CH_TX)
- most_resume_enqueue(&mdev->iface, channel);
-
- kfree(anchor);
+ mdev->is_channel_healthy[channel] = true;
+ most_resume_enqueue(&mdev->iface, channel);
+ mutex_unlock(&mdev->io_mutex);
}
/**
@@ -958,7 +888,9 @@ static const struct file_operations hdm_usb_fops = {
*/
static struct usb_device_id usbid[] = {
{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), },
- { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_INIC), },
+ { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), },
+ { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), },
+ { USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), },
{ } /* Terminating entry */
};
@@ -970,6 +902,10 @@ static struct usb_device_id usbid[] = {
struct most_dci_attribute most_dci_attr_##_name = \
__ATTR(_name, S_IRUGO | S_IWUSR, show_value, store_value)
+#define MOST_DCI_WO_ATTR(_name) \
+ struct most_dci_attribute most_dci_attr_##_name = \
+ __ATTR(_name, S_IWUSR, NULL, store_value)
+
/**
* struct most_dci_attribute - to access the attributes of a dci object
* @attr: attributes of a dci object
@@ -1046,45 +982,68 @@ static void most_dci_release(struct kobject *kobj)
kfree(dci_obj);
}
+struct regs {
+ const char *name;
+ u16 reg;
+};
+
+static const struct regs ro_regs[] = {
+ { "ni_state", DRCI_REG_NI_STATE },
+ { "packet_bandwidth", DRCI_REG_PACKET_BW },
+ { "node_address", DRCI_REG_NODE_ADDR },
+ { "node_position", DRCI_REG_NODE_POS },
+};
+
+static const struct regs rw_regs[] = {
+ { "mep_filter", DRCI_REG_MEP_FILTER },
+ { "mep_hash0", DRCI_REG_HASH_TBL0 },
+ { "mep_hash1", DRCI_REG_HASH_TBL1 },
+ { "mep_hash2", DRCI_REG_HASH_TBL2 },
+ { "mep_hash3", DRCI_REG_HASH_TBL3 },
+ { "mep_eui48_hi", DRCI_REG_HW_ADDR_HI },
+ { "mep_eui48_mi", DRCI_REG_HW_ADDR_MI },
+ { "mep_eui48_lo", DRCI_REG_HW_ADDR_LO },
+};
+
+static int get_stat_reg_addr(const struct regs *regs, int size,
+ const char *name, u16 *reg_addr)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (!strcmp(name, regs[i].name)) {
+ *reg_addr = regs[i].reg;
+ return 0;
+ }
+ }
+ return -EFAULT;
+}
+
+#define get_static_reg_addr(regs, name, reg_addr) \
+ get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr)
+
static ssize_t show_value(struct most_dci_obj *dci_obj,
struct most_dci_attribute *attr, char *buf)
{
- u16 tmp_val;
+ const char *name = attr->attr.name;
+ u16 val;
u16 reg_addr;
int err;
- if (!strcmp(attr->attr.name, "ni_state"))
- reg_addr = DRCI_REG_NI_STATE;
- else if (!strcmp(attr->attr.name, "packet_bandwidth"))
- reg_addr = DRCI_REG_PACKET_BW;
- else if (!strcmp(attr->attr.name, "node_address"))
- reg_addr = DRCI_REG_NODE_ADDR;
- else if (!strcmp(attr->attr.name, "node_position"))
- reg_addr = DRCI_REG_NODE_POS;
- else if (!strcmp(attr->attr.name, "mep_filter"))
- reg_addr = DRCI_REG_MEP_FILTER;
- else if (!strcmp(attr->attr.name, "mep_hash0"))
- reg_addr = DRCI_REG_HASH_TBL0;
- else if (!strcmp(attr->attr.name, "mep_hash1"))
- reg_addr = DRCI_REG_HASH_TBL1;
- else if (!strcmp(attr->attr.name, "mep_hash2"))
- reg_addr = DRCI_REG_HASH_TBL2;
- else if (!strcmp(attr->attr.name, "mep_hash3"))
- reg_addr = DRCI_REG_HASH_TBL3;
- else if (!strcmp(attr->attr.name, "mep_eui48_hi"))
- reg_addr = DRCI_REG_HW_ADDR_HI;
- else if (!strcmp(attr->attr.name, "mep_eui48_mi"))
- reg_addr = DRCI_REG_HW_ADDR_MI;
- else if (!strcmp(attr->attr.name, "mep_eui48_lo"))
- reg_addr = DRCI_REG_HW_ADDR_LO;
- else
- return -EIO;
+ if (!strcmp(name, "arb_address"))
+ return snprintf(buf, PAGE_SIZE, "%04x\n", dci_obj->reg_addr);
+
+ if (!strcmp(name, "arb_value"))
+ reg_addr = dci_obj->reg_addr;
+ else if (get_static_reg_addr(ro_regs, name, &reg_addr) &&
+ get_static_reg_addr(rw_regs, name, &reg_addr))
+ return -EFAULT;
- err = drci_rd_reg(dci_obj->usb_device, reg_addr, &tmp_val);
+ err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val);
if (err < 0)
return err;
- return snprintf(buf, PAGE_SIZE, "%04x\n", tmp_val);
+ return snprintf(buf, PAGE_SIZE, "%04x\n", val);
}
static ssize_t store_value(struct most_dci_obj *dci_obj,
@@ -1093,31 +1052,28 @@ static ssize_t store_value(struct most_dci_obj *dci_obj,
{
u16 val;
u16 reg_addr;
- int err;
-
- if (!strcmp(attr->attr.name, "mep_filter"))
- reg_addr = DRCI_REG_MEP_FILTER;
- else if (!strcmp(attr->attr.name, "mep_hash0"))
- reg_addr = DRCI_REG_HASH_TBL0;
- else if (!strcmp(attr->attr.name, "mep_hash1"))
- reg_addr = DRCI_REG_HASH_TBL1;
- else if (!strcmp(attr->attr.name, "mep_hash2"))
- reg_addr = DRCI_REG_HASH_TBL2;
- else if (!strcmp(attr->attr.name, "mep_hash3"))
- reg_addr = DRCI_REG_HASH_TBL3;
- else if (!strcmp(attr->attr.name, "mep_eui48_hi"))
- reg_addr = DRCI_REG_HW_ADDR_HI;
- else if (!strcmp(attr->attr.name, "mep_eui48_mi"))
- reg_addr = DRCI_REG_HW_ADDR_MI;
- else if (!strcmp(attr->attr.name, "mep_eui48_lo"))
- reg_addr = DRCI_REG_HW_ADDR_LO;
- else
- return -EIO;
+ const char *name = attr->attr.name;
+ int err = kstrtou16(buf, 16, &val);
- err = kstrtou16(buf, 16, &val);
if (err)
return err;
+ if (!strcmp(name, "arb_address")) {
+ dci_obj->reg_addr = val;
+ 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)) {
+ return -EFAULT;
+ }
+
err = drci_wr_reg(dci_obj->usb_device, reg_addr, val);
if (err < 0)
return err;
@@ -1129,6 +1085,7 @@ static MOST_DCI_RO_ATTR(ni_state);
static MOST_DCI_RO_ATTR(packet_bandwidth);
static MOST_DCI_RO_ATTR(node_address);
static MOST_DCI_RO_ATTR(node_position);
+static MOST_DCI_WO_ATTR(sync_ep);
static MOST_DCI_ATTR(mep_filter);
static MOST_DCI_ATTR(mep_hash0);
static MOST_DCI_ATTR(mep_hash1);
@@ -1137,6 +1094,8 @@ static MOST_DCI_ATTR(mep_hash3);
static MOST_DCI_ATTR(mep_eui48_hi);
static MOST_DCI_ATTR(mep_eui48_mi);
static MOST_DCI_ATTR(mep_eui48_lo);
+static MOST_DCI_ATTR(arb_address);
+static MOST_DCI_ATTR(arb_value);
/**
* most_dci_def_attrs - array of default attribute files of the dci object
@@ -1146,6 +1105,7 @@ static struct attribute *most_dci_def_attrs[] = {
&most_dci_attr_packet_bandwidth.attr,
&most_dci_attr_node_address.attr,
&most_dci_attr_node_position.attr,
+ &most_dci_attr_sync_ep.attr,
&most_dci_attr_mep_filter.attr,
&most_dci_attr_mep_hash0.attr,
&most_dci_attr_mep_hash1.attr,
@@ -1154,6 +1114,8 @@ static struct attribute *most_dci_def_attrs[] = {
&most_dci_attr_mep_eui48_hi.attr,
&most_dci_attr_mep_eui48_mi.attr,
&most_dci_attr_mep_eui48_lo.attr,
+ &most_dci_attr_arb_address.attr,
+ &most_dci_attr_arb_value.attr,
NULL,
};
@@ -1176,10 +1138,9 @@ static struct kobj_type most_dci_ktype = {
static struct
most_dci_obj *create_most_dci_obj(struct kobject *parent)
{
- struct most_dci_obj *most_dci;
+ struct most_dci_obj *most_dci = kzalloc(sizeof(*most_dci), GFP_KERNEL);
int retval;
- most_dci = kzalloc(sizeof(*most_dci), GFP_KERNEL);
if (!most_dci)
return NULL;
@@ -1216,21 +1177,17 @@ static void destroy_most_dci_obj(struct most_dci_obj *p)
static int
hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
+ struct usb_host_interface *usb_iface_desc = interface->cur_altsetting;
+ struct usb_device *usb_dev = interface_to_usbdev(interface);
+ struct device *dev = &usb_dev->dev;
+ struct most_dev *mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
unsigned int i;
unsigned int num_endpoints;
struct most_channel_capability *tmp_cap;
- struct most_dev *mdev;
- struct usb_device *usb_dev;
- struct device *dev;
- struct usb_host_interface *usb_iface_desc;
struct usb_endpoint_descriptor *ep_desc;
int ret = 0;
int err;
- usb_iface_desc = interface->cur_altsetting;
- usb_dev = interface_to_usbdev(interface);
- dev = &usb_dev->dev;
- mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
if (!mdev)
goto exit_ENOMEM;
@@ -1276,9 +1233,9 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
if (!mdev->ep_address)
goto exit_free2;
- mdev->anchor_list =
- kcalloc(num_endpoints, sizeof(*mdev->anchor_list), GFP_KERNEL);
- if (!mdev->anchor_list)
+ mdev->busy_urbs =
+ kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL);
+ if (!mdev->busy_urbs)
goto exit_free3;
tmp_cap = mdev->cap;
@@ -1297,21 +1254,21 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE;
tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE;
tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
- MOST_CH_ISOC_AVP | MOST_CH_SYNC;
+ MOST_CH_ISOC | MOST_CH_SYNC;
if (usb_endpoint_dir_in(ep_desc))
tmp_cap->direction = MOST_CH_RX;
else
tmp_cap->direction = MOST_CH_TX;
tmp_cap++;
- INIT_LIST_HEAD(&mdev->anchor_list[i]);
- spin_lock_init(&mdev->anchor_list_lock[i]);
+ 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)
- pr_warn("DCI Sync for EP %02x failed",
- ep_desc->bEndpointAddress);
+ 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),
@@ -1332,7 +1289,9 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
}
mutex_lock(&mdev->io_mutex);
- if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_INIC) {
+ if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 ||
+ le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 ||
+ le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) {
/* this increments the reference count of the instance
* object of the core
*/
@@ -1351,7 +1310,7 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
return 0;
exit_free4:
- kfree(mdev->anchor_list);
+ kfree(mdev->busy_urbs);
exit_free3:
kfree(mdev->ep_address);
exit_free2:
@@ -1379,9 +1338,8 @@ exit_ENOMEM:
*/
static void hdm_disconnect(struct usb_interface *interface)
{
- struct most_dev *mdev;
+ struct most_dev *mdev = usb_get_intfdata(interface);
- mdev = usb_get_intfdata(interface);
mutex_lock(&mdev->io_mutex);
usb_set_intfdata(interface, NULL);
mdev->usb_device = NULL;
@@ -1393,7 +1351,7 @@ static void hdm_disconnect(struct usb_interface *interface)
destroy_most_dci_obj(mdev->dci);
most_deregister_interface(&mdev->iface);
- kfree(mdev->anchor_list);
+ kfree(mdev->busy_urbs);
kfree(mdev->cap);
kfree(mdev->conf);
kfree(mdev->ep_address);
OpenPOWER on IntegriCloud