1
0
mirror of https://github.com/DigitalDevices/dddvb.git synced 2023-10-10 13:37:43 +02:00
Merge branch 'internal'
This commit is contained in:
none 2021-01-20 17:42:47 +01:00
commit 7d8a151127
21 changed files with 620 additions and 384 deletions

View File

@ -1,10 +1,11 @@
all: ddtest octonet octokey ddflash all: ddtest octonet octokey ddflash ddupdate
install: all install: all
install -m 0755 ddtest $(DESTDIR)/usr/bin install -m 0755 ddtest $(DESTDIR)/usr/bin
install -m 0755 octonet $(DESTDIR)/usr/bin install -m 0755 octonet $(DESTDIR)/usr/bin
install -m 0755 octokey $(DESTDIR)/usr/bin install -m 0755 octokey $(DESTDIR)/usr/bin
install -m 0755 ddflash $(DESTDIR)/usr/bin install -m 0755 ddflash $(DESTDIR)/usr/bin
install -m 0755 ddupdate $(DESTDIR)/usr/bin
ddflash: ddflash.c flash.h flash.c ddflash: ddflash.c flash.h flash.c
$(CC) -o ddflash ddflash.c $(CC) -o ddflash ddflash.c
@ -12,6 +13,9 @@ ddflash: ddflash.c flash.h flash.c
ddtest: ddtest.c flash.h flash.c ddtest: ddtest.c flash.h flash.c
$(CC) -o ddtest ddtest.c $(CC) -o ddtest ddtest.c
ddupdate: ddupdate.c flash.h flash.c
$(CC) -o ddupdate ddupdate.c
octonet: octonet.c octonet: octonet.c
$(CC) -o octonet octonet.c $(CC) -o octonet octonet.c

View File

