www.pudn.com > IrDA.rar > irdial.c


/*
  Copyright (C) 2002-2003 Gerd Rausch, BlauLogic (http://blaulogic.com)
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.

  3. Except as contained in this notice, neither the name of BlauLogic
     nor the name(s) of the author(s) may be used to endorse or promote
     products derived from this software without specific prior written
     permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE AUTHOR(S) OR BLAULOGIC BE LIABLE FOR ANY CLAIM,
  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
  THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include 
#include 
#include 
#include 

#if FLASHEND>=0x3FFF
#define IRDIAL_WITH_IRCOMM
#endif

#include 
#include 
#include 
#include 
#ifdef IRDIAL_WITH_IRCOMM
#include 
#endif

#include "rules.h"
#include "dtmf.h"

typedef enum File_Type {
  FILE_TYPE_UNKNOWN,
  FILE_TYPE_TEXT_UNKNOWN,
  FILE_TYPE_TEXT_RULES,
  FILE_TYPE_TEXT_DIAL,
  FILE_TYPE_TEXT_OTHER,
  FILE_TYPE_VCARD
} File_Type;

typedef enum Text_Parse_State {
  TEXT_PARSE_STATE_INITIAL,
  TEXT_PARSE_STATE_WHITESPACE,
  TEXT_PARSE_STATE_NUMBER,
  TEXT_PARSE_STATE_DONE
} Text_Parse_State;

typedef struct Text_Parse_Context {
  uint8_t state;
} Text_Parse_Context;

typedef enum VCard_Parse_State {
  VCARD_PARSE_STATE_INITIAL,
  VCARD_PARSE_STATE_KEYWORD,
  VCARD_PARSE_STATE_MATCH,
  VCARD_PARSE_STATE_NEXT_FIELD,
  VCARD_PARSE_STATE_NEXT_LINE,
  VCARD_PARSE_STATE_NUMBER,
  VCARD_PARSE_STATE_DONE
} VCard_Parse_State;

typedef enum VCard_Match_Field {
  VCARD_MATCH_FIELD_TEL,
  VCARD_MATCH_FIELD_PREF,
  VCARD_MATCH_FIELD_QUOTED_PRINTABLE,
} VCard_Match_Field;

typedef struct VCard_Parse_Context {
  uint8_t state;
  char escape_char;
  uint8_t escaped;
  uint8_t is_tel, is_pref;
  uint8_t match_field;
  prog_char *match_p;
} VCard_Parse_Context;

#ifdef IRDIAL_WITH_IRCOMM

typedef enum IRComm_Parse_State {
  IRCOMM_PARSE_STATE_INITIAL,
  IRCOMM_PARSE_STATE_GOT_A,
  IRCOMM_PARSE_STATE_GOT_AT,
  IRCOMM_PARSE_STATE_ECHO,
  IRCOMM_PARSE_STATE_NUMBER,
  IRCOMM_PARSE_STATE_DONE
} IRComm_Parse_State;

typedef struct IRComm_Parse_Context {
  uint8_t state;
} IRComm_Parse_Context;

#endif /* IRDIAL_WITH_IRCOMM */

typedef struct Parse_Context {
  uint8_t file_type;
  uint8_t number_len;
  char number[64];
  union {
    Text_Parse_Context text;
    VCard_Parse_Context vcard;
#ifdef IRDIAL_WITH_IRCOMM
    IRComm_Parse_Context ircomm;
#endif
    Rules_Parse_Context rules;
  } u;
} Parse_Context;

static uint8_t append_digit(Parse_Context *context_p, char a_c)
{
  char c;

  switch(a_c) {
  case ' ':
  case '\t':
  case '-':
  case '/':
  case '.':
  case ')':
    return 1;
  case '(':
    c='=';
    break;
  case '+':
  case '=':
  case ',':
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
  case '*':
  case '#':
    c=a_c;
    break;
  default:
    return 0;
  }

  if(context_p->number_lennumber)-1)
    context_p->number[context_p->number_len++]=c;

  return 1;
}

