Commit | Line | Data |
---|---|---|
8c6c4460 HK |
1 | /* |
2 | * Copyright (c) 1996-2002 Winbond Electronic Corporation | |
3 | * | |
4 | * Module Name: | |
5 | * Wb35Tx.c | |
6 | * | |
7 | * Abstract: | |
8 | * Processing the Tx message and put into down layer | |
9 | * | |
10 | */ | |
80aba536 | 11 | #include <linux/usb.h> |
5a0e3ad6 | 12 | #include <linux/gfp.h> |
66101de1 | 13 | |
80aba536 | 14 | #include "wb35tx_f.h" |
9ce922fd | 15 | #include "mds_f.h" |
66101de1 PM |
16 | |
17 | unsigned char | |
81416446 | 18 | Wb35Tx_get_tx_buffer(struct hw_data *pHwData, u8 **pBuffer) |
66101de1 | 19 | { |
eb62f3ea | 20 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
66101de1 PM |
21 | |
22 | *pBuffer = pWb35Tx->TxBuffer[0]; | |
279b6ccc | 23 | return true; |
66101de1 PM |
24 | } |
25 | ||
5c58093e PE |
26 | static void Wb35Tx(struct wbsoft_priv *adapter); |
27 | ||
81416446 | 28 | static void Wb35Tx_complete(struct urb *pUrb) |
66101de1 | 29 | { |
5c58093e | 30 | struct wbsoft_priv *adapter = pUrb->context; |
81416446 | 31 | struct hw_data *pHwData = &adapter->sHwData; |
eb62f3ea | 32 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
b7caf94f | 33 | struct wb35_mds *pMds = &adapter->Mds; |
66101de1 | 34 | |
5c58093e | 35 | printk("wb35: tx complete\n"); |
8c6c4460 | 36 | /* Variable setting */ |
5c58093e | 37 | pWb35Tx->EP4vm_state = VM_COMPLETED; |
8c6c4460 HK |
38 | pWb35Tx->EP4VM_status = pUrb->status; /* Store the last result of Irp */ |
39 | /* Set the owner. Free the owner bit always. */ | |
81416446 | 40 | pMds->TxOwner[pWb35Tx->TxSendIndex] = 0; |
5c58093e PE |
41 | pWb35Tx->TxSendIndex++; |
42 | pWb35Tx->TxSendIndex %= MAX_USB_TX_BUFFER_NUMBER; | |
66101de1 | 43 | |
907af425 | 44 | if (pHwData->SurpriseRemove) /* Let WbWlanHalt handle surprise remove */ |
5c58093e | 45 | goto error; |
66101de1 | 46 | |
5c58093e PE |
47 | if (pWb35Tx->tx_halt) |
48 | goto error; | |
49 | ||
8c6c4460 | 50 | /* The URB is completed, check the result */ |
5c58093e PE |
51 | if (pWb35Tx->EP4VM_status != 0) { |
52 | printk("URB submission failed\n"); | |
53 | pWb35Tx->EP4vm_state = VM_STOP; | |
54 | goto error; | |
55 | } | |
56 | ||
57 | Mds_Tx(adapter); | |
58 | Wb35Tx(adapter); | |
59 | return; | |
60 | ||
61 | error: | |
62 | atomic_dec(&pWb35Tx->TxFireCounter); | |
63 | pWb35Tx->EP4vm_state = VM_STOP; | |
64 | } | |
65 | ||
66 | static void Wb35Tx(struct wbsoft_priv *adapter) | |
66101de1 | 67 | { |
81416446 | 68 | struct hw_data *pHwData = &adapter->sHwData; |
eb62f3ea | 69 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
8b384e0c | 70 | u8 *pTxBufferAddress; |
b7caf94f | 71 | struct wb35_mds *pMds = &adapter->Mds; |
81416446 HK |
72 | struct urb *pUrb = (struct urb *)pWb35Tx->Tx4Urb; |
73 | int retv; | |
66101de1 PM |
74 | u32 SendIndex; |
75 | ||
87cb9a63 | 76 | if (pHwData->SurpriseRemove) |
66101de1 PM |
77 | goto cleanup; |
78 | ||
79 | if (pWb35Tx->tx_halt) | |
80 | goto cleanup; | |
81 | ||
8c6c4460 | 82 | /* Ownership checking */ |
66101de1 | 83 | SendIndex = pWb35Tx->TxSendIndex; |
8c6c4460 HK |
84 | /* No more data need to be sent, return immediately */ |
85 | if (!pMds->TxOwner[SendIndex]) | |
66101de1 PM |
86 | goto cleanup; |
87 | ||
88 | pTxBufferAddress = pWb35Tx->TxBuffer[SendIndex]; | |
8c6c4460 HK |
89 | |
90 | /* Issuing URB */ | |
2894c6cd PE |
91 | usb_fill_bulk_urb(pUrb, pHwData->udev, |
92 | usb_sndbulkpipe(pHwData->udev, 4), | |
81416446 | 93 | pTxBufferAddress, pMds->TxBufferSize[SendIndex], |
42c84bb4 | 94 | Wb35Tx_complete, adapter); |
66101de1 PM |
95 | |
96 | pWb35Tx->EP4vm_state = VM_RUNNING; | |
7c126043 | 97 | retv = usb_submit_urb(pUrb, GFP_ATOMIC); |
81416446 | 98 | if (retv < 0) { |
66101de1 PM |
99 | printk("EP4 Tx Irp sending error\n"); |
100 | goto cleanup; | |
101 | } | |
102 | ||
8c6c4460 | 103 | /* Check if driver needs issue Irp for EP2 */ |
66101de1 PM |
104 | pWb35Tx->TxFillCount += pMds->TxCountInBuffer[SendIndex]; |
105 | if (pWb35Tx->TxFillCount > 12) | |
42c84bb4 | 106 | Wb35Tx_EP2VM_start(adapter); |
66101de1 PM |
107 | |
108 | pWb35Tx->ByteTransfer += pMds->TxBufferSize[SendIndex]; | |
109 | return; | |
110 | ||
111 | cleanup: | |
112 | pWb35Tx->EP4vm_state = VM_STOP; | |
44e8541c | 113 | atomic_dec(&pWb35Tx->TxFireCounter); |
66101de1 PM |
114 | } |
115 | ||
5c58093e | 116 | void Wb35Tx_start(struct wbsoft_priv *adapter) |
66101de1 | 117 | { |
81416446 | 118 | struct hw_data *pHwData = &adapter->sHwData; |
eb62f3ea | 119 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
66101de1 | 120 | |
8c6c4460 | 121 | /* Allow only one thread to run into function */ |
5c58093e PE |
122 | if (atomic_inc_return(&pWb35Tx->TxFireCounter) == 1) { |
123 | pWb35Tx->EP4vm_state = VM_RUNNING; | |
124 | Wb35Tx(adapter); | |
125 | } else | |
126 | atomic_dec(&pWb35Tx->TxFireCounter); | |
66101de1 PM |
127 | } |
128 | ||
81416446 | 129 | unsigned char Wb35Tx_initial(struct hw_data *pHwData) |
66101de1 | 130 | { |
eb62f3ea | 131 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
66101de1 | 132 | |
f3d20188 | 133 | pWb35Tx->Tx4Urb = usb_alloc_urb(0, GFP_ATOMIC); |
66101de1 | 134 | if (!pWb35Tx->Tx4Urb) |
279b6ccc | 135 | return false; |
66101de1 | 136 | |
f3d20188 | 137 | pWb35Tx->Tx2Urb = usb_alloc_urb(0, GFP_ATOMIC); |
926ae525 | 138 | if (!pWb35Tx->Tx2Urb) { |
81416446 | 139 | usb_free_urb(pWb35Tx->Tx4Urb); |
279b6ccc | 140 | return false; |
66101de1 PM |
141 | } |
142 | ||
279b6ccc | 143 | return true; |
66101de1 PM |
144 | } |
145 | ||
81416446 | 146 | void Wb35Tx_stop(struct hw_data *pHwData) |
66101de1 | 147 | { |
eb62f3ea | 148 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
66101de1 | 149 | |
8c6c4460 | 150 | /* Try to cancel the Trp of EP2 */ |
66101de1 | 151 | if (pWb35Tx->EP2vm_state == VM_RUNNING) |
907af425 | 152 | /* Only use unlink, let Wb35Tx_destroy free them */ |
81416446 | 153 | usb_unlink_urb(pWb35Tx->Tx2Urb); |
2855bb79 | 154 | pr_debug("EP2 Tx stop\n"); |
66101de1 | 155 | |
8c6c4460 | 156 | /* Try to cancel the Irp of EP4 */ |
66101de1 | 157 | if (pWb35Tx->EP4vm_state == VM_RUNNING) |
907af425 | 158 | /* Only use unlink, let Wb35Tx_destroy free them */ |
81416446 | 159 | usb_unlink_urb(pWb35Tx->Tx4Urb); |
2855bb79 | 160 | pr_debug("EP4 Tx stop\n"); |
66101de1 PM |
161 | } |
162 | ||
81416446 | 163 | void Wb35Tx_destroy(struct hw_data *pHwData) |
66101de1 | 164 | { |
eb62f3ea | 165 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
66101de1 | 166 | |
8c6c4460 | 167 | /* Wait for VM stop */ |
66101de1 | 168 | do { |
8c6c4460 | 169 | msleep(10); /* Delay for waiting function enter 940623.1.a */ |
81416446 | 170 | } while ((pWb35Tx->EP2vm_state != VM_STOP) && (pWb35Tx->EP4vm_state != VM_STOP)); |
8c6c4460 | 171 | msleep(10); /* Delay for waiting function enter 940623.1.b */ |
66101de1 | 172 | |
ee692cfa HK |
173 | usb_free_urb(pWb35Tx->Tx4Urb); |
174 | usb_free_urb(pWb35Tx->Tx2Urb); | |
66101de1 | 175 | |
2855bb79 | 176 | pr_debug("Wb35Tx_destroy OK\n"); |
66101de1 PM |
177 | } |
178 | ||
1e8a2b60 | 179 | void Wb35Tx_CurrentTime(struct wbsoft_priv *adapter, u32 TimeCount) |
66101de1 | 180 | { |
81416446 | 181 | struct hw_data *pHwData = &adapter->sHwData; |
eb62f3ea | 182 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
ffb324aa | 183 | bool Trigger = false; |
66101de1 PM |
184 | |
185 | if (pWb35Tx->TxTimer > TimeCount) | |
279b6ccc | 186 | Trigger = true; |
66101de1 | 187 | else if (TimeCount > (pWb35Tx->TxTimer+500)) |
279b6ccc | 188 | Trigger = true; |
66101de1 PM |
189 | |
190 | if (Trigger) { | |
191 | pWb35Tx->TxTimer = TimeCount; | |
42c84bb4 | 192 | Wb35Tx_EP2VM_start(adapter); |
66101de1 PM |
193 | } |
194 | } | |
195 | ||
5c58093e | 196 | static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter); |
66101de1 | 197 | |
81416446 | 198 | static void Wb35Tx_EP2VM_complete(struct urb *pUrb) |
66101de1 | 199 | { |
1e8a2b60 | 200 | struct wbsoft_priv *adapter = pUrb->context; |
81416446 | 201 | struct hw_data *pHwData = &adapter->sHwData; |
c4d562a9 | 202 | struct T02_descriptor T02, TSTATUS; |
eb62f3ea | 203 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
81416446 | 204 | u32 *pltmp = (u32 *)pWb35Tx->EP2_buf; |
66101de1 PM |
205 | u32 i; |
206 | u16 InterruptInLength; | |
207 | ||
8c6c4460 | 208 | /* Variable setting */ |
66101de1 PM |
209 | pWb35Tx->EP2vm_state = VM_COMPLETED; |
210 | pWb35Tx->EP2VM_status = pUrb->status; | |
211 | ||
8c6c4460 | 212 | /* For Linux 2.4. Interrupt will always trigger */ |
907af425 | 213 | if (pHwData->SurpriseRemove) /* Let WbWlanHalt handle surprise remove */ |
dc7e04fe PE |
214 | goto error; |
215 | ||
216 | if (pWb35Tx->tx_halt) | |
217 | goto error; | |
218 | ||
8c6c4460 | 219 | /* The Urb is completed, check the result */ |
dc7e04fe | 220 | if (pWb35Tx->EP2VM_status != 0) { |
0c59dbaa | 221 | printk("EP2 IoCompleteRoutine return error\n"); |
81416446 | 222 | pWb35Tx->EP2vm_state = VM_STOP; |
dc7e04fe PE |
223 | goto error; |
224 | } | |
66101de1 | 225 | |
8c6c4460 | 226 | /* Update the Tx result */ |
dc7e04fe | 227 | InterruptInLength = pUrb->actual_length; |
8c6c4460 HK |
228 | /* Modify for minimum memory access and DWORD alignment. */ |
229 | T02.value = cpu_to_le32(pltmp[0]) >> 8; /* [31:8] -> [24:0] */ | |
230 | InterruptInLength -= 1; /* 20051221.1.c Modify the follow for more stable */ | |
231 | InterruptInLength >>= 2; /* InterruptInLength/4 */ | |
dc7e04fe PE |
232 | for (i = 1; i <= InterruptInLength; i++) { |
233 | T02.value |= ((cpu_to_le32(pltmp[i]) & 0xff) << 24); | |
234 | ||
8c6c4460 | 235 | TSTATUS.value = T02.value; /* 20061009 anson's endian */ |
81416446 | 236 | Mds_SendComplete(adapter, &TSTATUS); |
dc7e04fe PE |
237 | T02.value = cpu_to_le32(pltmp[i]) >> 8; |
238 | } | |
239 | ||
240 | return; | |
241 | error: | |
44e8541c | 242 | atomic_dec(&pWb35Tx->TxResultCount); |
66101de1 PM |
243 | pWb35Tx->EP2vm_state = VM_STOP; |
244 | } | |
245 | ||
5c58093e PE |
246 | static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter) |
247 | { | |
81416446 | 248 | struct hw_data *pHwData = &adapter->sHwData; |
eb62f3ea | 249 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
81416446 HK |
250 | struct urb *pUrb = (struct urb *)pWb35Tx->Tx2Urb; |
251 | u32 *pltmp = (u32 *)pWb35Tx->EP2_buf; | |
5c58093e PE |
252 | int retv; |
253 | ||
87cb9a63 | 254 | if (pHwData->SurpriseRemove) |
5c58093e PE |
255 | goto error; |
256 | ||
257 | if (pWb35Tx->tx_halt) | |
258 | goto error; | |
259 | ||
8c6c4460 | 260 | /* Issuing URB */ |
81416446 | 261 | usb_fill_int_urb(pUrb, pHwData->udev, usb_rcvintpipe(pHwData->udev, 2), |
926ae525 HK |
262 | pltmp, MAX_INTERRUPT_LENGTH, Wb35Tx_EP2VM_complete, |
263 | adapter, 32); | |
5c58093e PE |
264 | |
265 | pWb35Tx->EP2vm_state = VM_RUNNING; | |
266 | retv = usb_submit_urb(pUrb, GFP_ATOMIC); | |
267 | ||
268 | if (retv < 0) { | |
2855bb79 | 269 | pr_debug("EP2 Tx Irp sending error\n"); |
5c58093e PE |
270 | goto error; |
271 | } | |
272 | ||
273 | return; | |
274 | error: | |
275 | pWb35Tx->EP2vm_state = VM_STOP; | |
276 | atomic_dec(&pWb35Tx->TxResultCount); | |
277 | } | |
278 | ||
279 | void Wb35Tx_EP2VM_start(struct wbsoft_priv *adapter) | |
280 | { | |
81416446 | 281 | struct hw_data *pHwData = &adapter->sHwData; |
eb62f3ea | 282 | struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx; |
5c58093e | 283 | |
8c6c4460 | 284 | /* Allow only one thread to run into function */ |
5c58093e PE |
285 | if (atomic_inc_return(&pWb35Tx->TxResultCount) == 1) { |
286 | pWb35Tx->EP2vm_state = VM_RUNNING; | |
287 | Wb35Tx_EP2VM(adapter); | |
af12cc50 | 288 | } else |
5c58093e PE |
289 | atomic_dec(&pWb35Tx->TxResultCount); |
290 | } |