== AsteriskVer0-1-0/Main2Dial  ==

== main ==

* int main(int argc, char *argv[])
 * if (geteuid()) {
  * Must be run as root, exit(1);
 * }
 * while((c=getopt(argc, argv, "dvq")) != EOF) {
  * case d, v, q
  * exit(1);
 * }
 * signal 처리
 * if (init_logger()) event_log 화일을 append mode 로 open 한다.
  * exit(1);
 * if (load_pbx()) builtin function 을 ast_app 에 등록한다.
  * exit(1);
 * if (load_modules()) module 들의 load_module 을 수행하고, module_list 에 등록한다.
  * exit(1);
  * module 이 application 이면, application list 에 link 시키고, channel deiver이면 해당되는 config 파일을 읽어 channel(interface) list 를 만들고 chanlist 에 link 시킨다.
  * channel driver 는 각각의 channel(interface) 에 입력이 들어오기를 기다린다.
 * select(0,NULL,NULL,NULL,NULL);
  * interrupt 가 들어 올 때까지 대기상태로 있는다.
 * return 0;
{{{
If the readfs , writefs , and errorfds arguments are all null pointers and the timeout  argument is a null pointer, select()  blocks until interrupted by a signal.
}}}
{{{
model.txt
Description of call model:

Incoming Call:

        Channel backend waits for a RING or equivalent on some sort of
interface. Typically this is done in its own thread.  When a RING is
detected, the backend should create a channel structure and then call
ast_pbx_start() on that channel, which will create a thread to monitor
that interface.  At this point, the PBX and/or applications it launches
will manage the interface, and it need not be monitored by the
aforementioned thread.  When the applications are finished, the requisite
hangup function will be called, at which the channel can be considered to
be no longer valid, and the thread that controls it will immenantly be
terminated.
}}}

* include/logger.h
 * #define EVENTLOG "event_log"

* logger.c
 * #define AST_EVENT_LOG AST_LOG_DIR "/" EVENTLOG
 * AST_EVENT_LOG: /var/log/asterisk/event_log
* int init_logger(void)
 * mkdir(AST_LOG_DIR, 0755);
 * eventlog = fopen(AST_EVENT_LOG, "a");
 * if (eventlog) {
  * return 0;
 * } else
 * return -1;

* pbx.c
{{{
struct ast_app {
        /* Name of the application */
        char name[AST_MAX_APP];
        int (*execute)(struct ast_channel *chan, void *data);
        struct ast_app *next;
};

static struct ast_app *apps = NULL;
}}}

{{{
static struct pbx_builtin {
        char name[AST_MAX_APP];
        int (*execute)(struct ast_channel *chan, void *data);
} builtins[] =
{
        /* These applications are built into the PBX core and do not
           need separate modules */
        { "Answer", pbx_builtin_answer },
        { "Goto", pbx_builtin_goto },
        { "Hangup", pbx_builtin_hangup },
        { "DigitTimeout", pbx_builtin_dtimeout },
        { "ResponseTimeout", pbx_builtin_rtimeout },
        { "BackGround", pbx_builtin_background },
        { "Wait", pbx_builtin_wait },
};
}}}
* int load_pbx(void)
 * for (x=0;x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
  * if (ast_register_application(builtins[x].name, builtins[x].execute)) {
   * return -1;
  * }
 * }
 * return 0;

* int ast_register_application(char *app, int (*execute)(struct ast_channel *, void *))
 * application list 에 lock 을 걸수 없으면 return -1;
 * tmp = apps;
 *  while(tmp) {
  * 이미 등록되어 있으면 return -1;
 * }
 * tmp = malloc(sizeof(struct ast_app));
 * if (tmp) {
  * strncpy(tmp->name, app, sizeof(tmp->name));
  * tmp->execute = execute;
 * } else {
  * return -1;
 * }
 * return 0;

* int load_modules()
 * module.h:#define AST_MODULE_CONFIG "modules.conf" 
 * cfg = ast_load(AST_MODULE_CONFIG);
 * if (cfg) {
  * modules.conf 파일이 있으면
  * v = ast_variable_browse(cfg, "modules");
  * while(v) {
   * modules category 가 있으면
   * if (!strcasecmp(v->name, "load")) {
    * load 가 있으면
    * if (ast_load_resource(v->value)) {
     * return -1;
 * if (!cfg || ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
  * modules.conf 파일이 없거나, modules category 에 autoload 가 있으면
  * mods = opendir(AST_MODULE_DIR);
  * if (mods) {
   * while((d = readdir(mods))) {
    * .so file 이고 module list 에 없으면 {
     * cfg 에 noload 면 skip
     * if (ast_load_resource(d->d_name)) {
      * return -1;
     * }
    * }  
   * }
  * } else {
  * }
 * }
 * ast_destroy(cfg);
 * return 0;
    * }
   * }
   * v=v->next;
  * }
 * }