static void parse_text(Parse_Context *context_p, uint8_t *data, uint16_t size)
{
  uint16_t i;

  i=0;
  while(iu.text.state) {
    case TEXT_PARSE_STATE_INITIAL:
      if(data[i]=='\r' || data[i]=='\n')
	context_p->u.text.state=TEXT_PARSE_STATE_WHITESPACE;
      i++;
      break;
    case TEXT_PARSE_STATE_WHITESPACE:
      if(data[i]!=' ' && data[i]!='\t' && data[i]!='\r' && data[i]!='\n')
	context_p->u.text.state=TEXT_PARSE_STATE_NUMBER;
      else
	i++;
      break;
    case TEXT_PARSE_STATE_NUMBER:
      if(!append_digit(context_p, data[i]))
	context_p->u.text.state=TEXT_PARSE_STATE_DONE;
      else
	i++;
      break;
    default:
      return;
    }
  }
}

static void parse_vcard(Parse_Context *context_p, uint8_t *data, uint16_t size)
{
  uint16_t i;

  i=0;
  while(iu.vcard.state) {
    case VCARD_PARSE_STATE_INITIAL:
      context_p->u.vcard.escape_char='\\';
      context_p->u.vcard.escaped=0;
      context_p->u.vcard.is_tel=0;
      context_p->u.vcard.is_pref=0;
      context_p->u.vcard.state=VCARD_PARSE_STATE_KEYWORD;
      break;
    case VCARD_PARSE_STATE_KEYWORD:
      switch(toupper(data[i])) {
      case 'T':
	context_p->u.vcard.state=VCARD_PARSE_STATE_MATCH;
	context_p->u.vcard.match_field=VCARD_MATCH_FIELD_TEL;
	context_p->u.vcard.match_p=PSTR("EL");
	break;
      case 'P':
	context_p->u.vcard.state=VCARD_PARSE_STATE_MATCH;
	context_p->u.vcard.match_field=VCARD_MATCH_FIELD_PREF;
	context_p->u.vcard.match_p=PSTR("REF");
	break;
      case 'E':
	context_p->u.vcard.state=VCARD_PARSE_STATE_MATCH;
	context_p->u.vcard.match_field=VCARD_MATCH_FIELD_QUOTED_PRINTABLE;
	context_p->u.vcard.match_p=PSTR("NCODING=QUOTED-PRINTABLE");
	break;
      case ';':
	break;
      case ':':
	context_p->u.vcard.state=VCARD_PARSE_STATE_NEXT_LINE;
	break;
      case '\n':
	context_p->u.vcard.state=VCARD_PARSE_STATE_INITIAL;
	break;
      default:
	context_p->u.vcard.state=VCARD_PARSE_STATE_NEXT_FIELD;
      }
      i++;
      break;
    case VCARD_PARSE_STATE_MATCH:
      switch(data[i]) {
      case 0:
	context_p->u.vcard.state=VCARD_PARSE_STATE_DONE;
	break;
      case ';':
      case ':':
	if(!PRG_RDB(context_p->u.vcard.match_p)) {
	  switch(context_p->u.vcard.match_field) {
	  case VCARD_MATCH_FIELD_TEL:
	    context_p->u.vcard.is_tel=1;
	    break;
	  case VCARD_MATCH_FIELD_PREF:
	    context_p->u.vcard.is_pref=1;
	    break;
	  case VCARD_MATCH_FIELD_QUOTED_PRINTABLE:
	    context_p->u.vcard.escape_char='=';
	    break;
	  }
	}
	context_p->u.vcard.state=VCARD_PARSE_STATE_NEXT_FIELD;
	break;
      default:
	if(toupper(data[i])==PRG_RDB(context_p->u.vcard.match_p)) {
	  context_p->u.vcard.match_p++;
	  i++;
	} else
	  context_p->u.vcard.state=VCARD_PARSE_STATE_NEXT_FIELD;
      }
      break;
    case VCARD_PARSE_STATE_NEXT_FIELD:
      switch(data[i]) {
      case ';':
	context_p->u.vcard.state=VCARD_PARSE_STATE_KEYWORD;
	break;
      case ':':
	if(context_p->u.vcard.is_tel &&
	   (!context_p->number_len || context_p->u.vcard.is_pref)) {
	  context_p->number_len=0;
	  context_p->u.vcard.state=VCARD_PARSE_STATE_NUMBER;
	} else
	  context_p->u.vcard.state=VCARD_PARSE_STATE_NEXT_LINE;
	break;
      case '\n':
	context_p->u.vcard.state=VCARD_PARSE_STATE_INITIAL;
	break;
      }
      i++;
      break;
    case VCARD_PARSE_STATE_NEXT_LINE:
      if(!context_p->u.vcard.escaped) {
	if(data[i]==context_p->u.vcard.escape_char)
	  context_p->u.vcard.escaped=1;
	else if(data[i]=='\n')
	  context_p->u.vcard.state=VCARD_PARSE_STATE_INITIAL;
      } else
	if(data[i]!='\r')
	  context_p->u.vcard.escaped=0;
      i++;
      break;
    case VCARD_PARSE_STATE_NUMBER:
      if(!append_digit(context_p, data[i]))
	context_p->u.text.state=VCARD_PARSE_STATE_NEXT_LINE;
      else
	i++;
      break;
    default:
      return;
    }
  }
}

