diff options
Diffstat (limited to 'driver/hdm-usb/hdm_usb.c')
-rw-r--r-- | driver/hdm-usb/hdm_usb.c | 312 |
1 files changed, 95 insertions, 217 deletions
diff --git a/driver/hdm-usb/hdm_usb.c b/driver/hdm-usb/hdm_usb.c index 9ab744b..3433646 100644 --- a/driver/hdm-usb/hdm_usb.c +++ b/driver/hdm-usb/hdm_usb.c @@ -65,17 +65,6 @@ #define DRCI_WRITE_REQ 0xA1 /** - * struct buf_anchor - used to create a list of pending URBs - * @urb: pointer to USB request block - * @list: linked list - * @urb_completion: - */ -struct buf_anchor { - struct urb *urb; - struct list_head list; -}; - -/** * struct most_dci_obj - Direct Communication Interface * @kobj:position in sysfs * @usb_device: pointer to the usb device @@ -108,15 +97,13 @@ 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 - * @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 @@ -128,16 +115,14 @@ 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 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 clear_hold_work clear_work[MAX_NUM_ENDPOINTS]; - struct list_head *anchor_list; + struct usb_anchor *busy_urbs; struct mutex io_mutex; struct timer_list link_stat_timer; struct work_struct poll_work_obj; @@ -197,41 +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 buf_anchor *anchor, *tmp; - spinlock_t *lock = mdev->anchor_list_lock + channel; - unsigned long flags; - - spin_lock_irqsave(lock, flags); - list_for_each_entry_safe(anchor, tmp, &mdev->anchor_list[channel], - list) { - struct urb *urb = anchor->urb; - - spin_unlock_irqrestore(lock, flags); - if (likely(urb)) { - 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); - } - spin_lock_irqsave(lock, flags); - list_del(&anchor->list); - kfree(anchor); - } - spin_unlock_irqrestore(lock, flags); + return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1); } /** @@ -248,7 +201,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: @@ -283,7 +236,7 @@ static int hdm_poison_channel(struct most_interface *iface, int channel) { struct most_dev *mdev = to_mdev(iface); unsigned long flags; - spinlock_t *lock; + spinlock_t *lock; /* temp. lock */ if (unlikely(!iface)) { dev_warn(&mdev->usb_device->dev, "Poison: Bad interface.\n"); @@ -294,7 +247,7 @@ static int hdm_poison_channel(struct most_interface *iface, int channel) return -ECHRNG; } - lock = mdev->anchor_list_lock + channel; + lock = mdev->channel_lock + channel; spin_lock_irqsave(lock, flags); mdev->is_channel_healthy[channel] = false; spin_unlock_irqrestore(lock, flags); @@ -302,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; @@ -394,46 +347,37 @@ static int hdm_remove_padding(struct most_dev *mdev, int channel, static void hdm_write_completion(struct urb *urb) { struct mbo *mbo = urb->context; - struct buf_anchor *anchor = mbo->priv; 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->anchor_list_lock + channel; + spinlock_t *lock = mdev->channel_lock + channel; 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; } - list_del(&anchor->list); spin_unlock_irqrestore(lock, flags); - kfree(anchor); if (likely(mbo->complete)) mbo->complete(mbo); @@ -551,53 +495,45 @@ static void hdm_write_completion(struct urb *urb) static void hdm_read_completion(struct urb *urb) { struct mbo *mbo = urb->context; - struct buf_anchor *anchor = mbo->priv; 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->anchor_list_lock + channel; + spinlock_t *lock = mdev->channel_lock + channel; 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; - } } - list_del(&anchor->list); spin_unlock_irqrestore(lock, flags); - kfree(anchor); if (likely(mbo->complete)) mbo->complete(mbo); @@ -623,15 +559,12 @@ 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; - spinlock_t *lock; if (unlikely(!iface || !mbo)) return -EIO; @@ -646,19 +579,8 @@ 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; - goto _error; - } - - anchor->urb = urb; - mbo->priv = anchor; if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] && hdm_add_padding(mdev, channel, mbo)) { @@ -678,7 +600,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, @@ -691,10 +613,7 @@ static int hdm_enqueue(struct most_interface *iface, int channel, } urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - lock = mdev->anchor_list_lock + channel; - spin_lock_irqsave(lock, flags); - list_add_tail(&anchor->list, &mdev->anchor_list[channel]); - spin_unlock_irqrestore(lock, flags); + usb_anchor_urb(urb, &mdev->busy_urbs[channel]); retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { @@ -704,10 +623,7 @@ static int hdm_enqueue(struct most_interface *iface, int channel, return 0; _error_1: - spin_lock_irqsave(lock, flags); - list_del(&anchor->list); - spin_unlock_irqrestore(lock, flags); - kfree(anchor); + usb_unanchor_urb(urb); _error: usb_free_urb(urb); return retval; @@ -748,7 +664,7 @@ static int hdm_configure_channel(struct most_interface *iface, int channel, } if (conf->data_type != MOST_CH_SYNC && - !(conf->data_type == MOST_CH_ISOC_AVP && + !(conf->data_type == MOST_CH_ISOC && conf->packets_per_xact != 0xFF)) { mdev->padding_active[channel] = false; goto exit; @@ -784,56 +700,12 @@ static int hdm_configure_channel(struct most_interface *iface, int channel, - conf->buffer_size; exit: mdev->conf[channel] = *conf; - return 0; -} - -/** - * 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 (conf->data_type == MOST_CH_ASYNC) { + u16 ep = mdev->ep_address[channel]; - 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; } @@ -857,7 +729,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 @@ -873,33 +745,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 (hdm_update_netinfo(mdev) < 0) + 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 (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]); + } + + 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; + } + + 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); } /** @@ -917,7 +803,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"); @@ -1037,14 +923,14 @@ struct regs { u16 reg; }; -const static struct regs ro_regs[] = { +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 }, }; -const static struct regs rw_regs[] = { +static const struct regs rw_regs[] = { { "mep_filter", DRCI_REG_MEP_FILTER }, { "mep_hash0", DRCI_REG_HASH_TBL0 }, { "mep_hash1", DRCI_REG_HASH_TBL1 }, @@ -1103,6 +989,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) @@ -1113,16 +1000,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")) { - reg_addr = DRCI_REG_BASE + DRCI_COMMAND + val * 16; - val = 1; - } else if (get_static_reg_addr(ro_regs, name, ®_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, ®_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; @@ -1234,7 +1120,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; @@ -1281,9 +1166,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; @@ -1302,21 +1187,14 @@ 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]); - 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); + init_usb_anchor(&mdev->busy_urbs[i]); + spin_lock_init(&mdev->channel_lock[i]); } dev_notice(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n", le16_to_cpu(usb_dev->descriptor.idVendor), @@ -1358,7 +1236,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: @@ -1399,7 +1277,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); |