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