This source file includes following definitions.
- zt_build_gains
- zt_open_range
- FIO_CONFIGURE_SPAN_FUNCTION
- FIO_CONFIGURE_FUNCTION
- FIO_OPEN_FUNCTION
- FIO_CLOSE_FUNCTION
- FIO_COMMAND_FUNCTION
- FIO_GET_ALARMS_FUNCTION
- FIO_WAIT_FUNCTION
- FIO_SPAN_POLL_EVENT_FUNCTION
- zt_channel_process_event
- FIO_CHANNEL_NEXT_EVENT_FUNCTION
- FIO_SPAN_NEXT_EVENT_FUNCTION
- FIO_READ_FUNCTION
- FIO_WRITE_FUNCTION
- FIO_CHANNEL_DESTROY_FUNCTION
- FIO_IO_LOAD_FUNCTION
- FIO_IO_UNLOAD_FUNCTION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 #include "private/ftdm_core.h"
35 #include "ftmod_zt.h"
36
37
38 #ifndef ELAST
39 #define ELAST 500
40 #endif
41
42
43
44
45 static struct {
46 uint32_t codec_ms;
47 uint32_t wink_ms;
48 uint32_t flash_ms;
49 uint32_t eclevel;
50 uint32_t etlevel;
51 float rxgain;
52 float txgain;
53 } zt_globals;
54
55 #if defined(__FreeBSD__)
56 typedef unsigned long ioctlcmd;
57 #else
58 typedef int ioctlcmd;
59 #endif
60
61
62
63
64 struct ioctl_codes {
65 ioctlcmd GET_BLOCKSIZE;
66 ioctlcmd SET_BLOCKSIZE;
67 ioctlcmd FLUSH;
68 ioctlcmd SYNC;
69 ioctlcmd GET_PARAMS;
70 ioctlcmd SET_PARAMS;
71 ioctlcmd HOOK;
72 ioctlcmd GETEVENT;
73 ioctlcmd IOMUX;
74 ioctlcmd SPANSTAT;
75 ioctlcmd MAINT;
76 ioctlcmd GETCONF;
77 ioctlcmd SETCONF;
78 ioctlcmd CONFLINK;
79 ioctlcmd CONFDIAG;
80 ioctlcmd GETGAINS;
81 ioctlcmd SETGAINS;
82 ioctlcmd SPANCONFIG;
83 ioctlcmd CHANCONFIG;
84 ioctlcmd SET_BUFINFO;
85 ioctlcmd GET_BUFINFO;
86 ioctlcmd AUDIOMODE;
87 ioctlcmd ECHOCANCEL;
88 ioctlcmd HDLCRAWMODE;
89 ioctlcmd HDLCFCSMODE;
90 ioctlcmd SPECIFY;
91 ioctlcmd SETLAW;
92 ioctlcmd SETLINEAR;
93 ioctlcmd GETCONFMUTE;
94 ioctlcmd ECHOTRAIN;
95 ioctlcmd SETTXBITS;
96 ioctlcmd GETRXBITS;
97 };
98
99
100
101
102 static struct ioctl_codes zt_ioctl_codes = {
103 .GET_BLOCKSIZE = ZT_GET_BLOCKSIZE,
104 .SET_BLOCKSIZE = ZT_SET_BLOCKSIZE,
105 .FLUSH = ZT_FLUSH,
106 .SYNC = ZT_SYNC,
107 .GET_PARAMS = ZT_GET_PARAMS,
108 .SET_PARAMS = ZT_SET_PARAMS,
109 .HOOK = ZT_HOOK,
110 .GETEVENT = ZT_GETEVENT,
111 .IOMUX = ZT_IOMUX,
112 .SPANSTAT = ZT_SPANSTAT,
113 .MAINT = ZT_MAINT,
114 .GETCONF = ZT_GETCONF,
115 .SETCONF = ZT_SETCONF,
116 .CONFLINK = ZT_CONFLINK,
117 .CONFDIAG = ZT_CONFDIAG,
118 .GETGAINS = ZT_GETGAINS,
119 .SETGAINS = ZT_SETGAINS,
120 .SPANCONFIG = ZT_SPANCONFIG,
121 .CHANCONFIG = ZT_CHANCONFIG,
122 .SET_BUFINFO = ZT_SET_BUFINFO,
123 .GET_BUFINFO = ZT_GET_BUFINFO,
124 .AUDIOMODE = ZT_AUDIOMODE,
125 .ECHOCANCEL = ZT_ECHOCANCEL,
126 .HDLCRAWMODE = ZT_HDLCRAWMODE,
127 .HDLCFCSMODE = ZT_HDLCFCSMODE,
128 .SPECIFY = ZT_SPECIFY,
129 .SETLAW = ZT_SETLAW,
130 .SETLINEAR = ZT_SETLINEAR,
131 .GETCONFMUTE = ZT_GETCONFMUTE,
132 .ECHOTRAIN = ZT_ECHOTRAIN,
133 .SETTXBITS = ZT_SETTXBITS,
134 .GETRXBITS = ZT_GETRXBITS
135 };
136
137
138
139
140 static struct ioctl_codes dahdi_ioctl_codes = {
141 .GET_BLOCKSIZE = DAHDI_GET_BLOCKSIZE,
142 .SET_BLOCKSIZE = DAHDI_SET_BLOCKSIZE,
143 .FLUSH = DAHDI_FLUSH,
144 .SYNC = DAHDI_SYNC,
145 .GET_PARAMS = DAHDI_GET_PARAMS,
146 .SET_PARAMS = DAHDI_SET_PARAMS,
147 .HOOK = DAHDI_HOOK,
148 .GETEVENT = DAHDI_GETEVENT,
149 .IOMUX = DAHDI_IOMUX,
150 .SPANSTAT = DAHDI_SPANSTAT,
151 .MAINT = DAHDI_MAINT,
152 .GETCONF = DAHDI_GETCONF,
153 .SETCONF = DAHDI_SETCONF,
154 .CONFLINK = DAHDI_CONFLINK,
155 .CONFDIAG = DAHDI_CONFDIAG,
156 .GETGAINS = DAHDI_GETGAINS,
157 .SETGAINS = DAHDI_SETGAINS,
158 .SPANCONFIG = DAHDI_SPANCONFIG,
159 .CHANCONFIG = DAHDI_CHANCONFIG,
160 .SET_BUFINFO = DAHDI_SET_BUFINFO,
161 .GET_BUFINFO = DAHDI_GET_BUFINFO,
162 .AUDIOMODE = DAHDI_AUDIOMODE,
163 .ECHOCANCEL = DAHDI_ECHOCANCEL,
164 .HDLCRAWMODE = DAHDI_HDLCRAWMODE,
165 .HDLCFCSMODE = DAHDI_HDLCFCSMODE,
166 .SPECIFY = DAHDI_SPECIFY,
167 .SETLAW = DAHDI_SETLAW,
168 .SETLINEAR = DAHDI_SETLINEAR,
169 .GETCONFMUTE = DAHDI_GETCONFMUTE,
170 .ECHOTRAIN = DAHDI_ECHOTRAIN,
171 .SETTXBITS = DAHDI_SETTXBITS,
172 .GETRXBITS = DAHDI_GETRXBITS
173 };
174
175 #define ZT_INVALID_SOCKET -1
176 static struct ioctl_codes codes;
177 static const char *ctlpath = NULL;
178 static const char *chanpath = NULL;
179
180 static const char dahdi_ctlpath[] = "/dev/dahdi/ctl";
181 static const char dahdi_chanpath[] = "/dev/dahdi/channel";
182
183 static const char zt_ctlpath[] = "/dev/zap/ctl";
184 static const char zt_chanpath[] = "/dev/zap/channel";
185
186 static ftdm_socket_t CONTROL_FD = ZT_INVALID_SOCKET;
187
188 FIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event);
189 FIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event);
190 FIO_CHANNEL_NEXT_EVENT_FUNCTION(zt_channel_next_event);
191
192
193
194
195
196
197
198
199 static void zt_build_gains(struct zt_gains *g, float rxgain, float txgain, int codec)
200 {
201 int j;
202 int k;
203 float linear_rxgain = pow(10.0, rxgain / 20.0);
204 float linear_txgain = pow(10.0, txgain / 20.0);
205
206 switch (codec) {
207 case FTDM_CODEC_ALAW:
208 for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) {
209 if (rxgain) {
210 k = (int) (((float) alaw_to_linear(j)) * linear_rxgain);
211 if (k > 32767) k = 32767;
212 if (k < -32767) k = -32767;
213 g->receive_gain[j] = linear_to_alaw(k);
214 } else {
215 g->receive_gain[j] = j;
216 }
217 if (txgain) {
218 k = (int) (((float) alaw_to_linear(j)) * linear_txgain);
219 if (k > 32767) k = 32767;
220 if (k < -32767) k = -32767;
221 g->transmit_gain[j] = linear_to_alaw(k);
222 } else {
223 g->transmit_gain[j] = j;
224 }
225 }
226 break;
227 case FTDM_CODEC_ULAW:
228 for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) {
229 if (rxgain) {
230 k = (int) (((float) ulaw_to_linear(j)) * linear_rxgain);
231 if (k > 32767) k = 32767;
232 if (k < -32767) k = -32767;
233 g->receive_gain[j] = linear_to_ulaw(k);
234 } else {
235 g->receive_gain[j] = j;
236 }
237 if (txgain) {
238 k = (int) (((float) ulaw_to_linear(j)) * linear_txgain);
239 if (k > 32767) k = 32767;
240 if (k < -32767) k = -32767;
241 g->transmit_gain[j] = linear_to_ulaw(k);
242 } else {
243 g->transmit_gain[j] = j;
244 }
245 }
246 break;
247 }
248 }
249
250
251
252
253
254
255
256
257
258
259
260
261 static unsigned zt_open_range(ftdm_span_t *span, unsigned start, unsigned end, ftdm_chan_type_t type, char *name, char *number, unsigned char cas_bits)
262 {
263 unsigned configured = 0, x;
264 zt_params_t ztp;
265
266 memset(&ztp, 0, sizeof(ztp));
267
268 if (type == FTDM_CHAN_TYPE_CAS) {
269 ftdm_log(FTDM_LOG_DEBUG, "Configuring CAS channels with abcd == 0x%X\n", cas_bits);
270 }
271 for(x = start; x < end; x++) {
272 ftdm_channel_t *ftdmchan;
273 ftdm_socket_t sockfd = ZT_INVALID_SOCKET;
274 int len;
275
276 sockfd = open(chanpath, O_RDWR);
277 if (sockfd != ZT_INVALID_SOCKET && ftdm_span_add_channel(span, sockfd, type, &ftdmchan) == FTDM_SUCCESS) {
278
279 if (ioctl(sockfd, codes.SPECIFY, &x)) {
280 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s chan %d fd %d (%s)\n", chanpath, x, sockfd, strerror(errno));
281 close(sockfd);
282 continue;
283 }
284
285 if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
286 struct zt_bufferinfo binfo;
287 memset(&binfo, 0, sizeof(binfo));
288 binfo.txbufpolicy = 0;
289 binfo.rxbufpolicy = 0;
290 binfo.numbufs = 32;
291 binfo.bufsize = 1024;
292 if (ioctl(sockfd, codes.SET_BUFINFO, &binfo)) {
293 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd);
294 close(sockfd);
295 continue;
296 }
297 }
298
299 if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO) {
300 struct zt_chanconfig cc;
301 memset(&cc, 0, sizeof(cc));
302 cc.chan = cc.master = x;
303
304 switch(type) {
305 case FTDM_CHAN_TYPE_FXS:
306 {
307 switch(span->start_type) {
308 case FTDM_ANALOG_START_KEWL:
309 cc.sigtype = ZT_SIG_FXOKS;
310 break;
311 case FTDM_ANALOG_START_LOOP:
312 cc.sigtype = ZT_SIG_FXOLS;
313 break;
314 case FTDM_ANALOG_START_GROUND:
315 cc.sigtype = ZT_SIG_FXOGS;
316 break;
317 default:
318 break;
319 }
320 }
321 break;
322 case FTDM_CHAN_TYPE_FXO:
323 {
324 switch(span->start_type) {
325 case FTDM_ANALOG_START_KEWL:
326 cc.sigtype = ZT_SIG_FXSKS;
327 break;
328 case FTDM_ANALOG_START_LOOP:
329 cc.sigtype = ZT_SIG_FXSLS;
330 break;
331 case FTDM_ANALOG_START_GROUND:
332 cc.sigtype = ZT_SIG_FXSGS;
333 break;
334 default:
335 break;
336 }
337 }
338 break;
339 default:
340 break;
341 }
342
343 if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) {
344 ftdm_log(FTDM_LOG_WARNING, "this ioctl fails on older ftdmtel but is harmless if you used ztcfg\n[device %s chan %d fd %d (%s)]\n", chanpath, x, CONTROL_FD, strerror(errno));
345 }
346 }
347
348 if (type == FTDM_CHAN_TYPE_CAS) {
349 struct zt_chanconfig cc;
350 memset(&cc, 0, sizeof(cc));
351 cc.chan = cc.master = x;
352 cc.sigtype = ZT_SIG_CAS;
353 cc.idlebits = cas_bits;
354 if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) {
355 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d err:%s", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd, strerror(errno));
356 close(sockfd);
357 continue;
358 }
359 }
360
361 if (ftdmchan->type != FTDM_CHAN_TYPE_DQ921 && ftdmchan->type != FTDM_CHAN_TYPE_DQ931) {
362 len = zt_globals.codec_ms * 8;
363 if (ioctl(ftdmchan->sockfd, codes.SET_BLOCKSIZE, &len)) {
364 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d err:%s\n",
365 chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd, strerror(errno));
366 close(sockfd);
367 continue;
368 }
369
370 ftdmchan->packet_len = len;
371 ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8;
372
373 if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) {
374 ftdmchan->packet_len *= 2;
375 }
376 }
377
378 if (ioctl(sockfd, codes.GET_PARAMS, &ztp) < 0) {
379 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd);
380 close(sockfd);
381 continue;
382 }
383
384 if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
385 if (
386 (ztp.sig_type != ZT_SIG_HDLCRAW) &&
387 (ztp.sig_type != ZT_SIG_HDLCFCS) &&
388 (ztp.sig_type != ZT_SIG_HARDHDLC)
389 ) {
390 ftdm_log(FTDM_LOG_ERROR, "Failure configuring device %s as FreeTDM device %d:%d fd:%d, hardware signaling is not HDLC, fix your Zap/DAHDI configuration!\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd);
391 close(sockfd);
392 continue;
393 }
394 }
395
396 ftdm_log(FTDM_LOG_INFO, "configuring device %s channel %d as FreeTDM device %d:%d fd:%d\n", chanpath, x, ftdmchan->span_id, ftdmchan->chan_id, sockfd);
397
398 ftdmchan->rate = 8000;
399 ftdmchan->physical_span_id = ztp.span_no;
400 ftdmchan->physical_chan_id = ztp.chan_no;
401
402 if (type == FTDM_CHAN_TYPE_FXS || type == FTDM_CHAN_TYPE_FXO || type == FTDM_CHAN_TYPE_EM || type == FTDM_CHAN_TYPE_B) {
403 if (ztp.g711_type == ZT_G711_ALAW) {
404 ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_ALAW;
405 } else if (ztp.g711_type == ZT_G711_MULAW) {
406 ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_ULAW;
407 } else {
408 int type;
409
410 if (ftdmchan->span->trunk_type == FTDM_TRUNK_E1) {
411 type = FTDM_CODEC_ALAW;
412 } else {
413 type = FTDM_CODEC_ULAW;
414 }
415
416 ftdmchan->native_codec = ftdmchan->effective_codec = type;
417
418 }
419 }
420
421 ztp.wink_time = zt_globals.wink_ms;
422 ztp.flash_time = zt_globals.flash_ms;
423
424 if (ioctl(sockfd, codes.SET_PARAMS, &ztp) < 0) {
425 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, sockfd);
426 close(sockfd);
427 continue;
428 }
429
430 if (!ftdm_strlen_zero(name)) {
431 ftdm_copy_string(ftdmchan->chan_name, name, sizeof(ftdmchan->chan_name));
432 }
433 if (!ftdm_strlen_zero(number)) {
434 ftdm_copy_string(ftdmchan->chan_number, number, sizeof(ftdmchan->chan_number));
435 }
436 configured++;
437 } else {
438 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s\n", chanpath);
439 }
440 }
441
442
443
444 return configured;
445 }
446
447
448
449
450
451
452
453
454
455
456 static FIO_CONFIGURE_SPAN_FUNCTION(zt_configure_span)
457 {
458
459 int items, i;
460 char *mydata, *item_list[10];
461 char *ch, *mx;
462 unsigned char cas_bits = 0;
463 int channo;
464 int top = 0;
465 unsigned configured = 0;
466
467 assert(str != NULL);
468
469
470 mydata = ftdm_strdup(str);
471 assert(mydata != NULL);
472
473
474 items = ftdm_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0])));
475
476 for(i = 0; i < items; i++) {
477 ch = item_list[i];
478
479 if (!(ch)) {
480 ftdm_log(FTDM_LOG_ERROR, "Invalid input\n");
481 continue;
482 }
483
484 channo = atoi(ch);
485
486 if (channo < 0) {
487 ftdm_log(FTDM_LOG_ERROR, "Invalid channel number %d\n", channo);
488 continue;
489 }
490
491 if ((mx = strchr(ch, '-'))) {
492 mx++;
493 top = atoi(mx) + 1;
494 } else {
495 top = channo + 1;
496 }
497
498
499 if (top < 0) {
500 ftdm_log(FTDM_LOG_ERROR, "Invalid range number %d\n", top);
501 continue;
502 }
503 if (FTDM_CHAN_TYPE_CAS == type && ftdm_config_get_cas_bits(ch, &cas_bits)) {
504 ftdm_log(FTDM_LOG_ERROR, "Failed to get CAS bits in CAS channel\n");
505 continue;
506 }
507 configured += zt_open_range(span, channo, top, type, name, number, cas_bits);
508
509 }
510
511 ftdm_safe_free(mydata);
512
513 return configured;
514
515 }
516
517
518
519
520
521
522
523
524
525 static FIO_CONFIGURE_FUNCTION(zt_configure)
526 {
527
528 int num;
529 float fnum;
530
531 if (!strcasecmp(category, "defaults")) {
532 if (!strcasecmp(var, "codec_ms")) {
533 num = atoi(val);
534 if (num < 10 || num > 60) {
535 ftdm_log(FTDM_LOG_WARNING, "invalid codec ms at line %d\n", lineno);
536 } else {
537 zt_globals.codec_ms = num;
538 }
539 } else if (!strcasecmp(var, "wink_ms")) {
540 num = atoi(val);
541 if (num < 50 || num > 3000) {
542 ftdm_log(FTDM_LOG_WARNING, "invalid wink ms at line %d\n", lineno);
543 } else {
544 zt_globals.wink_ms = num;
545 }
546 } else if (!strcasecmp(var, "flash_ms")) {
547 num = atoi(val);
548 if (num < 50 || num > 3000) {
549 ftdm_log(FTDM_LOG_WARNING, "invalid flash ms at line %d\n", lineno);
550 } else {
551 zt_globals.flash_ms = num;
552 }
553 } else if (!strcasecmp(var, "echo_cancel_level")) {
554 num = atoi(val);
555 if (num < 0 || num > 256) {
556 ftdm_log(FTDM_LOG_WARNING, "invalid echo can val at line %d\n", lineno);
557 } else {
558 zt_globals.eclevel = num;
559 }
560 } else if (!strcasecmp(var, "echo_train_level")) {
561 if (zt_globals.eclevel < 1) {
562 ftdm_log(FTDM_LOG_WARNING, "can't set echo train level without setting echo cancel level first at line %d\n", lineno);
563 } else {
564 num = atoi(val);
565 if (num < 0 || num > 256) {
566 ftdm_log(FTDM_LOG_WARNING, "invalid echo train val at line %d\n", lineno);
567 } else {
568 zt_globals.etlevel = num;
569 }
570 }
571 } else if (!strcasecmp(var, "rxgain")) {
572 fnum = (float)atof(val);
573 if (fnum < -100.0 || fnum > 100.0) {
574 ftdm_log(FTDM_LOG_WARNING, "invalid rxgain val at line %d\n", lineno);
575 } else {
576 zt_globals.rxgain = fnum;
577 ftdm_log(FTDM_LOG_INFO, "Setting rxgain val to %f\n", fnum);
578 }
579 } else if (!strcasecmp(var, "txgain")) {
580 fnum = (float)atof(val);
581 if (fnum < -100.0 || fnum > 100.0) {
582 ftdm_log(FTDM_LOG_WARNING, "invalid txgain val at line %d\n", lineno);
583 } else {
584 zt_globals.txgain = fnum;
585 ftdm_log(FTDM_LOG_INFO, "Setting txgain val to %f\n", fnum);
586 }
587 } else {
588 ftdm_log(FTDM_LOG_WARNING, "Ignoring unknown setting '%s'\n", var);
589 }
590 }
591
592 return FTDM_SUCCESS;
593 }
594
595
596
597
598
599
600 static FIO_OPEN_FUNCTION(zt_open)
601 {
602 ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL);
603
604 if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921 || ftdmchan->type == FTDM_CHAN_TYPE_DQ931) {
605 ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_NONE;
606 } else {
607 int blocksize = zt_globals.codec_ms * (ftdmchan->rate / 1000);
608 int err;
609 if ((err = ioctl(ftdmchan->sockfd, codes.SET_BLOCKSIZE, &blocksize))) {
610 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno));
611 return FTDM_FAIL;
612 } else {
613 ftdmchan->effective_interval = ftdmchan->native_interval;
614 ftdmchan->packet_len = blocksize;
615 ftdmchan->native_codec = ftdmchan->effective_codec;
616 }
617
618 if (ftdmchan->type == FTDM_CHAN_TYPE_B) {
619 int one = 1;
620 if (ioctl(ftdmchan->sockfd, codes.AUDIOMODE, &one)) {
621 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno));
622 ftdm_log(FTDM_LOG_ERROR, "%s\n", ftdmchan->last_error);
623 return FTDM_FAIL;
624 }
625 }
626 if (zt_globals.rxgain || zt_globals.txgain) {
627 struct zt_gains gains;
628 memset(&gains, 0, sizeof(gains));
629
630 gains.chan_no = ftdmchan->physical_chan_id;
631 zt_build_gains(&gains, zt_globals.rxgain, zt_globals.txgain, ftdmchan->native_codec);
632
633 if (zt_globals.rxgain)
634 ftdm_log(FTDM_LOG_INFO, "Setting rxgain to %f on channel %d\n", zt_globals.rxgain, gains.chan_no);
635
636 if (zt_globals.txgain)
637 ftdm_log(FTDM_LOG_INFO, "Setting txgain to %f on channel %d\n", zt_globals.txgain, gains.chan_no);
638
639 if (ioctl(ftdmchan->sockfd, codes.SETGAINS, &gains) < 0) {
640 ftdm_log(FTDM_LOG_ERROR, "failure configuring device %s as FreeTDM device %d:%d fd:%d\n", chanpath, ftdmchan->span_id, ftdmchan->chan_id, ftdmchan->sockfd);
641 }
642 }
643
644 if (zt_globals.eclevel >= 0) {
645 int len = zt_globals.eclevel;
646 if (len) {
647 ftdm_log(FTDM_LOG_INFO, "Setting echo cancel to %d taps for %d:%d\n", len, ftdmchan->span_id, ftdmchan->chan_id);
648 } else {
649 ftdm_log(FTDM_LOG_INFO, "Disable echo cancel for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id);
650 }
651 if (ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &len)) {
652 ftdm_log(FTDM_LOG_WARNING, "Echo cancel not available for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id);
653 } else if (zt_globals.etlevel > 0) {
654 len = zt_globals.etlevel;
655 if (ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &len)) {
656 ftdm_log(FTDM_LOG_WARNING, "Echo training not available for %d:%d\n", ftdmchan->span_id, ftdmchan->chan_id);
657 }
658 }
659 }
660
661 }
662 return FTDM_SUCCESS;
663 }
664
665
666
667
668
669
670 static FIO_CLOSE_FUNCTION(zt_close)
671 {
672 if (ftdmchan->type == FTDM_CHAN_TYPE_B) {
673 int value = 0;
674 if (ioctl(ftdmchan->sockfd, codes.AUDIOMODE, &value)) {
675 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno));
676 ftdm_log(FTDM_LOG_ERROR, "%s\n", ftdmchan->last_error);
677 return FTDM_FAIL;
678 }
679 }
680 return FTDM_SUCCESS;
681 }
682
683
684
685
686
687
688
689
690 static FIO_COMMAND_FUNCTION(zt_command)
691 {
692 zt_params_t ztp;
693 int err = 0;
694
695 memset(&ztp, 0, sizeof(ztp));
696
697 switch(command) {
698 case FTDM_COMMAND_ENABLE_ECHOCANCEL:
699 {
700 int level = FTDM_COMMAND_OBJ_INT;
701 err = ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &level);
702 FTDM_COMMAND_OBJ_INT = level;
703 }
704 case FTDM_COMMAND_DISABLE_ECHOCANCEL:
705 {
706 int level = 0;
707 err = ioctl(ftdmchan->sockfd, codes.ECHOCANCEL, &level);
708 FTDM_COMMAND_OBJ_INT = level;
709 }
710 break;
711 case FTDM_COMMAND_ENABLE_ECHOTRAIN:
712 {
713 int level = FTDM_COMMAND_OBJ_INT;
714 err = ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &level);
715 FTDM_COMMAND_OBJ_INT = level;
716 }
717 case FTDM_COMMAND_DISABLE_ECHOTRAIN:
718 {
719 int level = 0;
720 err = ioctl(ftdmchan->sockfd, codes.ECHOTRAIN, &level);
721 FTDM_COMMAND_OBJ_INT = level;
722 }
723 break;
724 case FTDM_COMMAND_OFFHOOK:
725 {
726 int command = ZT_OFFHOOK;
727 if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) {
728 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "OFFHOOK Failed");
729 return FTDM_FAIL;
730 }
731 ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
732 }
733 break;
734 case FTDM_COMMAND_ONHOOK:
735 {
736 int command = ZT_ONHOOK;
737 if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) {
738 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ONHOOK Failed");
739 return FTDM_FAIL;
740 }
741 ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_OFFHOOK);
742 }
743 break;
744 case FTDM_COMMAND_FLASH:
745 {
746 int command = ZT_FLASH;
747 if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) {
748 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "FLASH Failed");
749 return FTDM_FAIL;
750 }
751 }
752 break;
753 case FTDM_COMMAND_WINK:
754 {
755 int command = ZT_WINK;
756 if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) {
757 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "WINK Failed");
758 return FTDM_FAIL;
759 }
760 }
761 break;
762 case FTDM_COMMAND_GENERATE_RING_ON:
763 {
764 int command = ZT_RING;
765 if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) {
766 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring Failed");
767 return FTDM_FAIL;
768 }
769 ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING);
770 }
771 break;
772 case FTDM_COMMAND_GENERATE_RING_OFF:
773 {
774 int command = ZT_RINGOFF;
775 if (ioctl(ftdmchan->sockfd, codes.HOOK, &command)) {
776 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Ring-off failed");
777 return FTDM_FAIL;
778 }
779 ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_RINGING);
780 }
781 break;
782 case FTDM_COMMAND_GET_INTERVAL:
783 {
784
785 if (!(err = ioctl(ftdmchan->sockfd, codes.GET_BLOCKSIZE, &ftdmchan->packet_len))) {
786 ftdmchan->native_interval = ftdmchan->packet_len / 8;
787 if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) {
788 ftdmchan->packet_len *= 2;
789 }
790 FTDM_COMMAND_OBJ_INT = ftdmchan->native_interval;
791 }
792 }
793 break;
794 case FTDM_COMMAND_SET_INTERVAL:
795 {
796 int interval = FTDM_COMMAND_OBJ_INT;
797 int len = interval * 8;
798
799 if (!(err = ioctl(ftdmchan->sockfd, codes.SET_BLOCKSIZE, &len))) {
800 ftdmchan->packet_len = len;
801 ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8;
802
803 if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) {
804 ftdmchan->packet_len *= 2;
805 }
806 }
807 }
808 break;
809 case FTDM_COMMAND_SET_CAS_BITS:
810 {
811 int bits = FTDM_COMMAND_OBJ_INT;
812 err = ioctl(ftdmchan->sockfd, codes.SETTXBITS, &bits);
813 }
814 break;
815 case FTDM_COMMAND_GET_CAS_BITS:
816 {
817 err = ioctl(ftdmchan->sockfd, codes.GETRXBITS, &ftdmchan->rx_cas_bits);
818 if (!err) {
819 FTDM_COMMAND_OBJ_INT = ftdmchan->rx_cas_bits;
820 }
821 }
822 break;
823 case FTDM_COMMAND_FLUSH_TX_BUFFERS:
824 {
825 int flushmode = ZT_FLUSH_WRITE;
826 err = ioctl(ftdmchan->sockfd, codes.FLUSH, &flushmode);
827 }
828 break;
829 case FTDM_COMMAND_FLUSH_RX_BUFFERS:
830 {
831 int flushmode = ZT_FLUSH_READ;
832 err = ioctl(ftdmchan->sockfd, codes.FLUSH, &flushmode);
833 }
834 break;
835 case FTDM_COMMAND_FLUSH_BUFFERS:
836 {
837 int flushmode = ZT_FLUSH_BOTH;
838 err = ioctl(ftdmchan->sockfd, codes.FLUSH, &flushmode);
839 }
840 break;
841 default:
842 err = FTDM_NOTIMPL;
843 break;
844 };
845
846 if (err && err != FTDM_NOTIMPL) {
847 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno));
848 return FTDM_FAIL;
849 }
850
851
852 return err == 0 ? FTDM_SUCCESS : err;
853 }
854
855
856
857
858
859
860 static FIO_GET_ALARMS_FUNCTION(zt_get_alarms)
861 {
862 struct zt_spaninfo info;
863
864 memset(&info, 0, sizeof(info));
865 info.span_no = ftdmchan->physical_span_id;
866
867 if (ioctl(CONTROL_FD, codes.SPANSTAT, &info)) {
868 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "ioctl failed (%s)", strerror(errno));
869 snprintf(ftdmchan->span->last_error, sizeof(ftdmchan->span->last_error), "ioctl failed (%s)", strerror(errno));
870 return FTDM_FAIL;
871 }
872
873 ftdmchan->alarm_flags = info.alarms;
874
875 return FTDM_SUCCESS;
876 }
877
878
879
880
881
882
883
884
885 static FIO_WAIT_FUNCTION(zt_wait)
886 {
887 int32_t inflags = 0;
888 int result;
889 struct pollfd pfds[1];
890
891 if (*flags & FTDM_READ) {
892 inflags |= POLLIN;
893 }
894
895 if (*flags & FTDM_WRITE) {
896 inflags |= POLLOUT;
897 }
898
899 if (*flags & FTDM_EVENTS) {
900 inflags |= POLLPRI;
901 }
902
903 pollagain:
904 memset(&pfds[0], 0, sizeof(pfds[0]));
905 pfds[0].fd = ftdmchan->sockfd;
906 pfds[0].events = inflags;
907 result = poll(pfds, 1, to);
908 *flags = 0;
909
910 if (result < 0 && errno == EINTR) {
911 ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "DAHDI wait got interrupted, trying again\n");
912 goto pollagain;
913 }
914
915 if (pfds[0].revents & POLLERR) {
916 ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "DAHDI device got POLLERR\n");
917 result = -1;
918 }
919
920 if (result > 0) {
921 inflags = pfds[0].revents;
922 }
923
924 *flags = FTDM_NO_FLAGS;
925
926 if (result < 0){
927 snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Poll failed");
928 ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to poll DAHDI device: %s\n", strerror(errno));
929 return FTDM_FAIL;
930 }
931
932 if (result == 0) {
933 return FTDM_TIMEOUT;
934 }
935
936 if (inflags & POLLIN) {
937 *flags |= FTDM_READ;
938 }
939
940 if (inflags & POLLOUT) {
941 *flags |= FTDM_WRITE;
942 }
943
944 if (inflags & POLLPRI) {
945 *flags |= FTDM_EVENTS;
946 }
947
948 return FTDM_SUCCESS;
949
950 }
951
952
953
954
955
956
957
958 FIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event)
959 {
960 struct pollfd pfds[FTDM_MAX_CHANNELS_SPAN];
961 uint32_t i, j = 0, k = 0;
962 int r;
963
964 for(i = 1; i <= span->chan_count; i++) {
965 memset(&pfds[j], 0, sizeof(pfds[j]));
966 pfds[j].fd = span->channels[i]->sockfd;
967 pfds[j].events = POLLPRI;
968 j++;
969 }
970
971 r = poll(pfds, j, ms);
972
973 if (r == 0) {
974 return FTDM_TIMEOUT;
975 } else if (r < 0 || (pfds[i-1].revents & POLLERR)) {
976 snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
977 return FTDM_FAIL;
978 }
979
980 for(i = 1; i <= span->chan_count; i++) {
981 if (pfds[i-1].revents & POLLPRI) {
982 ftdm_set_flag(span->channels[i], FTDM_CHANNEL_EVENT);
983 span->channels[i]->last_event_time = ftdm_current_time_in_ms();
984 k++;
985 }
986 }
987
988 if (!k) {
989 snprintf(span->last_error, sizeof(span->last_error), "no matching descriptor");
990 }
991
992 return k ? FTDM_SUCCESS : FTDM_FAIL;
993 }
994
995
996
997
998
999
1000
1001
1002 static __inline__ ftdm_status_t zt_channel_process_event(ftdm_channel_t *fchan, ftdm_oob_event_t *event_id, zt_event_t zt_event_id)
1003 {
1004 switch(zt_event_id) {
1005 case ZT_EVENT_RINGEROFF:
1006 {
1007 return FTDM_FAIL;
1008 }
1009 break;
1010 case ZT_EVENT_RINGERON:
1011 {
1012 return FTDM_FAIL;
1013 }
1014 break;
1015 case ZT_EVENT_RINGBEGIN:
1016 {
1017 *event_id = FTDM_OOB_RING_START;
1018 }
1019 break;
1020 case ZT_EVENT_ONHOOK:
1021 {
1022 *event_id = FTDM_OOB_ONHOOK;
1023 }
1024 break;
1025 case ZT_EVENT_WINKFLASH:
1026 {
1027 if (fchan->state == FTDM_CHANNEL_STATE_DOWN || fchan->state == FTDM_CHANNEL_STATE_DIALING) {
1028 *event_id = FTDM_OOB_WINK;
1029 } else {
1030 *event_id = FTDM_OOB_FLASH;
1031 }
1032 }
1033 break;
1034 case ZT_EVENT_RINGOFFHOOK:
1035 {
1036 if (fchan->type == FTDM_CHAN_TYPE_FXS || (fchan->type == FTDM_CHAN_TYPE_EM && fchan->state != FTDM_CHANNEL_STATE_UP)) {
1037 ftdm_set_flag_locked(fchan, FTDM_CHANNEL_OFFHOOK);
1038 *event_id = FTDM_OOB_OFFHOOK;
1039 } else if (fchan->type == FTDM_CHAN_TYPE_FXO) {
1040 *event_id = FTDM_OOB_RING_START;
1041 } else {
1042 *event_id = FTDM_OOB_NOOP;
1043 }
1044 }
1045 break;
1046 case ZT_EVENT_ALARM:
1047 {
1048 *event_id = FTDM_OOB_ALARM_TRAP;
1049 }
1050 break;
1051 case ZT_EVENT_NOALARM:
1052 {
1053 *event_id = FTDM_OOB_ALARM_CLEAR;
1054 }
1055 break;
1056 case ZT_EVENT_BITSCHANGED:
1057 {
1058 *event_id = FTDM_OOB_CAS_BITS_CHANGE;
1059 int bits = 0;
1060 int err = ioctl(fchan->sockfd, codes.GETRXBITS, &bits);
1061 if (err) {
1062 return FTDM_FAIL;
1063 }
1064 fchan->rx_cas_bits = bits;
1065 }
1066 break;
1067 case ZT_EVENT_NONE:
1068 {
1069 ftdm_log_chan_msg(fchan, FTDM_LOG_DEBUG, "No event\n");
1070 *event_id = FTDM_OOB_NOOP;
1071 }
1072 break;
1073 default:
1074 {
1075 ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Unhandled event %d\n", zt_event_id);
1076 *event_id = FTDM_OOB_INVALID;
1077 }
1078 break;
1079 }
1080 return FTDM_SUCCESS;
1081 }
1082
1083
1084
1085
1086
1087
1088
1089 FIO_CHANNEL_NEXT_EVENT_FUNCTION(zt_channel_next_event)
1090 {
1091 uint32_t event_id = FTDM_OOB_INVALID;
1092 zt_event_t zt_event_id = 0;
1093 ftdm_span_t *span = ftdmchan->span;
1094
1095 if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
1096 ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
1097 }
1098
1099 if (ioctl(ftdmchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
1100 ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed retrieving event from channel: %s\n",
1101 strerror(errno));
1102 return FTDM_FAIL;
1103 }
1104
1105
1106 if ((zt_channel_process_event(ftdmchan, &event_id, zt_event_id)) != FTDM_SUCCESS) {
1107 ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to process event from channel\n");
1108 return FTDM_FAIL;
1109 }
1110
1111 ftdmchan->last_event_time = 0;
1112 span->event_header.e_type = FTDM_EVENT_OOB;
1113 span->event_header.enum_id = event_id;
1114 span->event_header.channel = ftdmchan;
1115 *event = &span->event_header;
1116 return FTDM_SUCCESS;
1117 }
1118
1119
1120
1121
1122
1123
1124
1125 FIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event)
1126 {
1127 uint32_t i, event_id = FTDM_OOB_INVALID;
1128 zt_event_t zt_event_id = 0;
1129
1130 for(i = 1; i <= span->chan_count; i++) {
1131 ftdm_channel_t *fchan = span->channels[i];
1132 if (ftdm_test_flag(fchan, FTDM_CHANNEL_EVENT)) {
1133 ftdm_clear_flag(fchan, FTDM_CHANNEL_EVENT);
1134 if (ioctl(fchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
1135 snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
1136 return FTDM_FAIL;
1137 }
1138
1139 ftdm_channel_lock(fchan);
1140 if ((zt_channel_process_event(fchan, &event_id, zt_event_id)) != FTDM_SUCCESS) {
1141 ftdm_log_chan_msg(fchan, FTDM_LOG_ERROR, "Failed to process event from channel\n");
1142 ftdm_channel_unlock(fchan);
1143 return FTDM_FAIL;
1144 }
1145 ftdm_channel_unlock(fchan);
1146
1147 fchan->last_event_time = 0;
1148 span->event_header.e_type = FTDM_EVENT_OOB;
1149 span->event_header.enum_id = event_id;
1150 span->event_header.channel = fchan;
1151 *event = &span->event_header;
1152 return FTDM_SUCCESS;
1153 }
1154 }
1155
1156 return FTDM_FAIL;
1157 }
1158
1159
1160
1161
1162
1163
1164
1165
1166 static FIO_READ_FUNCTION(zt_read)
1167 {
1168 ftdm_ssize_t r = 0;
1169 int errs = 0;
1170
1171 while (errs++ < 30) {
1172 if ((r = read(ftdmchan->sockfd, data, *datalen)) > 0) {
1173 break;
1174 }
1175 else if (r == 0) {
1176 ftdm_sleep(10);
1177 if (errs) errs--;
1178 }
1179 else {
1180 if (errno == EAGAIN || errno == EINTR)
1181 continue;
1182 if (errno == ELAST)
1183 break;
1184
1185 ftdm_log(FTDM_LOG_ERROR, "read failed: %s\n", strerror(errno));
1186 }
1187 }
1188 if (r > 0) {
1189 *datalen = r;
1190 if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
1191 *datalen -= 2;
1192 }
1193 return FTDM_SUCCESS;
1194 }
1195 else if (errno == ELAST) {
1196 return FTDM_SUCCESS;
1197 }
1198 return r == 0 ? FTDM_TIMEOUT : FTDM_FAIL;
1199 }
1200
1201
1202
1203
1204
1205
1206
1207
1208 static FIO_WRITE_FUNCTION(zt_write)
1209 {
1210 ftdm_ssize_t w = 0;
1211 ftdm_size_t bytes = *datalen;
1212
1213 if (ftdmchan->type == FTDM_CHAN_TYPE_DQ921) {
1214 memset(data+bytes, 0, 2);
1215 bytes += 2;
1216 }
1217
1218 tryagain:
1219 w = write(ftdmchan->sockfd, data, bytes);
1220
1221 if (w >= 0) {
1222 *datalen = w;
1223 return FTDM_SUCCESS;
1224 }
1225
1226 if (errno == ELAST) {
1227 zt_event_t zt_event_id = 0;
1228 if (ioctl(ftdmchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
1229 ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed retrieving event after ELAST on write: %s\n", strerror(errno));
1230 return FTDM_FAIL;
1231 }
1232
1233 ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Dropping event %d to be able to write data\n", zt_event_id);
1234 goto tryagain;
1235 }
1236
1237 return FTDM_FAIL;
1238 }
1239
1240
1241
1242
1243
1244
1245 static FIO_CHANNEL_DESTROY_FUNCTION(zt_channel_destroy)
1246 {
1247 close(ftdmchan->sockfd);
1248 ftdmchan->sockfd = ZT_INVALID_SOCKET;
1249
1250 return FTDM_SUCCESS;
1251 }
1252
1253
1254
1255
1256 static ftdm_io_interface_t zt_interface;
1257
1258
1259
1260
1261
1262
1263 static FIO_IO_LOAD_FUNCTION(zt_init)
1264 {
1265 assert(fio != NULL);
1266 struct stat statbuf;
1267 memset(&zt_interface, 0, sizeof(zt_interface));
1268 memset(&zt_globals, 0, sizeof(zt_globals));
1269
1270 if (!stat(zt_ctlpath, &statbuf)) {
1271 ftdm_log(FTDM_LOG_NOTICE, "Using Zaptel control device\n");
1272 ctlpath = zt_ctlpath;
1273 chanpath = zt_chanpath;
1274 memcpy(&codes, &zt_ioctl_codes, sizeof(codes));
1275 } else if (!stat(dahdi_ctlpath, &statbuf)) {
1276 ftdm_log(FTDM_LOG_NOTICE, "Using DAHDI control device\n");
1277 ctlpath = dahdi_ctlpath;
1278 chanpath = dahdi_chanpath;
1279 memcpy(&codes, &dahdi_ioctl_codes, sizeof(codes));
1280 } else {
1281 ftdm_log(FTDM_LOG_ERROR, "No DAHDI or Zap control device found in /dev/\n");
1282 return FTDM_FAIL;
1283 }
1284 if ((CONTROL_FD = open(ctlpath, O_RDWR)) < 0) {
1285 ftdm_log(FTDM_LOG_ERROR, "Cannot open control device %s: %s\n", ctlpath, strerror(errno));
1286 return FTDM_FAIL;
1287 }
1288
1289 zt_globals.codec_ms = 20;
1290 zt_globals.wink_ms = 150;
1291 zt_globals.flash_ms = 750;
1292 zt_globals.eclevel = 0;
1293 zt_globals.etlevel = 0;
1294
1295 zt_interface.name = "zt";
1296 zt_interface.configure = zt_configure;
1297 zt_interface.configure_span = zt_configure_span;
1298 zt_interface.open = zt_open;
1299 zt_interface.close = zt_close;
1300 zt_interface.command = zt_command;
1301 zt_interface.wait = zt_wait;
1302 zt_interface.read = zt_read;
1303 zt_interface.write = zt_write;
1304 zt_interface.poll_event = zt_poll_event;
1305 zt_interface.next_event = zt_next_event;
1306 zt_interface.channel_destroy = zt_channel_destroy;
1307 zt_interface.get_alarms = zt_get_alarms;
1308 *fio = &zt_interface;
1309
1310 return FTDM_SUCCESS;
1311 }
1312
1313
1314
1315
1316
1317 static FIO_IO_UNLOAD_FUNCTION(zt_destroy)
1318 {
1319 close(CONTROL_FD);
1320 memset(&zt_interface, 0, sizeof(zt_interface));
1321 return FTDM_SUCCESS;
1322 }
1323
1324
1325
1326
1327 ftdm_module_t ftdm_module = {
1328 "zt",
1329 zt_init,
1330 zt_destroy,
1331 };
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342