X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/060c85b4cbcec4bd77bf5485c6bbc7539aecb62b..878684ff47f116c5a75e8d1640bf32ad5922dc19:/package/rt2x00/src/rt2x00usb.c diff --git a/package/rt2x00/src/rt2x00usb.c b/package/rt2x00/src/rt2x00usb.c index 6b193d023..568d73847 100644 --- a/package/rt2x00/src/rt2x00usb.c +++ b/package/rt2x00/src/rt2x00usb.c @@ -21,7 +21,6 @@ /* Module: rt2x00usb Abstract: rt2x00 generic usb device routines. - Supported chipsets: rt2570, rt2571W & rt2671. */ /* @@ -31,8 +30,6 @@ #include #include -#include -#include #include #include "rt2x00.h" @@ -42,192 +39,78 @@ * Interfacing with the HW. */ int rt2x00usb_vendor_request(const struct rt2x00_dev *rt2x00dev, - const u8 request, const u8 type, const u16 offset, - u32 value, void *buffer, const u16 buffer_length, const u16 timeout) + const u8 request, const u8 requesttype, + const u16 offset, const u16 value, + void *buffer, const u16 buffer_length, + const int timeout) { - struct usb_device *usb_dev = interface_to_usbdev( - rt2x00dev_usb(rt2x00dev)); + struct usb_device *usb_dev = + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); int status; unsigned int i; + unsigned int pipe = + (requesttype == USB_VENDOR_REQUEST_IN) ? + usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - status = usb_control_msg( - usb_dev, - (type == USB_VENDOR_REQUEST_IN) ? - usb_rcvctrlpipe(usb_dev, 0) : - usb_sndctrlpipe(usb_dev, 0), - request, type, value, offset, buffer, buffer_length, - timeout); + status = usb_control_msg(usb_dev, pipe, request, requesttype, + value, offset, buffer, buffer_length, + timeout); if (status >= 0) return 0; + + /* + * Check for errors + * -ENODEV: Device has disappeared, no point continuing. + * All other errors: Try again. + */ + else if (status == -ENODEV) + break; } - ERROR(rt2x00dev, "vendor request error. Request 0x%02x failed " - "for offset 0x%04x with error %d.\n", request, offset, status); + ERROR(rt2x00dev, + "Vendor Request 0x%02x failed for offset 0x%04x with error %d.\n", + request, offset, status); return status; } EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request); -/* - * Radio handlers - */ -void rt2x00usb_enable_radio(struct rt2x00_dev *rt2x00dev) +int rt2x00usb_vendor_request_buff(const struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length, const int timeout) { - unsigned int i; - - /* - * Start the RX ring. - */ - for (i = 0; i < rt2x00dev->rx->stats.limit; i++) { - __set_bit(ENTRY_OWNER_NIC, &rt2x00dev->rx->entry[i].flags); - usb_submit_urb(rt2x00dev->rx->entry[i].priv, GFP_ATOMIC); - } -} -EXPORT_SYMBOL_GPL(rt2x00usb_enable_radio); - -void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring; - unsigned int i; - - rt2x00usb_vendor_request(rt2x00dev, USB_RX_CONTROL, - USB_VENDOR_REQUEST_OUT, 0x00, 0x00, NULL, 0, REGISTER_TIMEOUT); + int status; /* - * Cancel all rings. + * Check for Cache availability. */ - ring_for_each(rt2x00dev, ring) { - for (i = 0; i < ring->stats.limit; i++) - usb_kill_urb(ring->entry[i].priv); + if (unlikely(!rt2x00dev->csr_cache || buffer_length > CSR_CACHE_SIZE)) { + ERROR(rt2x00dev, "CSR cache not available.\n"); + return -ENOMEM; } -} -EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); - -/* - * Beacon handlers. - */ -int rt2x00usb_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct usb_device *usb_dev = - interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); - struct data_ring *ring = - rt2x00_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); - struct data_entry *beacon; - struct data_entry *guardian; - int length; - - /* - * Just in case the ieee80211 doesn't set this, - * but we need this queue set for the descriptor - * initialization. - */ - control->queue = IEEE80211_TX_QUEUE_BEACON; - - /* - * Obtain 2 entries, one for the guardian byte, - * the second for the actual beacon. - */ - guardian = rt2x00_get_data_entry(ring); - rt2x00_ring_index_inc(ring); - beacon = rt2x00_get_data_entry(ring); - - /* - * First we create the beacon. - */ - skb_push(skb, ring->desc_size); - rt2x00lib_write_tx_desc(rt2x00dev, beacon, - (struct data_desc*)skb->data, - (struct ieee80211_hdr*)(skb->data + ring->desc_size), - skb->len - ring->desc_size, - control); - - /* - * Length passed to usb_fill_urb cannot be an odd number, - * so add 1 byte to make it even. - */ - length = skb->len; - if (length % 2) - length++; - - usb_fill_bulk_urb( - beacon->priv, - usb_dev, - usb_sndbulkpipe(usb_dev, 1), - skb->data, - length, - rt2x00usb_beacondone, - beacon); - - beacon->skb = skb; - - /* - * Second we need to create the guardian byte. - * We only need a single byte, so lets recycle - * the 'flags' field we are not using for beacons. - */ - guardian->flags = 0; - usb_fill_bulk_urb( - guardian->priv, - usb_dev, - usb_sndbulkpipe(usb_dev, 1), - &guardian->flags, - 1, - rt2x00usb_beacondone, - guardian); - /* - * Send out the guardian byte. - */ - usb_submit_urb(guardian->priv, GFP_ATOMIC); + if (requesttype == USB_VENDOR_REQUEST_OUT) + memcpy(rt2x00dev->csr_cache, buffer, buffer_length); - /* - * Enable beacon generation. - */ - rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); + status = rt2x00usb_vendor_request(rt2x00dev, request, requesttype, + offset, 0, rt2x00dev->csr_cache, + buffer_length, timeout); - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00usb_beacon_update); - -void rt2x00usb_beacondone(struct urb *urb) -{ - struct data_entry *entry = (struct data_entry*)urb->context; - struct data_ring *ring = entry->ring; - - if (!test_bit(DEVICE_ENABLED_RADIO, &ring->rt2x00dev->flags)) - return; + if (!status && requesttype == USB_VENDOR_REQUEST_IN) + memcpy(buffer, rt2x00dev->csr_cache, buffer_length); - /* - * Check if this was the guardian beacon, - * if that was the case we need to send the real beacon now. - * Otherwise we should free the sk_buffer, the device - * should be doing the rest of the work now. - */ - if (ring->index == 1) { - rt2x00_ring_index_done_inc(ring); - entry = rt2x00_get_data_entry(ring); - usb_submit_urb(entry->priv, GFP_ATOMIC); - rt2x00_ring_index_inc(ring); - } else if (ring->index_done == 1) { - entry = rt2x00_get_data_entry_done(ring); - if (entry->skb) { - dev_kfree_skb(entry->skb); - entry->skb = NULL; - } - rt2x00_ring_index_done_inc(ring); - } + return status; } -EXPORT_SYMBOL_GPL(rt2x00usb_beacondone); +EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); /* * TX data handlers. */ static void rt2x00usb_interrupt_txdone(struct urb *urb) { - struct data_entry *entry = (struct data_entry*)urb->context; + struct data_entry *entry = (struct data_entry *)urb->context; struct data_ring *ring = entry->ring; struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; struct data_desc *txd = (struct data_desc *)entry->skb->data; @@ -265,19 +148,18 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) */ if (!rt2x00_ring_full(ring)) ieee80211_wake_queue(rt2x00dev->hw, - entry->tx_status.control.queue); + entry->tx_status.control.queue); } int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *skb, - struct ieee80211_tx_control *control) + struct data_ring *ring, struct sk_buff *skb, + struct ieee80211_tx_control *control) { struct usb_device *usb_dev = - interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); - struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr*)skb->data; + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); struct data_entry *entry = rt2x00_get_data_entry(ring); - struct data_desc *txd; - u32 length = skb->len; + int pipe = usb_sndbulkpipe(usb_dev, 1); + u32 length; if (rt2x00_ring_full(ring)) { ieee80211_stop_queue(rt2x00dev->hw, control->queue); @@ -286,37 +168,39 @@ int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, if (test_bit(ENTRY_OWNER_NIC, &entry->flags)) { ERROR(rt2x00dev, - "Arrived at non-free entry in the non-full queue %d.\n" - "Please file bug report to %s.\n", - control->queue, DRV_PROJECT); - ieee80211_stop_queue( rt2x00dev->hw, control->queue); + "Arrived at non-free entry in the non-full queue %d.\n" + "Please file bug report to %s.\n", + control->queue, DRV_PROJECT); + ieee80211_stop_queue(rt2x00dev->hw, control->queue); return -EINVAL; } - skb_push(skb, rt2x00dev->hw->extra_tx_headroom); - txd = (struct data_desc*)skb->data; - rt2x00lib_write_tx_desc(rt2x00dev, entry, txd, ieee80211hdr, - length, control); + /* + * Add the descriptor in front of the skb. + */ + skb_push(skb, ring->desc_size); + memset(skb->data, 0, ring->desc_size); + + rt2x00lib_write_tx_desc(rt2x00dev, (struct data_desc *)skb->data, + (struct ieee80211_hdr *)(skb->data + + ring->desc_size), + skb->len - ring->desc_size, control); memcpy(&entry->tx_status.control, control, sizeof(*control)); entry->skb = skb; /* - * Length passed to usb_fill_urb cannot be an odd number, - * so add 1 byte to make it even. + * USB devices cannot blindly pass the skb->len as the + * length of the data to usb_fill_bulk_urb. Pass the skb + * to the driver to determine what the length should be. */ - length += rt2x00dev->hw->extra_tx_headroom; - if (length % 2) - length++; + length = rt2x00dev->ops->lib->get_tx_data_len(rt2x00dev, skb); + /* + * Initialize URB and send the frame to the device. + */ __set_bit(ENTRY_OWNER_NIC, &entry->flags); - usb_fill_bulk_urb( - entry->priv, - usb_dev, - usb_sndbulkpipe(usb_dev, 1), - skb->data, - length, - rt2x00usb_interrupt_txdone, - entry); + usb_fill_bulk_urb(entry->priv, usb_dev, pipe, + skb->data, length, rt2x00usb_interrupt_txdone, entry); usb_submit_urb(entry->priv, GFP_ATOMIC); rt2x00_ring_index_inc(ring); @@ -328,11 +212,157 @@ int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, } EXPORT_SYMBOL_GPL(rt2x00usb_write_tx_data); +/* + * RX data handlers. + */ +static void rt2x00usb_interrupt_rxdone(struct urb *urb) +{ + struct data_entry *entry = (struct data_entry *)urb->context; + struct data_ring *ring = entry->ring; + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + struct rxdata_entry_desc desc; + int header_size; + int frame_size; + + if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags) || + !test_and_clear_bit(ENTRY_OWNER_NIC, &entry->flags)) + return; + + /* + * Check if the received data is simply too small + * to be actually valid, or if the urb is signaling + * a problem. + */ + if (urb->actual_length < entry->ring->desc_size || urb->status) + goto skip_entry; + + memset(&desc, 0x00, sizeof(desc)); + rt2x00dev->ops->lib->fill_rxdone(entry, &desc); + + /* + * Allocate a new sk buffer to replace the current one. + * If allocation fails, we should drop the current frame + * so we can recycle the existing sk buffer for the new frame. + * As alignment we use 2 and not NET_IP_ALIGN because we need + * to be sure we have 2 bytes room in the head. (NET_IP_ALIGN + * can be 0 on some hardware). We use these 2 bytes for frame + * alignment later, we assume that the chance that + * header_size % 4 == 2 is bigger then header_size % 2 == 0 + * and thus optimize alignment by reserving the 2 bytes in + * advance. + */ + frame_size = entry->ring->data_size + entry->ring->desc_size; + skb = dev_alloc_skb(frame_size + 2); + if (!skb) + goto skip_entry; + + skb_reserve(skb, 2); + skb_put(skb, frame_size); + + /* + * The data behind the ieee80211 header must be + * aligned on a 4 byte boundary. + * After that trim the entire buffer down to only + * contain the valid frame data excluding the device + * descriptor. + */ + hdr = (struct ieee80211_hdr *)entry->skb->data; + header_size = + ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + + if (header_size % 4 == 0) { + skb_push(entry->skb, 2); + memmove(entry->skb->data, entry->skb->data + 2, skb->len - 2); + } + skb_trim(entry->skb, desc.size); + + /* + * Send the frame to rt2x00lib for further processing. + */ + rt2x00lib_rxdone(entry, entry->skb, &desc); + + /* + * Replace current entry's skb with the newly allocated one, + * and reinitialize the urb. + */ + entry->skb = skb; + urb->transfer_buffer = entry->skb->data; + urb->transfer_buffer_length = entry->skb->len; + +skip_entry: + if (test_bit(DEVICE_ENABLED_RADIO, &ring->rt2x00dev->flags)) { + __set_bit(ENTRY_OWNER_NIC, &entry->flags); + usb_submit_urb(urb, GFP_ATOMIC); + } + + rt2x00_ring_index_inc(ring); +} + +/* + * Radio handlers + */ +void rt2x00usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct usb_device *usb_dev = + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + struct data_ring *ring; + struct data_entry *entry; + unsigned int i; + + /* + * Initialize the TX rings + */ + txringall_for_each(rt2x00dev, ring) { + for (i = 0; i < ring->stats.limit; i++) + ring->entry[i].flags = 0; + + rt2x00_ring_index_clear(ring); + } + + /* + * Initialize and start the RX ring. + */ + rt2x00_ring_index_clear(rt2x00dev->rx); + + for (i = 0; i < rt2x00dev->rx->stats.limit; i++) { + entry = &rt2x00dev->rx->entry[i]; + + usb_fill_bulk_urb(entry->priv, usb_dev, + usb_rcvbulkpipe(usb_dev, 1), + entry->skb->data, entry->skb->len, + rt2x00usb_interrupt_rxdone, entry); + + __set_bit(ENTRY_OWNER_NIC, &entry->flags); + usb_submit_urb(entry->priv, GFP_ATOMIC); + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_enable_radio); + +void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0x0000, 0x0000, + REGISTER_TIMEOUT); + + /* + * Cancel all rings. + */ + ring_for_each(rt2x00dev, ring) { + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); + /* * Device initialization handlers. */ -static int rt2x00usb_alloc_ring(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring) +static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring) { unsigned int i; @@ -348,6 +378,22 @@ static int rt2x00usb_alloc_ring(struct rt2x00_dev *rt2x00dev, return 0; } +static void rt2x00usb_free_urb(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring) +{ + unsigned int i; + + if (!ring->entry) + return; + + for (i = 0; i < ring->stats.limit; i++) { + usb_kill_urb(ring->entry[i].priv); + usb_free_urb(ring->entry[i].priv); + if (ring->entry[i].skb) + kfree_skb(ring->entry[i].skb); + } +} + int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) { struct data_ring *ring; @@ -360,7 +406,7 @@ int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) * Allocate DMA */ ring_for_each(rt2x00dev, ring) { - status = rt2x00usb_alloc_ring(rt2x00dev, ring); + status = rt2x00usb_alloc_urb(rt2x00dev, ring); if (status) goto exit; } @@ -368,7 +414,7 @@ int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) /* * For the RX ring, skb's should be allocated. */ - entry_size = ring->data_size + ring->desc_size; + entry_size = rt2x00dev->rx->data_size + rt2x00dev->rx->desc_size; for (i = 0; i < rt2x00dev->rx->stats.limit; i++) { skb = dev_alloc_skb(NET_IP_ALIGN + entry_size); if (!skb) @@ -392,30 +438,56 @@ EXPORT_SYMBOL_GPL(rt2x00usb_initialize); void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) { struct data_ring *ring; - unsigned int i; - ring_for_each(rt2x00dev, ring) { - if (!ring->entry) - continue; - - for (i = 0; i < ring->stats.limit; i++) { - usb_kill_urb(ring->entry[i].priv); - usb_free_urb(ring->entry[i].priv); - if (ring->entry[i].skb) - kfree_skb(ring->entry[i].skb); - } - } + ring_for_each(rt2x00dev, ring) + rt2x00usb_free_urb(rt2x00dev, ring); } EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); /* * USB driver handlers. */ +static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rf); + rt2x00dev->rf = NULL; + + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; + + kfree(rt2x00dev->csr_cache); + rt2x00dev->csr_cache = NULL; +} + +static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) +{ + rt2x00dev->csr_cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); + if (!rt2x00dev->csr_cache) + goto exit; + + rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); + if (!rt2x00dev->eeprom) + goto exit; + + rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); + if (!rt2x00dev->rf) + goto exit; + + return 0; + +exit: + ERROR_PROBE("Failed to allocate registers.\n"); + + rt2x00usb_free_reg(rt2x00dev); + + return -ENOMEM; +} + int rt2x00usb_probe(struct usb_interface *usb_intf, - const struct usb_device_id *id) + const struct usb_device_id *id) { struct usb_device *usb_dev = interface_to_usbdev(usb_intf); - struct rt2x00_ops *ops = (struct rt2x00_ops*)id->driver_info; + struct rt2x00_ops *ops = (struct rt2x00_ops *)id->driver_info; struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; @@ -433,16 +505,27 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, rt2x00dev = hw->priv; rt2x00dev->dev = usb_intf; - rt2x00dev->device = &usb_intf->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; - retval = rt2x00lib_probe_dev(rt2x00dev); + rt2x00dev->usb_maxpacket = + usb_maxpacket(usb_dev, usb_sndbulkpipe(usb_dev, 1), 1); + if (!rt2x00dev->usb_maxpacket) + rt2x00dev->usb_maxpacket = 1; + + retval = rt2x00usb_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; + retval = rt2x00lib_probe_dev(rt2x00dev); + if (retval) + goto exit_free_reg; + return 0; +exit_free_reg: + rt2x00usb_free_reg(rt2x00dev); + exit_free_device: ieee80211_free_hw(hw); @@ -464,6 +547,7 @@ void rt2x00usb_disconnect(struct usb_interface *usb_intf) * Free all allocated data. */ rt2x00lib_remove_dev(rt2x00dev); + rt2x00usb_free_reg(rt2x00dev); ieee80211_free_hw(hw); /* @@ -485,6 +569,8 @@ int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) if (retval) return retval; + rt2x00usb_free_reg(rt2x00dev); + /* * Decrease usbdev refcount. */ @@ -498,10 +584,24 @@ int rt2x00usb_resume(struct usb_interface *usb_intf) { struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); struct rt2x00_dev *rt2x00dev = hw->priv; + int retval; usb_get_dev(interface_to_usbdev(usb_intf)); - return rt2x00lib_resume(rt2x00dev); + retval = rt2x00usb_alloc_reg(rt2x00dev); + if (retval) + return retval; + + retval = rt2x00lib_resume(rt2x00dev); + if (retval) + goto exit_free_reg; + + return 0; + +exit_free_reg: + rt2x00usb_free_reg(rt2x00dev); + + return retval; } EXPORT_SYMBOL_GPL(rt2x00usb_resume); #endif /* CONFIG_PM */