blob: e4401c77015c5e00fee49aaed355ee5cd4c20911 [file] [log] [blame]
#include "fm_low_struc.h"
#include "radio-s610.h"
#include "fm_rds.h"
extern struct s610_radio *gradio;
#ifdef USE_RINGBUFF_API
void ringbuf_reset(struct ringbuf_t *rb)
{
rb->head = rb->tail = rb->buf;
}
int ringbuf_buffer_size(const struct ringbuf_t *rb)
{
return rb->size;
}
int ringbuf_capacity(const struct ringbuf_t *rb)
{
return ringbuf_buffer_size(rb) - 1;
}
static const u8 *ringbuf_end(const struct ringbuf_t *rb)
{
return rb->buf + ringbuf_buffer_size(rb);
}
int ringbuf_bytes_free(const struct ringbuf_t *rb)
{
if (rb->head >= rb->tail)
return ringbuf_capacity(rb) - (rb->head - rb->tail);
else
return rb->tail - rb->head - 1;
}
int ringbuf_bytes_used(const struct ringbuf_t *rb)
{
return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
}
int ringbuf_is_full(const struct ringbuf_t *rb)
{
return ringbuf_bytes_free(rb) == 0;
}
int ringbuf_is_empty(const struct ringbuf_t *rb)
{
return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
}
const void *ringbuf_tail(const struct ringbuf_t *rb)
{
return rb->tail;
}
const void *ringbuf_head(const struct ringbuf_t *rb)
{
return rb->head;
}
static u8 *ringbuf_nextp(struct ringbuf_t *rb, const u8 *p)
{
/*
* The assert guarantees the expression (++p - rb->buf) is
* non-negative; therefore, the modulus operation is safe and
* portable.
*/
return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));
}
void *ringbuf_memcpy_into(struct ringbuf_t *dst, const void *src, int count)
{
const u8 *u8src = src;
const u8 *bufend = ringbuf_end(dst);
int overflow = count > ringbuf_bytes_free(dst);
int nread = 0;
int n = 0;
while (nread != count) {
n = MIN(bufend - dst->head, count - nread);
memcpy(dst->head, u8src + nread, n);
dst->head += n;
nread += n;
/* wrap? */
if (dst->head == bufend)
dst->head = dst->buf;
}
if (overflow)
dst->tail = ringbuf_nextp(dst, dst->head);
return dst->head;
}
void *ringbuf_memcpy_from(void *dst, struct ringbuf_t *src, int count)
{
int n = 0;
int bytes_used = ringbuf_bytes_used(src);
u8 *u8dst = dst;
const u8 *bufend = ringbuf_end(src);
int nwritten = 0;
if (count > bytes_used)
return 0;
while (nwritten != count) {
n = MIN(bufend - src->tail, count - nwritten);
memcpy(u8dst + nwritten, src->tail, n);
src->tail += n;
nwritten += n;
/* wrap ? */
if (src->tail == bufend)
src->tail = src->buf;
}
return src->tail;
}
void * ringbuf_memcpy_remove(struct ringbuf_t *dst, int count)
{
int n = 0;
int bytes_used = ringbuf_bytes_used(dst);
const u8 *bufend = ringbuf_end(dst);
int nwritten = 0;
unsigned char *cls_start;
if (count > bytes_used)
return 0;
while (nwritten != count) {
n = MIN(bufend - dst->head, count - nwritten);
cls_start = dst->head - n;
memset(cls_start, 0, n);
dst->head -= n;
nwritten += n;
/* wrap ? */
if (dst->head == bufend)
dst->head = dst->buf;
}
return dst->head;
}
#endif /* USE_RINGBUFF_API */
#ifdef USE_RINGBUFF_API
void fm_rds_write_data(struct s610_radio *radio,
u16 rds_data, fm_rds_block_type_enum blk_type,
fm_host_rds_errors_enum errors)
{
u8 buf_ptr[HOST_RDS_BLOCK_SIZE];
u16 usage;
if (ringbuf_is_full(&radio->rds_rb)) {
RDSEBUG(radio, "%s():>>>RB full! H[%ld]T[%ld]",
__func__,
(unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
(unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
goto skip_into_buf;
}
buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff);
buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8)(rds_data >> 8);
buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] =
(blk_type << HOST_RDS_DATA_BLKTYPE_POSI)
| (errors << HOST_RDS_DATA_ERRORS_POSI)
| HOST_RDS_DATA_AVAIL_MASK;
if (!ringbuf_memcpy_into(&radio->rds_rb, buf_ptr, HOST_RDS_BLOCK_SIZE)) {
usage = ringbuf_bytes_used(&radio->rds_rb);
RDSEBUG(radio,
"%s():>>>RB memcpy into fail! usage:%04d",
__func__, ringbuf_bytes_used(&radio->rds_rb));
if (!usage)
return;
}
skip_into_buf:
usage = ringbuf_bytes_used(&radio->rds_rb);
if (usage >= HOST_RDS_BLOCK_SIZE)
radio->low->fm_state.status |= STATUS_MASK_RDS_AVA;
if (radio->low->fm_state.rds_mem_thresh != 0) {
if (usage >= (radio->low->fm_state.rds_mem_thresh+(HOST_RDS_BLOCK_SIZE*4))) {
if (atomic_read(&radio->is_rds_new))
return;
fm_set_flag_bits(radio, FLAG_BUF_FUL);
atomic_set(&radio->is_rds_new, 1);
wake_up_interruptible(&radio->core->rds_read_queue);
radio->rds_n_count++;
if (!(radio->rds_n_count%200)) {
fm_update_rssi_work(radio);
RDSEBUG(radio,
">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]",
radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt,
usage, radio->rds_sync_loss_cnt);
}
}
}
}
#else /* USE_RINGBUFF_API */
void fm_rds_write_data(struct s610_radio *radio, u16 rds_data,
fm_rds_block_type_enum blk_type, fm_host_rds_errors_enum errors)
{
u8 *buf_ptr;
u16 usage;
u16 capa;
capa = radio->low->rds_buffer->size;
if (radio->low->rds_buffer->outdex
> radio->low->rds_buffer->index)
usage = radio->low->rds_buffer->size
- radio->low->rds_buffer->outdex
+ radio->low->rds_buffer->index;
else
usage = radio->low->rds_buffer->index
- radio->low->rds_buffer->outdex;
if ((capa - usage) >= (HOST_RDS_BLOCK_SIZE * 4)) {
buf_ptr = radio->low->rds_buffer->base
+ radio->low->rds_buffer->index;
buf_ptr[HOST_RDS_BLOCK_FMT_LSB] = (u8)(rds_data & 0xff);
buf_ptr[HOST_RDS_BLOCK_FMT_MSB] = (u8) (rds_data >> 8);
buf_ptr[HOST_RDS_BLOCK_FMT_STATUS] = (blk_type
<< HOST_RDS_DATA_BLKTYPE_POSI)
| (errors << HOST_RDS_DATA_ERRORS_POSI)
| HOST_RDS_DATA_AVAIL_MASK;
/* Advances the buffer's index */
radio->low->rds_buffer->index
+= HOST_RDS_BLOCK_SIZE;
/* Check if the buffer's index wraps */
if (radio->low->rds_buffer->index >=
radio->low->rds_buffer->size) {
radio->low->rds_buffer->index -=
radio->low->rds_buffer->size;
}
if (usage >= HOST_RDS_BLOCK_SIZE)
radio->low->fm_state.status |= STATUS_MASK_RDS_AVA;
}
if (radio->low->fm_state.rds_mem_thresh != 0) {
if (usage >= (radio->low->fm_state.rds_mem_thresh+(HOST_RDS_BLOCK_SIZE*4)
/** HOST_RDS_BLOCK_SIZE*/)) {
if (atomic_read(&radio->is_rds_new))
return;
fm_set_flag_bits(radio, FLAG_BUF_FUL);
atomic_set(&radio->is_rds_new, 1);
wake_up_interruptible(&radio->core->rds_read_queue);
radio->rds_n_count++;
if (!(radio->rds_n_count%200)) {
fm_update_rssi_work(radio);
dev_info(radio->dev,
">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]",
radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt,
usage, radio->rds_sync_loss_cnt);
}
}
}
}
#endif /* USE_RINGBUFF_API */
#ifdef USE_RDS_BLOCK_SEQ_CORRECT
#ifdef USE_RINGBUFF_API
void fm_rds_write_data_remove(struct s610_radio *radio,
fm_rds_rm_align_enum removeblock)
{
unsigned long pre_head, cur_head;
pre_head = (unsigned long) radio->rds_rb.head;
ringbuf_memcpy_remove(&radio->rds_rb,
(int)removeblock*HOST_RDS_BLOCK_SIZE);
cur_head = (unsigned long) radio->rds_rb.head;
RDSEBUG(radio, ">>> pre-head :%08lX, cur-head :%08lX\n",
pre_head, cur_head);
}
#else
void fm_rds_write_data_remove(struct s610_radio *radio,
fm_rds_rm_align_enum removeblock)
{
int i;
u8 *buf_ptr;
for (i = 0; i < removeblock*HOST_RDS_BLOCK_SIZE; i++) {
buf_ptr = radio->low->rds_buffer->base
+ radio->low->rds_buffer->index;
buf_ptr[0] = 0;
if (radio->low->rds_buffer->index == 0)
radio->low->rds_buffer->index = radio->low->rds_buffer->size;
radio->low->rds_buffer->index--;
}
RDSEBUG(radio, "%s():<<<WR-RM:%d index[%04d]",
__func__, removeblock,
radio->low->rds_buffer->index);
}
#endif /*USE_RINGBUFF_API*/
#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
#ifdef USE_RINGBUFF_API
int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
u16 *blocks)
{
u16 rsize = size;
if (ringbuf_is_empty(&radio->rds_rb)) {
RDSEBUG(radio,
"%s():>>>RB empty!! H[%04ld]T[%04ld]",
__func__,
(unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
(unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
return 0;
}
radio->rb_used = ringbuf_bytes_used(&radio->rds_rb);
if (!ringbuf_memcpy_from(buffer, &radio->rds_rb, rsize)) {
RDSEBUG(radio,
"%s():>>>RB memcpy from fail! H[%04ld]T[%04ld]",
__func__,
(unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
(unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
/* ringbuff reset */
ringbuf_reset(&radio->rds_rb);
if (blocks)
blocks = 0;
return 0;
}
if (blocks)
*blocks = rsize / HOST_RDS_BLOCK_SIZE;
/* Update RDS flags */
if ((rsize / HOST_RDS_BLOCK_SIZE) < radio->low->fm_state.rds_mem_thresh)
fm_clear_flag_bits(radio, FLAG_BUF_FUL);
RDSEBUG(radio,
"%s():>>>RB1 H[%04ld]T[%04ld]",
__func__,
(unsigned long) (radio->rds_rb.head - radio->rds_rb.buf),
(unsigned long) (radio->rds_rb.tail - radio->rds_rb.buf));
return rsize;
}
#else /* USE_RINGBUFF_API */
u16 fm_rds_get_avail_bytes(struct s610_radio *radio)
{
u16 avail_bytes;
if (radio->low->rds_buffer->outdex >
radio->low->rds_buffer->index)
avail_bytes = (radio->low->rds_buffer->size
- radio->low->rds_buffer->outdex
+ radio->low->rds_buffer->index);
else
avail_bytes = (radio->low->rds_buffer->index
- radio->low->rds_buffer->outdex);
return avail_bytes;
}
int fm_read_rds_data(struct s610_radio *radio, u8 *buffer, int size,
u16 *blocks)
{
u16 avail_bytes;
s16 avail_blocks;
s16 orig_avail;
u8 *buf_ptr;
if (radio->low->rds_buffer == NULL) {
size = 0;
if (blocks)
*blocks = 0;
return FALSE;
}
orig_avail = avail_bytes = fm_rds_get_avail_bytes(radio);
if (avail_bytes > size)
avail_bytes = size;
avail_blocks = avail_bytes / HOST_RDS_BLOCK_SIZE;
avail_bytes = avail_blocks * HOST_RDS_BLOCK_SIZE;
if (avail_bytes == 0) {
size = 0;
if (blocks)
*blocks = 0;
return FALSE;
}
buf_ptr = radio->low->rds_buffer->base
+ radio->low->rds_buffer->outdex;
(void) memcpy(buffer, buf_ptr, avail_bytes);
/* advances the buffer's outdex */
radio->low->rds_buffer->outdex += avail_bytes;
/* Check if the buffer's outdex wraps */
if (radio->low->rds_buffer->outdex >= radio->low->rds_buffer->size)
radio->low->rds_buffer->outdex -= radio->low->rds_buffer->size;
if (orig_avail == avail_bytes) {
buffer[(avail_blocks - 1) * HOST_RDS_BLOCK_SIZE
+ HOST_RDS_BLOCK_FMT_STATUS] &=
~HOST_RDS_DATA_AVAIL_MASK;
radio->low->fm_state.status &= ~STATUS_MASK_RDS_AVA;
}
size = avail_bytes; /* number of bytes read */
if (blocks)
*blocks = avail_bytes / HOST_RDS_BLOCK_SIZE;
/* Update RDS flags */
if ((avail_bytes / HOST_RDS_BLOCK_SIZE)
< radio->low->fm_state.rds_mem_thresh)
fm_clear_flag_bits(radio, FLAG_BUF_FUL);
return size;
}
#endif /* USE_RINGBUFF_API */
#ifdef USE_RDS_HW_DECODER
void fm_rds_change_state(struct s610_radio *radio,
fm_rds_state_enum new_state)
{
fm_rds_state_enum old_state =
(fm_rds_state_enum) radio->low->fm_rds_state.current_state;
radio->low->fm_rds_state.current_state = new_state;
if ((old_state == RDS_STATE_FULL_SYNC)
&& (new_state == RDS_STATE_HAVE_DATA)) {
fm_update_rds_sync_status(radio, FALSE); /* unsynced */
} else if ((old_state != RDS_STATE_FULL_SYNC)
&& (new_state == RDS_STATE_FULL_SYNC)) {
fm_update_rds_sync_status(radio, TRUE); /* synced */
}
}
#else /*USE_RDS_HW_DECODER*/
void fm_rds_change_state(struct s610_radio *radio,
fm_rds_state_enum new_state)
{
radio->low->fm_rds_state.current_state = new_state;
}
fm_rds_state_enum fm_rds_get_state(struct s610_radio *radio)
{
return radio->low->fm_rds_state.current_state;
}
#endif /*USE_RDS_HW_DECODER*/
void fm_rds_update_error_status(struct s610_radio *radio, u16 errors)
{
if (errors == 0) {
radio->low->fm_rds_state.error_bits = 0;
radio->low->fm_rds_state.error_blks = 0;
} else {
radio->low->fm_rds_state.error_bits += errors;
radio->low->fm_rds_state.error_blks++;
}
if (radio->low->fm_rds_state.error_blks
>= radio->low->fm_state.rds_unsync_blk_cnt) {
if (radio->low->fm_rds_state.error_bits
>= radio->low->fm_state.rds_unsync_bit_cnt) {
/* sync-loss */
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
RDSEBUG(radio, "%s() >>>>> RDS sync-loss[%08d]!!!!!",
__func__, radio->rds_sync_loss_cnt);
#ifdef USE_RDS_HW_DECODER
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
#else
if (!radio->rds_sync_loss_cnt) {
#ifdef USE_RINGBUFF_API
ringbuf_reset(&radio->rds_rb);
#else
radio->low->rds_buffer->index = radio->low->rds_buffer->outdex = 0;
#endif
} else {
/*remove data*/
fm_rds_write_data_remove(radio, (fm_rds_rm_align_enum)fm_rds_get_state(radio));
}
fm_rds_change_state(radio, RDS_STATE_INIT);
#endif /*USE_RDS_HW_DECODER*/
}
radio->low->fm_rds_state.error_bits = 0;
radio->low->fm_rds_state.error_blks = 0;
radio->rds_sync_loss_cnt++;
}
}
static fm_host_rds_errors_enum fm_rds_process_block(
struct s610_radio *radio,
u16 data, fm_rds_block_type_enum block_type,
u16 err_count)
{
fm_host_rds_errors_enum error_type;
struct fm_rds_parser_info *pi;
if (radio->rds_parser_enable)
pi = &(radio->pi);
if (err_count == 0) {
error_type = HOST_RDS_ERRS_NONE;
} else if ((err_count <= 2)
&& (err_count
<= radio->low->fm_config.rds_error_limit)) {
error_type = HOST_RDS_ERRS_2CORR;
} else if ((err_count <= 5)
&& (err_count
<= radio->low->fm_config.rds_error_limit)) {
error_type = HOST_RDS_ERRS_5CORR;
} else {
error_type = HOST_RDS_ERRS_UNCORR;
}
/* Write the data into the buffer */
if ((block_type != RDS_BLKTYPE_E)
|| (radio->low->fm_state.save_eblks)) {
if (radio->rds_parser_enable) {
fm_rds_parser(pi, data, block_type, error_type);
fm_rds_write_data_pi(radio, pi);
} else {
fm_rds_write_data(radio, data, block_type, error_type);
}
}
return error_type;
}
#ifdef USE_RDS_BLOCK_SEQ_CORRECT
fm_rds_rm_align_enum fm_check_block_seq(fm_rds_block_type_enum pre_block_type,
fm_rds_block_type_enum curr_block_type)
{
fm_rds_rm_align_enum ret = RDS_RM_ALIGN_NONE;
if ((pre_block_type == RDS_BLKTYPE_A) && (curr_block_type != RDS_BLKTYPE_B)) {
ret = RDS_RM_ALIGN_1;
}
else if ((pre_block_type == RDS_BLKTYPE_B) && (curr_block_type != RDS_BLKTYPE_C)) {
ret = RDS_RM_ALIGN_2;
}
else if ((pre_block_type == RDS_BLKTYPE_C) && (curr_block_type != RDS_BLKTYPE_D)) {
ret = RDS_RM_ALIGN_3;
}
else if ((pre_block_type == RDS_BLKTYPE_D) && (curr_block_type != RDS_BLKTYPE_A)) {
ret = RDS_RM_ALIGN_0;
}
return ret;
}
#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
#ifdef USE_RDS_HW_DECODER
void fm_process_rds_data(struct s610_radio *radio)
{
u32 fifo_data;
u16 i;
u16 avail_blocks;
u16 data;
u8 status;
u16 err_count;
fm_rds_block_type_enum block_type;
#ifdef USE_RDS_BLOCK_SEQ_CORRECT
fm_rds_rm_align_enum rm_blk = RDS_RM_ALIGN_NONE;
#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
API_ENTRY(radio);
if (!radio->low->fm_state.rds_rx_enabled)
return;
avail_blocks = RDS_MEM_MAX_THRESH/4;
for (i = 0; i < avail_blocks; i++) {
/* Fetch the RDS word data. */
atomic_set(&radio->is_rds_doing, 1);
fifo_data = fmspeedy_get_reg_work(0xFFF3C0);
radio->rds_fifo_rd_cnt++;
data = (u16)((fifo_data >> 16) & 0xFFFF);
status = (u8)((fifo_data >> 8) & 0xFF);
block_type =
(fm_rds_block_type_enum) ((status & RDS_BLK_TYPE_MASK)
>> RDS_BLK_TYPE_SHIFT);
err_count = (status & RDS_ERR_CNT_MASK);
atomic_set(&radio->is_rds_doing, 0);
switch (radio->low->fm_rds_state.current_state) {
case RDS_STATE_INIT:
APIEBUG(radio, "RDS_STATE_INIT");
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
case RDS_STATE_HAVE_DATA:
APIEBUG(radio, "RDS_STATE_HAVE_DATA");
if ((block_type == RDS_BLKTYPE_A)
&& (err_count == 0)) {
#ifdef USE_RDS_BLOCK_SEQ_CORRECT
radio->block_seq = RDS_BLKTYPE_A;
#endif /*USE_RDS_BLOCK_SEQ_CORRECT */
/* Move to full sync */
fm_rds_change_state(radio,
RDS_STATE_FULL_SYNC);
fm_rds_process_block(radio,
data, block_type, err_count);
}
break;
case RDS_STATE_PRE_SYNC:
break;
case RDS_STATE_FULL_SYNC:
APIEBUG(radio, "RDS_STATE_FULL_SYNC");
#ifdef USE_RDS_BLOCK_SEQ_CORRECT
rm_blk = fm_check_block_seq(radio->block_seq, block_type);
if (rm_blk < RDS_RM_ALIGN_NONE) {
RDSEBUG(radio,
"pre block[%02d],curr block[%02d],err count[%08d]",
radio->block_seq, block_type, radio->rds_fifo_err_cnt);
if (rm_blk != RDS_RM_ALIGN_0) {
fm_rds_write_data_remove(radio, rm_blk);
radio->block_seq = RDS_BLKTYPE_D;
}
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
radio->rds_fifo_err_cnt++;
break;
}
radio->block_seq = block_type;
#endif /* USE_RDS_BLOCK_SEQ_CORRECT */
if (fm_rds_process_block(radio,
data, block_type, err_count)
== HOST_RDS_ERRS_UNCORR) {
fm_rds_update_error_status(radio,
radio->low->fm_state.rds_unsync_uncorr_weight);
} else {
fm_rds_update_error_status(radio, err_count);
}
break;
}
}
API_EXIT(radio);
}
#endif /* USE_RDS_HW_DECODER */
void find_pi_data(struct fm_rds_parser_info *pi, u16 info)
{
pi->pi_buf[pi->pi_idx % 2] = info;
if (!(++pi->pi_idx % 2)) {
if (pi->pi_buf[0] == pi->pi_buf[1]) {
pi->rds_event |= RDS_EVENT_PI_MASK;
RDSEBUG(gradio, "[RDS] PI : 0x%x\n", pi->pi_buf[0]);
}
}
}
void find_ecc_data(struct fm_rds_parser_info *pi, u16 info)
{
pi->ecc_buf[pi->ecc_idx % 2] = info & 0xFF;
if (!(++pi->ecc_idx % 2)) {
if (pi->ecc_buf[0] == pi->ecc_buf[1]) {
pi->rds_event |= RDS_EVENT_ECC_MASK;
RDSEBUG(gradio, "[RDS] ECC : %d\n", pi->ecc_buf[0]);
}
}
}
void store_ps_data(struct fm_rds_parser_info *pi,
u16 info, u8 err_cnt)
{
char a, b;
u32 i = pi->ps_idx % 3;
u8 seg = pi->ps_segment;
if (pi->drop_blk)
return;
a = (info >> 8) & 0xff;
b = info & 0xff;
pi->ps_buf[i][seg * 2] = a;
pi->ps_buf[i][seg * 2 + 1] = b;
pi->ps_err[i][seg] = err_cnt;
/*RDSEBUG(gradio,
"[RDS] PS: [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
a, b, (int)a, (int)b, i, seg);*/
}
void validate_ps_data(struct fm_rds_parser_info *pi)
{
u32 i;
bool match = true;
for (i = 0; i < pi->ps_len / 2; i++) {
if (pi->ps_err[pi->ps_idx % 3][i] > 0)
break;
}
if (i == pi->ps_len / 2) {
memcpy(pi->ps_candidate,
pi->ps_buf[pi->ps_idx % 3],
pi->ps_len);
memset(pi->ps_err[pi->ps_idx % 3],
0xFF, MAX_PS / 2);
pi->ps_candidate[pi->ps_len] = 0;
pi->rds_event |= RDS_EVENT_PS_MASK;
pi->ps_idx++;
RDSEBUG(gradio,
"[RDS] ### PS candidate[i]: %s\n",
pi->ps_candidate);
} else {
if (++pi->ps_idx >= 3) {
for (i = 0; i < pi->ps_len; i++) {
if (pi->ps_buf[0][i] == pi->ps_buf[1][i])
pi->ps_candidate[i] = pi->ps_buf[0][i];
else if (pi->ps_buf[0][i] == pi->ps_buf[2][i])
pi->ps_candidate[i] = pi->ps_buf[0][i];
else if (pi->ps_buf[1][i] == pi->ps_buf[2][i])
pi->ps_candidate[i] = pi->ps_buf[1][i];
else
match = false;
}
if (match) {
pi->ps_candidate[pi->ps_len] = 0;
pi->rds_event |= RDS_EVENT_PS_MASK;
RDSEBUG(gradio,
"[RDS] ### PS candidate[m]: %s\n",
pi->ps_candidate);
}
}
}
i = pi->ps_idx - 1;
pi->ps_buf[i % 3][pi->ps_len] = 0;
for (i = 0; i < 3; i++)
RDSEBUG(gradio,
"[RDS] ### PS received[%d]: %s\n",
i, pi->ps_buf[i]);
}
void store_rt_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt)
{
char a, b;
u32 i = pi->rt_idx % 3;
u8 seg = pi->rt_segment;
if (pi->drop_blk)
return;
a = (info >> 8) & 0xff;
b = info & 0xff;
switch (blk_type) {
case RDS_BLKTYPE_C:
pi->rt_buf[i][seg * 4] = a;
pi->rt_buf[i][seg * 4 + 1] = b;
pi->rt_err[i][seg * 2] = err_cnt;
/*RDSEBUG(gradio,
"[RDS] RT_A(C): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
a, b, (int)a, (int)b, i, seg);*/
break;
case RDS_BLKTYPE_D:
if (pi->grp == RDS_GRPTYPE_2A) {
pi->rt_buf[i][seg * 4 + 2] = a;
pi->rt_buf[i][seg * 4 + 3] = b;
pi->rt_err[i][seg * 2 + 1] = err_cnt;
/*RDSEBUG(gradio,
"[RDS] RT_A(D): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
a, b, (int)a, (int)b, i, seg);*/
} else if (pi->grp == RDS_GRPTYPE_2B) {
pi->rt_buf[i][seg * 2] = a;
pi->rt_buf[i][seg * 2 + 1] = b;
pi->rt_err[i][seg] = err_cnt;
/*RDSEBUG(gradio,
"[RDS] RT_B(C): [%c][%c] [%d][%d], modulo idx=%d, seg=%d\n",
a, b, (int)a, (int)b, i, seg);*/
}
default:
break;
}
}
void validate_rt_data(struct fm_rds_parser_info *pi)
{
u32 i;
bool match = true;
for (i = 0; i < pi->rt_len / 2; i++) {
if (pi->rt_err[pi->rt_idx % 3][i] > 0)
break;
}
if (i == pi->rt_len / 2) {
memcpy(pi->rt_candidate, pi->rt_buf[pi->rt_idx % 3], pi->rt_len);
memset(pi->rt_err[pi->rt_idx % 3], 0xFF, MAX_RT / 2);
pi->rt_candidate[pi->rt_len] = 0;
if (strlen(pi->rt_candidate) >= (pi->rt_len - 2)) {
pi->rds_event |= RDS_EVENT_RT_MASK;
pi->rt_validated = 1;
pi->rt_idx++;
RDSEBUG(gradio,
"[RDS] ### RT candidate[i]: %s, %d, %d\n",
pi->rt_candidate, (int)strlen(pi->rt_candidate), pi->rt_len);
}
} else {
if (++pi->rt_idx >= 3) {
for (i = 0; i < pi->rt_len; i++) {
if (pi->rt_buf[0][i] == pi->rt_buf[1][i])
pi->rt_candidate[i] = pi->rt_buf[0][i];
else if (pi->rt_buf[0][i] == pi->rt_buf[2][i])
pi->rt_candidate[i] = pi->rt_buf[0][i];
else if (pi->rt_buf[1][i] == pi->rt_buf[2][i])
pi->rt_candidate[i] = pi->rt_buf[1][i];
else
match = false;
}
if (match) {
pi->rt_candidate[pi->rt_len] = 0;
if (strlen(pi->rt_candidate) >= (pi->rt_len - 2)) {
pi->rds_event |= RDS_EVENT_RT_MASK;
pi->rt_validated = 1;
RDSEBUG(gradio,
"[RDS] ### RT candidate[m]: %s, %d, %d\n",
pi->rt_candidate, (int)strlen(pi->rt_candidate), pi->rt_len);
}
}
}
}
i = pi->rt_idx - 1;
pi->rt_buf[i % 3][pi->rt_len] = 0;
for (i = 0; i < 3; i++)
RDSEBUG(gradio,
"[RDS] ### RT received[%d]: %s\n",
i, pi->rt_buf[i]);
}
void reset_rtp_data(struct fm_rds_parser_info *pi)
{
memset(&(pi->rtp_data), 0x0, sizeof(struct rtp_info));
memset(pi->rtp_raw_data, 0x0, sizeof(u16) * 3);
}
void validate_rtp_data(struct fm_rds_parser_info *pi)
{
u8 i;
static u16 rtp_validated;
struct rtp_tag_info tag[2];
if (!pi->rtp_data.running) {
RDSEBUG(gradio, "[RDS] RTP running is stopped\n");
return;
}
tag[0].content_type = ((pi->rtp_raw_data[RDS_BLKTYPE_B - 1] & 0x0007) << 3) |
((pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0xE000) >> 13);
tag[0].start_pos = (pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x1F80) >> 7;
tag[0].len = (pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x007E) >> 1;
tag[1].content_type = ((pi->rtp_raw_data[RDS_BLKTYPE_C - 1] & 0x0001) << 5) |
((pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0xF800) >> 11);
tag[1].start_pos = (pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0x07E0) >> 5;
tag[1].len = pi->rtp_raw_data[RDS_BLKTYPE_D - 1] & 0x001F;
RDSEBUG(gradio, "[RDS] RTP tag[0]:[%d,%d,%d]\n", tag[0].content_type, tag[0].start_pos, tag[0].len);
RDSEBUG(gradio, "[RDS] RTP tag[1]:[%d,%d,%d]\n", tag[1].content_type, tag[1].start_pos, tag[1].len);
/* Check overlap */
if (((tag[1].content_type != 0) && (tag[0].start_pos < tag[1].start_pos) && ((tag[0].start_pos + tag[0].len) >= tag[1].start_pos)) ||
((tag[0].content_type != 0) && (tag[1].start_pos < tag[0].start_pos) && ((tag[1].start_pos + tag[1].len) >= tag[0].start_pos))) {
RDSEBUG(gradio, "[RDS] RTP tag[0] & tag[1] are overlapped.\n");
return;
}
for (i = 0; i < MAX_RTP_TAG; i++) {
if (tag[i].content_type == pi->rtp_data.tag[i].content_type &&
tag[i].start_pos == pi->rtp_data.tag[i].start_pos &&
tag[i].len == pi->rtp_data.tag[i].len) {
rtp_validated++;
RDSEBUG(gradio, "[RDS] RTP tag validation check count:0x%x\n", (int)rtp_validated);
} else {
pi->rtp_data.tag[i].content_type = tag[i].content_type;
pi->rtp_data.tag[i].start_pos = tag[i].start_pos;
pi->rtp_data.tag[i].len = tag[i].len;
rtp_validated = 0;
}
}
/* RT is ready to be displayed along with RTP data */
if (pi->rt_validated && rtp_validated > 2)
pi->rds_event |= RDS_EVENT_RTP_MASK;
}
void store_rtp_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type)
{
u8 toggle;
if (pi->drop_blk) {
return;
}
if (pi->grp != pi->rtp_code_group) {
RDSEBUG(gradio, "[RDS] Received unexpected code group(0x%x), expected=0x%x\n",
(int)pi->grp, (int)pi->rtp_code_group);
return;
}
switch (blk_type) {
case RDS_BLKTYPE_B:
toggle = (info & 0x010) >> 4;
if (toggle != pi->rtp_data.toggle) {
reset_rtp_data(pi);
pi->rtp_data.toggle = toggle;
}
pi->rtp_data.running = (info & 0x0008) >> 3;
pi->rtp_raw_data[blk_type-1] = info;
RDSEBUG(gradio, "[RDS] Received RTP B block/0x%x Group\n", (int)pi->grp);
break;
case RDS_BLKTYPE_C:
pi->rtp_raw_data[blk_type-1] = info;
RDSEBUG(gradio, "[RDS] Received RTP C block/0x%x Group\n", (int)pi->grp);
break;
case RDS_BLKTYPE_D:
pi->rtp_raw_data[blk_type-1] = info;
RDSEBUG(gradio, "[RDS] Received RTP D block/0x%x Group\n", (int)pi->grp);
validate_rtp_data(pi);
break;
default:
break;
}
}
void find_rtp_data(struct fm_rds_parser_info *pi, u16 info, u8 blk_type)
{
if (pi->drop_blk)
return;
switch (blk_type) {
case RDS_BLKTYPE_B:
pi->rtp_code_group = info & 0x1f;
RDSEBUG(gradio, "[RDS] RTP code group:0x%x\n", (int)pi->rtp_code_group);
break;
case RDS_BLKTYPE_C:
/* Not support SCB/RTP template */
RDSEBUG(gradio, "[RDS] RTP not support SCB/RTP template\n");
break;
case RDS_BLKTYPE_D:
if (info != RDS_RTP_AID) {
RDSEBUG(gradio, "[RDS] Invalid RTP aid=0x%x\n", (int)info);
pi->rtp_code_group = 0;
}
break;
default:
break;
}
}
void find_group_data(struct fm_rds_parser_info *pi, u16 info)
{
u8 segment, rt_change;
pi->grp = (info >> 11) & 0x1f;
pi->pty = (info >> 5) & 0x1f;
pi->rds_event |= RDS_EVENT_PTY_MASK;
switch (pi->grp) {
case RDS_GRPTYPE_0A:
case RDS_GRPTYPE_0B:
segment = info & 0x3;
if (!segment && pi->ps_segment != 0xFF)
validate_ps_data(pi);
pi->ps_segment = segment;
if (pi->ps_len < (segment + 1) * 2)
pi->ps_len = (segment + 1) * 2;
/*RDSEBUG(gradio,
"[RDS] PS: seg=%d, len=%d\n",
segment, pi->ps_len);*/
break;
case RDS_GRPTYPE_2A:
case RDS_GRPTYPE_2B:
segment = info & 0xF;
rt_change = (info & 0x10) >> 4;
RDSEBUG(gradio,
"[RDS] segment=%d, pi->rt_segment=%d, pi->rt_change=%d, rt_change=%d\n",
segment, pi->rt_segment, pi->rt_change, rt_change);
if ((!segment && pi->rt_segment != 0xFF)
|| (pi->rt_change != 0xFF && pi->rt_change != rt_change)) {
validate_rt_data(pi);
if (pi->rt_change != 0xFF && pi->rt_change != rt_change) {
pi->rt_len = 0;
pi->rt_idx = 0;
memset(pi->rt_buf, 0, 3 * (MAX_RT + 1));
memset(pi->rt_err, 0xFF, 3 * MAX_RT / 2);
pi->rt_validated = 0;
}
}
pi->rt_segment = segment;
pi->rt_change = rt_change;
if (pi->grp == RDS_GRPTYPE_2A) {
if (pi->rt_len < (segment + 1) * 4)
pi->rt_len = (segment + 1) * 4;
} else {
if (pi->rt_len < (segment + 1) * 2)
pi->rt_len = (segment + 1) * 2;
}
RDSEBUG(gradio,
"[RDS] RT: seg=%d, tc=%d, len=%d\n",
segment, rt_change, pi->rt_len);
break;
case RDS_GRPTYPE_3A:
find_rtp_data(pi, info, RDS_BLKTYPE_B);
break;
case RDS_GRPTYPE_5A:
case RDS_GRPTYPE_6A:
case RDS_GRPTYPE_7A:
case RDS_GRPTYPE_8A:
case RDS_GRPTYPE_9A:
case RDS_GRPTYPE_11A:
case RDS_GRPTYPE_12A:
case RDS_GRPTYPE_13A:
store_rtp_data(pi, info, RDS_BLKTYPE_B);
break;
default:
break;
}
}
void find_af_data(struct fm_rds_parser_info *pi, u16 info)
{
pi->af_buf[pi->af_idx % 2] = info;
if (!(++pi->af_idx % 2)) {
if (pi->af_buf[0] == pi->af_buf[1])
pi->rds_event |= RDS_EVENT_AF_MASK;
}
}
void fm_rds_parser_reset(struct fm_rds_parser_info *pi)
{
/* reset rds parser data structure */
memset(pi, 0x0, sizeof(struct fm_rds_parser_info));
pi->ps_segment = 0xFF;
pi->rt_segment = 0xFF;
pi->rt_change = 0xFF;
pi->rtp_data.toggle = 0xFF;
memset(pi->rt_err, 0xFF, 3 * MAX_RT / 2);
memset(pi->ps_err, 0xFF, 3 * MAX_PS / 2);
}
void fm_rds_parser(struct fm_rds_parser_info *pi, u16 info, u8 blk_type, u8 err_cnt)
{
switch (blk_type) {
case RDS_BLKTYPE_A:
find_pi_data(pi, info);
break;
case RDS_BLKTYPE_B:
if (err_cnt > 2) {
pi->grp = RDS_GRPTYPE_NONE;
pi->drop_blk = true;
break;
} else {
pi->drop_blk = false;
}
find_group_data(pi, info);
break;
case RDS_BLKTYPE_C:
if (err_cnt > 5)
return;
switch (pi->grp) {
case RDS_GRPTYPE_0A:
find_af_data(pi, info);
break;
case RDS_GRPTYPE_0B:
find_pi_data(pi, info);
break;
case RDS_GRPTYPE_1A:
find_ecc_data(pi, info);
break;
case RDS_GRPTYPE_2A:
store_rt_data(pi, info, blk_type, err_cnt);
break;
case RDS_GRPTYPE_2B:
find_pi_data(pi, info);
break;
case RDS_GRPTYPE_3A:
find_rtp_data(pi, info, blk_type);
break;
case RDS_GRPTYPE_5A:
case RDS_GRPTYPE_6A:
case RDS_GRPTYPE_7A:
case RDS_GRPTYPE_8A:
case RDS_GRPTYPE_9A:
case RDS_GRPTYPE_11A:
case RDS_GRPTYPE_12A:
case RDS_GRPTYPE_13A:
store_rtp_data(pi, info, blk_type);
break;
default:
break;
}
break;
case RDS_BLKTYPE_D:
if (err_cnt > 5)
return;
switch (pi->grp) {
case RDS_GRPTYPE_0A:
case RDS_GRPTYPE_0B:
store_ps_data(pi, info, err_cnt);
break;
case RDS_GRPTYPE_2A:
case RDS_GRPTYPE_2B:
store_rt_data(pi, info, blk_type, err_cnt);
break;
case RDS_GRPTYPE_3A:
find_rtp_data(pi, info, blk_type);
break;
case RDS_GRPTYPE_5A:
case RDS_GRPTYPE_6A:
case RDS_GRPTYPE_7A:
case RDS_GRPTYPE_8A:
case RDS_GRPTYPE_9A:
case RDS_GRPTYPE_11A:
case RDS_GRPTYPE_12A:
case RDS_GRPTYPE_13A:
store_rtp_data(pi, info, blk_type);
break;
default:
break;
}
break;
}
}
void fm_rds_write_data_pi(struct s610_radio *radio,
struct fm_rds_parser_info *pi)
{
u8 buf_ptr[RDS_MEM_MAX_THRESH_PARSER];
u8 i, str_len;
int total_size = 0;
u16 usage;
#ifdef USE_RINGBUFF_API
if (ringbuf_bytes_free(&radio->rds_rb) < RDS_MEM_MAX_THRESH_PARSER) {
RDSEBUG(radio, ">>> ringbuff not free, wait..");
/*ringbuf_reset(&radio->rds_rb);*/
return;
}
#else
dev_info(radio->dev, ">>> ringbuff API not used, fail..");
return;
#endif /* USE_RINGBUFF_API */
memset(buf_ptr, 0, RDS_MEM_MAX_THRESH_PARSER);
while (pi->rds_event) {
RDSEBUG(radio,
"%s() rds_event[%04X]\n", __func__, pi->rds_event);
if (pi->rds_event & RDS_EVENT_PI_MASK) {
pi->rds_event &= ~RDS_EVENT_PI_MASK;
buf_ptr[total_size] = RDS_EVENT_PI;
buf_ptr[total_size+1] = 2;
buf_ptr[total_size+2] = (u8)(pi->pi_buf[0] & 0xFF);
buf_ptr[total_size+3] = (u8)(pi->pi_buf[0] >> 8);
RDSEBUG(radio,
"[RDS] Enqueue PI data[%02X:%02X:%02X:%02X]. total=%d\n",
buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],buf_ptr[total_size+3],
total_size+4);
total_size += 4;
} else if (pi->rds_event & RDS_EVENT_AF_MASK) {
pi->rds_event &= ~RDS_EVENT_AF_MASK;
buf_ptr[total_size] = RDS_EVENT_AF;
buf_ptr[total_size+1] = 2;
buf_ptr[total_size+2] = (u8)(pi->af_buf[0] & 0xFF);
buf_ptr[total_size+3] = (u8)(pi->af_buf[0] >> 8);
RDSEBUG(radio,
"[RDS] Enqueue AF data[%02X:%02X:%02X:%02X]. total=%d\n",
buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],buf_ptr[total_size+3],
total_size+4);
total_size += 4;
} else if (pi->rds_event & RDS_EVENT_PS_MASK) {
pi->rds_event &= ~RDS_EVENT_PS_MASK;
str_len = strlen(pi->ps_candidate);
buf_ptr[total_size] = RDS_EVENT_PS;
buf_ptr[total_size+1] = str_len;
for (i = 0; i < str_len; i++) {
buf_ptr[total_size + i + 2] = pi->ps_candidate[i];
}
total_size += str_len + 2;
RDSEBUG(radio,
"[RDS] Enqueue PS data. total=%d,%d,%d\n", total_size, pi->ps_len + 2, str_len);
} else if (pi->rds_event & RDS_EVENT_RT_MASK) {
pi->rds_event &= ~RDS_EVENT_RT_MASK;
str_len = strlen(pi->rt_candidate);
buf_ptr[total_size] = RDS_EVENT_RT;
buf_ptr[total_size+1] = str_len;
for (i = 0; i < str_len; i++) {
buf_ptr[total_size + i + 2] = pi->rt_candidate[i];
}
total_size += str_len + 2;
RDSEBUG(radio,
"[RDS] Enqueue RT data. total=%d,%d,%d\n", total_size, pi->rt_len + 2, str_len + 2);
} else if (pi->rds_event & RDS_EVENT_ECC_MASK) {
pi->rds_event &= ~RDS_EVENT_ECC_MASK;
buf_ptr[total_size] = RDS_EVENT_ECC;
buf_ptr[total_size+1] = 1;
buf_ptr[total_size+2] = (u8)pi->ecc_buf[0];
RDSEBUG(radio,
"[RDS] Enqueue ECC data[%02d:%02d:%02d]. total=%d\n",
buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],
total_size+3);
total_size += 3;
} else if (pi->rds_event & RDS_EVENT_RTP_MASK) {
pi->rds_event &= ~RDS_EVENT_RTP_MASK;
buf_ptr[total_size] = RDS_EVENT_RTP;
buf_ptr[total_size+1] = sizeof(struct rtp_info);
memcpy(buf_ptr+total_size+2, &(pi->rtp_data), sizeof(struct rtp_info));
RDSEBUG(radio, "[RDS] Enqueue RTPlus data\n");
RDSEBUG(radio, "[RDS] Toggle:%d, Running:%d, Validated;%d\n", pi->rtp_data.toggle, pi->rtp_data.running, pi->rtp_data.validated);
RDSEBUG(radio, "[%d,%d,%d]\n", pi->rtp_data.tag[0].content_type, pi->rtp_data.tag[0].start_pos, pi->rtp_data.tag[0].len);
RDSEBUG(radio, "[%d,%d,%d]\n", pi->rtp_data.tag[1].content_type, pi->rtp_data.tag[1].start_pos, pi->rtp_data.tag[1].len);
total_size += sizeof(struct rtp_info) + 2;
} else if (pi->rds_event & RDS_EVENT_PTY_MASK) {
pi->rds_event &= ~RDS_EVENT_PTY_MASK;
buf_ptr[total_size] = RDS_EVENT_PTY;
buf_ptr[total_size+1] = 1;
buf_ptr[total_size+2] = pi->pty;
RDSEBUG(radio,
"[RDS] Enqueue PTY data[%02X:%02X:%02X]. total=%d\n",
buf_ptr[total_size],buf_ptr[total_size+1],buf_ptr[total_size+2],
total_size+3);
total_size += 3;
}
if (!pi->rds_event && total_size) {
#ifdef USE_RINGBUFF_API
if (!ringbuf_memcpy_into(&radio->rds_rb, buf_ptr, total_size)) {
usage = ringbuf_bytes_used(&radio->rds_rb);
RDSEBUG(radio,
"%s():>>>RB memcpy into fail! usage:%04d",
__func__, ringbuf_bytes_used(&radio->rds_rb));
if (!usage)
return;
}
usage = ringbuf_bytes_used(&radio->rds_rb);
#else
RDSEBUG(radio, ">>> ringbuff API not used, fail..");
#endif /* USE_RINGBUFF_API */
if (atomic_read(&radio->is_rds_new))
return;
fm_set_flag_bits(radio, FLAG_BUF_FUL);
atomic_set(&radio->is_rds_new, 1);
wake_up_interruptible(&radio->core->rds_read_queue);
radio->rds_n_count++;
if (!(radio->rds_n_count%200)) {
fm_update_rssi_work(radio);
RDSEBUG(radio,
">>>[FM] RSSI[%03d] NCOUNT[%08d] FIFO_ERR[%08d] USAGE[%04d] SYNCLOSS[%08d]",
radio->low->fm_state.rssi, radio->rds_n_count, radio->rds_fifo_err_cnt,
usage, radio->rds_sync_loss_cnt);
}
}
}
}
#ifndef USE_RDS_HW_DECODER
struct fm_decoded_data {
u16 info;
fm_rds_block_type_enum blk_type;
u8 err_cnt;
};
void put_bit_to_byte(u8 *fifo, u32 data)
{
s8 i, j;
u32 mask = 0x80000000;
for (i = BUF_LEN - 1, j = 0; i >= 0; i--, j++) {
*(fifo + j) = (mask & data) ? 1 : 0;
mask = mask >> 1;
}
}
u32 get_block_data(u8 *fifo, u32 sp)
{
u8 i, j;
u32 data = 0;
data |= *(fifo + (sp++ % 160));
for (i = BLK_LEN-1, j = 0; i > 0; i--, j++) {
data <<= 1;
data |= *(fifo + (sp++ % 160));
}
return data;
}
u8 find_code_word(u32 *data, fm_rds_block_type_enum b_type, bool seq_lock)
{
u16 first13;
u16 second13;
u16 syndrome;
first13 = *data >> 13;
second13 = (*data & 0x1FFF) ^ OFFSET_WORD[b_type];
syndrome = CRC_LUT[(CRC_LUT[first13] << 3) ^ second13];
if (syndrome) {
u32 corrected;
u8 i, j;
u8 maxerr = (b_type == RDS_BLKTYPE_A) ? 2 : sizeof(burstErrorPattern) / sizeof(u8);
for (i = 0; i < maxerr; i++) {
for (j = 0; j <= BLK_LEN - burstErrorLen[i]; j++) {
corrected = *data ^ (burstErrorPattern[i] << j);
first13 = corrected >> 13;
second13 = (corrected & 0x1FFF) ^ OFFSET_WORD[b_type];
syndrome = CRC_LUT[(CRC_LUT[first13] << 3) ^ second13];
if (!syndrome) {
*data = corrected;
return burstErrorLen[i];
}
}
}
} else {
return 0;
}
return 6;
}
void fm_process_rds_data(struct s610_radio *radio)
{
u16 i, j, k, l;
u16 avail_blocks;
u32 fifo_data;
u32 data;
fm_host_rds_errors_enum err_cnt;
u8 min_pos = 0;
u8 min_blk = 0;
u8 min_blk_tmp = 0;
u8 min_pos_sum, min_blk_sum;
static u32 idx;
static u8 fifo[160];
static u32 start_pos;
static u32 end_pos;
static bool fetch_data;
static bool seq_lock;
static bool remains;
static struct fm_decoded_data decoded[BLK_LEN][RDS_NUM_BLOCK_TYPES - 1][RDS_NUM_BLOCK_TYPES - 1];
u32 fifo_status;
fifo_status = fmspeedy_get_reg_work(0xFFF398);
avail_blocks = (((fifo_status >> 8) & 0x3F));
RDSEBUG(radio,"%s(): avail_blocks:%d fifo_status[%08X]",__func__, avail_blocks, fifo_status);
while (avail_blocks) {
/* Fetch the RDS raw data. */
if (fetch_data) {
fifo_data = fmspeedy_get_reg_work(0xFFF3C0);
put_bit_to_byte(fifo + 32 * ((idx++) % 5), fifo_data);
avail_blocks--;
}
switch (fm_rds_get_state(radio)) {
case RDS_STATE_INIT:
fm_rds_change_state(radio, RDS_STATE_HAVE_DATA);
fm_update_rds_sync_status(radio, false);
radio->low->fm_rds_state.error_bits = 0;
radio->low->fm_rds_state.error_blks = 0;
idx = 0;
start_pos = 0;
end_pos = BLK_LEN - 1;
fetch_data = false;
seq_lock = false;
memset(fifo, 0, sizeof(fifo) / sizeof(u8));
case RDS_STATE_HAVE_DATA:
if (idx < 5) {
fifo_data = fmspeedy_get_reg_work(0xFFF3C0);
put_bit_to_byte(fifo + 32 * ((idx++) % 5), fifo_data);
avail_blocks--;
if (idx == 5) {
for (i = 0; i < BLK_LEN; i++) {
for (j = 0; j < RDS_NUM_BLOCK_TYPES - 1; j++) {
start_pos = i;
for (k = 0, l = j; k < RDS_NUM_BLOCK_TYPES - 1; k++) {
data = get_block_data(fifo, start_pos);
err_cnt = find_code_word(&data, l, seq_lock);
decoded[i][j][k].info = data >> 10;
decoded[i][j][k].blk_type = l;
decoded[i][j][k].err_cnt = err_cnt;
start_pos += BLK_LEN;
l = (l + 1) % (RDS_NUM_BLOCK_TYPES - 1);
}
}
}
for (i = 0, min_pos_sum = 0xFF; i < BLK_LEN; i++) {
for (j = 0, min_blk_sum = 0xFF; j < RDS_NUM_BLOCK_TYPES - 1; j++) {
for (k = 0, err_cnt = 0; k < RDS_NUM_BLOCK_TYPES - 1; k++) {
err_cnt += decoded[i][j][k].err_cnt;
}
if (min_blk_sum > err_cnt) {
min_blk_sum = err_cnt;
min_blk_tmp = j;
}
}
if (min_pos_sum > min_blk_sum) {
min_pos_sum = min_blk_sum;
min_pos = i;
min_blk = min_blk_tmp;
}
}
fm_rds_change_state(radio, RDS_STATE_PRE_SYNC);
} else
break;
}
case RDS_STATE_PRE_SYNC:
for (i = 0; i < RDS_NUM_BLOCK_TYPES - 1; i++) {
if (decoded[min_pos][min_blk][i].blk_type == RDS_BLKTYPE_A) {
fm_update_rds_sync_status(radio, TRUE);
}
if (fm_get_rds_sync_status(radio)) {
if (fm_rds_process_block(radio, decoded[min_pos][min_blk][i].info,
decoded[min_pos][min_blk][i].blk_type, decoded[min_pos][min_blk][i].err_cnt)
== HOST_RDS_ERRS_UNCORR) {
fm_rds_update_error_status(radio,
radio->low->fm_state.rds_unsync_uncorr_weight);
} else {
fm_rds_update_error_status(radio, decoded[min_pos][min_blk][i].err_cnt);
}
}
}
start_pos = min_pos + BLK_LEN * 3;
end_pos = start_pos + BLK_LEN - 1;
fm_rds_change_state(radio, (min_blk + 3) % (RDS_NUM_BLOCK_TYPES - 1));
seq_lock = false;
remains = true;
case RDS_STATE_FOUND_BL_A:
case RDS_STATE_FOUND_BL_B:
case RDS_STATE_FOUND_BL_C:
case RDS_STATE_FOUND_BL_D:
if ((end_pos / BUF_LEN != (end_pos + BLK_LEN) / BUF_LEN) && !fetch_data && !remains) {
fetch_data = true;
} else {
if (end_pos + BLK_LEN >= 160 && remains) {
remains = false;
fetch_data = true;
break;
}
start_pos += BLK_LEN;
end_pos += BLK_LEN;
data = get_block_data(fifo, start_pos);
fetch_data = false;
i = (fm_rds_get_state(radio) + 1) % (RDS_NUM_BLOCK_TYPES - 1);
fm_rds_change_state(radio, i);
err_cnt = find_code_word(&data, i, seq_lock);
RDSEBUG(radio,"%s(): data:0x%x, errcnt:%d, gcount:%d", __func__, data, err_cnt, radio->rds_gcnt++);
if (fm_rds_process_block(radio, data >> 10, i, err_cnt) == HOST_RDS_ERRS_UNCORR) {
fm_rds_update_error_status(radio, radio->low->fm_state.rds_unsync_uncorr_weight);
} else {
fm_rds_update_error_status(radio, err_cnt);
}
}
break;
default:
break;
}
}
}
#endif /*USE_RDS_HW_DECODER*/