static uint8_t put_name_cb(char *name, void *user_data)
{
  Parse_Context *context_p=(Parse_Context *)user_data;
  char *ext;

  if((ext=strrchr(name, '.'))) {
    ext++;
    if(strcasecmp_P(ext, PSTR("txt"))==0) {
      context_p->file_type=FILE_TYPE_TEXT_UNKNOWN;
    } else if(strcasecmp_P(ext, PSTR("vcf"))==0) {
      context_p->file_type=FILE_TYPE_VCARD;
      context_p->u.vcard.state=VCARD_PARSE_STATE_INITIAL;
    } else
      context_p->file_type=FILE_TYPE_UNKNOWN;
  } else
    context_p->file_type=FILE_TYPE_UNKNOWN;

  return 1;
}

static uint8_t put_data_cb(uint8_t *data, uint16_t size, void *user_data)
{
  Parse_Context *context_p=(Parse_Context *)user_data;

  if(context_p->file_type==FILE_TYPE_TEXT_UNKNOWN) {
    if(size>=6 && strncmp_P(data, PSTR("@rules"), 6)==0) {
      context_p->file_type=FILE_TYPE_TEXT_RULES;
      context_p->u.rules.state=RULES_PARSE_STATE_INITIAL;
    } else if(size>=5 && strncmp_P(data, PSTR("@dial"), 5)==0) {
      context_p->file_type=FILE_TYPE_TEXT_DIAL;
      context_p->u.text.state=TEXT_PARSE_STATE_INITIAL;
    } else
      context_p->file_type=FILE_TYPE_TEXT_OTHER;
  }

  switch(context_p->file_type) {
  case FILE_TYPE_TEXT_RULES:
    rules_parse(&context_p->u.rules, data, size);
    break;
  case FILE_TYPE_TEXT_DIAL:
    parse_text(context_p, data, size);
    break;
  case FILE_TYPE_VCARD:
    parse_vcard(context_p, data, size);
    break;
  }

  return 1;
}

#ifdef IRDIAL_WITH_IRCOMM

