2 Driver for Samsung S5H1420 QPSK Demodulator
4 Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <linux/string.h>
27 #include <linux/slab.h>
28 #include <linux/delay.h>
30 #include "dvb_frontend.h"
35 #define TONE_FREQ 22000
37 struct s5h1420_state {
38 struct i2c_adapter* i2c;
39 struct dvb_frontend_ops ops;
40 const struct s5h1420_config* config;
41 struct dvb_frontend frontend;
46 fe_code_rate_t fec_inner;
50 static u32 s5h1420_getsymbolrate(struct s5h1420_state* state);
51 static int s5h1420_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings);
55 #define dprintk if (debug) printk
57 static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data)
59 u8 buf [] = { reg, data };
60 struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
63 if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
64 dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
71 static u8 s5h1420_readreg (struct s5h1420_state* state, u8 reg)
76 struct i2c_msg msg1 = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 };
77 struct i2c_msg msg2 = { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 };
79 if ((ret = i2c_transfer (state->i2c, &msg1, 1)) != 1)
82 if ((ret = i2c_transfer (state->i2c, &msg2, 1)) != 1)
88 static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
90 struct s5h1420_state* state = fe->demodulator_priv;
94 s5h1420_writereg(state, 0x3c, (s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02);
98 s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03);
101 case SEC_VOLTAGE_OFF:
102 s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd);
109 static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
111 struct s5h1420_state* state = fe->demodulator_priv;
115 s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x74) | 0x08);
119 s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01);
126 static int s5h1420_send_master_cmd (struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
128 struct s5h1420_state* state = fe->demodulator_priv;
131 unsigned long timeout;
134 /* setup for DISEQC */
135 val = s5h1420_readreg(state, 0x3b);
136 s5h1420_writereg(state, 0x3b, 0x02);
139 /* write the DISEQC command bytes */
140 for(i=0; i< cmd->msg_len; i++) {
141 s5h1420_writereg(state, 0x3c + i, cmd->msg[i]);
144 /* kick off transmission */
145 s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | ((cmd->msg_len-1) << 4) | 0x08);
147 /* wait for transmission to complete */
148 timeout = jiffies + ((100*HZ) / 1000);
149 while(time_before(jiffies, timeout)) {
150 if (s5h1420_readreg(state, 0x3b) & 0x08)
155 if (time_after(jiffies, timeout))
158 /* restore original settings */
159 s5h1420_writereg(state, 0x3b, val);
164 static int s5h1420_recv_slave_reply (struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply)
166 struct s5h1420_state* state = fe->demodulator_priv;
170 unsigned long timeout;
173 /* setup for DISEQC recieve */
174 val = s5h1420_readreg(state, 0x3b);
175 s5h1420_writereg(state, 0x3b, 0x82); /* FIXME: guess - do we need to set DIS_RDY(0x08) in receive mode? */
178 /* wait for reception to complete */
179 timeout = jiffies + ((reply->timeout*HZ) / 1000);
180 while(time_before(jiffies, timeout)) {
181 if (!(s5h1420_readreg(state, 0x3b) & 0x80)) /* FIXME: do we test DIS_RDY(0x08) or RCV_EN(0x80)? */
186 if (time_after(jiffies, timeout)) {
191 /* check error flag - FIXME: not sure what this does - docs do not describe
192 * beyond "error flag for diseqc receive data :( */
193 if (s5h1420_readreg(state, 0x49)) {
199 length = (s5h1420_readreg(state, 0x3b) & 0x70) >> 4;
200 if (length > sizeof(reply->msg)) {
204 reply->msg_len = length;
207 for(i=0; i< length; i++) {
208 reply->msg[i] = s5h1420_readreg(state, 0x3c + i);
212 /* restore original settings */
213 s5h1420_writereg(state, 0x3b, val);
218 static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
220 struct s5h1420_state* state = fe->demodulator_priv;
223 unsigned long timeout;
225 /* setup for tone burst */
226 val = s5h1420_readreg(state, 0x3b);
227 s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x70) | 0x01);
229 /* set value for B position if requested */
230 if (minicmd == SEC_MINI_B) {
231 s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x04);
235 /* start transmission */
236 s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x08);
238 /* wait for transmission to complete */
239 timeout = jiffies + ((20*HZ) / 1000);
240 while(time_before(jiffies, timeout)) {
241 if (!(s5h1420_readreg(state, 0x3b) & 0x08))
246 if (time_after(jiffies, timeout))
249 /* restore original settings */
250 s5h1420_writereg(state, 0x3b, val);
255 static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state)
258 fe_status_t status = 0;
260 val = s5h1420_readreg(state, 0x14);
262 status |= FE_HAS_SIGNAL; // FIXME: not sure if this is right
264 status |= FE_HAS_CARRIER; // FIXME: not sure if this is right
265 val = s5h1420_readreg(state, 0x36);
267 status |= FE_HAS_VITERBI;
269 status |= FE_HAS_SYNC;
270 if (status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC))
271 status |= FE_HAS_LOCK;
276 static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status)
278 struct s5h1420_state* state = fe->demodulator_priv;
284 /* determine lock state */
285 *status = s5h1420_get_status_bits(state);
287 /* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert the inversion,
288 wait a bit and check again */
289 if (*status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI)) {
290 val = s5h1420_readreg(state, 0x32);
291 if ((val & 0x07) == 0x03) {
293 s5h1420_writereg(state, 0x31, 0x13);
295 s5h1420_writereg(state, 0x31, 0x1b);
297 /* wait a bit then update lock status */
299 *status = s5h1420_get_status_bits(state);
303 /* perform post lock setup */
304 if ((*status & FE_HAS_LOCK) && (!state->postlocked)) {
306 /* calculate the data rate */
307 u32 tmp = s5h1420_getsymbolrate(state);
308 switch(s5h1420_readreg(state, 0x32) & 0x07) {
310 tmp = (tmp * 2 * 1) / 2;
314 tmp = (tmp * 2 * 2) / 3;
318 tmp = (tmp * 2 * 3) / 4;
322 tmp = (tmp * 2 * 5) / 6;
326 tmp = (tmp * 2 * 6) / 7;
330 tmp = (tmp * 2 * 7) / 8;
333 tmp = state->fclk / tmp;
335 /* set the MPEG_CLK_INTL for the calculated data rate */
350 s5h1420_writereg(state, 0x22, val);
353 s5h1420_writereg(state, 0x1f, s5h1420_readreg(state, 0x1f) | 0x01);
355 /* kicker disable + remove DC offset */
356 s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) & 0x6f);
358 /* post-lock processing has been done! */
359 state->postlocked = 1;
365 static int s5h1420_read_ber(struct dvb_frontend* fe, u32* ber)
367 struct s5h1420_state* state = fe->demodulator_priv;
369 s5h1420_writereg(state, 0x46, 0x1d);
371 return (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47);
374 static int s5h1420_read_signal_strength(struct dvb_frontend* fe, u16* strength)
376 struct s5h1420_state* state = fe->demodulator_priv;
378 u8 val = 0xff - s5h1420_readreg(state, 0x15);
380 return (int) ((val << 8) | val);
383 static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
385 struct s5h1420_state* state = fe->demodulator_priv;
387 s5h1420_writereg(state, 0x46, 0x1f);
389 return (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47);
392 static void s5h1420_reset(struct s5h1420_state* state)
394 s5h1420_writereg (state, 0x01, 0x08);
395 s5h1420_writereg (state, 0x01, 0x00);
399 static void s5h1420_setsymbolrate(struct s5h1420_state* state, struct dvb_frontend_parameters *p)
403 val = (p->u.qpsk.symbol_rate / 1000) * (1<<24);
404 if (p->u.qpsk.symbol_rate <= 21000000) {
407 do_div(val, (state->fclk / 1000));
409 s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) & 0x7f);
410 s5h1420_writereg(state, 0x11, val >> 16);
411 s5h1420_writereg(state, 0x12, val >> 8);
412 s5h1420_writereg(state, 0x13, val & 0xff);
413 s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) | 0x80);
416 static u32 s5h1420_getsymbolrate(struct s5h1420_state* state)
421 if (s5h1420_readreg(state, 0x05) & 0x2)
424 s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08);
425 val = s5h1420_readreg(state, 0x11) << 16;
426 val |= s5h1420_readreg(state, 0x12) << 8;
427 val |= s5h1420_readreg(state, 0x13);
428 s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7);
430 val *= (state->fclk / 1000);
431 do_div(val, ((1<<24) * sampling));
433 return (u32) (val * 1000);
436 static void s5h1420_setfreqoffset(struct s5h1420_state* state, int freqoffset)
440 /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so
441 * divide fclk by 1000000 to get the correct value. */
442 val = -(int) ((freqoffset * (1<<24)) / (state->fclk / 1000000));
444 s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) & 0xbf);
445 s5h1420_writereg(state, 0x0e, val >> 16);
446 s5h1420_writereg(state, 0x0f, val >> 8);
447 s5h1420_writereg(state, 0x10, val & 0xff);
448 s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) | 0x40);
451 static int s5h1420_getfreqoffset(struct s5h1420_state* state)
455 s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08);
456 val = s5h1420_readreg(state, 0x0e) << 16;
457 val |= s5h1420_readreg(state, 0x0f) << 8;
458 val |= s5h1420_readreg(state, 0x10);
459 s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7);
464 /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so
465 * divide fclk by 1000000 to get the correct value. */
466 val = - ((val * (state->fclk/1000000)) / (1<<24));
471 static void s5h1420_setfec(struct s5h1420_state* state, struct dvb_frontend_parameters *p)
473 if ((p->u.qpsk.fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) {
474 s5h1420_writereg(state, 0x31, 0x00);
475 s5h1420_writereg(state, 0x30, 0x3f);
477 switch(p->u.qpsk.fec_inner) {
479 s5h1420_writereg(state, 0x31, 0x10);
480 s5h1420_writereg(state, 0x30, 0x01);
484 s5h1420_writereg(state, 0x31, 0x11);
485 s5h1420_writereg(state, 0x30, 0x02);
489 s5h1420_writereg(state, 0x31, 0x12);
490 s5h1420_writereg(state, 0x30, 0x04);
494 s5h1420_writereg(state, 0x31, 0x13);
495 s5h1420_writereg(state, 0x30, 0x08);
499 s5h1420_writereg(state, 0x31, 0x14);
500 s5h1420_writereg(state, 0x30, 0x10);
504 s5h1420_writereg(state, 0x31, 0x15);
505 s5h1420_writereg(state, 0x30, 0x20);
514 static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state)
516 switch(s5h1420_readreg(state, 0x32) & 0x07) {
539 static void s5h1420_setinversion(struct s5h1420_state* state, struct dvb_frontend_parameters *p)
541 if ((p->u.qpsk.fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) {
542 s5h1420_writereg(state, 0x31, 0x00);
543 s5h1420_writereg(state, 0x30, 0x3f);
545 u8 tmp = s5h1420_readreg(state, 0x31) & 0xf7;
548 if (p->inversion == INVERSION_ON)
551 s5h1420_writereg(state, 0x31, tmp);
555 static fe_spectral_inversion_t s5h1420_getinversion(struct s5h1420_state* state)
557 if (s5h1420_readreg(state, 0x32) & 0x08)
560 return INVERSION_OFF;
563 static int s5h1420_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
565 struct s5h1420_state* state = fe->demodulator_priv;
567 struct dvb_frontend_tune_settings fesettings;
569 /* check if we should do a fast-tune */
570 memcpy(&fesettings.parameters, p, sizeof(struct dvb_frontend_parameters));
571 s5h1420_get_tune_settings(fe, &fesettings);
572 frequency_delta = p->frequency - state->tunedfreq;
573 if ((frequency_delta > -fesettings.max_drift) && (frequency_delta < fesettings.max_drift) &&
574 (frequency_delta != 0) &&
575 (state->fec_inner == p->u.qpsk.fec_inner) &&
576 (state->symbol_rate == p->u.qpsk.symbol_rate)) {
578 s5h1420_setfreqoffset(state, frequency_delta);
582 /* first of all, software reset */
583 s5h1420_reset(state);
586 if (state->config->pll_set) {
587 s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1);
588 state->config->pll_set(fe, p, &state->tunedfreq);
589 s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe);
592 /* set s5h1420 fclk PLL according to desired symbol rate */
593 if (p->u.qpsk.symbol_rate > 28000000) {
594 state->fclk = 88000000;
595 s5h1420_writereg(state, 0x03, 0x50);
596 s5h1420_writereg(state, 0x04, 0x40);
597 s5h1420_writereg(state, 0x05, 0xae);
598 } else if (p->u.qpsk.symbol_rate > 21000000) {
599 state->fclk = 59000000;
600 s5h1420_writereg(state, 0x03, 0x33);
601 s5h1420_writereg(state, 0x04, 0x40);
602 s5h1420_writereg(state, 0x05, 0xae);
604 state->fclk = 88000000;
605 s5h1420_writereg(state, 0x03, 0x50);
606 s5h1420_writereg(state, 0x04, 0x40);
607 s5h1420_writereg(state, 0x05, 0xac);
610 /* set misc registers */
611 s5h1420_writereg(state, 0x02, 0x00);
612 s5h1420_writereg(state, 0x07, 0xb0);
613 s5h1420_writereg(state, 0x0a, 0x67);
614 s5h1420_writereg(state, 0x0b, 0x78);
615 s5h1420_writereg(state, 0x0c, 0x48);
616 s5h1420_writereg(state, 0x0d, 0x6b);
617 s5h1420_writereg(state, 0x2e, 0x8e);
618 s5h1420_writereg(state, 0x35, 0x33);
619 s5h1420_writereg(state, 0x38, 0x01);
620 s5h1420_writereg(state, 0x39, 0x7d);
621 s5h1420_writereg(state, 0x3a, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
622 s5h1420_writereg(state, 0x3c, 0x00);
623 s5h1420_writereg(state, 0x45, 0x61);
624 s5h1420_writereg(state, 0x46, 0x1d);
627 s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) | 1);
629 /* set the frequency offset to adjust for PLL inaccuracy */
630 s5h1420_setfreqoffset(state, p->frequency - state->tunedfreq);
632 /* set the reset of the parameters */
633 s5h1420_setsymbolrate(state, p);
634 s5h1420_setinversion(state, p);
635 s5h1420_setfec(state, p);
637 state->fec_inner = p->u.qpsk.fec_inner;
638 state->symbol_rate = p->u.qpsk.symbol_rate;
639 state->postlocked = 0;
643 static int s5h1420_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
645 struct s5h1420_state* state = fe->demodulator_priv;
647 p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state);
648 p->inversion = s5h1420_getinversion(state);
649 p->u.qpsk.symbol_rate = s5h1420_getsymbolrate(state);
650 p->u.qpsk.fec_inner = s5h1420_getfec(state);
655 static int s5h1420_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
657 if (fesettings->parameters.u.qpsk.symbol_rate > 20000000) {
658 fesettings->min_delay_ms = 50;
659 fesettings->step_size = 2000;
660 fesettings->max_drift = 8000;
661 } else if (fesettings->parameters.u.qpsk.symbol_rate > 12000000) {
662 fesettings->min_delay_ms = 100;
663 fesettings->step_size = 1500;
664 fesettings->max_drift = 9000;
665 } else if (fesettings->parameters.u.qpsk.symbol_rate > 8000000) {
666 fesettings->min_delay_ms = 100;
667 fesettings->step_size = 1000;
668 fesettings->max_drift = 8000;
669 } else if (fesettings->parameters.u.qpsk.symbol_rate > 4000000) {
670 fesettings->min_delay_ms = 100;
671 fesettings->step_size = 500;
672 fesettings->max_drift = 7000;
673 } else if (fesettings->parameters.u.qpsk.symbol_rate > 2000000) {
674 fesettings->min_delay_ms = 200;
675 fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000);
676 fesettings->max_drift = 14 * fesettings->step_size;
678 fesettings->min_delay_ms = 200;
679 fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000);
680 fesettings->max_drift = 18 * fesettings->step_size;
686 static int s5h1420_init (struct dvb_frontend* fe)
688 struct s5h1420_state* state = fe->demodulator_priv;
690 /* disable power down and do reset */
691 s5h1420_writereg(state, 0x02, 0x10);
693 s5h1420_reset(state);
696 if (state->config->pll_init) {
697 s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1);
698 state->config->pll_init(fe);
699 s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe);
705 static int s5h1420_sleep(struct dvb_frontend* fe)
707 struct s5h1420_state* state = fe->demodulator_priv;
709 return s5h1420_writereg(state, 0x02, 0x12);
712 static void s5h1420_release(struct dvb_frontend* fe)
714 struct s5h1420_state* state = fe->demodulator_priv;
718 static struct dvb_frontend_ops s5h1420_ops;
720 struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, struct i2c_adapter* i2c)
722 struct s5h1420_state* state = NULL;
725 /* allocate memory for the internal state */
726 state = kmalloc(sizeof(struct s5h1420_state), GFP_KERNEL);
730 /* setup the state */
731 state->config = config;
733 memcpy(&state->ops, &s5h1420_ops, sizeof(struct dvb_frontend_ops));
734 state->postlocked = 0;
735 state->fclk = 88000000;
736 state->tunedfreq = 0;
737 state->fec_inner = FEC_NONE;
738 state->symbol_rate = 0;
740 /* check if the demod is there + identify it */
741 identity = s5h1420_readreg(state, 0x00);
742 if (identity != 0x03)
745 /* create dvb_frontend */
746 state->frontend.ops = &state->ops;
747 state->frontend.demodulator_priv = state;
748 return &state->frontend;
755 static struct dvb_frontend_ops s5h1420_ops = {
758 .name = "Samsung S5H1420 DVB-S",
760 .frequency_min = 950000,
761 .frequency_max = 2150000,
762 .frequency_stepsize = 125, /* kHz for QPSK frontends */
763 .frequency_tolerance = 29500,
764 .symbol_rate_min = 1000000,
765 .symbol_rate_max = 45000000,
766 /* .symbol_rate_tolerance = ???,*/
767 .caps = FE_CAN_INVERSION_AUTO |
768 FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
769 FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
773 .release = s5h1420_release,
775 .init = s5h1420_init,
776 .sleep = s5h1420_sleep,
778 .set_frontend = s5h1420_set_frontend,
779 .get_frontend = s5h1420_get_frontend,
780 .get_tune_settings = s5h1420_get_tune_settings,
782 .read_status = s5h1420_read_status,
783 .read_ber = s5h1420_read_ber,
784 .read_signal_strength = s5h1420_read_signal_strength,
785 .read_ucblocks = s5h1420_read_ucblocks,
787 .diseqc_send_master_cmd = s5h1420_send_master_cmd,
788 .diseqc_recv_slave_reply = s5h1420_recv_slave_reply,
789 .diseqc_send_burst = s5h1420_send_burst,
790 .set_tone = s5h1420_set_tone,
791 .set_voltage = s5h1420_set_voltage,
794 module_param(debug, int, 0644);
796 MODULE_DESCRIPTION("Samsung S5H1420 DVB-S Demodulator driver");
797 MODULE_AUTHOR("Andrew de Quincey");
798 MODULE_LICENSE("GPL");
800 EXPORT_SYMBOL(s5h1420_attach);