blob: 708c5b05f86c6e929d98fe8e4f9512970a37d525 [file] [log] [blame]
/*
* 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) {
printk("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) {
printk("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) {
printk("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);
}