aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/winbond/wb35tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/winbond/wb35tx.c')
-rw-r--r--drivers/staging/winbond/wb35tx.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/drivers/staging/winbond/wb35tx.c b/drivers/staging/winbond/wb35tx.c
new file mode 100644
index 00000000000..870cff39a22
--- /dev/null
+++ b/drivers/staging/winbond/wb35tx.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 1996-2002 Winbond Electronic Corporation
+ *
+ * Module Name:
+ * Wb35Tx.c
+ *
+ * Abstract:
+ * Processing the Tx message and put into down layer
+ *
+ */
+#include <linux/usb.h>
+#include <linux/gfp.h>
+
+#include "wb35tx_f.h"
+#include "mds_f.h"
+
+unsigned char
+Wb35Tx_get_tx_buffer(struct hw_data *pHwData, u8 **pBuffer)
+{
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+
+ *pBuffer = pWb35Tx->TxBuffer[0];
+ return true;
+}
+
+static void Wb35Tx(struct wbsoft_priv *adapter);
+
+static void Wb35Tx_complete(struct urb *pUrb)
+{
+ struct wbsoft_priv *adapter = pUrb->context;
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+ struct wb35_mds *pMds = &adapter->Mds;
+
+ printk("wb35: tx complete\n");
+ /* Variable setting */
+ pWb35Tx->EP4vm_state = VM_COMPLETED;
+ pWb35Tx->EP4VM_status = pUrb->status; /* Store the last result of Irp */
+ /* Set the owner. Free the owner bit always. */
+ pMds->TxOwner[pWb35Tx->TxSendIndex] = 0;
+ pWb35Tx->TxSendIndex++;
+ pWb35Tx->TxSendIndex %= MAX_USB_TX_BUFFER_NUMBER;
+
+ if (pHwData->SurpriseRemove) /* Let WbWlanHalt handle surprise remove */
+ goto error;
+
+ if (pWb35Tx->tx_halt)
+ goto error;
+
+ /* The URB is completed, check the result */
+ if (pWb35Tx->EP4VM_status != 0) {
+ dev_err(&pUrb->dev->dev, "URB submission failed\n");
+ pWb35Tx->EP4vm_state = VM_STOP;
+ goto error;
+ }
+
+ Mds_Tx(adapter);
+ Wb35Tx(adapter);
+ return;
+
+error:
+ atomic_dec(&pWb35Tx->TxFireCounter);
+ pWb35Tx->EP4vm_state = VM_STOP;
+}
+
+static void Wb35Tx(struct wbsoft_priv *adapter)
+{
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+ u8 *pTxBufferAddress;
+ struct wb35_mds *pMds = &adapter->Mds;
+ struct urb *pUrb = (struct urb *)pWb35Tx->Tx4Urb;
+ int retv;
+ u32 SendIndex;
+
+ if (pHwData->SurpriseRemove)
+ goto cleanup;
+
+ if (pWb35Tx->tx_halt)
+ goto cleanup;
+
+ /* Ownership checking */
+ SendIndex = pWb35Tx->TxSendIndex;
+ /* No more data need to be sent, return immediately */
+ if (!pMds->TxOwner[SendIndex])
+ goto cleanup;
+
+ pTxBufferAddress = pWb35Tx->TxBuffer[SendIndex];
+
+ /* Issuing URB */
+ usb_fill_bulk_urb(pUrb, pHwData->udev,
+ usb_sndbulkpipe(pHwData->udev, 4),
+ pTxBufferAddress, pMds->TxBufferSize[SendIndex],
+ Wb35Tx_complete, adapter);
+
+ pWb35Tx->EP4vm_state = VM_RUNNING;
+ retv = usb_submit_urb(pUrb, GFP_ATOMIC);
+ if (retv < 0) {
+ dev_err(&pUrb->dev->dev, "EP4 Tx Irp sending error\n");
+ goto cleanup;
+ }
+
+ /* Check if driver needs issue Irp for EP2 */
+ pWb35Tx->TxFillCount += pMds->TxCountInBuffer[SendIndex];
+ if (pWb35Tx->TxFillCount > 12)
+ Wb35Tx_EP2VM_start(adapter);
+
+ pWb35Tx->ByteTransfer += pMds->TxBufferSize[SendIndex];
+ return;
+
+ cleanup:
+ pWb35Tx->EP4vm_state = VM_STOP;
+ atomic_dec(&pWb35Tx->TxFireCounter);
+}
+
+void Wb35Tx_start(struct wbsoft_priv *adapter)
+{
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+
+ /* Allow only one thread to run into function */
+ if (atomic_inc_return(&pWb35Tx->TxFireCounter) == 1) {
+ pWb35Tx->EP4vm_state = VM_RUNNING;
+ Wb35Tx(adapter);
+ } else
+ atomic_dec(&pWb35Tx->TxFireCounter);
+}
+
+unsigned char Wb35Tx_initial(struct hw_data *pHwData)
+{
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+
+ pWb35Tx->Tx4Urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!pWb35Tx->Tx4Urb)
+ return false;
+
+ pWb35Tx->Tx2Urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!pWb35Tx->Tx2Urb) {
+ usb_free_urb(pWb35Tx->Tx4Urb);
+ return false;
+ }
+
+ return true;
+}
+
+void Wb35Tx_stop(struct hw_data *pHwData)
+{
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+
+ /* Try to cancel the Trp of EP2 */
+ if (pWb35Tx->EP2vm_state == VM_RUNNING)
+ /* Only use unlink, let Wb35Tx_destroy free them */
+ usb_unlink_urb(pWb35Tx->Tx2Urb);
+ pr_debug("EP2 Tx stop\n");
+
+ /* Try to cancel the Irp of EP4 */
+ if (pWb35Tx->EP4vm_state == VM_RUNNING)
+ /* Only use unlink, let Wb35Tx_destroy free them */
+ usb_unlink_urb(pWb35Tx->Tx4Urb);
+ pr_debug("EP4 Tx stop\n");
+}
+
+void Wb35Tx_destroy(struct hw_data *pHwData)
+{
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+
+ /* Wait for VM stop */
+ do {
+ msleep(10); /* Delay for waiting function enter 940623.1.a */
+ } while ((pWb35Tx->EP2vm_state != VM_STOP) && (pWb35Tx->EP4vm_state != VM_STOP));
+ msleep(10); /* Delay for waiting function enter 940623.1.b */
+
+ usb_free_urb(pWb35Tx->Tx4Urb);
+ usb_free_urb(pWb35Tx->Tx2Urb);
+
+ pr_debug("Wb35Tx_destroy OK\n");
+}
+
+void Wb35Tx_CurrentTime(struct wbsoft_priv *adapter, u32 TimeCount)
+{
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+ bool Trigger = false;
+
+ if (pWb35Tx->TxTimer > TimeCount)
+ Trigger = true;
+ else if (TimeCount > (pWb35Tx->TxTimer+500))
+ Trigger = true;
+
+ if (Trigger) {
+ pWb35Tx->TxTimer = TimeCount;
+ Wb35Tx_EP2VM_start(adapter);
+ }
+}
+
+static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter);
+
+static void Wb35Tx_EP2VM_complete(struct urb *pUrb)
+{
+ struct wbsoft_priv *adapter = pUrb->context;
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct T02_descriptor T02, TSTATUS;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+ u32 *pltmp = (u32 *)pWb35Tx->EP2_buf;
+ u32 i;
+ u16 InterruptInLength;
+
+ /* Variable setting */
+ pWb35Tx->EP2vm_state = VM_COMPLETED;
+ pWb35Tx->EP2VM_status = pUrb->status;
+
+ /* For Linux 2.4. Interrupt will always trigger */
+ if (pHwData->SurpriseRemove) /* Let WbWlanHalt handle surprise remove */
+ goto error;
+
+ if (pWb35Tx->tx_halt)
+ goto error;
+
+ /* The Urb is completed, check the result */
+ if (pWb35Tx->EP2VM_status != 0) {
+ dev_err(&pUrb->dev->dev, "EP2 IoCompleteRoutine return error\n");
+ pWb35Tx->EP2vm_state = VM_STOP;
+ goto error;
+ }
+
+ /* Update the Tx result */
+ InterruptInLength = pUrb->actual_length;
+ /* Modify for minimum memory access and DWORD alignment. */
+ T02.value = cpu_to_le32(pltmp[0]) >> 8; /* [31:8] -> [24:0] */
+ InterruptInLength -= 1; /* 20051221.1.c Modify the follow for more stable */
+ InterruptInLength >>= 2; /* InterruptInLength/4 */
+ for (i = 1; i <= InterruptInLength; i++) {
+ T02.value |= ((cpu_to_le32(pltmp[i]) & 0xff) << 24);
+
+ TSTATUS.value = T02.value; /* 20061009 anson's endian */
+ Mds_SendComplete(adapter, &TSTATUS);
+ T02.value = cpu_to_le32(pltmp[i]) >> 8;
+ }
+
+ return;
+error:
+ atomic_dec(&pWb35Tx->TxResultCount);
+ pWb35Tx->EP2vm_state = VM_STOP;
+}
+
+static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter)
+{
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+ struct urb *pUrb = (struct urb *)pWb35Tx->Tx2Urb;
+ u32 *pltmp = (u32 *)pWb35Tx->EP2_buf;
+ int retv;
+
+ if (pHwData->SurpriseRemove)
+ goto error;
+
+ if (pWb35Tx->tx_halt)
+ goto error;
+
+ /* Issuing URB */
+ usb_fill_int_urb(pUrb, pHwData->udev, usb_rcvintpipe(pHwData->udev, 2),
+ pltmp, MAX_INTERRUPT_LENGTH, Wb35Tx_EP2VM_complete,
+ adapter, 32);
+
+ pWb35Tx->EP2vm_state = VM_RUNNING;
+ retv = usb_submit_urb(pUrb, GFP_ATOMIC);
+
+ if (retv < 0) {
+ pr_debug("EP2 Tx Irp sending error\n");
+ goto error;
+ }
+
+ return;
+error:
+ pWb35Tx->EP2vm_state = VM_STOP;
+ atomic_dec(&pWb35Tx->TxResultCount);
+}
+
+void Wb35Tx_EP2VM_start(struct wbsoft_priv *adapter)
+{
+ struct hw_data *pHwData = &adapter->sHwData;
+ struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
+
+ /* Allow only one thread to run into function */
+ if (atomic_inc_return(&pWb35Tx->TxResultCount) == 1) {
+ pWb35Tx->EP2vm_state = VM_RUNNING;
+ Wb35Tx_EP2VM(adapter);
+ } else
+ atomic_dec(&pWb35Tx->TxResultCount);
+}