@ -38,25 +38,6 @@
#include "flash.h" #include "flash.h"
#include "flash.c" #include "flash.c"
static int reboot(uint32_t off)
{
FILE *f;
uint32_t time;
if ((f = fopen ("/sys/class/rtc/rtc0/since_epoch", "r")) == NULL)
return -1;
fscanf(f, "%u", &time);
fclose(f);
if ((f = fopen ("/sys/class/rtc/rtc0/wakealarm", "r+")) == NULL)
return -1;
fprintf(f, "%u", time + off);
fclose(f);
system("/sbin/poweroff");
return 0;
}
static int update_flash(struct ddflash *ddf) static int update_flash(struct ddflash *ddf)
{ {
char *fname; char *fname;

View File

@ -11,7 +11,6 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <linux/types.h> #include <linux/types.h>
#include "flash.h" #include "flash.h"
#include "flash.c" #include "flash.c"
@ -118,8 +117,8 @@ static int update_flash(struct ddflash *ddf)
printf("Card: %s\n", name); printf("Card: %s\n", name);
if (ddf->flash_name) if (ddf->flash_name)
printf("Flash: %s\n", ddf->flash_name); printf("Flash: %s\n", ddf->flash_name);
printf("Version:%08x\n", ddf->id.hw); printf("Version: %08x\n", ddf->id.hw);
//printf("REGMAPa: %08x\n", ddf->id.regmap); printf("REGMAP : %08x\n", ddf->id.regmap);
if ((res = update_image(ddf, fname, 0x10000, 0x100000, 1, 0)) == 1) if ((res = update_image(ddf, fname, 0x10000, 0x100000, 1, 0)) == 1)
stat |= 1; stat |= 1;
return stat; return stat;
@ -142,7 +141,7 @@ static int update_link(struct ddflash *ddf)
return ret; return ret;
} }
static int update_card(int ddbnum, char *fname) static int update_card(int ddbnum, char *fname, int force)
{ {
struct ddflash ddf; struct ddflash ddf;
char ddbname[80]; char ddbname[80];
@ -156,6 +155,7 @@ static int update_card(int ddbnum, char *fname)
ddf.fd = ddb; ddf.fd = ddb;
ddf.link = 0; ddf.link = 0;
ddf.fname = fname; ddf.fname = fname;
ddf.force = force;
links = 1; links = 1;
for (link = 0; link < links; link++) { for (link = 0; link < links; link++) {
@ -206,18 +206,20 @@ static int usage()
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int ddbnum = -1, all = 0, i, force = 0; int ddbnum = -1, all = 0, i, force = 0, reboot_len = -1;
char *fname = 0; char *fname = 0;
int ret;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
int c; int c;
static struct option long_options[] = { static struct option long_options[] = {
{"reboot", optional_argument , NULL, 'r'},
{"help", no_argument , NULL, 'h'}, {"help", no_argument , NULL, 'h'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
c = getopt_long(argc, argv, c = getopt_long(argc, argv,
"n:havfb:", "n:havfb:r::",
long_options, &option_index); long_options, &option_index);
if (c==-1) if (c==-1)
break; break;
@ -232,9 +234,20 @@ int main(int argc, char **argv)
case 'a': case 'a':
all = 1; all = 1;
break; break;
case 'f':
force = 1;
break;
case 'v': case 'v':
verbose++; verbose++;
break; break;
case 'r':
if (optarg)
reboot_len = strtol(optarg, NULL, 0);
else
reboot_len = 40;
if (!reboot_len)
reboot(40);
break;
case 'h': case 'h':
usage(); usage();
break; break;
@ -253,10 +266,10 @@ int main(int argc, char **argv)
} }
if (!all) if (!all)
return update_card(ddbnum, fname); ret = update_card(ddbnum, fname, force);
else
for (i = 0; i < 100; i++) { for (i = 0; i < 100; i++) {
int ret = update_card(i, 0); ret = update_card(i, 0, 0);
if (ret == -3) /* could not open, no more cards! */ if (ret == -3) /* could not open, no more cards! */
break; break;
@ -265,5 +278,7 @@ int main(int argc, char **argv)
if (verbose >= 1) if (verbose >= 1)
printf("card %d up to date\n", i); printf("card %d up to date\n", i);
} }
if (reboot_len > 0)
reboot(reboot_len);
return 0; return 0;
} }

View File

@ -1,7 +1,28 @@
static int reboot(uint32_t off)
{
FILE *f;
uint32_t time;
if ((f = fopen ("/sys/class/rtc/rtc0/since_epoch", "r")) == NULL)
return -1;
fscanf(f, "%u", &time);
fclose(f);
if ((f = fopen ("/sys/class/rtc/rtc0/wakealarm", "r+")) == NULL)
return -1;
fprintf(f, "%u", time + off);
fclose(f);
system("/sbin/poweroff");
return 0;
}
static uint32_t linknr = 0; static uint32_t linknr = 0;
int flashio(int ddb, int link, int flashio(int ddb, int link,
uint8_t *wbuf, uint32_t wlen, uint8_t *rbuf, uint32_t rlen) uint8_t *wbuf, uint32_t wlen,
uint8_t *rbuf, uint32_t rlen)
{ {
struct ddb_flashio fio = { struct ddb_flashio fio = {
.write_buf=wbuf, .write_buf=wbuf,
@ -118,7 +139,7 @@ int flashread(int ddb, int link, uint8_t *buf, uint32_t addr, uint32_t len)
uint32_t l; uint32_t l;
while (len) { while (len) {
cmd[0] = 3; cmd[0] = 0x03;
cmd[1] = (addr >> 16) & 0xff; cmd[1] = (addr >> 16) & 0xff;
cmd[2] = (addr >> 8) & 0xff; cmd[2] = (addr >> 8) & 0xff;
cmd[3] = addr & 0xff; cmd[3] = addr & 0xff;
@ -137,12 +158,12 @@ int flashread(int ddb, int link, uint8_t *buf, uint32_t addr, uint32_t len)
return 0; return 0;
} }
#else #else
static int flashread(int ddb, uint8_t *buf, uint32_t addr, uint32_t len) static int flashread(int ddb, int link, uint8_t *buf, uint32_t addr, uint32_t len)
{ {
uint8_t cmd[4]= {0x03, (addr >> 16) & 0xff, uint8_t cmd[5]= {0x0b, (addr >> 16) & 0xff,
(addr >> 8) & 0xff, addr & 0xff}; (addr >> 8) & 0xff, addr & 0xff, 0x00};
return flashio(ddb, linknr, cmd, 4, buf, len); return flashio(ddb, link, cmd, 5, buf, len);
} }
#endif #endif
@ -500,10 +521,10 @@ int FlashWritePageMode(int dev, uint32_t FlashOffset,
uint8_t Cmd[260]; uint8_t Cmd[260];
int i, j; int i, j;
if( (BufferSize % 4096) != 0 ) return -1; // Must be multiple of sector size if ((BufferSize % 4096))
return -1; // Must be multiple of sector size
do do {
{
Cmd[0] = 0x50; // EWSR Cmd[0] = 0x50; // EWSR
err = flashio(dev,linknr, Cmd,1,NULL,0); err = flashio(dev,linknr, Cmd,1,NULL,0);
if( err < 0 ) break; if( err < 0 ) break;
@ -513,8 +534,7 @@ int FlashWritePageMode(int dev, uint32_t FlashOffset,
err = flashio(dev,linknr, Cmd,2,NULL,0); err = flashio(dev,linknr, Cmd,2,NULL,0);
if( err < 0 ) break; if( err < 0 ) break;
for(i = 0; i < BufferSize; i += 4096 ) for(i = 0; i < BufferSize; i += 4096 ) {
{
if( (i & 0xFFFF) == 0 ) if( (i & 0xFFFF) == 0 )
{ {
printf(" Erase %08x\n",FlashOffset + i); printf(" Erase %08x\n",FlashOffset + i);
@ -588,63 +608,70 @@ int FlashWritePageMode(int dev, uint32_t FlashOffset,
return err; return err;
} }
static int flash_wait(int fd, uint32_t link)
{
while (1) {
uint8_t rcmd = 0x05; // RDSR
int err = flashio(fd, link, &rcmd, 1, &rcmd, 1);
if (err < 0)
return err;
if ((rcmd & 0x01) == 0)
break;
}
return 0;
}
int flashwrite_pagemode(struct ddflash *ddf, int dev, uint32_t FlashOffset, int flashwrite_pagemode(struct ddflash *ddf, int dev, uint32_t FlashOffset,
uint8_t LockBits, uint32_t fw_off) uint8_t LockBits, uint32_t fw_off, int be)
{ {
int err = 0; int err = 0;
uint8_t cmd[260]; uint8_t cmd[260];
int i, j; int i, j;
uint32_t flen, blen; uint32_t flen, blen;
int blockerase = be && ((FlashOffset & 0xFFFF) == 0 ) && (flen >= 0x10000);
blen = flen = lseek(dev, 0, SEEK_END) - fw_off; blen = flen = lseek(dev, 0, SEEK_END) - fw_off;
if (blen % 0xff) if (blen % 0xff)
blen = (blen + 0xff) & 0xffffff00; blen = (blen + 0xff) & 0xffffff00;
printf("blen = %u, flen = %u\n", blen, flen); //printf("blen = %u, flen = %u\n", blen, flen);
setbuf(stdout, NULL);
do {
cmd[0] = 0x50; // EWSR cmd[0] = 0x50; // EWSR
err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0);
if (err < 0) if (err < 0)
break; return err;
cmd[0] = 0x01; // WRSR cmd[0] = 0x01; // WRSR
cmd[1] = 0x00; // BPx = 0, Unlock all blocks cmd[1] = 0x00; // BPx = 0, Unlock all blocks
err = flashio(ddf->fd, ddf->link, cmd, 2, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 2, NULL, 0);
if (err < 0) if (err < 0)
break; return err;
for (i = 0; i < flen; i += 4096) {
if ((i & 0xFFFF) == 0)
printf(" Erase %08x\n", FlashOffset + i);
for (i = 0; i < flen; ) {
printf(" Erase %08x\r", FlashOffset + i);
cmd[0] = 0x06; // WREN cmd[0] = 0x06; // WREN
err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0);
if (err < 0) if (err < 0)
break; return err;
cmd[0] = 0x20; // Sector erase ( 4Kb)
cmd[1] = ( (( FlashOffset + i ) >> 16) & 0xFF ); cmd[1] = ( (( FlashOffset + i ) >> 16) & 0xFF );
cmd[2] = ( (( FlashOffset + i ) >> 8) & 0xFF ); cmd[2] = ( (( FlashOffset + i ) >> 8) & 0xFF );
cmd[3] = 0x00; cmd[3] = 0x00;
if (blockerase && ((flen - i) >= 0x10000) ) {
cmd[0] = 0xd8;
i += 0x10000;
} else {
cmd[0] = 0x20; // Sector erase ( 4Kb)
i += 0x1000;
}
err = flashio(ddf->fd, ddf->link, cmd, 4, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 4, NULL, 0);
if (err < 0) if (err < 0)
break; return err;
err = flash_wait(ddf->fd, ddf->link);
while (1) {
cmd[0] = 0x05; // RDSR
err = flashio(ddf->fd, ddf->link, cmd, 1, &cmd[0], 1);
if (err < 0) if (err < 0)
break; return err;
if ((cmd[0] & 0x01) == 0)
break;
} }
if (err < 0)
break;
}
if (err < 0)
break;
for (j = blen - 256; j >= 0; j -= 256 ) { for (j = blen - 256; j >= 0; j -= 256 ) {
uint32_t len = 256; uint32_t len = 256;
ssize_t rlen; ssize_t rlen;
@ -662,16 +689,12 @@ int flashwrite_pagemode(struct ddflash *ddf, int dev, uint32_t FlashOffset,
printf("file read error %d,%d at %u\n", rlen, errno, j); printf("file read error %d,%d at %u\n", rlen, errno, j);
return -1; return -1;
} }
//printf ("write %u bytes at %08x\n", len, j); printf(" Program %08x\r", FlashOffset + j);
if ((j & 0xFFFF) == 0)
printf(" Program %08x\n", FlashOffset + j);
cmd[0] = 0x06; // WREN cmd[0] = 0x06; // WREN
err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0);
if (err < 0) if (err < 0)
break; goto out;
cmd[0] = 0x02; // PP cmd[0] = 0x02; // PP
cmd[1] = ( (( FlashOffset + j ) >> 16) & 0xFF ); cmd[1] = ( (( FlashOffset + j ) >> 16) & 0xFF );
@ -680,32 +703,22 @@ int flashwrite_pagemode(struct ddflash *ddf, int dev, uint32_t FlashOffset,
memcpy(&cmd[4], ddf->buffer, 256); memcpy(&cmd[4], ddf->buffer, 256);
err = flashio(ddf->fd, ddf->link, cmd, 260, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 260, NULL, 0);
if (err < 0) if (err < 0)
break; goto out;
err = flash_wait(ddf->fd, ddf->link);
while(1) {
cmd[0] = 0x05; // RDRS
err = flashio(ddf->fd, ddf->link, cmd,1, &cmd[0], 1);
if (err < 0) if (err < 0)
break; goto out;
if ((cmd[0] & 0x01) == 0)
break;
} }
if (err < 0) printf("\n");
break;
}
if (err < 0)
break;
cmd[0] = 0x50; // EWSR cmd[0] = 0x50; // EWSR
err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 1, NULL, 0);
if (err < 0) if (err < 0)
break; goto out;
cmd[0] = 0x01; // WRSR cmd[0] = 0x01; // WRSR
cmd[1] = LockBits; // BPx = 0, Lock all blocks cmd[1] = LockBits; // BPx = 0, Lock all blocks
err = flashio(ddf->fd, ddf->link, cmd, 2, NULL, 0); err = flashio(ddf->fd, ddf->link, cmd, 2, NULL, 0);
} while(0); out:
return err; return err;
} }
@ -845,7 +858,7 @@ static int flashwrite(struct ddflash *ddf, int fs, uint32_t addr, uint32_t maxle
case SSTI_SST25VF032B: case SSTI_SST25VF032B:
return flashwrite_SSTI(ddf, fs, addr, maxlen, fw_off); return flashwrite_SSTI(ddf, fs, addr, maxlen, fw_off);
case SSTI_SST25VF064C: case SSTI_SST25VF064C:
return flashwrite_pagemode(ddf, fs, addr, 0x3c, fw_off); return flashwrite_pagemode(ddf, fs, addr, 0x3c, fw_off, 0);
case SPANSION_S25FL116K: case SPANSION_S25FL116K:
case SPANSION_S25FL132K: case SPANSION_S25FL132K:
case SPANSION_S25FL164K: case SPANSION_S25FL164K:
@ -853,7 +866,8 @@ static int flashwrite(struct ddflash *ddf, int fs, uint32_t addr, uint32_t maxle
case WINBOND_W25Q32JV: case WINBOND_W25Q32JV:
case WINBOND_W25Q64JV: case WINBOND_W25Q64JV:
case WINBOND_W25Q128JV: case WINBOND_W25Q128JV:
return flashwrite_pagemode(ddf, fs, addr, 0x1c, fw_off); default:
return flashwrite_pagemode(ddf, fs, addr, 0x1c, fw_off, 1);
} }
return -1; return -1;
} }
@ -892,97 +906,55 @@ static int get_id(struct ddflash *ddf)
return 0; return 0;
} }
struct devids {
uint16_t id;
char *name;
char *fname;
};
#define DEV(_id, _name, _fname) { .id = _id, .name = _name, .fname = _fname }
static const struct devids ids[] = {
DEV(0x0002, "Octopus 35", "DVBBridgeV1A_DVBBridgeV1A.bit"),
DEV(0x0003, "Octopus", "DVBBridgeV1B_DVBBridgeV1B.fpga"),
DEV(0x0005, "Octopus Classic", "DVBBridgeV2A_DD01_0005_STD.fpga"),
DEV(0x0006, "CineS2 V7", "DVBBridgeV2A_DD01_0006_STD.fpga"),
DEV(0x0007, "Octopus 4/8", "DVBBridgeV2A_DD01_0007_MXL.fpga"),
DEV(0x0008, "Octopus 4/8", "DVBBridgeV2A_DD01_0008_CXD.fpga"),
DEV(0x0009, "Octopus MAXSX8", "DVBBridgeV2A_DD01_0009_SX8.fpga"),
DEV(0x000b, "Octopus MAXSX8 Basic", "DVBBridgeV2A_DD01_000B_SX8.fpga"),
DEV(0x000a, "Octopus MAXM4", "DVBBridgeV2A_DD01_000A_M4.fpga"),
DEV(0x0011, "Octopus CI", "DVBBridgeV2B_DD01_0011.fpga"),
DEV(0x0012, "Octopus CI", "DVBBridgeV2B_DD01_0012_STD.fpga"),
DEV(0x0013, "Octopus PRO", "DVBBridgeV2B_DD01_0013_PRO.fpga"),
DEV(0x0020, "Octopus GT Mini", "DVBBridgeV2C_DD01_0020.fpga"),
DEV(0x0201, "Modulator", "DVBModulatorV1B_DVBModulatorV1B.bit"),
DEV(0x0203, "Modulator Test", "DVBModulatorV1B_DD01_0203.fpga"),
DEV(0x0210, "Modulator V2", "DVBModulatorV2A_DD01_0210.fpga"),
DEV(0x0220, "SDRModulator ATV", "SDRModulatorV1A_DD01_0220.fpga"),
DEV(0x0221, "SDRModulator IQ", "SDRModulatorV1A_DD01_0221_IQ.fpga"),
DEV(0x0222, "SDRModulator DVBT", "SDRModulatorV1A_DD01_0222_DVBT.fpga"),
DEV(0x0223, "SDRModulator IQ2", "SDRModulatorV1A_DD01_0223_IQ2.fpga"),
DEV(0x0000, "UNKNOWN", 0),
};
static char *devid2fname(uint16_t devid, char **name) static char *devid2fname(uint16_t devid, char **name)
{ {
int i;
char *fname = 0; char *fname = 0;
switch (devid) { for (i = 0; ; i++) {
case 0x0002: const struct devids *id = &ids[i];
fname="DVBBridgeV1A_DVBBridgeV1A.bit";
*name = "Octopus 35"; if (devid == id->id || !id->id) {
break; *name = id->name;
case 0x0003: fname = id->fname;
fname="DVBBridgeV1B_DVBBridgeV1B.fpga";
*name = "Octopus";
break;
case 0x0005:
fname="DVBBridgeV2A_DD01_0005_STD.fpga";
*name = "Octopus Classic";
break;
case 0x0006:
fname="DVBBridgeV2A_DD01_0006_STD.fpga";
*name = "CineS2 V7";
break;
case 0x0007:
fname="DVBBridgeV2A_DD01_0007_MXL.fpga";
*name = "Octopus 4/8";
break;
case 0x0008:
fname="DVBBridgeV2A_DD01_0008_CXD.fpga";
*name = "Octopus 4/8";
break;
case 0x0009:
fname="DVBBridgeV2A_DD01_0009_SX8.fpga";
*name = "Octopus MAXSX8";
break;
case 0x000b:
fname="DVBBridgeV2A_DD01_000B_SX8.fpga";
*name = "Octopus MAXSX8 Basic";
break;
case 0x000a:
fname="DVBBridgeV2A_DD01_000A_M4.fpga";
*name = "Octopus MAXM4";
break;
case 0x0011:
fname="DVBBridgeV2B_DD01_0011.fpga";
*name = "Octopus CI";
break;
case 0x0012:
fname="DVBBridgeV2B_DD01_0012_STD.fpga";
*name = "Octopus CI";
break;
case 0x0013:
fname="DVBBridgeV2B_DD01_0013_PRO.fpga";
*name = "Octopus PRO";
break;
case 0x0020:
fname="DVBBridgeV2C_DD01_0020.fpga";
*name = "Octopus GT Mini";
break;
case 0x0201:
fname="DVBModulatorV1B_DVBModulatorV1B.bit";
*name = "Modulator";
break;
case 0x0203:
fname="DVBModulatorV1B_DD01_0203.fpga";
*name = "Modulator Test";
break;
case 0x0210:
fname="DVBModulatorV2A_DD01_0210.fpga";
*name = "Modulator V2";
break;
case 0x0220:
fname="SDRModulatorV1A_DD01_0220.fpga";
*name = "SDRModulator ATV";
break;
case 0x0221:
fname="SDRModulatorV1A_DD01_0221_IQ.fpga";
*name = "SDRModulator IQ";
break;
case 0x0222:
fname="SDRModulatorV1A_DD01_0222_DVBT.fpga";
*name = "SDRModulator DVBT";
break;
default:
*name = "UNKNOWN";
break; break;
} }
}
return fname; return fname;
} }
static int flashcmp(struct ddflash *ddf, int fs, uint32_t addr, uint32_t maxlen, uint32_t fw_off) static int flashcmp(struct ddflash *ddf, int fs, uint32_t addr, uint32_t maxlen, uint32_t fw_off)
{ {
off_t off; off_t off;
@ -1015,9 +987,9 @@ static int flashcmp(struct ddflash *ddf, int fs, uint32_t addr, uint32_t maxlen,
if (memcmp(buf, buf2, bl)) { if (memcmp(buf, buf2, bl)) {
printf("flash differs at %08x (offset %u)\n", addr, j); printf("flash differs at %08x (offset %u)\n", addr, j);
dump(buf, bl); //dump(buf, bl);
printf("\n"); //printf("\n");
dump(buf2, bl); //dump(buf2, bl);
return addr; return addr;
} }
} }
@ -1080,7 +1052,7 @@ static int check_fw(struct ddflash *ddf, char *fn, uint32_t *fw_off)
unsigned int devid, version, length; unsigned int devid, version, length;
unsigned int cid[8]; unsigned int cid[8];
int cids = 0; int cids = 0;
uint32_t maxlen = 2 * 1024 * 1024, crc; uint32_t maxlen = 2 * 1024 * 1024, crc, fcrc;
fd = open(fn, O_RDONLY); fd = open(fn, O_RDONLY);
if (fd < 0) { if (fd < 0) {
@ -1148,11 +1120,17 @@ static int check_fw(struct ddflash *ddf, char *fn, uint32_t *fw_off)
} }
p++; p++;
*fw_off = p; *fw_off = p;
printf(" CRC = %08x\n", crc); fcrc = crc32(buf + p, length, 0xffffffff);
printf(" CRC file = %08x\n", fcrc);
printf(" CRC flash = %08x\n", crc);
printf(" devid = %04x\n", devid); printf(" devid = %04x\n", devid);
printf(" version = %08x (current image = %08x)\n", version, ddf->id.hw); printf(" version = %08x (current image = %08x)\n", version, ddf->id.hw);
//printf(" length = %u\n", length); //printf(" length = %u\n", length);
//printf("fsize = %u, p = %u, f-p = %u\n", fsize, p, fsize - p); //printf("fsize = %u, p = %u, f-p = %u\n", fsize, p, fsize - p);
if (fcrc != crc) {
printf("CRC error in file %s!\n", fn);
return -4;
}
if (devid == ddf->id.device) { if (devid == ddf->id.device) {
if (version <= (ddf->id.hw & 0xffffff)) { if (version <= (ddf->id.hw & 0xffffff)) {
printf("%s is older or same version as flash\n", fn); printf("%s is older or same version as flash\n", fn);
@ -1192,6 +1170,10 @@ static int update_image(struct ddflash *ddf, char *fn,
res = flashcmp(ddf, fs, adr, len, fw_off); res = flashcmp(ddf, fs, adr, len, fw_off);
if (res == -2) { if (res == -2) {
printf("Flash already identical to %s\n", fn); printf("Flash already identical to %s\n", fn);
if (ddf->force) {
printf("but force enabled!\n");
res = 0;
}
} }
if (res < 0) if (res < 0)
goto out; goto out;
@ -1200,6 +1182,9 @@ static int update_image(struct ddflash *ddf, char *fn,
res = flashcmp(ddf, fs, adr, len, fw_off); res = flashcmp(ddf, fs, adr, len, fw_off);
if (res == -2) { if (res == -2) {
res = 1; res = 1;
printf("Flash verify OK!\n");
} else {
printf("Flash verify ERROR!\n");
} }
} }

View File

@ -89,6 +89,7 @@ struct ddflash {
int fd; int fd;
uint32_t link; uint32_t link;
char *fname; char *fname;
int force;
struct ddb_id id; struct ddb_id id;
uint32_t version; uint32_t version;

View File

@ -33,8 +33,10 @@ void proc_ts(int i, uint8_t *buf)
if (ccin & 0x10) { if (ccin & 0x10) {
if( cc[pid] != 0 ) { if( cc[pid] != 0 ) {
// TODO: 1 repetition allowed // TODO: 1 repetition allowed
if ((((cc[pid] + 1) & 0x0F) != (ccin & 0x0F)) ) if ((((cc[pid] + 1) & 0x0F) != (ccin & 0x0F)) ) {
cc_errors += 1; cc_errors += 1;
printf("%04x: %u != %u\n", pid, (cc[pid] + 1) & 0x0F, ccin & 0x0F);
}
} }
cc[pid] = ccin; cc[pid] = ccin;
} }

View File

@ -2507,8 +2507,10 @@ static void ddb_input_init(struct ddb_port *port, int nr, int pnr, int anr)
rm = io_regmap(input, 1); rm = io_regmap(input, 1);
input->regs = DDB_LINK_TAG(port->lnr) | input->regs = DDB_LINK_TAG(port->lnr) |
(rm->input->base + rm->input->size * nr); (rm->input->base + rm->input->size * nr);
#if 0
dev_info(dev->dev, "init link %u, input %u, regs %08x\n", dev_info(dev->dev, "init link %u, input %u, regs %08x\n",
port->lnr, nr, input->regs); port->lnr, nr, input->regs);
#endif
if (dev->has_dma) { if (dev->has_dma) {
const struct ddb_regmap *rm0 = io_regmap(input, 0); const struct ddb_regmap *rm0 = io_regmap(input, 0);
u32 base = rm0->irq_base_idma; u32 base = rm0->irq_base_idma;
@ -3210,6 +3212,12 @@ struct ddb_i2c_msg {
__u32 mlen; __u32 mlen;
}; };
struct ddb_mci_msg {
__u32 link;
struct mci_command cmd;
struct mci_result res;
};
#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio) #define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio)
#define IOCTL_DDB_GPIO_IN _IOWR(DDB_MAGIC, 0x01, struct ddb_gpio) #define IOCTL_DDB_GPIO_IN _IOWR(DDB_MAGIC, 0x01, struct ddb_gpio)
#define IOCTL_DDB_GPIO_OUT _IOWR(DDB_MAGIC, 0x02, struct ddb_gpio) #define IOCTL_DDB_GPIO_OUT _IOWR(DDB_MAGIC, 0x02, struct ddb_gpio)
@ -3222,6 +3230,7 @@ struct ddb_i2c_msg {
#define IOCTL_DDB_WRITE_MDIO _IOR(DDB_MAGIC, 0x09, struct ddb_mdio) #define IOCTL_DDB_WRITE_MDIO _IOR(DDB_MAGIC, 0x09, struct ddb_mdio)
#define IOCTL_DDB_READ_I2C _IOWR(DDB_MAGIC, 0x0a, struct ddb_i2c_msg) #define IOCTL_DDB_READ_I2C _IOWR(DDB_MAGIC, 0x0a, struct ddb_i2c_msg)
#define IOCTL_DDB_WRITE_I2C _IOR(DDB_MAGIC, 0x0b, struct ddb_i2c_msg) #define IOCTL_DDB_WRITE_I2C _IOR(DDB_MAGIC, 0x0b, struct ddb_i2c_msg)
#define IOCTL_DDB_MCI_CMD _IOWR(DDB_MAGIC, 0x0c, struct ddb_mci_msg)
#define DDB_NAME "ddbridge" #define DDB_NAME "ddbridge"
@ -3433,6 +3442,24 @@ static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
return -EIO; return -EIO;
break; break;
} }
case IOCTL_DDB_MCI_CMD:
{
struct ddb_mci_msg msg;
struct ddb_link *link;
int res;
if (copy_from_user(&msg, parg, sizeof(msg)))
return -EFAULT;
if (msg.link > 3)
return -EFAULT;
link = &dev->link[msg.link];
if (!link->mci_base)
return -EFAULT;
res = ddb_mci_cmd_link(link, &msg.cmd, &msg.res);
if (copy_to_user(parg, &msg, sizeof(msg)))
return -EFAULT;
return res;
}
default: default:
return -ENOTTY; return -ENOTTY;
} }
@ -4414,6 +4441,13 @@ static int ddb_init_boards(struct ddb *dev)
usleep_range(2000, 3000); usleep_range(2000, 3000);
} }
ddb_init_tempmon(link); ddb_init_tempmon(link);
if (info->regmap->mci) {
if (link->info->type == DDB_OCTOPUS_MCI ||
((link->info->type == DDB_MOD) &&
(link->ids.regmapid & 0xfff0)))
mci_init(link);
}
} }
return 0; return 0;
} }

View File

@ -124,6 +124,32 @@ static const struct ddb_regset octopus_i2c_buf = {
/****************************************************************************/ /****************************************************************************/
static const struct ddb_regset max_mci = {
.base = 0x500,
.num = 0x01,
.size = 0x04,
};
static const struct ddb_regset max_mci_buf = {
.base = 0x600,
.num = 0x01,
.size = 0x100,
};
static const struct ddb_regset sdr_mci = {
.base = 0x260,
.num = 0x01,
.size = 0x04,
};
static const struct ddb_regset sdr_mci_buf = {
.base = 0x300,
.num = 0x01,
.size = 0x100,
};
/****************************************************************************/
static const struct ddb_regset octopro_input = { static const struct ddb_regset octopro_input = {
.base = 0x400, .base = 0x400,
.num = 0x14, .num = 0x14,
@ -224,6 +250,25 @@ static const struct ddb_regmap octopus_map = {
.output = &octopus_output, .output = &octopus_output,
}; };
static const struct ddb_regmap octopus_mci_map = {
.irq_version = 1,
.irq_base_i2c = 0,
.irq_base_idma = 8,
.irq_base_odma = 16,
.irq_base_mci = 0,
.i2c = &octopus_i2c,
.i2c_buf = &octopus_i2c_buf,
.idma = &octopus_idma,
.idma_buf = &octopus_idma_buf,
.odma = &octopus_odma,
.odma_buf = &octopus_odma_buf,
.input = &octopus_input,
.output = &octopus_output,
.mci = &max_mci,
.mci_buf = &max_mci_buf,
};
static const struct ddb_regmap octopro_map = { static const struct ddb_regmap octopro_map = {
.irq_version = 2, .irq_version = 2,
.irq_base_i2c = 32, .irq_base_i2c = 32,
@ -280,10 +325,14 @@ static const struct ddb_regmap octopus_sdr_map = {
.irq_version = 2, .irq_version = 2,
.irq_base_odma = 64, .irq_base_odma = 64,
.irq_base_rate = 32, .irq_base_rate = 32,
.irq_base_mci = 10,
.output = &octopus_sdr_output, .output = &octopus_sdr_output,
.odma = &octopus_mod_2_odma, .odma = &octopus_mod_2_odma,
.odma_buf = &octopus_mod_2_odma_buf, .odma_buf = &octopus_mod_2_odma_buf,
.channel = &octopus_mod_2_channel, .channel = &octopus_mod_2_channel,
.mci = &sdr_mci,
.mci_buf = &sdr_mci_buf,
}; };
static const struct ddb_regmap gtl_mini = { static const struct ddb_regmap gtl_mini = {
@ -537,6 +586,16 @@ static const struct ddb_info ddb_sdr_iq = {
.tempmon_irq = 8, .tempmon_irq = 8,
}; };
static const struct ddb_info ddb_sdr_iq2 = {
.type = DDB_MOD,
.name = "Digital Devices SDR IQ2",
.version = 17,
.regmap = &octopus_sdr_map,
.port_num = 4,
.temp_num = 1,
.tempmon_irq = 8,
};
static const struct ddb_info ddb_sdr_dvbt = { static const struct ddb_info ddb_sdr_dvbt = {
.type = DDB_MOD, .type = DDB_MOD,
.name = "Digital Devices DVBT", .name = "Digital Devices DVBT",
@ -568,7 +627,7 @@ static const struct ddb_info ddb_octopro = {
static const struct ddb_info ddb_s2_48 = { static const struct ddb_info ddb_s2_48 = {
.type = DDB_OCTOPUS_MAX, .type = DDB_OCTOPUS_MAX,
.name = "Digital Devices MAX S8 4/8", .name = "Digital Devices MAX S8 4/8",
.regmap = &octopus_map, .regmap = &octopus_mci_map,
.port_num = 4, .port_num = 4,
.i2c_mask = 0x01, .i2c_mask = 0x01,
.board_control = 1, .board_control = 1,
@ -635,10 +694,12 @@ static const struct ddb_info ddb_c2t2i_8 = {
.tempmon_irq = 24, .tempmon_irq = 24,
}; };
/****************************************************************************/
static const struct ddb_info ddb_s2x_48 = { static const struct ddb_info ddb_s2x_48 = {
.type = DDB_OCTOPUS_MCI, .type = DDB_OCTOPUS_MCI,
.name = "Digital Devices MAX SX8", .name = "Digital Devices MAX SX8",
.regmap = &octopus_map, .regmap = &octopus_mci_map,
.port_num = 4, .port_num = 4,
.i2c_mask = 0x00, .i2c_mask = 0x00,
.tempmon_irq = 24, .tempmon_irq = 24,
@ -650,7 +711,7 @@ static const struct ddb_info ddb_s2x_48 = {
static const struct ddb_info ddb_s2x_48_b = { static const struct ddb_info ddb_s2x_48_b = {
.type = DDB_OCTOPUS_MCI, .type = DDB_OCTOPUS_MCI,
.name = "Digital Devices MAX SX8 Basic", .name = "Digital Devices MAX SX8 Basic",
.regmap = &octopus_map, .regmap = &octopus_mci_map,
.port_num = 4, .port_num = 4,
.i2c_mask = 0x00, .i2c_mask = 0x00,
.tempmon_irq = 24, .tempmon_irq = 24,
@ -662,7 +723,7 @@ static const struct ddb_info ddb_s2x_48_b = {
static const struct ddb_info ddb_m4 = { static const struct ddb_info ddb_m4 = {
.type = DDB_OCTOPUS_MCI, .type = DDB_OCTOPUS_MCI,
.name = "Digital Devices MAX M4", .name = "Digital Devices MAX M4",
.regmap = &octopus_map, .regmap = &octopus_mci_map,
.port_num = 2, .port_num = 2,
.i2c_mask = 0x00, .i2c_mask = 0x00,
.tempmon_irq = 24, .tempmon_irq = 24,
@ -671,6 +732,8 @@ static const struct ddb_info ddb_m4 = {
.temp_num = 1, .temp_num = 1,
}; };
/****************************************************************************/
static const struct ddb_info ddb_gtl_mini = { static const struct ddb_info ddb_gtl_mini = {
.type = DDB_OCTOPUS, .type = DDB_OCTOPUS,
.name = "Digital Devices Octopus GT Mini", .name = "Digital Devices Octopus GT Mini",
@ -813,6 +876,8 @@ static const struct ddb_device_id ddb_device_ids[] = {
DDB_DEVID(0x0220, 0x0001, ddb_sdr_atv), DDB_DEVID(0x0220, 0x0001, ddb_sdr_atv),
DDB_DEVID(0x0221, 0x0001, ddb_sdr_iq), DDB_DEVID(0x0221, 0x0001, ddb_sdr_iq),
DDB_DEVID(0x0222, 0x0001, ddb_sdr_dvbt), DDB_DEVID(0x0222, 0x0001, ddb_sdr_dvbt),
DDB_DEVID(0x0223, 0x0001, ddb_sdr_iq2),
DDB_DEVID(0xffff, 0xffff, ddb_sdr_iq2),
/* testing on OctopusNet Pro */ /* testing on OctopusNet Pro */
DDB_DEVID(0x0320, 0xffff, ddb_octopro_hdin), DDB_DEVID(0x0320, 0xffff, ddb_octopro_hdin),

View File

@ -139,8 +139,8 @@ void gtlcpyfrom(struct ddb *dev, u8 *buf, u32 adr, long count)
u32 a = p & 3; u32 a = p & 3;
if (a) { if (a) {
val = ddbreadl(dev, p) >> (8 * a); val = ddbreadl(dev, p & ~3) >> (8 * a);
while (p & 3 && count) { while ((p & 3) && count) {
*buf = val & 0xff; *buf = val & 0xff;
val >>= 8; val >>= 8;
p++; p++;
@ -177,7 +177,12 @@ void ddbcpyto(struct ddb *dev, u32 adr, void *src, long count)
void ddbcpyfrom(struct ddb *dev, void *dst, u32 adr, long count) void ddbcpyfrom(struct ddb *dev, void *dst, u32 adr, long count)
{ {
return gtlcpyfrom(dev, dst, adr, count);
/* The possible 64 bit read in memcpy_fromio produces errors
on some platforms, i.e. arm64 rpi4
if (unlikely(adr & 0xf0000000)) if (unlikely(adr & 0xf0000000))
return gtlcpyfrom(dev, dst, adr, count); return gtlcpyfrom(dev, dst, adr, count);
return memcpy_fromio(dst, dev->regs + adr, count); return memcpy_fromio(dst, dev->regs + adr, count);
*/
} }

View File

@ -402,6 +402,8 @@ static int read_status(struct dvb_frontend *fe, enum fe_status *status)
ddb_mci_get_strength(fe); ddb_mci_get_strength(fe);
if (res.status == MCI_DEMOD_WAIT_SIGNAL) if (res.status == MCI_DEMOD_WAIT_SIGNAL)
*status = 0x01; *status = 0x01;
if (res.status == M4_DEMOD_WAIT_TS)
*status = 0x03;
if (res.status == MCI_DEMOD_LOCKED) { if (res.status == MCI_DEMOD_LOCKED) {
*status = 0x1f; *status = 0x1f;
ddb_mci_get_snr(fe); ddb_mci_get_snr(fe);

View File

@ -290,9 +290,11 @@ static int __devinit ddb_probe(struct pci_dev *pdev,
pci_set_master(pdev); pci_set_master(pdev);
if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
return -ENODEV; } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
} else return -ENODEV;
dev = vzalloc(sizeof(*dev)); dev = vzalloc(sizeof(*dev));
if (!dev) if (!dev)
@ -425,6 +427,7 @@ static const struct pci_device_id ddb_id_table[] __devinitconst = {
DDB_DEVICE_ANY(0x0220), DDB_DEVICE_ANY(0x0220),
DDB_DEVICE_ANY(0x0221), DDB_DEVICE_ANY(0x0221),
DDB_DEVICE_ANY(0x0222), DDB_DEVICE_ANY(0x0222),
DDB_DEVICE_ANY(0x0223),
DDB_DEVICE_ANY(0x0320), DDB_DEVICE_ANY(0x0320),
DDB_DEVICE_ANY(0x0321), DDB_DEVICE_ANY(0x0321),
DDB_DEVICE_ANY(0x0322), DDB_DEVICE_ANY(0x0322),

View File

@ -28,151 +28,178 @@
static LIST_HEAD(mci_list); static LIST_HEAD(mci_list);
static int mci_reset(struct mci *state) static int mci_reset(struct ddb_link *link)
{ {
struct ddb_link *link = state->base->link; const struct ddb_regmap *regmap = link->info->regmap;
u32 control;
u32 status = 0; u32 status = 0;
u32 timeout = 40; u32 timeout = 40;
ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL); if (!regmap || ! regmap->mci)
ddblwritel(link, 0, MCI_CONTROL + 4); /* 1= no internal init */ return -EINVAL;
msleep(300); control = regmap->mci->base;
ddblwritel(link, 0, MCI_CONTROL);
if ((link->info->type == DDB_OCTOPUS_MCI) &&
(ddblreadl(link, control) & MCI_CONTROL_START_COMMAND)) {
ddblwritel(link, MCI_CONTROL_RESET, control);
ddblwritel(link, 0, control + 4); /* 1= no internal init */
msleep(300);
}
ddblwritel(link, 0, control);
while (1) { while (1) {
status = ddblreadl(link, MCI_CONTROL); status = ddblreadl(link, control);
if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY) if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY)
break; break;
if (--timeout == 0) if (--timeout == 0)
break; break;
msleep(50); msleep(50);
} }
if ((status & MCI_CONTROL_READY) == 0) dev_info(link->dev->dev, "MCI control port @ %08x\n", control);
if ((status & MCI_CONTROL_READY) == 0) {
dev_err(link->dev->dev, "MCI init failed!\n");
return -1; return -1;
if (link->ids.device == 0x0009 || link->ids.device == 0x000b) }
ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG); dev_info(link->dev->dev, "MCI port OK, init time %u msecs\n", (40-timeout)*50);
return 0; return 0;
} }
int ddb_mci_config(struct mci *state, u32 config) static int ddb_mci_cmd_raw_unlocked(struct ddb_link *link,
{
struct ddb_link *link = state->base->link;
if (link->ids.device != 0x0009 && link->ids.device != 0x000b)
return -EINVAL;
ddblwritel(link, config, SX8_TSCONFIG);
return 0;
}
static int ddb_mci_cmd_raw_unlocked(struct mci *state,
u32 *cmd, u32 cmd_len, u32 *cmd, u32 cmd_len,
u32 *res, u32 res_len) u32 *res, u32 res_len)
{ {
struct ddb_link *link = state->base->link; const struct ddb_regmap *regmap = link->info->regmap;
u32 control, command, result;
u32 i, val; u32 i, val;
unsigned long stat; unsigned long stat;
val = ddblreadl(link, MCI_CONTROL); if (!regmap || ! regmap->mci)
return -EINVAL;
control = regmap->mci->base;
command = regmap->mci_buf->base;
result = command + MCI_COMMAND_SIZE;
val = ddblreadl(link, control);
if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND)) if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND))
return -EIO; return -EIO;
if (cmd && cmd_len) if (cmd && cmd_len)
for (i = 0; i < cmd_len; i++) for (i = 0; i < cmd_len; i++)
ddblwritel(link, cmd[i], MCI_COMMAND + i * 4); ddblwritel(link, cmd[i], command + i * 4);
val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT); val |= (MCI_CONTROL_START_COMMAND |
ddblwritel(link, val, MCI_CONTROL); MCI_CONTROL_ENABLE_DONE_INTERRUPT);
ddblwritel(link, val, control);
stat = wait_for_completion_timeout(&state->base->completion, HZ); stat = wait_for_completion_timeout(&link->mci_completion, HZ);
if (stat == 0) { if (stat == 0) {
u32 istat = ddblreadl(link, INTERRUPT_STATUS); u32 istat = ddblreadl(link, INTERRUPT_STATUS);
dev_err(state->base->link->dev->dev, "MCI timeout\n"); dev_err(link->dev->dev, "MCI timeout\n");
val = ddblreadl(link, MCI_CONTROL); val = ddblreadl(link, control);
if (val == 0xffffffff) { if (val == 0xffffffff) {
dev_err(state->base->link->dev->dev, dev_err(link->dev->dev,
"Lost PCIe link!\n"); "Lost PCIe link!\n");
return -EIO; return -EIO;
} else { } else {
dev_err(state->base->link->dev->dev, dev_err(link->dev->dev,
"DDBridge IRS %08x link %u\n", istat, link->nr); "DDBridge IRS %08x link %u\n",
istat, link->nr);
if (istat & 1) if (istat & 1)
ddblwritel(link, istat, INTERRUPT_ACK); ddblwritel(link, istat, INTERRUPT_ACK);
if (link->nr) if (link->nr)
ddbwritel(link->dev, 0xffffff, INTERRUPT_ACK); ddbwritel(link->dev,
0xffffff, INTERRUPT_ACK);
} }
} }
if (res && res_len) if (res && res_len)
for (i = 0; i < res_len; i++) for (i = 0; i < res_len; i++)
res[i] = ddblreadl(link, MCI_RESULT + i * 4); res[i] = ddblreadl(link, result + i * 4);
return 0; return 0;
} }
int ddb_mci_cmd_unlocked(struct mci *state, int ddb_mci_cmd_link(struct ddb_link *link,
struct mci_command *command, struct mci_command *command,
struct mci_result *result) struct mci_result *result)
{ {
u32 *cmd = (u32 *) command;
u32 *res = (u32 *) result;
return ddb_mci_cmd_raw_unlocked(state, cmd, sizeof(*command)/sizeof(u32),
res, sizeof(*result)/sizeof(u32));
}
int ddb_mci_cmd(struct mci *state,
struct mci_command *command,
struct mci_result *result)
{
int stat;
struct mci_result res; struct mci_result res;
int stat;
if (!result) if (!result)
result = &res; result = &res;
mutex_lock(&state->base->mci_lock); mutex_lock(&link->mci_lock);
stat = ddb_mci_cmd_raw_unlocked(state, stat = ddb_mci_cmd_raw_unlocked(link,
(u32 *)command, sizeof(*command)/sizeof(u32), (u32 *)command,
(u32 *)result, sizeof(*result)/sizeof(u32)); sizeof(*command)/sizeof(u32),
mutex_unlock(&state->base->mci_lock); (u32 *)result,
sizeof(*result)/sizeof(u32));
mutex_unlock(&link->mci_lock);
if (command && result && (result->status & 0x80)) if (command && result && (result->status & 0x80))
dev_warn(state->base->link->dev->dev, dev_warn(link->dev->dev,
"mci_command 0x%02x, error=0x%02x\n", "mci_command 0x%02x, error=0x%02x\n",
command->command, result->status); command->command, result->status);
return stat; return stat;
} }
static void mci_handler(void *priv)
{
struct ddb_link *link = (struct ddb_link *) priv;
complete(&link->mci_completion);
}
int mci_init(struct ddb_link *link)
{
int result;
mutex_init(&link->mci_lock);
init_completion(&link->mci_completion);
result = mci_reset(link);
if (result < 0)
return result;
if (link->ids.device == 0x0009 || link->ids.device == 0x000b)
ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG);
ddb_irq_set(link->dev, link->nr,
link->info->regmap->irq_base_mci,
mci_handler, link);
link->mci_ok = 1;
return result;
}
int mci_cmd_val(struct ddb_link *link, uint32_t cmd, uint32_t val)
{
struct mci_result result;
struct mci_command command = {
.command_word = cmd,
.params = { val },
};
return ddb_mci_cmd_link(link, &command, &result);
}
/****************************************************************************/
/****************************************************************************/
int ddb_mci_cmd(struct mci *state,
struct mci_command *command,
struct mci_result *result)
{
return ddb_mci_cmd_link(state->base->link, command, result);
}
int ddb_mci_cmd_raw(struct mci *state, int ddb_mci_cmd_raw(struct mci *state,
struct mci_command *command, u32 command_len, struct mci_command *command, u32 command_len,
struct mci_result *result, u32 result_len) struct mci_result *result, u32 result_len)
{ {
struct ddb_link *link = state->base->link;
int stat; int stat;
mutex_lock(&state->base->mci_lock); mutex_lock(&link->mci_lock);
stat = ddb_mci_cmd_raw_unlocked(state, stat = ddb_mci_cmd_raw_unlocked(link,
(u32 *)command, command_len, (u32 *)command, command_len,
(u32 *)result, result_len); (u32 *)result, result_len);
mutex_unlock(&state->base->mci_lock); mutex_unlock(&link->mci_lock);
return stat; return stat;
} }
#if 0
static int ddb_mci_get_iq(struct mci *mci, u32 demod, s16 *i, s16 *q)
{
int stat;
struct mci_command cmd;
struct mci_result res;
memset(&cmd, 0, sizeof(cmd));
memset(&res, 0, sizeof(res));
cmd.command = MCI_CMD_GET_IQSYMBOL;
cmd.demod = demod;
stat = ddb_mci_cmd(mci, &cmd, &res);
if (!stat) {
*i = res.iq_symbol.i;
*q = res.iq_symbol.q;
}
return stat;
}
#endif
int ddb_mci_get_status(struct mci *mci, struct mci_result *res) int ddb_mci_get_status(struct mci *mci, struct mci_result *res)
{ {
struct mci_command cmd; struct mci_command cmd;
@ -189,7 +216,8 @@ int ddb_mci_get_snr(struct dvb_frontend *fe)
p->cnr.len = 1; p->cnr.len = 1;
p->cnr.stat[0].scale = FE_SCALE_DECIBEL; p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
p->cnr.stat[0].svalue = (s64) mci->signal_info.dvbs2_signal_info.signal_to_noise * 10; p->cnr.stat[0].svalue =
(s64) mci->signal_info.dvbs2_signal_info.signal_to_noise * 10;
return 0; return 0;
} }
@ -266,6 +294,8 @@ void ddb_mci_proc_info(struct mci *mci, struct dtv_frontend_properties *p)
p->delivery_system = p->delivery_system =
(mci->signal_info.dvbs2_signal_info.standard == 2) ? (mci->signal_info.dvbs2_signal_info.standard == 2) ?
SYS_DVBS2 : SYS_DVBS; SYS_DVBS2 : SYS_DVBS;
p->inversion = (mci->signal_info.dvbs2_signal_info.roll_off & 0x80) ?
INVERSION_ON : INVERSION_OFF;
if (mci->signal_info.dvbs2_signal_info.standard == 2) { if (mci->signal_info.dvbs2_signal_info.standard == 2) {
u32 modcod; u32 modcod;
@ -306,6 +336,8 @@ void ddb_mci_proc_info(struct mci *mci, struct dtv_frontend_properties *p)
case SYS_ISDBT: case SYS_ISDBT:
break; break;
} }
/* post is correct, we cannot provide both pre and post at the same time */
/* set pre and post the same for now */
p->pre_bit_error.len = 1; p->pre_bit_error.len = 1;
p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
p->pre_bit_error.stat[0].uvalue = p->pre_bit_error.stat[0].uvalue =
@ -316,6 +348,16 @@ void ddb_mci_proc_info(struct mci *mci, struct dtv_frontend_properties *p)
p->pre_bit_count.stat[0].uvalue = p->pre_bit_count.stat[0].uvalue =
mci->signal_info.dvbs2_signal_info.ber_denominator; mci->signal_info.dvbs2_signal_info.ber_denominator;
p->post_bit_error.len = 1;
p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
p->post_bit_error.stat[0].uvalue =
mci->signal_info.dvbs2_signal_info.ber_numerator;
p->post_bit_count.len = 1;
p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
p->post_bit_count.stat[0].uvalue =
mci->signal_info.dvbs2_signal_info.ber_denominator;
p->block_error.len = 1; p->block_error.len = 1;
p->block_error.stat[0].scale = FE_SCALE_COUNTER; p->block_error.stat[0].scale = FE_SCALE_COUNTER;
p->block_error.stat[0].uvalue = p->block_error.stat[0].uvalue =
@ -329,17 +371,10 @@ void ddb_mci_proc_info(struct mci *mci, struct dtv_frontend_properties *p)
p->strength.len = 1; p->strength.len = 1;
p->strength.stat[0].scale = FE_SCALE_DECIBEL; p->strength.stat[0].scale = FE_SCALE_DECIBEL;
p->strength.stat[0].svalue = p->strength.stat[0].svalue = (s64)
mci->signal_info.dvbs2_signal_info.channel_power * 10; mci->signal_info.dvbs2_signal_info.channel_power * 10;
} }
static void mci_handler(void *priv)
{
struct mci_base *base = (struct mci_base *)priv;
complete(&base->completion);
}
static struct mci_base *match_base(void *key) static struct mci_base *match_base(void *key)
{ {
struct mci_base *p; struct mci_base *p;
@ -350,13 +385,8 @@ static struct mci_base *match_base(void *key)
return NULL; return NULL;
} }
static int probe(struct mci *state) struct dvb_frontend *ddb_mci_attach(struct ddb_input *input,
{ struct mci_cfg *cfg, int nr, int tuner)
mci_reset(state);
return 0;
}
struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr, int tuner)
{ {
struct ddb_port *port = input->port; struct ddb_port *port = input->port;
struct ddb *dev = port->dev; struct ddb *dev = port->dev;
@ -380,12 +410,11 @@ struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg
base->key = key; base->key = key;
base->count = 1; base->count = 1;
base->link = link; base->link = link;
mutex_init(&base->mci_lock); link->mci_base = base;
mutex_init(&base->tuner_lock); mutex_init(&base->tuner_lock);
ddb_irq_set(dev, link->nr, 0, mci_handler, base);
init_completion(&base->completion);
state->base = base; state->base = base;
if (probe(state) < 0) {
if (!link->mci_ok) {
kfree(base); kfree(base);
goto fail; goto fail;
} }

View File

@ -39,11 +39,6 @@
#define MIC_INTERFACE_OUT (0x0680) #define MIC_INTERFACE_OUT (0x0680)
#define MIC_INTERFACE_VER (0x06F0) #define MIC_INTERFACE_VER (0x06F0)
#define MCI_CONTROL (0x500)
#define MCI_COMMAND (0x600)
#define MCI_RESULT (0x680)
#define MCI_COMMAND_SIZE (0x80) #define MCI_COMMAND_SIZE (0x80)
#define MCI_RESULT_SIZE (0x80) #define MCI_RESULT_SIZE (0x80)
@ -686,7 +681,7 @@ struct mci_result {
} ISDBS_TMCCInfo; } ISDBS_TMCCInfo;
}; };
u32 version[3]; u32 version[3];
u32 version_rsvd; u8 version_rsvd;
u8 version_major; u8 version_major;
u8 version_minor; u8 version_minor;
u8 version_sub; u8 version_sub;
@ -767,9 +762,9 @@ struct mci_base {
struct list_head mci_list; struct list_head mci_list;
void *key; void *key;
struct ddb_link *link; struct ddb_link *link;
struct completion completion; // struct completion completion;
struct mutex tuner_lock; struct mutex tuner_lock;
struct mutex mci_lock; // struct mutex mci_lock;
int count; int count;
int type; int type;
}; };
@ -795,14 +790,14 @@ struct mci_cfg {
}; };
int ddb_mci_cmd(struct mci *state, struct mci_command *command, struct mci_result *result); int ddb_mci_cmd(struct mci *state, struct mci_command *command, struct mci_result *result);
int ddb_mci_cmd_raw(struct mci *state, struct mci_command *command, u32 command_len, int ddb_mci_cmd_link(struct ddb_link *link, struct mci_command *command, struct mci_result *result);
struct mci_result *result, u32 result_len);
int ddb_mci_config(struct mci *state, u32 config);
int ddb_mci_get_status(struct mci *mci, struct mci_result *res); int ddb_mci_get_status(struct mci *mci, struct mci_result *res);
int ddb_mci_get_snr(struct dvb_frontend *fe); int ddb_mci_get_snr(struct dvb_frontend *fe);
int ddb_mci_get_info(struct mci *mci); int ddb_mci_get_info(struct mci *mci);
int ddb_mci_get_strength(struct dvb_frontend *fe); int ddb_mci_get_strength(struct dvb_frontend *fe);
void ddb_mci_proc_info(struct mci *mci, struct dtv_frontend_properties *p); void ddb_mci_proc_info(struct mci *mci, struct dtv_frontend_properties *p);
int mci_init(struct ddb_link *link);
int mci_cmd_val(struct ddb_link *link, uint32_t cmd, uint32_t val);
extern struct mci_cfg ddb_max_sx8_cfg; extern struct mci_cfg ddb_max_sx8_cfg;
extern struct mci_cfg ddb_max_m4_cfg; extern struct mci_cfg ddb_max_m4_cfg;

View File

@ -644,6 +644,7 @@ static int mod_set_sdr_attenuator(struct ddb *dev, u32 value)
static int mod_set_sdr_gain(struct ddb *dev, u32 gain) static int mod_set_sdr_gain(struct ddb *dev, u32 gain)
{ {
u32 control = ddbreadl(dev, SDR_CONTROL); u32 control = ddbreadl(dev, SDR_CONTROL);
struct ddb_link *link = &dev->link[0];
if (control & 0x01000000) { if (control & 0x01000000) {
if (gain > 511) if (gain > 511)
@ -658,6 +659,8 @@ static int mod_set_sdr_gain(struct ddb *dev, u32 gain)
return -EINVAL; return -EINVAL;
ddbwritel(dev, gain, SDR_GAIN_F); ddbwritel(dev, gain, SDR_GAIN_F);
} }
if (link->mci_ok)
mci_cmd_val(link, 0xc0, gain);
return 0; return 0;
} }
@ -1516,6 +1519,7 @@ static int mod3_set_ari(struct ddb_mod *mod, u32 rate)
static int mod3_set_sample_rate(struct ddb_mod *mod, u32 rate) static int mod3_set_sample_rate(struct ddb_mod *mod, u32 rate)
{ {
struct ddb *dev = mod->port->dev;
u32 cic, inc, bypass = 0; u32 cic, inc, bypass = 0;
switch (rate) { switch (rate) {
@ -1556,19 +1560,52 @@ static int mod3_set_sample_rate(struct ddb_mod *mod, u32 rate)
inc = 0x7684BD82; //1988410754; inc = 0x7684BD82; //1988410754;
cic = 7; cic = 7;
break; break;
case SYS_DVBS2_22: case SYS_DVB_22:
inc = 0x72955555; // 1922389333; inc = 0x72955555; // 1922389333;
cic = 5; cic = 5;
bypass = 2; bypass = 2;
break; break;
case SYS_DVBS2_24: case SYS_DVB_24:
inc = 0x7d000000; inc = 0x7d000000;
cic = 5; cic = 5;
bypass = 2; bypass = 2;
break; break;
case SYS_DVB_30:
inc = 0x7d000000;
cic = 4;
bypass = 2;
break;
case SYS_ISDBS_2886:
inc = 0x78400000;
cic = 4;
bypass = 2;
break;
default: default:
{
u64 a;
if (rate < 1000000)
return -EINVAL; return -EINVAL;
if (rate > 30720000)
return -EINVAL;
bypass = 2;
if (rate > 24576000)
cic = 4;
else if (rate > 20480000)
cic = 5;
else if (rate > 17554286)
cic = 6;
else if (rate > 15360000)
cic = 7;
else
cic = 8;
a = (1ULL << 31) * rate * 2 * cic;
inc = div_s64(a, 245760000);
break;
} }
}
dev_info(dev->dev, "inc = %08x, cic = %u, bypass = %u\n", inc, cic, bypass);
ddbwritel(mod->port->dev, inc, SDR_CHANNEL_ARICW(mod->port->nr)); ddbwritel(mod->port->dev, inc, SDR_CHANNEL_ARICW(mod->port->nr));
ddbwritel(mod->port->dev, (cic << 8) | (bypass << 4), ddbwritel(mod->port->dev, (cic << 8) | (bypass << 4),
SDR_CHANNEL_CONFIG(mod->port->nr)); SDR_CHANNEL_CONFIG(mod->port->nr));
@ -1596,6 +1633,7 @@ static int mod3_prop_proc(struct ddb_mod *mod, struct dtv_property *tvp)
case MODULATOR_GAIN: case MODULATOR_GAIN:
return mod_set_sdr_gain(mod->port->dev, tvp->u.data); return mod_set_sdr_gain(mod->port->dev, tvp->u.data);
} }
return -EINVAL; return -EINVAL;
} }

View File

@ -114,6 +114,16 @@ static void release(struct dvb_frontend *fe)
kfree(state); kfree(state);
} }
static int ddb_mci_tsconfig(struct mci *state, u32 config)
{
struct ddb_link *link = state->base->link;
if (link->ids.device != 0x0009 && link->ids.device != 0x000b)
return -EINVAL;
ddblwritel(link, config, SX8_TSCONFIG);
return 0;
}
static int read_status(struct dvb_frontend *fe, enum fe_status *status) static int read_status(struct dvb_frontend *fe, enum fe_status *status)
{ {
int stat; int stat;
@ -172,7 +182,7 @@ static int stop_iq(struct dvb_frontend *fe)
cmd.command = SX8_CMD_STOP_IQ; cmd.command = SX8_CMD_STOP_IQ;
cmd.demod = state->mci.demod; cmd.demod = state->mci.demod;
ddb_mci_cmd(&state->mci, &cmd, NULL); ddb_mci_cmd(&state->mci, &cmd, NULL);
ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL); ddb_mci_tsconfig(&state->mci, SX8_TSCONFIG_MODE_NORMAL);
mutex_lock(&mci_base->tuner_lock); mutex_lock(&mci_base->tuner_lock);
sx8_base->tuner_use_count[input]--; sx8_base->tuner_use_count[input]--;
@ -209,7 +219,7 @@ static int stop(struct dvb_frontend *fe)
cmd.demod = state->mci.demod; cmd.demod = state->mci.demod;
cmd.output = 0; cmd.output = 0;
ddb_mci_cmd(&state->mci, &cmd, NULL); ddb_mci_cmd(&state->mci, &cmd, NULL);
ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL); ddb_mci_tsconfig(&state->mci, SX8_TSCONFIG_MODE_NORMAL);
} }
} }
mutex_lock(&mci_base->tuner_lock); mutex_lock(&mci_base->tuner_lock);
@ -331,7 +341,7 @@ unlock:
cmd.demod = state->mci.demod; cmd.demod = state->mci.demod;
cmd.output = p->stream_id & 7; cmd.output = p->stream_id & 7;
ddb_mci_cmd(&state->mci, &cmd, NULL); ddb_mci_cmd(&state->mci, &cmd, NULL);
ddb_mci_config(&state->mci, ts_config); ddb_mci_tsconfig(&state->mci, ts_config);
} }
if (p->stream_id != NO_STREAM_ID_FILTER && !(p->stream_id & 0xf0000000)) if (p->stream_id != NO_STREAM_ID_FILTER && !(p->stream_id & 0xf0000000))
flags |= 0x80; flags |= 0x80;
@ -411,7 +421,7 @@ static int start_iq(struct dvb_frontend *fe, u32 flags,
stat = ddb_mci_cmd(&state->mci, &cmd, NULL); stat = ddb_mci_cmd(&state->mci, &cmd, NULL);
if (stat) if (stat)
stop_iq(fe); stop_iq(fe);
ddb_mci_config(&state->mci, ts_config); ddb_mci_tsconfig(&state->mci, ts_config);
return stat; return stat;
} }

View File

@ -114,6 +114,7 @@ struct ddb_regmap {
u32 irq_base_odma; u32 irq_base_odma;
u32 irq_base_gtl; u32 irq_base_gtl;
u32 irq_base_rate; u32 irq_base_rate;
u32 irq_base_mci;
const struct ddb_regset *i2c; const struct ddb_regset *i2c;
const struct ddb_regset *i2c_buf; const struct ddb_regset *i2c_buf;
@ -127,6 +128,9 @@ struct ddb_regmap {
const struct ddb_regset *channel; const struct ddb_regset *channel;
const struct ddb_regset *gtl; const struct ddb_regset *gtl;
const struct ddb_regset *mci;
const struct ddb_regset *mci_buf;
}; };
struct ddb_ids { struct ddb_ids {
@ -427,6 +431,9 @@ struct ddb_link {
struct ddb_irq irq[256]; struct ddb_irq irq[256];
struct mci_base *mci_base; struct mci_base *mci_base;
struct completion mci_completion;
struct mutex mci_lock;
int mci_ok;
}; };
struct ddb { struct ddb {

13
docs/firmware Normal file
View File

@ -0,0 +1,13 @@
Firmware update:
Copy the firmware file to the dddvb/apps/ directory and
execute "./flashprog".
The program will try to identify the card version and
check if it finds the corresponding firmware file.
It will then prompt you to confirm to proceed
with the flashing procedure.
After the update the system needs a power cycle.

13
docs/iq_samples Normal file
View File

@ -0,0 +1,13 @@
~The Max SX8 can provide IQ samples in real time.
They are 8 bit signed values embedded in TS packets with PID 0x200.
API:
Currently DTV_STREAM_ID is misused.
0x10000000 - symbols (locked and tracked) at symbol rate
0x20000000 - samples at ADC rate (1550/24=64.583... MHz)
0x30000000 - samples at symbol rate
Max. sample rate is 64.583333 MHz.

View File

@ -44,8 +44,10 @@ enum mod_output_rate {
SYS_ISDBT_6 = 16, SYS_ISDBT_6 = 16,
SYS_J83B_64_6 = 24, SYS_J83B_64_6 = 24,
SYS_J83B_256_6 = 25, SYS_J83B_256_6 = 25,
SYS_DVBS2_22 = 32, SYS_DVB_22 = 32,
SYS_DVBS2_24 = 33, SYS_DVB_24 = 33,
SYS_DVB_30 = 34,
SYS_ISDBS_2886 = 48,
}; };

View File

@ -203,7 +203,7 @@ void proc_ts(int i, uint8_t *buf)
if (ccin & 0x10) { if (ccin & 0x10) {
if ( cc[pid]) { if ( cc[pid]) {
// TODO: 1 repetition allowed // TODO: 1 repetition allowed
if( ( ccin & 0x10 ) != 0 && (((cc[pid] + 1) & 0x0F) != (ccin & 0x0F)) ) if ((((cc[pid] + 1) & 0x0F) != (ccin & 0x0F)))
cc_errors += 1; cc_errors += 1;
} }
cc[pid] = ccin; cc[pid] = ccin;
@ -414,6 +414,8 @@ int main(int argc, char **argv)
delsys = SYS_ISDBC; delsys = SYS_ISDBC;
if (!strcmp(optarg, "ISDBT")) if (!strcmp(optarg, "ISDBT"))
delsys = SYS_ISDBT; delsys = SYS_ISDBT;
if (!strcmp(optarg, "ISDBS"))
delsys = SYS_ISDBS;
break; break;
case 'p': case 'p':
if (!strcmp(optarg, "h") || !strcmp(optarg, "H")) if (!strcmp(optarg, "h") || !strcmp(optarg, "H"))

View File

@ -128,7 +128,7 @@ static int set_fe_input(struct dddvb_fe *fe, uint32_t fr,
} }
if (input != DDDVB_UNDEF) if (input != DDDVB_UNDEF)
set_property(fd, DTV_INPUT, input); set_property(fd, DTV_INPUT, input);
fprintf(stderr, "bw =%u\n", fe->param.param[PARAM_BW_HZ]); //fprintf(stderr, "bw =%u\n", fe->param.param[PARAM_BW_HZ]);
if (fe->param.param[PARAM_BW_HZ] != DDDVB_UNDEF) if (fe->param.param[PARAM_BW_HZ] != DDDVB_UNDEF)
set_property(fd, DTV_BANDWIDTH_HZ, fe->param.param[PARAM_BW_HZ]); set_property(fd, DTV_BANDWIDTH_HZ, fe->param.param[PARAM_BW_HZ]);
if (fe->param.param[PARAM_ISI] != DDDVB_UNDEF) if (fe->param.param[PARAM_ISI] != DDDVB_UNDEF)
@ -320,11 +320,12 @@ static int tune_sat(struct dddvb_fe *fe)
} else { } else {
uint32_t input = lnb; uint32_t input = lnb;
if (input != DDDVB_UNDEF) //if (input != DDDVB_UNDEF)
input = 3 & (input >> 6); // input = 3 & (input >> 6);
//set_property(fe->fd, DTV_INPUT, 3 & (lnb >> 6)); //set_property(fe->fd, DTV_INPUT, 3 & (lnb >> 6));
diseqc(fe->fd, lnb, fe->param.param[PARAM_POL], hi); diseqc(fe->fd, lnb, fe->param.param[PARAM_POL], hi);
set_fe_input(fe, freq, fe->param.param[PARAM_SR], ds, input); //set_fe_input(fe, freq, fe->param.param[PARAM_SR], ds, input);
set_fe_input(fe, freq, fe->param.param[PARAM_SR], ds, ~(0U));
} }
} }
@ -509,6 +510,32 @@ static int tune_isdbt(struct dddvb_fe *fe)
return 0; return 0;
} }
static int tune_isdbs(struct dddvb_fe *fe)
{
struct dtv_property p[] = {
{ .cmd = DTV_CLEAR },
{ .cmd = DTV_FREQUENCY, .u.data = fe->param.param[PARAM_FREQ]},
//{ .cmd = DTV_SYMBOL_RATE, .u.data = fe->param.param[PARAM_SR] },
//{ .cmd = DTV_TUNE },
};
struct dtv_properties c;
int ret;
set_property(fe->fd, DTV_DELIVERY_SYSTEM, SYS_ISDBS);
c.num = ARRAY_SIZE(p);
c.props = p;
ret = ioctl(fe->fd, FE_SET_PROPERTY, &c);
if (ret < 0) {
fprintf(stderr, "FE_SET_PROPERTY returned %d\n", ret);
return -1;
}
if (fe->param.param[PARAM_ISI] != DDDVB_UNDEF)
set_property(fe->fd, DTV_STREAM_ID, fe->param.param[PARAM_ISI]);
set_property(fe->fd, DTV_TUNE, 0);
return 0;
}
static int tune(struct dddvb_fe *fe) static int tune(struct dddvb_fe *fe)
{ {
int ret; int ret;
@ -537,6 +564,9 @@ static int tune(struct dddvb_fe *fe)
case SYS_ISDBT: case SYS_ISDBT:
ret = tune_isdbt(fe); ret = tune_isdbt(fe);
break; break;
case SYS_ISDBS:
ret = tune_isdbs(fe);
break;
default: default:
break; break;
} }
@ -678,7 +708,7 @@ void dddvb_fe_handle(struct dddvb_fe *fe)
} else { } else {
max = 1; max = 1;
nolock++; nolock++;
if (nolock > 100) if (nolock > 10)
fe->tune = 1; fe->tune = 1;
} }
break; break;