{{{
struct module {
        int (*load_module)(void);
        int (*unload_module)(void);
        int (*usecount)(void);
        char *(*description)(void);
        void *lib;
        char resource[256];
        struct module *next;
};

static struct module *module_list=NULL;
}}}
* int ast_load_resource(char *resource_name)
 * struct module *m = malloc(sizeof(struct module));
 * strncpy(m->resource, resource_name, sizeof(m->resource));
 * m->lib = dlopen(fn, RTLD_NOW  | RTLD_GLOBAL);
 * m->load_module = dlsym(m->lib, "load_module");
 * m->unload_module = dlsym(m->lib, "unload_module");
 * m->usecount = dlsym(m->lib, "usecount");
 * m->description = dlsym(m->lib, "description");
 * if ((res = m->load_module())) {
  * return -1;
  * channel driver 인 경우 load 되고, wait 상태에 있게 된다.
 * }
 * m->next = module_list;
 * module_list = m;
 *  ast_update_use_count();
 * return 0;

* dlsym - obtain the address of a symbol from a dlopen object

== ast_load ==
* config.c

{{{
struct ast_variable {
        char *name;
        char *value;
        struct ast_variable *next;
};

struct ast_category {
        char name[80];
        struct ast_variable *root;
        struct ast_category *next;
};

struct ast_config {
        /* Maybe this structure isn't necessary but we'll keep it
           for now */
        struct ast_category *root;
};
}}}

* struct ast_config *ast_load(char *configfile)
 * asterisk.h:#define AST_CONFIG_DIR "/etc/asterisk"
 * fn = AST_CONFIG_DIR + '/' + configfile
 * f = fopen(fn, "r")
  * tmp = malloc(sizeof(struct ast_config));
  * tmp->root = NULL;
  * while(!feof(f))
   * fgets(buf, sizeof(buf), f);
    * category
     * tmpc = malloc(sizeof(struct ast_category));
     * strncpy(tmpc->name, cur+1, sizeof(tmpc->name));
    * variable = value
     * v = malloc(sizeof(struct ast_variable));
     * v->name = strdup(strip(cur));
     * v->value = strdup(strip(c));
     * category struct 와 연결
  * fclose(f);
 * return tmp;

* char *ast_category_browse(struct ast_config *config, char *prev)
 * prev 가 NULL 이면, 첫번째 category 명을 return
 * prev category 가 존재하면, 다음 category 명을 return

* struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
 * category 가 존재하면, 첫번째 variable 의 pointer return

* char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
 * category 존재하고, variable 존재하면, 해당 값을 return

== chan_ixj.c ==
{{{
Connected to the PhoneJack PCI card is a Nortel Venture phone. My
phone.conf file has "mode=dialtone", "format=slinear", "echocancel=off",
"txgain=100%", "rxgain=1.0", and "device => /dev/phone0"
}}}

