~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/drivers/net/ath/ath5k/ath5k_qcu.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
 
3
 * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
 
4
 *
 
5
 * Lightly modified for iPXE, July 2009, by Joshua Oreman <oremanj@rwcr.net>.
 
6
 *
 
7
 * Permission to use, copy, modify, and distribute this software for any
 
8
 * purpose with or without fee is hereby granted, provided that the above
 
9
 * copyright notice and this permission notice appear in all copies.
 
10
 *
 
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
18
 *
 
19
 */
 
20
 
 
21
FILE_LICENCE ( MIT );
 
22
 
 
23
/********************************************\
 
24
Queue Control Unit, DFS Control Unit Functions
 
25
\********************************************/
 
26
 
 
27
#include "ath5k.h"
 
28
#include "reg.h"
 
29
#include "base.h"
 
30
 
 
31
/*
 
32
 * Set properties for a transmit queue
 
33
 */
 
34
int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah,
 
35
                                const struct ath5k_txq_info *queue_info)
 
36
{
 
37
        if (ah->ah_txq.tqi_type == AR5K_TX_QUEUE_INACTIVE)
 
38
                return -EIO;
 
39
 
 
40
        memcpy(&ah->ah_txq, queue_info, sizeof(struct ath5k_txq_info));
 
41
 
 
42
        /*XXX: Is this supported on 5210 ?*/
 
43
        if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA &&
 
44
                        ((queue_info->tqi_subtype == AR5K_WME_AC_VI) ||
 
45
                        (queue_info->tqi_subtype == AR5K_WME_AC_VO))) ||
 
46
                        queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD)
 
47
                ah->ah_txq.tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
 
48
 
 
49
        return 0;
 
50
}
 
51
 
 
52
/*
 
53
 * Initialize a transmit queue
 
54
 */
 
55
int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
 
56
                struct ath5k_txq_info *queue_info)
 
57
{
 
58
        int ret;
 
59
 
 
60
        /*
 
61
         * Setup internal queue structure
 
62
         */
 
63
        memset(&ah->ah_txq, 0, sizeof(struct ath5k_txq_info));
 
64
        ah->ah_txq.tqi_type = queue_type;
 
65
 
 
66
        if (queue_info != NULL) {
 
67
                queue_info->tqi_type = queue_type;
 
68
                ret = ath5k_hw_set_tx_queueprops(ah, queue_info);
 
69
                if (ret)
 
70
                        return ret;
 
71
        }
 
72
 
 
73
        /*
 
74
         * We use ah_txq_status to hold a temp value for
 
75
         * the Secondary interrupt mask registers on 5211+
 
76
         * check out ath5k_hw_reset_tx_queue
 
77
         */
 
78
        AR5K_Q_ENABLE_BITS(ah->ah_txq_status, 0);
 
79
 
 
80
        return 0;
 
81
}
 
82
 
 
83
/*
 
84
 * Set a transmit queue inactive
 
85
 */
 
86
void ath5k_hw_release_tx_queue(struct ath5k_hw *ah)
 
87
{
 
88
        /* This queue will be skipped in further operations */
 
89
        ah->ah_txq.tqi_type = AR5K_TX_QUEUE_INACTIVE;
 
90
        /*For SIMR setup*/
 
91
        AR5K_Q_DISABLE_BITS(ah->ah_txq_status, 0);
 
92
}
 
93
 
 
94
/*
 
95
 * Set DFS properties for a transmit queue on DCU
 
96
 */
 
97
int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah)
 
