ar5416_radar.c revision 231927
1231927Sadrian/*
2231927Sadrian * Copyright (c) 2010-2011 Atheros Communications, Inc.
3231927Sadrian *
4231927Sadrian * Permission to use, copy, modify, and/or distribute this software for any
5231927Sadrian * purpose with or without fee is hereby granted, provided that the above
6231927Sadrian * copyright notice and this permission notice appear in all copies.
7231927Sadrian *
8231927Sadrian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9231927Sadrian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10231927Sadrian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11231927Sadrian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12231927Sadrian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13231927Sadrian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14231927Sadrian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15231927Sadrian *
16231927Sadrian * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c 231927 2012-02-20 03:07:07Z adrian $
17231927Sadrian */
18231927Sadrian#include "opt_ah.h"
19231927Sadrian
20231927Sadrian#include "ah.h"
21231927Sadrian#include "ah_internal.h"
22231927Sadrian#include "ah_devid.h"
23231927Sadrian#include "ah_desc.h"                    /* NB: for HAL_PHYERR* */
24231927Sadrian
25231927Sadrian#include "ar5416/ar5416.h"
26231927Sadrian#include "ar5416/ar5416reg.h"
27231927Sadrian#include "ar5416/ar5416phy.h"
28231927Sadrian
29231927Sadrian#include "ah_eeprom_v14.h"	/* for owl_get_ntxchains() */
30231927Sadrian
31231927Sadrian/*
32231927Sadrian * Get the radar parameter values and return them in the pe
33231927Sadrian * structure
34231927Sadrian */
35231927Sadrianvoid
36231927Sadrianar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
37231927Sadrian{
38231927Sadrian	uint32_t val, temp;
39231927Sadrian
40231927Sadrian	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
41231927Sadrian
42231927Sadrian	temp = MS(val,AR_PHY_RADAR_0_FIRPWR);
43231927Sadrian	temp |= 0xFFFFFF80;
44231927Sadrian	pe->pe_firpwr = temp;
45231927Sadrian	pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI);
46231927Sadrian	pe->pe_height =  MS(val, AR_PHY_RADAR_0_HEIGHT);
47231927Sadrian	pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI);
48231927Sadrian	pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND);
49231927Sadrian
50231927Sadrian	/* RADAR_1 values */
51231927Sadrian	val = OS_REG_READ(ah, AR_PHY_RADAR_1);
52231927Sadrian	pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH);
53231927Sadrian	pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH);
54231927Sadrian	pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN);
55231927Sadrian
56231927Sadrian	pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) &
57231927Sadrian	    AR_PHY_RADAR_EXT_ENA);
58231927Sadrian
59231927Sadrian	pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
60231927Sadrian	    AR_PHY_RADAR_1_USE_FIR128);
61231927Sadrian	pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
62231927Sadrian	    AR_PHY_RADAR_1_BLOCK_CHECK);
63231927Sadrian	pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
64231927Sadrian	    AR_PHY_RADAR_1_MAX_RRSSI);
65231927Sadrian	pe->pe_enabled = !!
66231927Sadrian	    (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA);
67231927Sadrian	pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
68231927Sadrian	    AR_PHY_RADAR_1_RELPWR_ENA);
69231927Sadrian	pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) &
70231927Sadrian	    AR_PHY_RADAR_1_RELSTEP_CHECK);
71231927Sadrian}
72231927Sadrian
73231927Sadrian/*
74231927Sadrian * Enable radar detection and set the radar parameters per the
75231927Sadrian * values in pe
76231927Sadrian */
77231927Sadrianvoid
78231927Sadrianar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe)
79231927Sadrian{
80231927Sadrian	uint32_t val;
81231927Sadrian
82231927Sadrian	val = OS_REG_READ(ah, AR_PHY_RADAR_0);
83231927Sadrian
84231927Sadrian	if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) {
85231927Sadrian		val &= ~AR_PHY_RADAR_0_FIRPWR;
86231927Sadrian		val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR);
87231927Sadrian	}
88231927Sadrian	if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) {
89231927Sadrian		val &= ~AR_PHY_RADAR_0_RRSSI;
90231927Sadrian		val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI);
91231927Sadrian	}
92231927Sadrian	if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) {
93231927Sadrian		val &= ~AR_PHY_RADAR_0_HEIGHT;
94231927Sadrian		val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT);
95231927Sadrian	}
96231927Sadrian	if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) {
97231927Sadrian		val &= ~AR_PHY_RADAR_0_PRSSI;
98231927Sadrian		val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI);
99231927Sadrian	}
100231927Sadrian	if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) {
101231927Sadrian		val &= ~AR_PHY_RADAR_0_INBAND;
102231927Sadrian		val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND);
103231927Sadrian	}
104231927Sadrian
105231927Sadrian	/*Enable FFT data*/
106231927Sadrian	val |= AR_PHY_RADAR_0_FFT_ENA;
107231927Sadrian	OS_REG_WRITE(ah, AR_PHY_RADAR_0, val);
108231927Sadrian
109231927Sadrian	/* Implicitly enable */
110231927Sadrian	if (pe->pe_enabled == 1)
111231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
112231927Sadrian	else if (pe->pe_enabled == 0)
113231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
114231927Sadrian
115231927Sadrian	if (pe->pe_usefir128 == 1)
116231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
117231927Sadrian	else if (pe->pe_usefir128 == 0)
118231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128);
119231927Sadrian
120231927Sadrian	if (pe->pe_enmaxrssi == 1)
121231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
122231927Sadrian	else if (pe->pe_enmaxrssi == 0)
123231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI);
124231927Sadrian
125231927Sadrian	if (pe->pe_blockradar == 1)
126231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
127231927Sadrian	else if (pe->pe_blockradar == 0)
128231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK);
129231927Sadrian
130231927Sadrian	if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) {
131231927Sadrian		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
132231927Sadrian		val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH;
133231927Sadrian		val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH);
134231927Sadrian		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
135231927Sadrian	}
136231927Sadrian	if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) {
137231927Sadrian		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
138231927Sadrian		val &= ~AR_PHY_RADAR_1_RELPWR_THRESH;
139231927Sadrian		val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH);
140231927Sadrian		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
141231927Sadrian	}
142231927Sadrian
143231927Sadrian	if (pe->pe_en_relstep_check == 1)
144231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
145231927Sadrian		    AR_PHY_RADAR_1_RELSTEP_CHECK);
146231927Sadrian	else if (pe->pe_en_relstep_check == 0)
147231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
148231927Sadrian		    AR_PHY_RADAR_1_RELSTEP_CHECK);
149231927Sadrian
150231927Sadrian	if (pe->pe_enrelpwr == 1)
151231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_1,
152231927Sadrian		    AR_PHY_RADAR_1_RELPWR_ENA);
153231927Sadrian	else if (pe->pe_enrelpwr == 0)
154231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1,
155231927Sadrian		    AR_PHY_RADAR_1_RELPWR_ENA);
156231927Sadrian
157231927Sadrian	if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) {
158231927Sadrian		val = OS_REG_READ(ah, AR_PHY_RADAR_1);
159231927Sadrian		val &= ~AR_PHY_RADAR_1_MAXLEN;
160231927Sadrian		val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN);
161231927Sadrian		OS_REG_WRITE(ah, AR_PHY_RADAR_1, val);
162231927Sadrian	}
163231927Sadrian
164231927Sadrian	/*
165231927Sadrian	 * Enable HT/40 if the upper layer asks;
166231927Sadrian	 * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS
167231927Sadrian	 * is available.
168231927Sadrian	 */
169231927Sadrian	if (pe->pe_extchannel == 1)
170231927Sadrian		OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
171231927Sadrian	else if (pe->pe_extchannel == 0)
172231927Sadrian		OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
173231927Sadrian}
174231927Sadrian
175231927Sadrian/*
176231927Sadrian * Extract the radar event information from the given phy error.
177231927Sadrian *
178231927Sadrian * Returns AH_TRUE if the phy error was actually a phy error,
179231927Sadrian * AH_FALSE if the phy error wasn't a phy error.
180231927Sadrian */
181231927Sadrian
182231927Sadrian/* Flags for pulse_bw_info */
183231927Sadrian#define	PRI_CH_RADAR_FOUND		0x01
184231927Sadrian#define	EXT_CH_RADAR_FOUND		0x02
185231927Sadrian#define	EXT_CH_RADAR_EARLY_FOUND	0x04
186231927Sadrian
187231927SadrianHAL_BOOL
188231927Sadrianar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs,
189231927Sadrian    uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event)
190231927Sadrian{
191231927Sadrian	HAL_BOOL doDfsExtCh;
192231927Sadrian	HAL_BOOL doDfsEnhanced;
193231927Sadrian	HAL_BOOL doDfsCombinedRssi;
194231927Sadrian
195231927Sadrian	uint8_t rssi = 0, ext_rssi = 0;
196231927Sadrian	uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0;
197231927Sadrian	uint32_t dur = 0;
198231927Sadrian	int pri_found = 1, ext_found = 0;
199231927Sadrian	int early_ext = 0;
200231927Sadrian	int is_dc = 0;
201231927Sadrian	uint16_t datalen;		/* length from the RX status field */
202231927Sadrian
203231927Sadrian	/* Check whether the given phy error is a radar event */
204231927Sadrian	if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) &&
205231927Sadrian	    (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) {
206231927Sadrian		return AH_FALSE;
207231927Sadrian	}
208231927Sadrian
209231927Sadrian	/* Grab copies of the capabilities; just to make the code clearer */
210231927Sadrian	doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport;
211231927Sadrian	doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport;
212231927Sadrian	doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi;
213231927Sadrian
214231927Sadrian	datalen = rxs->rs_datalen;
215231927Sadrian
216231927Sadrian	/* If hardware supports it, use combined RSSI, else use chain 0 RSSI */
217231927Sadrian	if (doDfsCombinedRssi)
218231927Sadrian		rssi = (uint8_t) rxs->rs_rssi;
219231927Sadrian	else
220231927Sadrian		rssi = (uint8_t) rxs->rs_rssi_ctl[0];
221231927Sadrian
222231927Sadrian	/* Set this; but only use it if doDfsExtCh is set */
223231927Sadrian	ext_rssi = (uint8_t) rxs->rs_rssi_ext[0];
224231927Sadrian
225231927Sadrian	/* Cap it at 0 if the RSSI is a negative number */
226231927Sadrian	if (rssi & 0x80)
227231927Sadrian		rssi = 0;
228231927Sadrian
229231927Sadrian	if (ext_rssi & 0x80)
230231927Sadrian		ext_rssi = 0;
231231927Sadrian
232231927Sadrian	/*
233231927Sadrian	 * Fetch the relevant data from the frame
234231927Sadrian	 */
235231927Sadrian	if (doDfsExtCh) {
236231927Sadrian		if (datalen < 3)
237231927Sadrian			return AH_FALSE;
238231927Sadrian
239231927Sadrian		/* Last three bytes of the frame are of interest */
240231927Sadrian		pulse_length_pri = *(buf + datalen - 3);
241231927Sadrian		pulse_length_ext = *(buf + datalen - 2);
242231927Sadrian		pulse_bw_info = *(buf + datalen - 1);
243231927Sadrian		HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d,"
244231927Sadrian		    " pulse_length_ext=%d, pulse_bw_info=%x\n",
245231927Sadrian		    __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext,
246231927Sadrian		    pulse_bw_info);
247231927Sadrian	} else {
248231927Sadrian		/* The pulse width is byte 0 of the data */
249231927Sadrian		if (datalen >= 1)
250231927Sadrian			dur = ((uint8_t) buf[0]) & 0xff;
251231927Sadrian		else
252231927Sadrian			dur = 0;
253231927Sadrian
254231927Sadrian		if (dur == 0 && rssi == 0) {
255231927Sadrian			HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__);
256231927Sadrian			return AH_FALSE;
257231927Sadrian		}
258231927Sadrian
259231927Sadrian		HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur);
260231927Sadrian
261231927Sadrian		/* Single-channel only */
262231927Sadrian		pri_found = 1;
263231927Sadrian		ext_found = 0;
264231927Sadrian	}
265231927Sadrian
266231927Sadrian	/*
267231927Sadrian	 * If doing extended channel data, pulse_bw_info must
268231927Sadrian	 * have one of the flags set.
269231927Sadrian	 */
270231927Sadrian	if (doDfsExtCh && pulse_bw_info == 0x0)
271231927Sadrian		return AH_FALSE;
272231927Sadrian
273231927Sadrian	/*
274231927Sadrian	 * If the extended channel data is available, calculate
275231927Sadrian	 * which to pay attention to.
276231927Sadrian	 */
277231927Sadrian	if (doDfsExtCh) {
278231927Sadrian		/* If pulse is on DC, take the larger duration of the two */
279231927Sadrian		if ((pulse_bw_info & EXT_CH_RADAR_FOUND) &&
280231927Sadrian		    (pulse_bw_info & PRI_CH_RADAR_FOUND)) {
281231927Sadrian			is_dc = 1;
282231927Sadrian			if (pulse_length_ext > pulse_length_pri) {
283231927Sadrian				dur = pulse_length_ext;
284231927Sadrian				pri_found = 0;
285231927Sadrian				ext_found = 1;
286231927Sadrian			} else {
287231927Sadrian				dur = pulse_length_pri;
288231927Sadrian				pri_found = 1;
289231927Sadrian				ext_found = 0;
290231927Sadrian			}
291231927Sadrian		} else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) {
292231927Sadrian			dur = pulse_length_ext;
293231927Sadrian			pri_found = 0;
294231927Sadrian			ext_found = 1;
295231927Sadrian			early_ext = 1;
296231927Sadrian		} else if (pulse_bw_info & PRI_CH_RADAR_FOUND) {
297231927Sadrian			dur = pulse_length_pri;
298231927Sadrian			pri_found = 1;
299231927Sadrian			ext_found = 0;
300231927Sadrian		} else if (pulse_bw_info & EXT_CH_RADAR_FOUND) {
301231927Sadrian			dur = pulse_length_ext;
302231927Sadrian			pri_found = 0;
303231927Sadrian			ext_found = 1;
304231927Sadrian		}
305231927Sadrian
306231927Sadrian	}
307231927Sadrian
308231927Sadrian	/*
309231927Sadrian	 * For enhanced DFS (Merlin and later), pulse_bw_info has
310231927Sadrian	 * implications for selecting the correct RSSI value.
311231927Sadrian	 */
312231927Sadrian	if (doDfsEnhanced) {
313231927Sadrian		switch (pulse_bw_info & 0x03) {
314231927Sadrian		case 0:
315231927Sadrian			/* No radar? */
316231927Sadrian			rssi = 0;
317231927Sadrian			break;
318231927Sadrian		case PRI_CH_RADAR_FOUND:
319231927Sadrian			/* Radar in primary channel */
320231927Sadrian			/* Cannot use ctrl channel RSSI if ext channel is stronger */
321231927Sadrian			if (ext_rssi >= (rssi + 3)) {
322231927Sadrian				rssi = 0;
323231927Sadrian			};
324231927Sadrian			break;
325231927Sadrian		case EXT_CH_RADAR_FOUND:
326231927Sadrian			/* Radar in extended channel */
327231927Sadrian			/* Cannot use ext channel RSSI if ctrl channel is stronger */
328231927Sadrian			if (rssi >= (ext_rssi + 12)) {
329231927Sadrian				rssi = 0;
330231927Sadrian			} else {
331231927Sadrian				rssi = ext_rssi;
332231927Sadrian			}
333231927Sadrian			break;
334231927Sadrian		case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
335231927Sadrian			/* When both are present, use stronger one */
336231927Sadrian			if (rssi < ext_rssi)
337231927Sadrian				rssi = ext_rssi;
338231927Sadrian			break;
339231927Sadrian		}
340231927Sadrian	}
341231927Sadrian
342231927Sadrian	/*
343231927Sadrian	 * If not doing enhanced DFS, choose the ext channel if
344231927Sadrian	 * it is stronger than the main channel
345231927Sadrian	 */
346231927Sadrian	if (doDfsExtCh && !doDfsEnhanced) {
347231927Sadrian		if ((ext_rssi > rssi) && (ext_rssi < 128))
348231927Sadrian			rssi = ext_rssi;
349231927Sadrian	}
350231927Sadrian
351231927Sadrian	/*
352231927Sadrian	 * XXX what happens if the above code decides the RSSI
353231927Sadrian	 * XXX wasn't valid, an sets it to 0?
354231927Sadrian	 */
355231927Sadrian
356231927Sadrian	/*
357231927Sadrian	 * Fill out dfs_event structure.
358231927Sadrian	 */
359231927Sadrian	event->re_full_ts = fulltsf;
360231927Sadrian	event->re_ts = rxs->rs_tstamp;
361231927Sadrian	event->re_rssi = rssi;
362231927Sadrian	event->re_dur = dur;
363231927Sadrian
364231927Sadrian	event->re_flags = 0;
365231927Sadrian	if (pri_found)
366231927Sadrian		event->re_flags |= HAL_DFS_EVENT_PRICH;
367231927Sadrian	if (ext_found)
368231927Sadrian		event->re_flags |= HAL_DFS_EVENT_EXTCH;
369231927Sadrian	if (early_ext)
370231927Sadrian		event->re_flags |= HAL_DFS_EVENT_EXTEARLY;
371231927Sadrian	if (is_dc)
372231927Sadrian		event->re_flags |= HAL_DFS_EVENT_ISDC;
373231927Sadrian
374231927Sadrian	return AH_TRUE;
375231927Sadrian}
376231927Sadrian
377231927Sadrian/*
378231927Sadrian * Return whether fast-clock is currently enabled for this
379231927Sadrian * channel.
380231927Sadrian */
381231927SadrianHAL_BOOL
382231927Sadrianar5416IsFastClockEnabled(struct ath_hal *ah)
383231927Sadrian{
384231927Sadrian	struct ath_hal_private *ahp = AH_PRIVATE(ah);
385231927Sadrian
386231927Sadrian	return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan);
387231927Sadrian}
388