* load_module()
 * static char *config = "ixj.conf";
 * cfg = ast_load(config);
 * v = ast_variable_browse(cfg, "interfaces");
 * while(v)
  * strcasecmp(v->name, "device")
   * tmp = mkif(v->value, mode);
   * tmp->next = iflist;
   * iflist = tmp;
   * 각각의 device 는 interface 에 해당되고 이것들은 각각의 channel 에 해당된다. iflist 를 이용하여 do_monitor 에서 각 interface 의 input 을 check 한다.
  * strcasecmp(v->name, "mode")
   * mode = MODE_DIALTONE or MODE_IMMEDIATE
  * strcasecmp(v->name, "context"
   * strncpy(context, v->value, sizeof(context));
   * context 값이 없으면, default 를 사용함.
 * ast_channel_register(type, tdesc, AST_FORMAT_G723_1, ixj_request)
 * restart_monitor();
  * 새로운 thread 를 생성시킨다. 이는 do_monitor 함수를 수행한다.
  * do_monitor 함수는 위에서 생성한 iflist 를 이용하여 각 channel 의 input, output, error 에 해당하는 fd(file descriptor) 들의 집합을 FD_SET 에 넣고, select 를 통해 입력을 기다린다.

* struct ixj_pvt *mkif(char *iface, int mode)
 * tmp->fd = open(iface, O_RDWR);
  * iface 는 /dec/phone0
 * device 를 open 하고, 관련 설정을 한다.
 * tmp->owner = NULL;
 * tmp->dialtone = 0;

== ast_channel_register ==
{{{
struct ast_channel {
        char name[AST_CHANNEL_NAME];            /* ASCII Description of channel name */
        pthread_t blocker;                                      /* If anyone is blocking, this is them */
        char *blockproc;                                        /* Procedure causing blocking */
        int blocking;                                           /* Whether or not we're blocking */
        struct sched_context *sched;            /* Schedule context */
        int streamid;                                   /* For streaming playback, the schedule ID */
        struct ast_filestream *stream;  /* Stream itself. */
        struct ast_channel *trans;              /* Translator if present */
        struct ast_channel *master;             /* Master channel, if this is a translator */
        int fd;                                 /* File descriptor for channel -- all must have
                                                   a file descriptor! */
        char *type;                             /* Type of channel */
        int state;                              /* State of line */
        int rings;                              /* Number of rings so far */
        int stack;                              /* Current level of application */
        int format;                             /* Kinds of data this channel can
                                                           natively handle */
        char *dnid;                             /* Malloc'd Dialed Number Identifier */
        char *callerid;                 /* Malloc'd Caller ID */
        char context[AST_MAX_EXTENSION];        /* Current extension context */
        char exten[AST_MAX_EXTENSION];          /* Current extension number */
        int priority;                                           /* Current extension priority */
        void *app[AST_CHANNEL_MAX_STACK];       /* Application information -- see assigned numbers */
        struct ast_channel_pvt *pvt;
                                                /* Private channel implementation details */
        jmp_buf jmp[AST_CHANNEL_MAX_STACK];             /* Jump buffer used for returning from applications */
        struct ast_pbx *pbx;
        struct ast_channel *next;               /* For easy linking */
};
}}}
{{{
struct chanlist {
        char type[80];
        char description[80];
        int capabilities;
        struct ast_channel * (*requester)(char *type, int format, void *data);
        struct chanlist *next;
} *backends = NULL;
}}}

* int ast_channel_register(char *type, char *description, int capabilities, struct ast_channel *(*requester)(char *type, int format, void *data))
 * chanlist 는 channel list 가 아니라 channel driver 의 list 임.
 * 실제 channel 즉 ast_channel 은 requester 에 의해 얻는다.
 * channel list 에 lock 을 걸수 없으면 return -1;
 * chan = backends;
 * while(chan) {
  * 이미 등록되어 있으면 return -1;
 * }
 * chan = malloc(sizeof(struct chanlist));
 * channel structure 에 type, description, capabilities, requester 을 설정
 * chan->next = NULL;
 * channel list 끝에 link
 * return 0;

* chanlist struct 의 requester 에 각 모듈의 request 함수를 assign 한다. 이는 ast_request 함수에 의해 수행된다.

* static int dial_exec(struct ast_channel *chan, void *data)
 * app_dial.c
 * tmp->chan = ast_request(tech, chan->format, number);

* struct ast_channel *ast_request(char *type, int format, void *data)
 * channel.c
 * c = chan->requester(type, format, data);

* static struct ast_channel *ixj_request(char *type, int format, void *data)
 * tmp = ixj_new(p, AST_STATE_DOWN);

== restart_monitor ==
 * monitor_thread == pthread_self()
 * thread 가 있으면, thread 를 없앤다.
  * pthread_cancel(monitor_thread); or pthread_join(monitor_thread, NULL);
 * pthread_create(&monitor_thread, NULL, do_monitor, NULL)

* do_monitor
 * for
  * FD_ZERO(&rfds);
  * FD_ZERO(&efds);
  * i = iflist;
  * dotone = 0;
  * while(i)
   * if (!i->owner)
    * FD_SET(i->fd, &rfds);
    * FD_SET(i->fd, &efds);
    * if (i->dialtone) 
     * if (write(i->fd, DialTone + tonepos, 240) != 240)
     * dotone++;
   * i = i->next;
  * if (dotone)
   * res = select(n + 1, &rfds, NULL, &efds, &tv);
   * 입력을 tv 시간동안 기다린후 return. 다시 loop 로 돌아가 DialTone 을 write 한다.
  * else
   * res = select(n + 1, &rfds, NULL, &efds, NULL);
   * 입력을 무한정 기다린다.
  * i = iflist;
  * while(i)
   * FD_ISSET(i->fd, &rfds)
    * ixj_mini_packet(i);
   * FD_ISSET(i->fd, &efds)
    * ixj_check_exception(i);
   * i=i->next;
{{{
read, write, exception, time
성공시, select는 파일지정자집합에 표함된 숫자를 반환하며, 리턴하기전에 타임아웃이 발생하면 0을 반환한다. 이러시 -1이 반환되며, errno는 적당한 값으로 설정된다.
}}}

* static void ixj_mini_packet(struct ixj_pvt *i)
 * res = read(i->fd, buf, sizeof(buf));

== ixj_check_exception ==
{{{
typedef struct
{
  unsigned int dtmf_ready:1;
  unsigned int hookstate:1;
  unsigned int pstn_ring:1;
  unsigned int caller_id:1;
  unsigned int pstn_wink:1;
  unsigned int f0:1;
  unsigned int f1:1;
  unsigned int f2:1;
  unsigned int f3:1;
  unsigned int reserved:23;
}IXJ_EXCEPT;

typedef union
{
  IXJ_EXCEPT bits;
  unsigned int bytes;
}IXJ_EXCEPTION;

The dtmf_ready bit indicates if there is data waiting in the DTMF buffer.  The hookstate bit is set if there is a change in hookstate status, it does not indicate the current state of the hookswitch.  
The pstn_ring bit indicates that the DAA on a LineJACK card has detected ring voltage on the PSTN port.  
The caller_id bit indicates that caller_id data has been received and is available.  
The pstn_wink bit indicates that the DAA on the LineJACK has received a wink from the telco switch
}}}

* static void ixj_check_exception(struct ixj_pvt *i)
 * ixje.bytes = ioctl(i->fd, IXJCTL_EXCEPTION);
 * if (ixje.bits.dtmf_ready) 숫자가 눌려지면
  * digit[0] = ioctl(i->fd, IXJCTL_GET_DTMF_ASCII); 입력된 숫자를 가져온다.
  * if (i->mode == MODE_DIALTONE)
   * ioctl IXJCTL_PLAY_STOP, IXJCTL_REC_STOP, IXJCTL_CPT_STOP
   * i->dialtone = 0;
   * if (strlen(i->ext) < AST_MAX_EXTENSION - 1)
    * strcat(i->ext, digit);
    * 입력된 숫자를 i->ext 에 추가한다.
    * AST_MAX_EXTENSION 의 갯수를 넘으면, 추가하지 않는다.
   * if (ast_exists_extension(NULL, i->context, i->ext, 1)) {
    * i->context 에서 해당하는 extension이 있으면
    * ixj_new(i, AST_STATE_UP);
    * /* No need to restart monitor, we are the monitor */
    * if (i->owner)
      * ixj_setup(i->owner);
   * } else if (ast_exists_extension(NULL, "default", i->ext, 1)) {
    * default context 에서 extension이 있으면
    * 위와 똑같은 일을 수행한다.
   * } else if ((strlen(i->ext) >= ast_pbx_longest_extension(i->context)) &&
                       (strlen(i->ext) >= ast_pbx_longest_extension("default"))) {
    * It's not a valid extension, give a busy signal
    * ioctl(i->fd, IXJCTL_BUSY);
   * }
  * }
 * if (ixje.bits.hookstate) hook state 가 변하면
  * offhook = ioctl(i->fd, IXJCTL_HOOKSTATE);
  * if (offhook) 수화기를 들면
   * ioctl(i->fd, IXJCTL_DIALTONE);
   * offhook 을 하면 dialtone 을 내 보내고, 다음 exception 을 기다린다.
  * else 수화기를 내려놓으면 
   * memset(i->ext, 0, sizeof(i->ext));
   * ioctl(i->fd, IXJCTL_CPT_STOP);
   * ioctl(i->fd, IXJCTL_PLAY_STOP);
   * ioctl(i->fd, IXJCTL_REC_STOP);
   * i->dialtone = 0;

 * if (ixje.bits.pstn_ring)
  * ast_verbose("Unit is ringing\n");
 * if (ixje.bits.caller_id)
  * ast_verbose("We have caller ID\n");

 * hook off + 숫자들
 * hook off 면 dial tone 을 내보내고 다음 입력을 기다린다.
 * 숫자가 들어오면 i->ext 에 추가하고 context 에 해당하는 extension 이 있는가 점검한다.
 * 해당하는 extension 이 있으면 ixj_new 를 실행한다.

