blob: 974978041558b2a08d1837432240e2abe011cf58 [file] [log] [blame]
Wai Yew CHAY8cc72362009-05-14 08:05:58 +02001/**
2 * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
3 *
4 * This source file is released under GPL v2 license (no other versions).
5 * See the COPYING file included in the main directory of this source
6 * distribution for the license terms and conditions.
7 *
8 * @File ctpcm.c
9 *
10 * @Brief
11 * This file contains the definition of the pcm device functions.
12 *
13 * @Author Liu Chun
14 * @Date Apr 2 2008
15 *
16 */
17
18#include "ctpcm.h"
Takashi Iwaib7bbf872009-06-05 16:11:07 +020019#include "cttimer.h"
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020021#include <sound/pcm.h>
22
23/* Hardware descriptions for playback */
24static struct snd_pcm_hardware ct_pcm_playback_hw = {
25 .info = (SNDRV_PCM_INFO_MMAP |
26 SNDRV_PCM_INFO_INTERLEAVED |
27 SNDRV_PCM_INFO_BLOCK_TRANSFER |
28 SNDRV_PCM_INFO_MMAP_VALID |
29 SNDRV_PCM_INFO_PAUSE),
30 .formats = (SNDRV_PCM_FMTBIT_U8 |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020031 SNDRV_PCM_FMTBIT_S16_LE |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020032 SNDRV_PCM_FMTBIT_S24_3LE |
Takashi Iwaid2b9b962009-06-02 14:39:05 +020033 SNDRV_PCM_FMTBIT_S32_LE |
34 SNDRV_PCM_FMTBIT_FLOAT_LE),
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020035 .rates = (SNDRV_PCM_RATE_CONTINUOUS |
36 SNDRV_PCM_RATE_8000_192000),
37 .rate_min = 8000,
38 .rate_max = 192000,
39 .channels_min = 1,
40 .channels_max = 2,
41 .buffer_bytes_max = (128*1024),
42 .period_bytes_min = (64),
43 .period_bytes_max = (128*1024),
Takashi Iwai775ffa12009-06-05 16:12:16 +020044 .periods_min = 2,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020045 .periods_max = 1024,
46 .fifo_size = 0,
47};
48
49static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
50 .info = (SNDRV_PCM_INFO_MMAP |
51 SNDRV_PCM_INFO_INTERLEAVED |
52 SNDRV_PCM_INFO_BLOCK_TRANSFER |
53 SNDRV_PCM_INFO_MMAP_VALID |
54 SNDRV_PCM_INFO_PAUSE),
Takashi Iwaid2b9b962009-06-02 14:39:05 +020055 .formats = SNDRV_PCM_FMTBIT_S16_LE,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020056 .rates = (SNDRV_PCM_RATE_48000 |
57 SNDRV_PCM_RATE_44100 |
58 SNDRV_PCM_RATE_32000),
59 .rate_min = 32000,
60 .rate_max = 48000,
61 .channels_min = 2,
62 .channels_max = 2,
63 .buffer_bytes_max = (128*1024),
64 .period_bytes_min = (64),
65 .period_bytes_max = (128*1024),
Takashi Iwai775ffa12009-06-05 16:12:16 +020066 .periods_min = 2,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020067 .periods_max = 1024,
68 .fifo_size = 0,
69};
70
71/* Hardware descriptions for capture */
72static struct snd_pcm_hardware ct_pcm_capture_hw = {
73 .info = (SNDRV_PCM_INFO_MMAP |
74 SNDRV_PCM_INFO_INTERLEAVED |
75 SNDRV_PCM_INFO_BLOCK_TRANSFER |
76 SNDRV_PCM_INFO_PAUSE |
77 SNDRV_PCM_INFO_MMAP_VALID),
78 .formats = (SNDRV_PCM_FMTBIT_U8 |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020079 SNDRV_PCM_FMTBIT_S16_LE |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020080 SNDRV_PCM_FMTBIT_S24_3LE |
Takashi Iwaid2b9b962009-06-02 14:39:05 +020081 SNDRV_PCM_FMTBIT_S32_LE |
82 SNDRV_PCM_FMTBIT_FLOAT_LE),
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020083 .rates = (SNDRV_PCM_RATE_CONTINUOUS |
84 SNDRV_PCM_RATE_8000_96000),
85 .rate_min = 8000,
86 .rate_max = 96000,
87 .channels_min = 1,
88 .channels_max = 2,
89 .buffer_bytes_max = (128*1024),
90 .period_bytes_min = (384),
91 .period_bytes_max = (64*1024),
92 .periods_min = 2,
93 .periods_max = 1024,
94 .fifo_size = 0,
95};
96
97static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
98{
99 struct ct_atc_pcm *apcm = atc_pcm;
100
Takashi Iwai35ebf6e2009-07-22 17:12:34 +0200101 if (!apcm->substream)
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200102 return;
103
104 snd_pcm_period_elapsed(apcm->substream);
105}
106
107static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
108{
109 struct ct_atc_pcm *apcm = runtime->private_data;
110 struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
111
112 atc->pcm_release_resources(atc, apcm);
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200113 ct_timer_instance_free(apcm->timer);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200114 kfree(apcm);
115 runtime->private_data = NULL;
116}
117
118/* pcm playback operations */
119static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
120{
121 struct ct_atc *atc = snd_pcm_substream_chip(substream);
122 struct snd_pcm_runtime *runtime = substream->runtime;
123 struct ct_atc_pcm *apcm;
124 int err;
125
126 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
Takashi Iwai35ebf6e2009-07-22 17:12:34 +0200127 if (!apcm)
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200128 return -ENOMEM;
129
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200130 apcm->substream = substream;
131 apcm->interrupt = ct_atc_pcm_interrupt;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200132 if (IEC958 == substream->pcm->device) {
133 runtime->hw = ct_spdif_passthru_playback_hw;
134 atc->spdif_out_passthru(atc, 1);
135 } else {
136 runtime->hw = ct_pcm_playback_hw;
137 if (FRONT == substream->pcm->device)
138 runtime->hw.channels_max = 8;
139 }
140
141 err = snd_pcm_hw_constraint_integer(runtime,
142 SNDRV_PCM_HW_PARAM_PERIODS);
143 if (err < 0) {
144 kfree(apcm);
145 return err;
146 }
147 err = snd_pcm_hw_constraint_minmax(runtime,
148 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
149 1024, UINT_MAX);
150 if (err < 0) {
151 kfree(apcm);
152 return err;
153 }
154
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200155 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
Julia Lawallfa2b30a2010-11-09 23:00:41 +0100156 if (!apcm->timer) {
157 kfree(apcm);
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200158 return -ENOMEM;
Julia Lawallfa2b30a2010-11-09 23:00:41 +0100159 }
160 runtime->private_data = apcm;
161 runtime->private_free = ct_atc_pcm_free_substream;
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200162
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200163 return 0;
164}
165
166static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
167{
168 struct ct_atc *atc = snd_pcm_substream_chip(substream);
169
170 /* TODO: Notify mixer inactive. */
171 if (IEC958 == substream->pcm->device)
172 atc->spdif_out_passthru(atc, 0);
173
174 /* The ct_atc_pcm object will be freed by runtime->private_free */
175
176 return 0;
177}
178
179static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
180 struct snd_pcm_hw_params *hw_params)
181{
Takashi Iwaia5990dc2009-06-09 08:19:02 +0200182 struct ct_atc *atc = snd_pcm_substream_chip(substream);
183 struct ct_atc_pcm *apcm = substream->runtime->private_data;
184 int err;
185
186 err = snd_pcm_lib_malloc_pages(substream,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200187 params_buffer_bytes(hw_params));
Takashi Iwaia5990dc2009-06-09 08:19:02 +0200188 if (err < 0)
189 return err;
190 /* clear previous resources */
191 atc->pcm_release_resources(atc, apcm);
192 return err;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200193}
194
195static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
196{
Takashi Iwaia5990dc2009-06-09 08:19:02 +0200197 struct ct_atc *atc = snd_pcm_substream_chip(substream);
198 struct ct_atc_pcm *apcm = substream->runtime->private_data;
199
200 /* clear previous resources */
201 atc->pcm_release_resources(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200202 /* Free snd-allocated pages */
203 return snd_pcm_lib_free_pages(substream);
204}
205
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200206
207static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
208{
209 int err;
210 struct ct_atc *atc = snd_pcm_substream_chip(substream);
211 struct snd_pcm_runtime *runtime = substream->runtime;
212 struct ct_atc_pcm *apcm = runtime->private_data;
213
214 if (IEC958 == substream->pcm->device)
215 err = atc->spdif_passthru_playback_prepare(atc, apcm);
216 else
217 err = atc->pcm_playback_prepare(atc, apcm);
218
219 if (err < 0) {
Sudip Mukherjee0cae90a2014-09-29 14:33:26 +0530220 dev_err(atc->card->dev,
221 "Preparing pcm playback failed!!!\n");
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200222 return err;
223 }
224
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200225 return 0;
226}
227
228static int
229ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
230{
231 struct ct_atc *atc = snd_pcm_substream_chip(substream);
232 struct snd_pcm_runtime *runtime = substream->runtime;
233 struct ct_atc_pcm *apcm = runtime->private_data;
234
235 switch (cmd) {
236 case SNDRV_PCM_TRIGGER_START:
237 case SNDRV_PCM_TRIGGER_RESUME:
238 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
239 atc->pcm_playback_start(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200240 break;
241 case SNDRV_PCM_TRIGGER_STOP:
242 case SNDRV_PCM_TRIGGER_SUSPEND:
243 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200244 atc->pcm_playback_stop(atc, apcm);
245 break;
246 default:
247 break;
248 }
249
250 return 0;
251}
252
253static snd_pcm_uframes_t
254ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
255{
256 unsigned long position;
257 struct ct_atc *atc = snd_pcm_substream_chip(substream);
258 struct snd_pcm_runtime *runtime = substream->runtime;
259 struct ct_atc_pcm *apcm = runtime->private_data;
260
261 /* Read out playback position */
262 position = atc->pcm_playback_position(atc, apcm);
263 position = bytes_to_frames(runtime, position);
Takashi Iwaiaf8500b2009-06-08 15:07:46 +0200264 if (position >= runtime->buffer_size)
265 position = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200266 return position;
267}
268
269/* pcm capture operations */
270static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
271{
272 struct ct_atc *atc = snd_pcm_substream_chip(substream);
273 struct snd_pcm_runtime *runtime = substream->runtime;
274 struct ct_atc_pcm *apcm;
275 int err;
276
277 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
Takashi Iwai35ebf6e2009-07-22 17:12:34 +0200278 if (!apcm)
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200279 return -ENOMEM;
280
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200281 apcm->started = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200282 apcm->substream = substream;
283 apcm->interrupt = ct_atc_pcm_interrupt;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200284 runtime->hw = ct_pcm_capture_hw;
285 runtime->hw.rate_max = atc->rsr * atc->msr;
286
287 err = snd_pcm_hw_constraint_integer(runtime,
288 SNDRV_PCM_HW_PARAM_PERIODS);
289 if (err < 0) {
290 kfree(apcm);
291 return err;
292 }
293 err = snd_pcm_hw_constraint_minmax(runtime,
294 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
295 1024, UINT_MAX);
296 if (err < 0) {
297 kfree(apcm);
298 return err;
299 }
300
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200301 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
Julia Lawallfa2b30a2010-11-09 23:00:41 +0100302 if (!apcm->timer) {
303 kfree(apcm);
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200304 return -ENOMEM;
Julia Lawallfa2b30a2010-11-09 23:00:41 +0100305 }
306 runtime->private_data = apcm;
307 runtime->private_free = ct_atc_pcm_free_substream;
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200308
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200309 return 0;
310}
311
312static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
313{
314 /* The ct_atc_pcm object will be freed by runtime->private_free */
315 /* TODO: Notify mixer inactive. */
316 return 0;
317}
318
319static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
320{
321 int err;
322 struct ct_atc *atc = snd_pcm_substream_chip(substream);
323 struct snd_pcm_runtime *runtime = substream->runtime;
324 struct ct_atc_pcm *apcm = runtime->private_data;
325
326 err = atc->pcm_capture_prepare(atc, apcm);
327 if (err < 0) {
Sudip Mukherjee0cae90a2014-09-29 14:33:26 +0530328 dev_err(atc->card->dev,
329 "Preparing pcm capture failed!!!\n");
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200330 return err;
331 }
332
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200333 return 0;
334}
335
336static int
337ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
338{
339 struct ct_atc *atc = snd_pcm_substream_chip(substream);
340 struct snd_pcm_runtime *runtime = substream->runtime;
341 struct ct_atc_pcm *apcm = runtime->private_data;
342
343 switch (cmd) {
344 case SNDRV_PCM_TRIGGER_START:
345 atc->pcm_capture_start(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200346 break;
347 case SNDRV_PCM_TRIGGER_STOP:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200348 atc->pcm_capture_stop(atc, apcm);
349 break;
350 default:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200351 atc->pcm_capture_stop(atc, apcm);
352 break;
353 }
354
355 return 0;
356}
357
358static snd_pcm_uframes_t
359ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
360{
361 unsigned long position;
362 struct ct_atc *atc = snd_pcm_substream_chip(substream);
363 struct snd_pcm_runtime *runtime = substream->runtime;
364 struct ct_atc_pcm *apcm = runtime->private_data;
365
366 /* Read out playback position */
367 position = atc->pcm_capture_position(atc, apcm);
368 position = bytes_to_frames(runtime, position);
Takashi Iwaiaf8500b2009-06-08 15:07:46 +0200369 if (position >= runtime->buffer_size)
370 position = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200371 return position;
372}
373
374/* PCM operators for playback */
Julia Lawall6769e982016-09-02 00:13:10 +0200375static const struct snd_pcm_ops ct_pcm_playback_ops = {
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200376 .open = ct_pcm_playback_open,
377 .close = ct_pcm_playback_close,
378 .ioctl = snd_pcm_lib_ioctl,
379 .hw_params = ct_pcm_hw_params,
380 .hw_free = ct_pcm_hw_free,
381 .prepare = ct_pcm_playback_prepare,
382 .trigger = ct_pcm_playback_trigger,
383 .pointer = ct_pcm_playback_pointer,
Takashi Iwaic76157d2009-06-02 15:26:19 +0200384 .page = snd_pcm_sgbuf_ops_page,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200385};
386
387/* PCM operators for capture */
Julia Lawall6769e982016-09-02 00:13:10 +0200388static const struct snd_pcm_ops ct_pcm_capture_ops = {
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200389 .open = ct_pcm_capture_open,
390 .close = ct_pcm_capture_close,
391 .ioctl = snd_pcm_lib_ioctl,
392 .hw_params = ct_pcm_hw_params,
393 .hw_free = ct_pcm_hw_free,
394 .prepare = ct_pcm_capture_prepare,
395 .trigger = ct_pcm_capture_trigger,
396 .pointer = ct_pcm_capture_pointer,
Takashi Iwaic76157d2009-06-02 15:26:19 +0200397 .page = snd_pcm_sgbuf_ops_page,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200398};
399
Takashi Iwai8d50cdc2012-08-03 18:03:17 +0200400static const struct snd_pcm_chmap_elem surround_map[] = {
401 { .channels = 1,
Takashi Iwai7b31d002012-09-12 18:06:54 +0200402 .map = { SNDRV_CHMAP_MONO } },
Takashi Iwai8d50cdc2012-08-03 18:03:17 +0200403 { .channels = 2,
404 .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
405 { }
406};
407
408static const struct snd_pcm_chmap_elem clfe_map[] = {
409 { .channels = 1,
Takashi Iwai7b31d002012-09-12 18:06:54 +0200410 .map = { SNDRV_CHMAP_MONO } },
Takashi Iwai8d50cdc2012-08-03 18:03:17 +0200411 { .channels = 2,
412 .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
413 { }
414};
415
416static const struct snd_pcm_chmap_elem side_map[] = {
417 { .channels = 1,
Takashi Iwai7b31d002012-09-12 18:06:54 +0200418 .map = { SNDRV_CHMAP_MONO } },
Takashi Iwai8d50cdc2012-08-03 18:03:17 +0200419 { .channels = 2,
420 .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
421 { }
422};
423
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200424/* Create ALSA pcm device */
425int ct_alsa_pcm_create(struct ct_atc *atc,
426 enum CTALSADEVS device,
427 const char *device_name)
428{
429 struct snd_pcm *pcm;
Takashi Iwai8d50cdc2012-08-03 18:03:17 +0200430 const struct snd_pcm_chmap_elem *map;
431 int chs;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200432 int err;
433 int playback_count, capture_count;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200434
Maarten Lankhorst391e6912011-08-24 00:48:59 +0200435 playback_count = (IEC958 == device) ? 1 : 256;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200436 capture_count = (FRONT == device) ? 1 : 0;
Takashi Iwai8372d4982009-06-02 14:27:56 +0200437 err = snd_pcm_new(atc->card, "ctxfi", device,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200438 playback_count, capture_count, &pcm);
439 if (err < 0) {
Sudip Mukherjee0cae90a2014-09-29 14:33:26 +0530440 dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",
441 err);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200442 return err;
443 }
444
445 pcm->private_data = atc;
446 pcm->info_flags = 0;
447 pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
Takashi Iwai8372d4982009-06-02 14:27:56 +0200448 strlcpy(pcm->name, device_name, sizeof(pcm->name));
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200449
450 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
451
452 if (FRONT == device)
453 snd_pcm_set_ops(pcm,
454 SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
455
Takashi Iwaic76157d2009-06-02 15:26:19 +0200456 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200457 snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
458
Takashi Iwai8d50cdc2012-08-03 18:03:17 +0200459 chs = 2;
460 switch (device) {
461 case FRONT:
462 chs = 8;
463 map = snd_pcm_std_chmaps;
464 break;
465 case SURROUND:
466 map = surround_map;
467 break;
468 case CLFE:
469 map = clfe_map;
470 break;
471 case SIDE:
472 map = side_map;
473 break;
474 default:
475 map = snd_pcm_std_chmaps;
476 break;
477 }
478 err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,
479 0, NULL);
480 if (err < 0)
481 return err;
482
Takashi Iwaic7561cd2012-08-14 18:12:04 +0200483#ifdef CONFIG_PM_SLEEP
Wai Yew CHAY29959a02009-06-22 14:52:34 +0200484 atc->pcms[device] = pcm;
485#endif
486
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200487 return 0;
488}