98
{
 
99
        u32 cw_min, cw_max, retry_lg, retry_sh;
 
100
        struct ath5k_txq_info *tq = &ah->ah_txq;
 
101
        const int queue = 0;
 
102
 
 
103
        tq = &ah->ah_txq;
 
104
 
 
105
        if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
 
106
                return 0;
 
107
 
 
108
        if (ah->ah_version == AR5K_AR5210) {
 
109
                /* Only handle data queues, others will be ignored */
 
110
                if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
 
111
                        return 0;
 
112
 
 
113
                /* Set Slot time */
 
114
                ath5k_hw_reg_write(ah, ah->ah_turbo ?
 
115
                        AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
 
116
                        AR5K_SLOT_TIME);
 
117
                /* Set ACK_CTS timeout */
 
118
                ath5k_hw_reg_write(ah, ah->ah_turbo ?
 
119
                        AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
 
120
                        AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
 
121
                /* Set Transmit Latency */
 
122
                ath5k_hw_reg_write(ah, ah->ah_turbo ?
 
123
                        AR5K_INIT_TRANSMIT_LATENCY_TURBO :
 
124
                        AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
 
125
 
 
126
                /* Set IFS0 */
 
127
                if (ah->ah_turbo) {
 
128
                         ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
 
129
                                (ah->ah_aifs + tq->tqi_aifs) *
 
130
                                AR5K_INIT_SLOT_TIME_TURBO) <<
 
131
                                AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
 
132
                                AR5K_IFS0);
 
133
                } else {
 
134
                        ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
 
135
                                (ah->ah_aifs + tq->tqi_aifs) *
 
136
                                AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
 
137
                                AR5K_INIT_SIFS, AR5K_IFS0);
 
138
                }
 
139
 
 
140
                /* Set IFS1 */
 
141
                ath5k_hw_reg_write(ah, ah->ah_turbo ?
 
142
                        AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
 
143
                        AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
 
144
                /* Set AR5K_PHY_SETTLING */
 
145
                ath5k_hw_reg_write(ah, ah->ah_turbo ?
 
146
                        (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
 
147
                        | 0x38 :
 
148
                        (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
 
149
                        | 0x1C,
 
150
                        AR5K_PHY_SETTLING);
 
151
                /* Set Frame Control Register */
 
152
                ath5k_hw_reg_write(ah, ah->ah_turbo ?
 
153
                        (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
 
154
                        AR5K_PHY_TURBO_SHORT | 0x2020) :
 
155
                        (AR5K_PHY_FRAME_CTL_INI | 0x1020),
 
156
                        AR5K_PHY_FRAME_CTL_5210);
 
157
        }
 
158
 
 
159
        /*
 
160
         * Calculate cwmin/max by channel mode
 
161
         */
 
162
        cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN;
 
163
        cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX;
 
164
        ah->ah_aifs = AR5K_TUNE_AIFS;
 
165
        /*XR is only supported on 5212*/
 
166
        if (IS_CHAN_XR(ah->ah_current_channel) &&
 
167
                        ah->ah_version == AR5K_AR5212) {
 
168
                cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR;
 
169
                cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR;
 
170
                ah->ah_aifs = AR5K_TUNE_AIFS_XR;
 
171
        /*B mode is not supported on 5210*/
 
172
        } else if (IS_CHAN_B(ah->ah_current_channel) &&
 
173
                        ah->ah_version != AR5K_AR5210) {
 
174
                cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B;
 
175
                cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B;
 
176
                ah->ah_aifs = AR5K_TUNE_AIFS_11B;
 
177
        }
 
178
 
 
179
        cw_min = 1;
 
180
        while (cw_min < ah->ah_cw_min)
 
181
                cw_min = (cw_min << 1) | 1;
 
182
 
 
183
        cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) :
 
184
                ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
 
185
        cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) :
 
186
                ((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1);
 
187
 
 
188
        /*
 
189
         * Calculate and set retry limits
 
190
         */
 
191
        if (ah->ah_software_retry) {
 
192
                /* XXX Need to test this */
 
193
                retry_lg = ah->ah_limit_tx_retries;
 
194
                retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
 
195
                        AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
 
196
        } else {
 
197
                retry_lg = AR5K_INIT_LG_RETRY;
 
198
                retry_sh = AR5K_INIT_SH_RETRY;
 
199
        }
 
200
 
 
201
        /*No QCU/DCU [5210]*/
 
202
        if (ah->ah_version == AR5K_AR5210) {
 
203
                ath5k_hw_reg_write(ah,
 
204
                        (cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
 
205
                        | AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
 
206
                                AR5K_NODCU_RETRY_LMT_SLG_RETRY)
 
207
                        | AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
 
208
                                AR5K_NODCU_RETRY_LMT_SSH_RETRY)
 
209
                        | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
 
210
                        | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
 
211
                        AR5K_NODCU_RETRY_LMT);
 
212
        } else {
 
213
                /*QCU/DCU [5211+]*/
 
214
                ath5k_hw_reg_write(ah,
 
215
                        AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
 
216
                                AR5K_DCU_RETRY_LMT_SLG_RETRY) |
 
217
                        AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
 
218
                                AR5K_DCU_RETRY_LMT_SSH_RETRY) |
 
219
                        AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
 
220
                        AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
 
221
                        AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
 
222
 
 
223
        /*===Rest is also for QCU/DCU only [5211+]===*/
 
224
 
 
225
                /*
 
226
                 * Set initial content window (cw_min/cw_max)
 
227
                 * and arbitrated interframe space (aifs)...
 
228
                 */
 
229
                ath5k_hw_reg_write(ah,
 
230
                        AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
 
231
                        AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
 
232
                        AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs,
 
233
                                AR5K_DCU_LCL_IFS_AIFS),
 
234
                        AR5K_QUEUE_DFS_LOCAL_IFS(queue));
 
235
 
 
236
                /*
 
237
                 * Set misc registers
 
238
                 */
 
239
                /* Enable DCU early termination for this queue */
 
240
                AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
 
241
                                        AR5K_QCU_MISC_DCU_EARLY);
 
242
 
 
243
                /* Enable DCU to wait for next fragment from QCU */
 
244
                AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
 
245
                                        AR5K_DCU_MISC_FRAG_WAIT);
 
246
 
 
247
                /* On Maui and Spirit use the global seqnum on DCU */
 
248
                if (ah->ah_mac_version < AR5K_SREV_AR5211)
 
249
                        AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
 
250
                                                AR5K_DCU_MISC_SEQNUM_CTL);
 
