[PATCH] v4l: fix tuning with MXB driver
[linux-2.6] / drivers / media / dvb / frontends / lgdt3302.c
1 /*
2  *    Support for LGDT3302 (DViCO FustionHDTV 3 Gold) - VSB/QAM
3  *
4  *    Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
5  *
6  *    Based on code from  Kirk Lapray <kirk_lapray@bigfoot.com>
7  *                           Copyright (C) 2005
8  *
9  *    This program is free software; you can redistribute it and/or modify
10  *    it under the terms of the GNU General Public License as published by
11  *    the Free Software Foundation; either version 2 of the License, or
12  *    (at your option) any later version.
13  *
14  *    This program is distributed in the hope that it will be useful,
15  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *    GNU General Public License for more details.
18  *
19  *    You should have received a copy of the GNU General Public License
20  *    along with this program; if not, write to the Free Software
21  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  */
24
25 /*
26  *                      NOTES ABOUT THIS DRIVER
27  *
28  * This driver supports DViCO FusionHDTV 3 Gold under Linux.
29  *
30  * TODO:
31  * BER and signal strength always return 0.
32  *
33  */
34
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/moduleparam.h>
38 #include <linux/init.h>
39 #include <linux/delay.h>
40 #include <asm/byteorder.h>
41
42 #include "dvb_frontend.h"
43 #include "dvb-pll.h"
44 #include "lgdt3302_priv.h"
45 #include "lgdt3302.h"
46
47 static int debug = 0;
48 module_param(debug, int, 0644);
49 MODULE_PARM_DESC(debug,"Turn on/off lgdt3302 frontend debugging (default:off).");
50 #define dprintk(args...) \
51 do { \
52 if (debug) printk(KERN_DEBUG "lgdt3302: " args); \
53 } while (0)
54
55 struct lgdt3302_state
56 {
57         struct i2c_adapter* i2c;
58         struct dvb_frontend_ops ops;
59
60         /* Configuration settings */
61         const struct lgdt3302_config* config;
62
63         struct dvb_frontend frontend;
64
65         /* Demodulator private data */
66         fe_modulation_t current_modulation;
67
68         /* Tuner private data */
69         u32 current_frequency;
70 };
71
72 static int i2c_writebytes (struct lgdt3302_state* state,
73                            u8 addr, /* demod_address or pll_address */
74                            u8 *buf, /* data bytes to send */
75                            int len  /* number of bytes to send */ )
76 {
77         u8 tmp[] = { buf[0], buf[1] };
78         struct i2c_msg msg =
79                 { .addr = addr, .flags = 0, .buf = tmp,  .len = 2 };
80         int err;
81         int i;
82
83         for (i=1; i<len; i++) {
84                 tmp[1] = buf[i];
85                 if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
86                         printk(KERN_WARNING "lgdt3302: %s error (addr %02x <- %02x, err == %i)\n", __FUNCTION__, addr, buf[0], err);
87                         if (err < 0)
88                                 return err;
89                         else
90                                 return -EREMOTEIO;
91                 }
92                 tmp[0]++;
93         }
94         return 0;
95 }
96
97 #if 0
98 static int i2c_readbytes (struct lgdt3302_state* state,
99                           u8 addr, /* demod_address or pll_address */
100                           u8 *buf, /* holds data bytes read */
101                           int len  /* number of bytes to read */ )
102 {
103         struct i2c_msg msg =
104                 { .addr = addr, .flags = I2C_M_RD, .buf = buf,  .len = len };
105         int err;
106
107         if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
108                 printk(KERN_WARNING "lgdt3302: %s error (addr %02x, err == %i)\n", __FUNCTION__, addr, err);
109                 return -EREMOTEIO;
110         }
111         return 0;
112 }
113 #endif
114
115 /*
116  * This routine writes the register (reg) to the demod bus
117  * then reads the data returned for (len) bytes.
118  */
119
120 static u8 i2c_selectreadbytes (struct lgdt3302_state* state,
121                                enum I2C_REG reg, u8* buf, int len)
122 {
123         u8 wr [] = { reg };
124         struct i2c_msg msg [] = {
125                 { .addr = state->config->demod_address,
126                   .flags = 0, .buf = wr,  .len = 1 },
127                 { .addr = state->config->demod_address,
128                   .flags = I2C_M_RD, .buf = buf, .len = len },
129         };
130         int ret;
131         ret = i2c_transfer(state->i2c, msg, 2);
132         if (ret != 2) {
133                 printk(KERN_WARNING "lgdt3302: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __FUNCTION__, state->config->demod_address, reg, ret);
134         } else {
135                 ret = 0;
136         }
137         return ret;
138 }
139
140 /* Software reset */
141 int lgdt3302_SwReset(struct lgdt3302_state* state)
142 {
143         u8 ret;
144         u8 reset[] = {
145                 IRQ_MASK,
146                 0x00 /* bit 6 is active low software reset
147                       * bits 5-0 are 1 to mask interrupts */
148         };
149
150         ret = i2c_writebytes(state,
151                              state->config->demod_address,
152                              reset, sizeof(reset));
153         if (ret == 0) {
154                 /* spec says reset takes 100 ns why wait */
155                 /* mdelay(100);    */ /* keep low for 100mS */
156                 reset[1] = 0x7f;      /* force reset high (inactive)
157                                        * and unmask interrupts */
158                 ret = i2c_writebytes(state,
159                                      state->config->demod_address,
160                                      reset, sizeof(reset));
161         }
162         /* Spec does not indicate a need for this either */
163         /*mdelay(5); */               /* wait 5 msec before doing more */
164         return ret;
165 }
166
167 static int lgdt3302_init(struct dvb_frontend* fe)
168 {
169         /* Hardware reset is done using gpio[0] of cx23880x chip.
170          * I'd like to do it here, but don't know how to find chip address.
171          * cx88-cards.c arranges for the reset bit to be inactive (high).
172          * Maybe there needs to be a callable function in cx88-core or
173          * the caller of this function needs to do it. */
174
175         dprintk("%s entered\n", __FUNCTION__);
176         return lgdt3302_SwReset((struct lgdt3302_state*) fe->demodulator_priv);
177 }
178
179 static int lgdt3302_read_ber(struct dvb_frontend* fe, u32* ber)
180 {
181         *ber = 0; /* Dummy out for now */
182         return 0;
183 }
184
185 static int lgdt3302_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
186 {
187         struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
188         u8 buf[2];
189
190         i2c_selectreadbytes(state, PACKET_ERR_COUNTER1, buf, sizeof(buf));
191
192         *ucblocks = (buf[0] << 8) | buf[1];
193         return 0;
194 }
195
196 static int lgdt3302_set_parameters(struct dvb_frontend* fe,
197                                    struct dvb_frontend_parameters *param)
198 {
199         struct lgdt3302_state* state =
200                 (struct lgdt3302_state*) fe->demodulator_priv;
201
202         /* Use 50MHz parameter values from spec sheet since xtal is 50 */
203         static u8 top_ctrl_cfg[]   = { TOP_CONTROL, 0x03 };
204         static u8 vsb_freq_cfg[]   = { VSB_CARRIER_FREQ0, 0x00, 0x87, 0x8e, 0x01 };
205         static u8 demux_ctrl_cfg[] = { DEMUX_CONTROL, 0xfb };
206         static u8 agc_rf_cfg[]     = { AGC_RF_BANDWIDTH0, 0x40, 0x93, 0x00 };
207         static u8 agc_ctrl_cfg[]   = { AGC_FUNC_CTRL2, 0xc6, 0x40 };
208         static u8 agc_delay_cfg[]  = { AGC_DELAY0, 0x07, 0x00, 0xfe };
209         static u8 agc_loop_cfg[]   = { AGC_LOOP_BANDWIDTH0, 0x08, 0x9a };
210
211         /* Change only if we are actually changing the modulation */
212         if (state->current_modulation != param->u.vsb.modulation) {
213                 switch(param->u.vsb.modulation) {
214                 case VSB_8:
215                         dprintk("%s: VSB_8 MODE\n", __FUNCTION__);
216
217                         /* Select VSB mode and serial MPEG interface */
218                         top_ctrl_cfg[1] = 0x07;
219
220                         /* Select ANT connector if supported by card */
221                         if (state->config->pll_rf_set)
222                                 state->config->pll_rf_set(fe, 1);
223                         break;
224
225                 case QAM_64:
226                         dprintk("%s: QAM_64 MODE\n", __FUNCTION__);
227
228                         /* Select QAM_64 mode and serial MPEG interface */
229                         top_ctrl_cfg[1] = 0x04;
230
231                         /* Select CABLE connector if supported by card */
232                         if (state->config->pll_rf_set)
233                                 state->config->pll_rf_set(fe, 0);
234                         break;
235
236                 case QAM_256:
237                         dprintk("%s: QAM_256 MODE\n", __FUNCTION__);
238
239                         /* Select QAM_256 mode and serial MPEG interface */
240                         top_ctrl_cfg[1] = 0x05;
241
242                         /* Select CABLE connector if supported by card */
243                         if (state->config->pll_rf_set)
244                                 state->config->pll_rf_set(fe, 0);
245                         break;
246                 default:
247                         printk(KERN_WARNING "lgdt3302: %s: Modulation type(%d) UNSUPPORTED\n", __FUNCTION__, param->u.vsb.modulation);
248                         return -1;
249                 }
250                 /* Initializations common to all modes */
251
252                 /* Select the requested mode */
253                 i2c_writebytes(state, state->config->demod_address,
254                                top_ctrl_cfg, sizeof(top_ctrl_cfg));
255
256                 /* Change the value of IFBW[11:0]
257                    of AGC IF/RF loop filter bandwidth register */
258                 i2c_writebytes(state, state->config->demod_address,
259                                agc_rf_cfg, sizeof(agc_rf_cfg));
260
261                 /* Change the value of bit 6, 'nINAGCBY' and
262                    'NSSEL[1:0] of ACG function control register 2 */
263                 /* Change the value of bit 6 'RFFIX'
264                    of AGC function control register 3 */
265                 i2c_writebytes(state, state->config->demod_address,
266                                agc_ctrl_cfg, sizeof(agc_ctrl_cfg));
267
268                 /* Change the TPCLK pin polarity
269                    data is valid on falling clock */
270                 i2c_writebytes(state, state->config->demod_address,
271                                demux_ctrl_cfg, sizeof(demux_ctrl_cfg));
272
273                 /* Change the value of NCOCTFV[25:0] of carrier
274                    recovery center frequency register */
275                 i2c_writebytes(state, state->config->demod_address,
276                                        vsb_freq_cfg, sizeof(vsb_freq_cfg));
277
278                 /* Set the value of 'INLVTHD' register 0x2a/0x2c to 0x7fe */
279                 i2c_writebytes(state, state->config->demod_address,
280                                agc_delay_cfg, sizeof(agc_delay_cfg));
281
282                 /* Change the value of IAGCBW[15:8]
283                    of inner AGC loop filter bandwith */
284                 i2c_writebytes(state, state->config->demod_address,
285                                agc_loop_cfg, sizeof(agc_loop_cfg));
286
287                 state->config->set_ts_params(fe, 0);
288                 state->current_modulation = param->u.vsb.modulation;
289         }
290
291         /* Change only if we are actually changing the channel */
292         if (state->current_frequency != param->frequency) {
293                 u8 buf[5];
294
295                 /* This must be done before the initialized msg is declared */
296                 state->config->pll_set(fe, param, buf);
297
298                 struct i2c_msg msg =
299                         { .addr = buf[0], .flags = 0, .buf = &buf[1], .len = 4 };
300                 int err;
301
302                 dprintk("%s: tuner at 0x%02x bytes: 0x%02x 0x%02x "
303                         "0x%02x 0x%02x\n", __FUNCTION__,
304                         buf[0],buf[1],buf[2],buf[3],buf[4]);
305                 if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
306                         printk(KERN_WARNING "lgdt3302: %s error (addr %02x <- %02x, err = %i)\n", __FUNCTION__, buf[0], buf[1], err);
307                         if (err < 0)
308                                 return err;
309                         else
310                                 return -EREMOTEIO;
311                 }
312 #if 0
313                 /* Check the status of the tuner pll */
314                 i2c_readbytes(state, buf[0], &buf[1], 1);
315                 dprintk("%s: tuner status byte = 0x%02x\n", __FUNCTION__, buf[1]);
316 #endif
317                 /* Update current frequency */
318                 state->current_frequency = param->frequency;
319         }
320         lgdt3302_SwReset(state);
321         return 0;
322 }
323
324 static int lgdt3302_get_frontend(struct dvb_frontend* fe,
325                                  struct dvb_frontend_parameters* param)
326 {
327         struct lgdt3302_state *state = fe->demodulator_priv;
328         param->frequency = state->current_frequency;
329         return 0;
330 }
331
332 static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
333 {
334         struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
335         u8 buf[3];
336
337         *status = 0; /* Reset status result */
338
339         /*
340          * You must set the Mask bits to 1 in the IRQ_MASK in order
341          * to see that status bit in the IRQ_STATUS register.
342          * This is done in SwReset();
343          */
344
345         /* AGC status register */
346         i2c_selectreadbytes(state, AGC_STATUS, buf, 1);
347         dprintk("%s: AGC_STATUS = 0x%02x\n", __FUNCTION__, buf[0]);
348         if ((buf[0] & 0x0c) == 0x8){
349                 /* Test signal does not exist flag */
350                 /* as well as the AGC lock flag.   */
351                 *status |= FE_HAS_SIGNAL;
352         } else {
353                 /* Without a signal all other status bits are meaningless */
354                 return 0;
355         }
356
357         /* signal status */
358         i2c_selectreadbytes(state, TOP_CONTROL, buf, sizeof(buf));
359         dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __FUNCTION__, buf[0], buf[1], buf[2]);
360
361 #if 0
362         /* Alternative method to check for a signal */
363         /* using the SNR good/bad interrupts.   */
364         if ((buf[2] & 0x30) == 0x10)
365                 *status |= FE_HAS_SIGNAL;
366 #endif
367
368         /* sync status */
369         if ((buf[2] & 0x03) == 0x01) {
370                 *status |= FE_HAS_SYNC;
371         }
372
373         /* FEC error status */
374         if ((buf[2] & 0x0c) == 0x08) {
375                 *status |= FE_HAS_LOCK;
376                 *status |= FE_HAS_VITERBI;
377         }
378
379         /* Carrier Recovery Lock Status Register */
380         i2c_selectreadbytes(state, CARRIER_LOCK, buf, 1);
381         dprintk("%s: CARRIER_LOCK = 0x%02x\n", __FUNCTION__, buf[0]);
382         switch (state->current_modulation) {
383         case QAM_256:
384         case QAM_64:
385                 /* Need to undestand why there are 3 lock levels here */
386                 if ((buf[0] & 0x07) == 0x07)
387                         *status |= FE_HAS_CARRIER;
388                 break;
389         case VSB_8:
390                 if ((buf[0] & 0x80) == 0x80)
391                         *status |= FE_HAS_CARRIER;
392                 break;
393         default:
394                 printk("KERN_WARNING lgdt3302: %s: Modulation set to unsupported value\n", __FUNCTION__);
395         }
396
397         return 0;
398 }
399
400 static int lgdt3302_read_signal_strength(struct dvb_frontend* fe, u16* strength)
401 {
402         /* not directly available. */
403         return 0;
404 }
405
406 static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr)
407 {
408 #ifdef SNR_IN_DB
409         /*
410          * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise)
411          * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker
412          * respectively. The following tables are built on these formulas.
413          * The usual definition is SNR = 20 log10(signal/noise)
414          * If the specification is wrong the value retuned is 1/2 the actual SNR in db.
415          *
416          * This table is a an ordered list of noise values computed by the
417          * formula from the spec sheet such that the index into the table
418          * starting at 43 or 45 is the SNR value in db. There are duplicate noise
419          * value entries at the beginning because the SNR varies more than
420          * 1 db for a change of 1 digit in noise at very small values of noise.
421          *
422          * Examples from SNR_EQ table:
423          * noise SNR
424          *   0    43
425          *   1    42
426          *   2    39
427          *   3    37
428          *   4    36
429          *   5    35
430          *   6    34
431          *   7    33
432          *   8    33
433          *   9    32
434          *   10   32
435          *   11   31
436          *   12   31
437          *   13   30
438          */
439
440         static const u32 SNR_EQ[] =
441                 { 1,     2,      2,      2, 3,      3,      4,     4,     5,     7,
442                   9,     11,     13,     17, 21,     26,     33,    41,    52,    65,
443                   81,    102,    129,    162, 204,    257,    323,   406,   511,   644,
444                   810,   1020,   1284,   1616, 2035,   2561,   3224,  4059,  5110,  6433,
445                   8098,  10195,  12835,  16158, 20341,  25608,  32238, 40585, 51094, 64323,
446                   80978, 101945, 128341, 161571, 203406, 256073, 0x40000
447                 };
448
449         static const u32 SNR_PH[] =
450                 { 1,     2,      2,      2,      3,      3,     4,     5,     6,     8,
451                   10,    12,     15,     19,     23,     29, 37,    46,    58,    73,
452                   91,    115,    144,    182,    229,    288, 362,   456,   574,   722,
453                   909,   1144,   1440,   1813,   2282,   2873, 3617,  4553,  5732,  7216,
454                   9084,  11436,  14396,  18124,  22817,  28724,  36161, 45524, 57312, 72151,
455                   90833, 114351, 143960, 181235, 228161, 0x040000
456                 };
457
458         static u8 buf[5];/* read data buffer */
459         static u32 noise;   /* noise value */
460         static u32 snr_db;  /* index into SNR_EQ[] */
461         struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
462
463         /* read both equalizer and pase tracker noise data */
464         i2c_selectreadbytes(state, EQPH_ERR0, buf, sizeof(buf));
465
466         if (state->current_modulation == VSB_8) {
467                 /* Equalizer Mean-Square Error Register for VSB */
468                 noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
469
470                 /*
471                  * Look up noise value in table.
472                  * A better search algorithm could be used...
473                  * watch out there are duplicate entries.
474                  */
475                 for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) {
476                         if (noise < SNR_EQ[snr_db]) {
477                                 *snr = 43 - snr_db;
478                                 break;
479                         }
480                 }
481         } else {
482                 /* Phase Tracker Mean-Square Error Register for QAM */
483                 noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
484
485                 /* Look up noise value in table. */
486                 for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) {
487                         if (noise < SNR_PH[snr_db]) {
488                                 *snr = 45 - snr_db;
489                                 break;
490                         }
491                 }
492         }
493 #else
494         /* Return the raw noise value */
495         static u8 buf[5];/* read data buffer */
496         static u32 noise;   /* noise value */
497         struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
498
499         /* read both equalizer and pase tracker noise data */
500         i2c_selectreadbytes(state, EQPH_ERR0, buf, sizeof(buf));
501
502         if (state->current_modulation == VSB_8) {
503                 /* Equalizer Mean-Square Error Register for VSB */
504                 noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
505         } else {
506                 /* Phase Tracker Mean-Square Error Register for QAM */
507                 noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
508         }
509
510         /* Small values for noise mean signal is better so invert noise */
511         /* Noise is 19 bit value so discard 3 LSB*/
512         *snr = ~noise>>3;
513 #endif
514
515         dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
516
517         return 0;
518 }
519
520 static int lgdt3302_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
521 {
522         /* I have no idea about this - it may not be needed */
523         fe_tune_settings->min_delay_ms = 500;
524         fe_tune_settings->step_size = 0;
525         fe_tune_settings->max_drift = 0;
526         return 0;
527 }
528
529 static void lgdt3302_release(struct dvb_frontend* fe)
530 {
531         struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
532         kfree(state);
533 }
534
535 static struct dvb_frontend_ops lgdt3302_ops;
536
537 struct dvb_frontend* lgdt3302_attach(const struct lgdt3302_config* config,
538                                      struct i2c_adapter* i2c)
539 {
540         struct lgdt3302_state* state = NULL;
541         u8 buf[1];
542
543         /* Allocate memory for the internal state */
544         state = (struct lgdt3302_state*) kmalloc(sizeof(struct lgdt3302_state), GFP_KERNEL);
545         if (state == NULL)
546                 goto error;
547         memset(state,0,sizeof(*state));
548
549         /* Setup the state */
550         state->config = config;
551         state->i2c = i2c;
552         memcpy(&state->ops, &lgdt3302_ops, sizeof(struct dvb_frontend_ops));
553         /* Verify communication with demod chip */
554         if (i2c_selectreadbytes(state, 2, buf, 1))
555                 goto error;
556
557         state->current_frequency = -1;
558         state->current_modulation = -1;
559
560         /* Create dvb_frontend */
561         state->frontend.ops = &state->ops;
562         state->frontend.demodulator_priv = state;
563         return &state->frontend;
564
565 error:
566         if (state)
567                 kfree(state);
568         dprintk("%s: ERROR\n",__FUNCTION__);
569         return NULL;
570 }
571
572 static struct dvb_frontend_ops lgdt3302_ops = {
573         .info = {
574                 .name= "LG Electronics LGDT3302 VSB/QAM Frontend",
575                 .type = FE_ATSC,
576                 .frequency_min= 54000000,
577                 .frequency_max= 858000000,
578                 .frequency_stepsize= 62500,
579                 /* Symbol rate is for all VSB modes need to check QAM */
580                 .symbol_rate_min    = 10762000,
581                 .symbol_rate_max    = 10762000,
582                 .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
583         },
584         .init                 = lgdt3302_init,
585         .set_frontend         = lgdt3302_set_parameters,
586         .get_frontend         = lgdt3302_get_frontend,
587         .get_tune_settings    = lgdt3302_get_tune_settings,
588         .read_status          = lgdt3302_read_status,
589         .read_ber             = lgdt3302_read_ber,
590         .read_signal_strength = lgdt3302_read_signal_strength,
591         .read_snr             = lgdt3302_read_snr,
592         .read_ucblocks        = lgdt3302_read_ucblocks,
593         .release              = lgdt3302_release,
594 };
595
596 MODULE_DESCRIPTION("LGDT3302 [DViCO FusionHDTV 3 Gold] (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
597 MODULE_AUTHOR("Wilson Michaels");
598 MODULE_LICENSE("GPL");
599
600 EXPORT_SYMBOL(lgdt3302_attach);
601
602 /*
603  * Local variables:
604  * c-basic-offset: 8
605  * compile-command: "make DVB=1"
606  * End:
607  */