*
* \brief VoiceTronix Interface driver
*
+ * \ingroup channel_drivers
*/
+/*** MODULEINFO
+ <depend>vpbapi</depend>
+ ***/
extern "C" {
-#include <stdio.h>
-#include <string.h>
-
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include <stdio.h>
+#include <string.h>
+
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
/**/
static const char desc[] = "VoiceTronix V6PCI/V12PCI/V4PCI API Support";
-static const char type[] = "vpb";
static const char tdesc[] = "Standard VoiceTronix API Driver";
static const char config[] = "vpb.conf";
#endif
/* grunt tone defn's */
+#if 0
static VPB_DETECT toned_grunt = { 3, VPB_GRUNT, 1, 2000, 3000, 0, 0, -40, 0, 0, 0, 40, { { VPB_DELAY, 1000, 0, 0 }, { VPB_RISING, 0, 40, 0 }, { 0, 100, 0, 0 } } };
+#endif
static VPB_DETECT toned_ungrunt = { 2, VPB_GRUNT, 1, 2000, 1, 0, 0, -40, 0, 0, 30, 40, { { 0, 0, 0, 0 } } };
+/* Use loop polarity detection for CID */
+static int UsePolarityCID=0;
+
/* Use loop drop detection */
static int UseLoopDrop=1;
struct ast_frame **fo;
int flags;
ast_mutex_t lock;
- pthread_cond_t cond;
+ ast_cond_t cond;
int endbridge;
} vpb_bridge_t;
char language[MAX_LANGUAGE]; /* language being used */
char callerid[AST_MAX_EXTENSION]; /* CallerId used for directly connected phone */
int callerid_type; /* Caller ID type: 0=>none 1=>vpb 2=>AstV23 3=>AstBell */
+ char cid_num[AST_MAX_EXTENSION];
+ char cid_name[AST_MAX_EXTENSION];
int dtmf_caller_pos; /* DTMF CallerID detection (Brazil)*/
ast_mutex_t record_lock; /* This one prevents reentering a record_buf block */
ast_mutex_t play_lock; /* This one prevents reentering a play_buf block */
int play_buf_time; /* How long the last play_buf took */
+ struct timeval lastplay; /* Last play time */
ast_mutex_t play_dtmf_lock;
char play_dtmf[16];
static int vpb_answer(struct ast_channel *ast);
static struct ast_frame *vpb_read(struct ast_channel *ast);
static int vpb_write(struct ast_channel *ast, struct ast_frame *frame);
-static enum ast_bridge_result vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
-static int vpb_indicate(struct ast_channel *ast, int condition);
+static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
+static int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static struct ast_channel_tech vpb_tech = {
- type: type,
+ type: "vpb",
description: tdesc,
capabilities: AST_FORMAT_SLINEAR,
- properties: NULL,
+ properties: 0,
requester: vpb_request,
devicestate: NULL,
send_digit: vpb_digit,
+ send_digit_begin: NULL,
+ send_digit_end: NULL,
call: vpb_call,
hangup: vpb_hangup,
answer: vpb_answer,
send_image: NULL,
send_html: NULL,
exception: NULL,
- bridge: vpb_bridge,
+ bridge: ast_vpb_bridge,
indicate: vpb_indicate,
fixup: vpb_fixup,
setoption: NULL,
};
static struct ast_channel_tech vpb_tech_indicate = {
- type: type,
+ type: "vpb",
description: tdesc,
capabilities: AST_FORMAT_SLINEAR,
- properties: NULL,
+ properties: 0,
requester: vpb_request,
devicestate: NULL,
send_digit: vpb_digit,
+ send_digit_begin: NULL,
+ send_digit_end: NULL,
call: vpb_call,
hangup: vpb_hangup,
answer: vpb_answer,
send_image: NULL,
send_html: NULL,
exception: NULL,
- bridge: vpb_bridge,
+ bridge: ast_vpb_bridge,
indicate: NULL,
fixup: vpb_fixup,
setoption: NULL,
bridged_channel: NULL
};
-/* Can't get vpb_bridge() working on v4pci without either a horrible
+/* Can't get ast_vpb_bridge() working on v4pci without either a horrible
* high pitched feedback noise or bad hiss noise depending on gain settings
* Get asterisk to do the bridging
*/
/* #define HALF_DUPLEX_BRIDGE */
/* This is the Native bridge code, which Asterisk will try before using its own bridging code */
-static enum ast_bridge_result vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
{
struct vpb_pvt *p0 = (struct vpb_pvt *)c0->tech_pvt;
struct vpb_pvt *p1 = (struct vpb_pvt *)c1->tech_pvt;
ast_verbose(VERBOSE_PREFIX_2 "%s: vpb_bridge: Bridging call entered with [%s, %s]\n",p0->dev, c0->name, c1->name);
}
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
+
#ifdef HALF_DUPLEX_BRIDGE
if (option_verbose>1)
int rc;
struct ast_channel *owner = p->owner;
/*
- void * ws;
char callerid[AST_MAX_EXTENSION] = "";
*/
#ifdef ANALYSE_CID
+ void * ws;
char * file="cidsams.wav";
#endif
ast_verbose(VERBOSE_PREFIX_4 "CID record - start\n");
/* Skip any trailing ringtone */
- vpb_sleep(RING_SKIP);
+ if (UsePolarityCID != 1){
+ vpb_sleep(RING_SKIP);
+ }
if (option_verbose>3)
- ast_verbose(VERBOSE_PREFIX_4 "CID record - skipped %ldms trailing ring\n",
+ ast_verbose(VERBOSE_PREFIX_4 "CID record - skipped %dms trailing ring\n",
ast_tvdiff_ms(ast_tvnow(), cid_record_time));
cid_record_time = ast_tvnow();
#endif
if (option_verbose>3)
- ast_verbose(VERBOSE_PREFIX_4 "CID record - recorded %ldms between rings\n",
+ ast_verbose(VERBOSE_PREFIX_4 "CID record - recorded %dms between rings\n",
ast_tvdiff_ms(ast_tvnow(), cid_record_time));
ast_mutex_unlock(&p->record_lock);
owner->cid.cid_num = strdup(cli_struct->cldn);
owner->cid.cid_name = strdup(cli_struct->cn);
*/
- ast_set_callerid(owner, cli_struct->cldn, cli_struct->cn, cli_struct->cldn);
+ if (owner){
+ ast_set_callerid(owner, cli_struct->cldn, cli_struct->cn, cli_struct->cldn);
+ } else {
+ strcpy(p->cid_num, cli_struct->cldn);
+ strcpy(p->cid_name, cli_struct->cn);
+
+ }
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "CID record - got [%s] [%s]\n",owner->cid.cid_num,owner->cid.cid_name );
snprintf(p->callerid,sizeof(p->callerid)-1,"%s %s",cli_struct->cldn,cli_struct->cn);
}
if (number)
ast_shrink_phone_number(number);
- if (number && !ast_strlen_zero(number)) {
- owner->cid.cid_num = strdup(number);
- owner->cid.cid_ani = strdup(number);
- if (name && !ast_strlen_zero(name)){
- owner->cid.cid_name = strdup(name);
- snprintf(p->callerid,(sizeof(p->callerid)-1),"%s %s",number,name);
- }
- else {
- snprintf(p->callerid,(sizeof(p->callerid)-1),"%s",number);
- }
+ ast_set_callerid(owner,
+ number, name,
+ owner->cid.cid_ani ? NULL : number);
+ if (!ast_strlen_zero(name)){
+ snprintf(p->callerid,(sizeof(p->callerid)-1),"%s %s",number,name);
+ } else {
+ snprintf(p->callerid,(sizeof(p->callerid)-1),"%s",number);
}
-
if (cs)
callerid_free(cs);
}
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_owned: got event: [%d=>%d]\n", p->dev, e->type, e->data);
- f.src = (char *)type;
+ f.src = "vpb";
switch (e->type) {
case VPB_RING:
if (p->mode == MODE_FXO) {
else if (e->data == VPB_FAX){
if (!p->faxhandled){
if (strcmp(p->owner->exten, "fax")) {
- const char *target_context = ast_strlen_zero(p->owner->macrocontext) ? p->owner->context : p->owner->macrocontext;
+ const char *target_context = S_OR(p->owner->macrocontext, p->owner->context);
if (ast_exists_extension(p->owner, target_context, "fax", 1, p->owner->cid.cid_num)) {
if (option_verbose > 2)
f.frametype = -1;
}
break;
+ case VPB_LOOP_ONHOOK:
+ if (p->owner->_state == AST_STATE_UP)
+ f.subclass = AST_CONTROL_HANGUP;
+ else
+ f.frametype = -1;
+ break;
case VPB_STATION_ONHOOK:
f.subclass = AST_CONTROL_HANGUP;
break;
ast_mutex_lock(&p->bridge->lock); {
p->bridge->endbridge = 1;
- pthread_cond_signal(&p->bridge->cond);
+ ast_cond_signal(&p->bridge->cond);
} ast_mutex_unlock(&p->bridge->lock);
}
}
}
switch(e->type) {
+ case VPB_LOOP_ONHOOK:
+ case VPB_LOOP_POLARITY:
+ if (UsePolarityCID == 1){
+ if (option_verbose>3)
+ ast_verbose(VERBOSE_PREFIX_4 "Polarity reversal\n");
+ if(p->callerid_type == 1) {
+ if (option_verbose>3)
+ ast_verbose(VERBOSE_PREFIX_4 "Using VPB Caller ID\n");
+ get_callerid(p); /* UK CID before 1st ring*/
+ }
+/* get_callerid_ast(p); */ /* Caller ID using the ast functions */
+ }
+ break;
case VPB_RING:
if (p->mode == MODE_FXO) /* FXO port ring, start * */ {
vpb_new(p, AST_STATE_RING, p->context);
- if(p->callerid_type == 1) {
- if (option_verbose>3)
- ast_verbose(VERBOSE_PREFIX_4 "Using VPB Caller ID\n");
- get_callerid(p); /* Australian Caller ID only between 1st and 2nd ring */
+ if (UsePolarityCID != 1){
+ if(p->callerid_type == 1) {
+ if (option_verbose>3)
+ ast_verbose(VERBOSE_PREFIX_4 "Using VPB Caller ID\n");
+ get_callerid(p); /* Australian CID only between 1st and 2nd ring */
+ }
+ get_callerid_ast(p); /* Caller ID using the ast functions */
+ }
+ else {
+ ast_log(LOG_ERROR, "Setting caller ID: %s %s\n",p->cid_num, p->cid_name);
+ ast_set_callerid(p->owner, p->cid_num, p->cid_name, p->cid_num);
+ p->cid_num[0]=0;
+ p->cid_name[0]=0;
}
- get_callerid_ast(p); /* Caller ID using the ast functions */
+
vpb_timer_stop(p->ring_timer);
vpb_timer_start(p->ring_timer);
}
memset(bridges,0,max_bridges * sizeof(vpb_bridge_t));
for(int i = 0; i < max_bridges; i++ ) {
ast_mutex_init(&bridges[i].lock);
- pthread_cond_init(&bridges[i].cond, NULL);
+ ast_cond_init(&bridges[i].cond, NULL);
}
}
}
vpb_echo_canc_enable();
ast_log(LOG_NOTICE, "Voicetronix echo cancellation ON\n");
if (ec_supp_threshold > -1){
+ #ifdef VPB_PRI
+ vpb_echo_canc_set_sup_thresh(0,(short *)&ec_supp_threshold);
+ #else
vpb_echo_canc_set_sup_thresh((short *)&ec_supp_threshold);
+ #endif
ast_log(LOG_NOTICE, "Voicetronix EC Sup Thres set\n");
}
}
return tmp;
}
-static int vpb_indicate(struct ast_channel *ast, int condition)
+static int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
{
struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
int res = 0;
else {
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "%s: vpb_fixup Calling vpb_indicate\n", p->dev);
- vpb_indicate(newchan, AST_CONTROL_RINGING);
+ vpb_indicate(newchan, AST_CONTROL_RINGING, NULL, 0);
}
}
else {
stoptone(p->handle);
}
+ #ifdef VPB_PRI
+ vpb_setloop_async(p->handle, VPB_OFFHOOK);
+ vpb_sleep(100);
+ vpb_setloop_async(p->handle, VPB_ONHOOK);
+ #endif
} else {
stoptone(p->handle); /* Terminates any dialing */
vpb_sethook_sync(p->handle, VPB_ONHOOK);
struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
static struct ast_frame f = {AST_FRAME_NULL};
- f.src = (char *)type;
+ f.src = "vpb";
ast_log(LOG_NOTICE, "%s: vpb_read: should never be called!\n", p->dev);
ast_verbose("%s: vpb_read: should never be called!\n", p->dev);
struct vpb_pvt *p = (struct vpb_pvt *)ast->tech_pvt;
int res = 0, fmt = 0;
struct timeval play_buf_time_start;
+ int tdiff;
+
/* ast_mutex_lock(&p->lock); */
if(option_verbose>5)
ast_verbose("%s: vpb_write: Writing to channel\n", p->dev);
}
/* ast_log(LOG_DEBUG, "%s: vpb_write: Checked frame type..\n", p->dev); */
+
fmt = ast2vpbformat(frame->subclass);
if (fmt < 0) {
ast_log(LOG_WARNING, "%s: vpb_write: Cannot handle frames of %d format!\n",ast->name, frame->subclass);
return -1;
}
+
+ tdiff = ast_tvdiff_ms(ast_tvnow(), p->lastplay);
+ ast_log(LOG_DEBUG, "%s: vpb_write: time since last play(%d) \n", p->dev, tdiff);
+ if (tdiff < (VPB_SAMPLES/8 - 1)){
+ ast_log(LOG_DEBUG, "%s: vpb_write: Asked to play too often (%d) (%d)\n", p->dev, tdiff,frame->datalen);
+// return 0;
+ }
+ p->lastplay = ast_tvnow();
/*
ast_log(LOG_DEBUG, "%s: vpb_write: Checked frame format..\n", p->dev);
*/
if(option_verbose>1) {
ast_verbose("%s: vpb_write: Starting play mode (codec=%d)[%s]\n",p->dev,fmt,ast2vpbformatname(frame->subclass));
}
+ p->lastoutput = fmt;
+ ast_mutex_unlock(&p->play_lock);
+ return 0;
} else if (p->lastoutput != fmt) {
vpb_play_buf_finish(p->handle);
vpb_play_buf_start(p->handle, fmt);
if(option_verbose>1)
ast_verbose("%s: vpb_write: Changed play format (%d=>%d)\n",p->dev,p->lastoutput,fmt);
+ ast_mutex_unlock(&p->play_lock);
+ return 0;
}
p->lastoutput = fmt;
a_gain_vector(p->txswgain - MAX_VPB_GAIN , (short*)frame->data, frame->datalen/sizeof(short));
/* ast_log(LOG_DEBUG, "%s: vpb_write: Applied gain..\n", p->dev); */
+/* ast_log(LOG_DEBUG, "%s: vpb_write: play_buf_time %d\n", p->dev, p->play_buf_time); */
if ((p->read_state == 1)&&(p->play_buf_time<5)){
play_buf_time_start = ast_tvnow();
+/* res = vpb_play_buf_sync(p->handle, (char*)frame->data, tdiff*8*2); */
res = vpb_play_buf_sync(p->handle, (char*)frame->data, frame->datalen);
if( res == VPB_OK && option_verbose > 5 ) {
short * data = (short*)frame->data;
int bridgerec = 0;
int afmt, readlen, res, fmt, trycnt=0;
int ignore_dtmf;
- char * getdtmf_var = NULL;
+ const char * getdtmf_var = NULL;
fr->frametype = AST_FRAME_VOICE;
- fr->src = (char *)type;
+ fr->src = "vpb";
fr->mallocd = 0;
fr->delivery.tv_sec = 0;
fr->delivery.tv_usec = 0;
if (p->lastinput == -1) {
vpb_record_buf_start(p->handle, fmt);
vpb_reset_record_fifo_alarm(p->handle);
+ p->lastinput = fmt;
if(option_verbose>1)
ast_verbose( VERBOSE_PREFIX_2 "%s: Starting record mode (codec=%d)[%s]\n",p->dev,fmt,ast2vpbformatname(afmt));
+ continue;
} else if (p->lastinput != fmt) {
vpb_record_buf_finish(p->handle);
vpb_record_buf_start(p->handle, fmt);
+ p->lastinput = fmt;
if(option_verbose>1)
ast_verbose( VERBOSE_PREFIX_2 "%s: Changed record format (%d=>%d)\n",p->dev,p->lastinput,fmt);
+ continue;
}
- p->lastinput = fmt;
/* Read only if up and not bridged, or a bridge for which we can read. */
if (option_verbose > 5) {
ast_verbose("%s: p->stopreads[%d] p->owner[%p]\n", p->dev, p->stopreads,(void *)p->owner);
}
}
- } else {
- ast_log(LOG_WARNING,"%s: Record failure (%s)\n", p->dev, vpb_strerror(res));
- vpb_record_buf_finish(p->handle);
- vpb_record_buf_start(p->handle, fmt);
}
if (option_verbose > 4)
ast_verbose("%s: chanreads: Finished cycle...\n", p->dev);
tmp->tech = &vpb_tech;
}
- strncpy(tmp->name, me->dev, sizeof(tmp->name) - 1);
- tmp->type = type;
+ ast_string_field_set(tmp, name, me->dev);
tmp->callgroup = me->callgroup;
tmp->pickupgroup = me->pickupgroup;
else
strncpy(tmp->exten, "s", sizeof(tmp->exten) - 1);
if (strlen(me->language))
- strncpy(tmp->language, me->language, sizeof(tmp->language)-1);
+ ast_string_field_set(tmp, language, me->language);
me->owner = tmp;
me->faxhandled =0;
me->lastgrunt = ast_tvnow(); /* Assume at least one grunt tone seen now. */
+ me->lastplay = ast_tvnow(); /* Assume at least one grunt tone seen now. */
ast_mutex_lock(&usecnt_lock);
usecnt++;
return gain;
}
+
+int unload_module()
+{
+ struct vpb_pvt *p;
+ /* First, take us out of the channel loop */
+ if (use_ast_ind == 1){
+ ast_channel_unregister(&vpb_tech_indicate);
+ }
+ else {
+ ast_channel_unregister(&vpb_tech);
+ }
+
+ ast_mutex_lock(&iflock); {
+ /* Hangup all interfaces if they have an owner */
+ p = iflist;
+ while(p) {
+ if (p->owner)
+ ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ p = p->next;
+ }
+ iflist = NULL;
+ } ast_mutex_unlock(&iflock);
+
+ ast_mutex_lock(&monlock); {
+ if (mthreadactive > -1) {
+ pthread_cancel(monitor_thread);
+ pthread_join(monitor_thread, NULL);
+ }
+ mthreadactive = -2;
+ } ast_mutex_unlock(&monlock);
+
+ ast_mutex_lock(&iflock); {
+ /* Destroy all the interfaces and free their memory */
+
+ while(iflist) {
+ p = iflist;
+ ast_mutex_destroy(&p->lock);
+ pthread_cancel(p->readthread);
+ ast_mutex_destroy(&p->owner_lock);
+ ast_mutex_destroy(&p->record_lock);
+ ast_mutex_destroy(&p->play_lock);
+ ast_mutex_destroy(&p->play_dtmf_lock);
+ p->readthread = 0;
+
+ vpb_close(p->handle);
+
+ iflist = iflist->next;
+
+ free(p);
+ }
+ iflist = NULL;
+ } ast_mutex_unlock(&iflock);
+
+ ast_mutex_lock(&bridge_lock); {
+ memset(bridges, 0, sizeof bridges);
+ } ast_mutex_unlock(&bridge_lock);
+ ast_mutex_destroy(&bridge_lock);
+ for(int i = 0; i < max_bridges; i++ ) {
+ ast_mutex_destroy(&bridges[i].lock);
+ ast_cond_destroy(&bridges[i].cond);
+ }
+ free(bridges);
+
+ return 0;
+}
+
int load_module()
{
struct ast_config *cfg;
callgroup = ast_get_group(v->value);
} else if (strcasecmp(v->name, "pickupgroup") == 0){
pickupgroup = ast_get_group(v->value);
+ } else if (strcasecmp(v->name, "usepolaritycid") == 0){
+ UsePolarityCID = atoi(v->value);
} else if (strcasecmp(v->name, "useloopdrop") == 0){
UseLoopDrop = atoi(v->value);
} else if (strcasecmp(v->name, "usenativebridge") == 0){
if (use_ast_ind == 1){
if (!error && ast_channel_register(&vpb_tech_indicate) != 0) {
- ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ ast_log(LOG_ERROR, "Unable to register channel class 'vpb'\n");
error = -1;
}
else {
}
else {
if (!error && ast_channel_register(&vpb_tech) != 0) {
- ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ ast_log(LOG_ERROR, "Unable to register channel class 'vpb'\n");
error = -1;
}
else {
return error;
}
-
-int unload_module()
-{
- struct vpb_pvt *p;
- /* First, take us out of the channel loop */
- if (use_ast_ind == 1){
- ast_channel_unregister(&vpb_tech_indicate);
- }
- else {
- ast_channel_unregister(&vpb_tech);
- }
-
- ast_mutex_lock(&iflock); {
- /* Hangup all interfaces if they have an owner */
- p = iflist;
- while(p) {
- if (p->owner)
- ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
- p = p->next;
- }
- iflist = NULL;
- } ast_mutex_unlock(&iflock);
-
- ast_mutex_lock(&monlock); {
- if (mthreadactive > -1) {
- pthread_cancel(monitor_thread);
- pthread_join(monitor_thread, NULL);
- }
- mthreadactive = -2;
- } ast_mutex_unlock(&monlock);
-
- ast_mutex_lock(&iflock); {
- /* Destroy all the interfaces and free their memory */
-
- while(iflist) {
- p = iflist;
- ast_mutex_destroy(&p->lock);
- pthread_cancel(p->readthread);
- ast_mutex_destroy(&p->owner_lock);
- ast_mutex_destroy(&p->record_lock);
- ast_mutex_destroy(&p->play_lock);
- ast_mutex_destroy(&p->play_dtmf_lock);
- p->readthread = 0;
-
- vpb_close(p->handle);
-
- iflist = iflist->next;
-
- free(p);
- }
- iflist = NULL;
- } ast_mutex_unlock(&iflock);
-
- ast_mutex_lock(&bridge_lock); {
- memset(bridges, 0, sizeof bridges);
- } ast_mutex_unlock(&bridge_lock);
- ast_mutex_destroy(&bridge_lock);
- for(int i = 0; i < max_bridges; i++ ) {
- ast_mutex_destroy(&bridges[i].lock);
- pthread_cond_destroy(&bridges[i].cond);
- }
- free(bridges);
-
- return 0;
-}
-
int usecount()
{
return usecnt;
}
-char *description()
+const char *description()
{
return (char *) desc;
}
-char *key()
+const char *key()
{
return ASTERISK_GPL_KEY;
}
}
#endif
/**/
+
+