{{{
#define HELPER_EXISTS 0
#define HELPER_SPAWN 1
#define HELPER_EXEC 2
}}}
* int ast_exists_extension(struct ast_channel *c, char *context, char *exten, int priority)
 * return pbx_extension_helper(c, context, exten, priority, HELPER_EXISTS);

* static int pbx_extension_helper(struct ast_channel *c, char *context, char *exten, int priority, int action)
 * context, extension, priority 가 같은 extension 을 찾는다
 * action 이 HELP_EXISTS 면 return -1;
 * action 이 HELPER_SPAWN 이면 newstack++;
 * action 이 HELPER_EXEC 이면 
  * app = pbx_findapp(e->app);
  * return pbx_exec(c, app->execute, e->data, newstack);
 * 없고, action 이 HELP_EXISTS 가 아니면 return -1;
 * 없고,  action 이 HELP_EXISTS 면 return 0;

* static struct ast_channel *ixj_new(struct ixj_pvt *i, int state)
 * tmp = ast_channel_alloc();
 * set ast_channel structure
  * tmp 에 type, i->fd, AST_FORMAT_G723_1, state 를 assign
  * if (state == AST_STATE_RING)
   * tmp->rings = 1;
  * tmp->pvt->pvt = i; i->ext 는 extension number
  * tmp->pvt 에 ixj_digit, ixj_call, ixj_hangup, ixj_answer, ixj_read, ixj_write 를 assign
  * tmp 에 context, extension 을 복사 
 * i->owner = tmp;
 * ast_update_use_count();
 * if (state != AST_STATE_DOWN)
  * if (state == AST_STATE_RING)
   * ioctl(tmp->fd, IXJCTL_RINGBACK);
  * if (ast_pbx_start(tmp))
   * ast_hangup(tmp);
 * return tmp;

 * i->ext 를 tmp 에 설정하고, ast_pbx_start 를 실행한다.