static int16_t put_ircomm_cb(uint8_t *data, uint16_t size,
			     uint8_t *resp_buf, uint16_t resp_buf_size,
			     uint8_t *terminate_p,
			     void *user_data)
{
  Parse_Context *context_p=(Parse_Context *)user_data;
  uint16_t n, i;

  n=0;
  i=0;
  while(iu.text.state) {
    case IRCOMM_PARSE_STATE_INITIAL:
      if(toupper(data[i])=='A')
	context_p->u.text.state=IRCOMM_PARSE_STATE_GOT_A;
      i++;
      break;
    case IRCOMM_PARSE_STATE_GOT_A:
      if(toupper(data[i])=='T') {
	context_p->u.text.state=IRCOMM_PARSE_STATE_GOT_AT;
	if(n+2<=resp_buf_size) {
	  resp_buf[n++]='A';
	  resp_buf[n++]='T';
	}
      } else
	context_p->u.text.state=IRCOMM_PARSE_STATE_INITIAL;
      i++;
      break;
    case IRCOMM_PARSE_STATE_GOT_AT:
      if(toupper(data[i])=='D') {
	context_p->u.text.state=IRCOMM_PARSE_STATE_NUMBER;
	i++;
      } else
	context_p->u.text.state=IRCOMM_PARSE_STATE_ECHO;
      break;
    case IRCOMM_PARSE_STATE_ECHO:
      if(data[i]=='\r' || data[i]=='\n') {
	context_p->u.text.state=IRCOMM_PARSE_STATE_INITIAL;
	if(n+6<=resp_buf_size) {
	  memcpy_P(resp_buf+n, PSTR("\r\nOK\r\n"), 6);
	  n+=6;
	}
      } else
	if(nu.text.state=IRCOMM_PARSE_STATE_DONE;
      break;
    default:
      *terminate_p=1;
      return n;
    }
  }

  return n;
}

#endif /* IRDIAL_WITH_IRCOMM */

int main(void)
{
  IrLAP_Context irlap_ctx;
  Parse_Context parse_ctx;
  uint8_t success;
#ifdef IRDIAL_WITH_IRCOMM
  IrIAS_Node *ias_nodes[3];
  int16_t dlsap_sel;
#endif

  irphy_reset();
  irlap_init_context(&irlap_ctx);

#ifdef IRDIAL_WITH_IRCOMM
  ias_nodes[0]=&irobex_ias_node;
  ias_nodes[1]=&ircomm_ias_node;
  ias_nodes[2]=0;
#endif

  for(;;) {
    irphy_wait(-1);

    parse_ctx.file_type=FILE_TYPE_UNKNOWN;
    parse_ctx.number_len=0;

#ifdef IRDIAL_WITH_IRCOMM
    if((dlsap_sel=irttp2_accept(&irlap_ctx, IRLMP_HINT1_OBEX | IRLMP_HINT1_COMM,
				ias_nodes))<0)
      continue;

    if(dlsap_sel==IRCOMM_LSAP_SEL_VAL) {
      parse_ctx.u.ircomm.state=IRCOMM_PARSE_STATE_INITIAL;
      ircomm2_serve(&irlap_ctx, put_ircomm_cb, &parse_ctx);
      success=1;
    } else
      success=irobex_receive(&irlap_ctx, put_name_cb, put_data_cb, &parse_ctx);
#else
    success=irobex_receive(&irlap_ctx, put_name_cb, put_data_cb, &parse_ctx);
#endif

    if(parse_ctx.file_type==FILE_TYPE_TEXT_RULES)
      rules_parse_finish(&parse_ctx.u.rules, success);

    if(!success)
      continue;

    parse_ctx.number[parse_ctx.number_len]=0;
    rules_apply(parse_ctx.number, sizeof(parse_ctx.number));

    if(!*parse_ctx.number)
      continue;

    /* pull line switch */
    sbi(DDRD, DDD3);
    cbi(PORTD, PD3);

    sleep_msec(3000);

    dtmf_dial(parse_ctx.number);

    sleep_msec(3000);

    /* release line switch */
    sbi(PORTD, PD3);
  }
}