summaryrefslogtreecommitdiff
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.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 aeae071f2823..26c9adb29308 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);