* int ast_pbx_start(struct ast_channel *c)
 * pthread_create(&t, NULL, pbx_thread, c)

* static void *pbx_thread(void *data)
 * extension.conf 에 있는 dialplan 을 수행한다.
== ast_pbx_start ==
* int ast_pbx_start(struct ast_channel *c)
 * if (pthread_create(&t, &attr, pbx_thread, c))
  * return -1
 * return 0

* static void *pbx_thread(void *data)
 * struct ast_channel *c = data;
 * if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->callerid)) {
  * strncpy(c->context, "default", sizeof(c->context)-1);
  * strncpy(c->exten, "s", sizeof(c->exten)-1);
  * c->priority = 1;
  * context, exten, priority 가 같은 것이 없으면, context 는 default, exten 은 s, priority 는 1 로 설정
 * }
 * for(;;) {
  * pos = 0;  digit = 0;
  * while(ast_exists_extension(c, c->context, c->exten, c->priority, c->callerid)) {
   * if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->callerid))) {
    * dialplan(extensions.conf) 의 application 이 수행된다.
    * goto out;
   * }
   * if (c->stream) {
    * digit = ast_waitstream(c, AST_DIGIT_ANY);
    * ast_stopstream(c);
    * if (digit < 0)
     * goto out;
    * else if (digit) {
     * ast_stopstream(c);
     * exten[pos++] = digit;
     * break;
    * }
   * }
   * firstpass = 0;
   * c->priority++;
  * }
  * while(!ast_exists_extension(c, c->context, exten, 1) && (
                       strlen(exten) < ast_pbx_longest_extension(c->context))) {
   * digit = ast_waitfordigit(c, waittime * 1000);
   * if (!digit)
    * break;
   * if (digit < 0)
    * goto out;
   * exten[pos++] = digit;
   * waittime = c->pbx->dtimeout;
  * }
  * if (ast_exists_extension(c, c->context, exten, 1)) {
   * strncpy(c->exten, exten, sizeof(c->exten));
   * c->priority = 1;
  * } else {
   * if (strlen(exten)) {
    * An invalid extension
    * if (ast_exists_extension(c, c->context, "i", 1)) {
    * } else {
     * Invalid extension, but no rule 'i' in context
     * goto out;
    * }
   * } else {
    * A simple timeout
    * if (ast_exists_extension(c, c->context, "t", 1)) {
     * strncpy(c->exten, "t", sizeof(c->exten))
     * c->priority = 1;
    * } else {
     * Timeout, but no rule 't' in context
     * goto out;
    * }
   * }
  * }
 * }
 * out:
 * pbx_destroy(c->pbx);
 * c->pbx = NULL;
 * ast_hangup(c);
 * pthread_exit(NULL)