251
 
 
252
                if (tq->tqi_cbr_period) {
 
253
                        ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
 
254
                                AR5K_QCU_CBRCFG_INTVAL) |
 
255
                                AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
 
256
                                AR5K_QCU_CBRCFG_ORN_THRES),
 
257
                                AR5K_QUEUE_CBRCFG(queue));
 
258
                        AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
 
259
                                AR5K_QCU_MISC_FRSHED_CBR);
 
260
                        if (tq->tqi_cbr_overflow_limit)
 
261
                                AR5K_REG_ENABLE_BITS(ah,
 
262
                                        AR5K_QUEUE_MISC(queue),
 
263
                                        AR5K_QCU_MISC_CBR_THRES_ENABLE);
 
264
                }
 
265
 
 
266
                if (tq->tqi_ready_time &&
 
267
                (tq->tqi_type != AR5K_TX_QUEUE_CAB))
 
268
                        ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
 
269
                                AR5K_QCU_RDYTIMECFG_INTVAL) |
 
270
                                AR5K_QCU_RDYTIMECFG_ENABLE,
 
271
                                AR5K_QUEUE_RDYTIMECFG(queue));
 
272
 
 
273
                if (tq->tqi_burst_time) {
 
274
                        ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
 
275
                                AR5K_DCU_CHAN_TIME_DUR) |
 
276
                                AR5K_DCU_CHAN_TIME_ENABLE,
 
277
                                AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
 
278
 
 
279
                        if (tq->tqi_flags
 
280
                        & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
 
281
                                AR5K_REG_ENABLE_BITS(ah,
 
282
                                        AR5K_QUEUE_MISC(queue),
 
283
                                        AR5K_QCU_MISC_RDY_VEOL_POLICY);
 
284
                }
 
285
 
 
286
                if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
 
287
                        ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
 
288
                                AR5K_QUEUE_DFS_MISC(queue));
 
289
 
 
290
                if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
 
291
                        ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
 
292
                                AR5K_QUEUE_DFS_MISC(queue));
 
293
 
 
294
                /* TODO: Handle frame compression */
 
295
 
 
296
                /*
 
297
                 * Enable interrupts for this tx queue
 
298
                 * in the secondary interrupt mask registers
 
299
                 */
 
300
                if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
 
301
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
 
302
 
 
303
                if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
 
304
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
 
305
 
 
306
                if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
 
307
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
 
308
 
 
309
                if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
 
310
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
 
311
 
 
312
                if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
 
313
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
 
314
 
 
315
                if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
 
316
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
 
317
 
 
318
                if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
 
319
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
 
320
 
 
321
                if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
 
322
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
 
323
 
 
324
                if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
 
325
                        AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
 
326
 
 
327
                /* Update secondary interrupt mask registers */
 
328
 
 
329
                /* Filter out inactive queues */
 
330
                ah->ah_txq_imr_txok &= ah->ah_txq_status;
 
331
                ah->ah_txq_imr_txerr &= ah->ah_txq_status;
 
332
                ah->ah_txq_imr_txurn &= ah->ah_txq_status;
 
333
                ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
 
334
                ah->ah_txq_imr_txeol &= ah->ah_txq_status;
 
335
                ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
 
336
                ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
 
337
                ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
 
338
                ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
 
339
 
 
340
                ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
 
341
                        AR5K_SIMR0_QCU_TXOK) |
 
342
                        AR5K_REG_SM(ah->ah_txq_imr_txdesc,
 
343
                        AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
 
344
                ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
 
345
                        AR5K_SIMR1_QCU_TXERR) |
 
346
                        AR5K_REG_SM(ah->ah_txq_imr_txeol,
 
347
                        AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
 
348
                /* Update simr2 but don't overwrite rest simr2 settings */
 
349
                AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
 
350
                AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
 
351
                        AR5K_REG_SM(ah->ah_txq_imr_txurn,
 
352
                        AR5K_SIMR2_QCU_TXURN));
 
353
                ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
 
354
                        AR5K_SIMR3_QCBRORN) |
 
355
                        AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
 
356
                        AR5K_SIMR3_QCBRURN), AR5K_SIMR3);
 
357
                ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
 
358
                        AR5K_SIMR4_QTRIG), AR5K_SIMR4);
 
359
                /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
 
360
                ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
 
361
                        AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
 
362
                /* No queue has TXNOFRM enabled, disable the interrupt
 
363
                 * by setting AR5K_TXNOFRM to zero */
 
364
                if (ah->ah_txq_imr_nofrm == 0)
 
365
                        ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
 
366
 
 
367
                /* Set QCU mask for this DCU to save power */
 
368
                AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
 
369
        }
 
370
 
 
371
        return 0;
 
372
}
 
373
 
 
374
/*
 
375
 * Set slot time on DCU
 
376
 */
 
377
int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
 
378
{
 
379
        if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX)
 
380
                return -EINVAL;
 
381
 
 
382
        if (ah->ah_version == AR5K_AR5210)
 
383
                ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time,
 
384
                                ah->ah_turbo), AR5K_SLOT_TIME);
 
385
        else
 
386
                ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT);
 
387
 
 
388
        return 0;
 
389
}
 
390