Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/emu10k1/io.c
10818 views
1
/*
2
* Copyright (c) by Jaroslav Kysela <[email protected]>
3
* Creative Labs, Inc.
4
* Routines for control of EMU10K1 chips
5
*
6
* BUGS:
7
* --
8
*
9
* TODO:
10
* --
11
*
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or
15
* (at your option) any later version.
16
*
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
21
*
22
* You should have received a copy of the GNU General Public License
23
* along with this program; if not, write to the Free Software
24
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
*
26
*/
27
28
#include <linux/time.h>
29
#include <sound/core.h>
30
#include <sound/emu10k1.h>
31
#include <linux/delay.h>
32
#include "p17v.h"
33
34
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
35
{
36
unsigned long flags;
37
unsigned int regptr, val;
38
unsigned int mask;
39
40
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
41
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
42
43
if (reg & 0xff000000) {
44
unsigned char size, offset;
45
46
size = (reg >> 24) & 0x3f;
47
offset = (reg >> 16) & 0x1f;
48
mask = ((1 << size) - 1) << offset;
49
50
spin_lock_irqsave(&emu->emu_lock, flags);
51
outl(regptr, emu->port + PTR);
52
val = inl(emu->port + DATA);
53
spin_unlock_irqrestore(&emu->emu_lock, flags);
54
55
return (val & mask) >> offset;
56
} else {
57
spin_lock_irqsave(&emu->emu_lock, flags);
58
outl(regptr, emu->port + PTR);
59
val = inl(emu->port + DATA);
60
spin_unlock_irqrestore(&emu->emu_lock, flags);
61
return val;
62
}
63
}
64
65
EXPORT_SYMBOL(snd_emu10k1_ptr_read);
66
67
void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
68
{
69
unsigned int regptr;
70
unsigned long flags;
71
unsigned int mask;
72
73
if (!emu) {
74
snd_printk(KERN_ERR "ptr_write: emu is null!\n");
75
dump_stack();
76
return;
77
}
78
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
79
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
80
81
if (reg & 0xff000000) {
82
unsigned char size, offset;
83
84
size = (reg >> 24) & 0x3f;
85
offset = (reg >> 16) & 0x1f;
86
mask = ((1 << size) - 1) << offset;
87
data = (data << offset) & mask;
88
89
spin_lock_irqsave(&emu->emu_lock, flags);
90
outl(regptr, emu->port + PTR);
91
data |= inl(emu->port + DATA) & ~mask;
92
outl(data, emu->port + DATA);
93
spin_unlock_irqrestore(&emu->emu_lock, flags);
94
} else {
95
spin_lock_irqsave(&emu->emu_lock, flags);
96
outl(regptr, emu->port + PTR);
97
outl(data, emu->port + DATA);
98
spin_unlock_irqrestore(&emu->emu_lock, flags);
99
}
100
}
101
102
EXPORT_SYMBOL(snd_emu10k1_ptr_write);
103
104
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
105
unsigned int reg,
106
unsigned int chn)
107
{
108
unsigned long flags;
109
unsigned int regptr, val;
110
111
regptr = (reg << 16) | chn;
112
113
spin_lock_irqsave(&emu->emu_lock, flags);
114
outl(regptr, emu->port + 0x20 + PTR);
115
val = inl(emu->port + 0x20 + DATA);
116
spin_unlock_irqrestore(&emu->emu_lock, flags);
117
return val;
118
}
119
120
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
121
unsigned int reg,
122
unsigned int chn,
123
unsigned int data)
124
{
125
unsigned int regptr;
126
unsigned long flags;
127
128
regptr = (reg << 16) | chn;
129
130
spin_lock_irqsave(&emu->emu_lock, flags);
131
outl(regptr, emu->port + 0x20 + PTR);
132
outl(data, emu->port + 0x20 + DATA);
133
spin_unlock_irqrestore(&emu->emu_lock, flags);
134
}
135
136
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
137
unsigned int data)
138
{
139
unsigned int reset, set;
140
unsigned int reg, tmp;
141
int n, result;
142
int err = 0;
143
144
/* This function is not re-entrant, so protect against it. */
145
spin_lock(&emu->spi_lock);
146
if (emu->card_capabilities->ca0108_chip)
147
reg = 0x3c; /* PTR20, reg 0x3c */
148
else {
149
/* For other chip types the SPI register
150
* is currently unknown. */
151
err = 1;
152
goto spi_write_exit;
153
}
154
if (data > 0xffff) {
155
/* Only 16bit values allowed */
156
err = 1;
157
goto spi_write_exit;
158
}
159
160
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
161
reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
162
set = reset | 0x10000; /* Set xxx1xxxx */
163
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
164
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
165
snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
166
result = 1;
167
/* Wait for status bit to return to 0 */
168
for (n = 0; n < 100; n++) {
169
udelay(10);
170
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
171
if (!(tmp & 0x10000)) {
172
result = 0;
173
break;
174
}
175
}
176
if (result) {
177
/* Timed out */
178
err = 1;
179
goto spi_write_exit;
180
}
181
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
182
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
183
err = 0;
184
spi_write_exit:
185
spin_unlock(&emu->spi_lock);
186
return err;
187
}
188
189
/* The ADC does not support i2c read, so only write is implemented */
190
int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
191
u32 reg,
192
u32 value)
193
{
194
u32 tmp;
195
int timeout = 0;
196
int status;
197
int retry;
198
int err = 0;
199
200
if ((reg > 0x7f) || (value > 0x1ff)) {
201
snd_printk(KERN_ERR "i2c_write: invalid values.\n");
202
return -EINVAL;
203
}
204
205
/* This function is not re-entrant, so protect against it. */
206
spin_lock(&emu->i2c_lock);
207
208
tmp = reg << 25 | value << 16;
209
210
/* This controls the I2C connected to the WM8775 ADC Codec */
211
snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
212
tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
213
214
for (retry = 0; retry < 10; retry++) {
215
/* Send the data to i2c */
216
tmp = 0;
217
tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
218
snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
219
220
/* Wait till the transaction ends */
221
while (1) {
222
mdelay(1);
223
status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
224
timeout++;
225
if ((status & I2C_A_ADC_START) == 0)
226
break;
227
228
if (timeout > 1000) {
229
snd_printk(KERN_WARNING
230
"emu10k1:I2C:timeout status=0x%x\n",
231
status);
232
break;
233
}
234
}
235
//Read back and see if the transaction is successful
236
if ((status & I2C_A_ADC_ABORT) == 0)
237
break;
238
}
239
240
if (retry == 10) {
241
snd_printk(KERN_ERR "Writing to ADC failed!\n");
242
snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
243
status, reg, value);
244
/* dump_stack(); */
245
err = -EINVAL;
246
}
247
248
spin_unlock(&emu->i2c_lock);
249
return err;
250
}
251
252
int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
253
{
254
unsigned long flags;
255
256
if (reg > 0x3f)
257
return 1;
258
reg += 0x40; /* 0x40 upwards are registers. */
259
if (value > 0x3f) /* 0 to 0x3f are values */
260
return 1;
261
spin_lock_irqsave(&emu->emu_lock, flags);
262
outl(reg, emu->port + A_IOCFG);
263
udelay(10);
264
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
265
udelay(10);
266
outl(value, emu->port + A_IOCFG);
267
udelay(10);
268
outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
269
spin_unlock_irqrestore(&emu->emu_lock, flags);
270
271
return 0;
272
}
273
274
int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
275
{
276
unsigned long flags;
277
if (reg > 0x3f)
278
return 1;
279
reg += 0x40; /* 0x40 upwards are registers. */
280
spin_lock_irqsave(&emu->emu_lock, flags);
281
outl(reg, emu->port + A_IOCFG);
282
udelay(10);
283
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
284
udelay(10);
285
*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
286
spin_unlock_irqrestore(&emu->emu_lock, flags);
287
288
return 0;
289
}
290
291
/* Each Destination has one and only one Source,
292
* but one Source can feed any number of Destinations simultaneously.
293
*/
294
int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
295
{
296
snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
297
snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
298
snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
299
snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
300
301
return 0;
302
}
303
304
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
305
{
306
unsigned long flags;
307
unsigned int enable;
308
309
spin_lock_irqsave(&emu->emu_lock, flags);
310
enable = inl(emu->port + INTE) | intrenb;
311
outl(enable, emu->port + INTE);
312
spin_unlock_irqrestore(&emu->emu_lock, flags);
313
}
314
315
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
316
{
317
unsigned long flags;
318
unsigned int enable;
319
320
spin_lock_irqsave(&emu->emu_lock, flags);
321
enable = inl(emu->port + INTE) & ~intrenb;
322
outl(enable, emu->port + INTE);
323
spin_unlock_irqrestore(&emu->emu_lock, flags);
324
}
325
326
void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
327
{
328
unsigned long flags;
329
unsigned int val;
330
331
spin_lock_irqsave(&emu->emu_lock, flags);
332
/* voice interrupt */
333
if (voicenum >= 32) {
334
outl(CLIEH << 16, emu->port + PTR);
335
val = inl(emu->port + DATA);
336
val |= 1 << (voicenum - 32);
337
} else {
338
outl(CLIEL << 16, emu->port + PTR);
339
val = inl(emu->port + DATA);
340
val |= 1 << voicenum;
341
}
342
outl(val, emu->port + DATA);
343
spin_unlock_irqrestore(&emu->emu_lock, flags);
344
}
345
346
void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
347
{
348
unsigned long flags;
349
unsigned int val;
350
351
spin_lock_irqsave(&emu->emu_lock, flags);
352
/* voice interrupt */
353
if (voicenum >= 32) {
354
outl(CLIEH << 16, emu->port + PTR);
355
val = inl(emu->port + DATA);
356
val &= ~(1 << (voicenum - 32));
357
} else {
358
outl(CLIEL << 16, emu->port + PTR);
359
val = inl(emu->port + DATA);
360
val &= ~(1 << voicenum);
361
}
362
outl(val, emu->port + DATA);
363
spin_unlock_irqrestore(&emu->emu_lock, flags);
364
}
365
366
void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
367
{
368
unsigned long flags;
369
370
spin_lock_irqsave(&emu->emu_lock, flags);
371
/* voice interrupt */
372
if (voicenum >= 32) {
373
outl(CLIPH << 16, emu->port + PTR);
374
voicenum = 1 << (voicenum - 32);
375
} else {
376
outl(CLIPL << 16, emu->port + PTR);
377
voicenum = 1 << voicenum;
378
}
379
outl(voicenum, emu->port + DATA);
380
spin_unlock_irqrestore(&emu->emu_lock, flags);
381
}
382
383
void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
384
{
385
unsigned long flags;
386
unsigned int val;
387
388
spin_lock_irqsave(&emu->emu_lock, flags);
389
/* voice interrupt */
390
if (voicenum >= 32) {
391
outl(HLIEH << 16, emu->port + PTR);
392
val = inl(emu->port + DATA);
393
val |= 1 << (voicenum - 32);
394
} else {
395
outl(HLIEL << 16, emu->port + PTR);
396
val = inl(emu->port + DATA);
397
val |= 1 << voicenum;
398
}
399
outl(val, emu->port + DATA);
400
spin_unlock_irqrestore(&emu->emu_lock, flags);
401
}
402
403
void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
404
{
405
unsigned long flags;
406
unsigned int val;
407
408
spin_lock_irqsave(&emu->emu_lock, flags);
409
/* voice interrupt */
410
if (voicenum >= 32) {
411
outl(HLIEH << 16, emu->port + PTR);
412
val = inl(emu->port + DATA);
413
val &= ~(1 << (voicenum - 32));
414
} else {
415
outl(HLIEL << 16, emu->port + PTR);
416
val = inl(emu->port + DATA);
417
val &= ~(1 << voicenum);
418
}
419
outl(val, emu->port + DATA);
420
spin_unlock_irqrestore(&emu->emu_lock, flags);
421
}
422
423
void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
424
{
425
unsigned long flags;
426
427
spin_lock_irqsave(&emu->emu_lock, flags);
428
/* voice interrupt */
429
if (voicenum >= 32) {
430
outl(HLIPH << 16, emu->port + PTR);
431
voicenum = 1 << (voicenum - 32);
432
} else {
433
outl(HLIPL << 16, emu->port + PTR);
434
voicenum = 1 << voicenum;
435
}
436
outl(voicenum, emu->port + DATA);
437
spin_unlock_irqrestore(&emu->emu_lock, flags);
438
}
439
440
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
441
{
442
unsigned long flags;
443
unsigned int sol;
444
445
spin_lock_irqsave(&emu->emu_lock, flags);
446
/* voice interrupt */
447
if (voicenum >= 32) {
448
outl(SOLEH << 16, emu->port + PTR);
449
sol = inl(emu->port + DATA);
450
sol |= 1 << (voicenum - 32);
451
} else {
452
outl(SOLEL << 16, emu->port + PTR);
453
sol = inl(emu->port + DATA);
454
sol |= 1 << voicenum;
455
}
456
outl(sol, emu->port + DATA);
457
spin_unlock_irqrestore(&emu->emu_lock, flags);
458
}
459
460
void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
461
{
462
unsigned long flags;
463
unsigned int sol;
464
465
spin_lock_irqsave(&emu->emu_lock, flags);
466
/* voice interrupt */
467
if (voicenum >= 32) {
468
outl(SOLEH << 16, emu->port + PTR);
469
sol = inl(emu->port + DATA);
470
sol &= ~(1 << (voicenum - 32));
471
} else {
472
outl(SOLEL << 16, emu->port + PTR);
473
sol = inl(emu->port + DATA);
474
sol &= ~(1 << voicenum);
475
}
476
outl(sol, emu->port + DATA);
477
spin_unlock_irqrestore(&emu->emu_lock, flags);
478
}
479
480
void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
481
{
482
volatile unsigned count;
483
unsigned int newtime = 0, curtime;
484
485
curtime = inl(emu->port + WC) >> 6;
486
while (wait-- > 0) {
487
count = 0;
488
while (count++ < 16384) {
489
newtime = inl(emu->port + WC) >> 6;
490
if (newtime != curtime)
491
break;
492
}
493
if (count > 16384)
494
break;
495
curtime = newtime;
496
}
497
}
498
499
unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
500
{
501
struct snd_emu10k1 *emu = ac97->private_data;
502
unsigned long flags;
503
unsigned short val;
504
505
spin_lock_irqsave(&emu->emu_lock, flags);
506
outb(reg, emu->port + AC97ADDRESS);
507
val = inw(emu->port + AC97DATA);
508
spin_unlock_irqrestore(&emu->emu_lock, flags);
509
return val;
510
}
511
512
void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
513
{
514
struct snd_emu10k1 *emu = ac97->private_data;
515
unsigned long flags;
516
517
spin_lock_irqsave(&emu->emu_lock, flags);
518
outb(reg, emu->port + AC97ADDRESS);
519
outw(data, emu->port + AC97DATA);
520
spin_unlock_irqrestore(&emu->emu_lock, flags);
521
}
522
523
/*
524
* convert rate to pitch
525
*/
526
527
unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
528
{
529
static u32 logMagTable[128] = {
530
0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
531
0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
532
0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
533
0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
534
0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
535
0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
536
0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
537
0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
538
0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
539
0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
540
0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
541
0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
542
0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
543
0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
544
0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
545
0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
546
};
547
static char logSlopeTable[128] = {
548
0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
549
0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
550
0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
551
0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
552
0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
553
0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
554
0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
555
0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
556
0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
557
0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
558
0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
559
0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
560
0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
561
0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
562
0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
563
0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
564
};
565
int i;
566
567
if (rate == 0)
568
return 0; /* Bail out if no leading "1" */
569
rate *= 11185; /* Scale 48000 to 0x20002380 */
570
for (i = 31; i > 0; i--) {
571
if (rate & 0x80000000) { /* Detect leading "1" */
572
return (((unsigned int) (i - 15) << 20) +
573
logMagTable[0x7f & (rate >> 24)] +
574
(0x7f & (rate >> 17)) *
575
logSlopeTable[0x7f & (rate >> 24)]);
576
}
577
rate <<= 1;
578
}
579
580
return 0; /* Should never reach this point */
581
}
582
583
584