6487a4ddb51167b72bd28c3d84d29bc8ed7c2ca7
[asterisk/asterisk.git] / apps / app_dahdibarge.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Special thanks to comphealth.com for sponsoring this
9  * GPL application.
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief DAHDI Barge support
25  *
26  * \author Mark Spencer <markster@digium.com>
27  *
28  * \note Special thanks to comphealth.com for sponsoring this
29  * GPL application.
30  * 
31  * \ingroup applications
32  */
33
34 /*** MODULEINFO
35         <depend>dahdi</depend>
36  ***/
37
38 #include "asterisk.h"
39
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41
42 #include <dahdi/user.h>
43
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/module.h"
49 #include "asterisk/config.h"
50 #include "asterisk/app.h"
51 #include "asterisk/cli.h"
52 #include "asterisk/say.h"
53 #include "asterisk/utils.h"
54
55 static char *app = "DAHDIBarge";
56 static char *deprecated_app = "ZapBarge";
57
58 static char *synopsis = "Barge in (monitor) DAHDI channel";
59
60 static char *descrip = 
61 "  DAHDIBarge([channel]): Barges in on a specified DAHDI\n"
62 "channel or prompts if one is not specified.  Returns\n"
63 "-1 when caller user hangs up and is independent of the\n"
64 "state of the channel being monitored.";
65
66
67 #define CONF_SIZE 160
68
69 static int careful_write(int fd, unsigned char *data, int len)
70 {
71         int res;
72         while(len) {
73                 res = write(fd, data, len);
74                 if (res < 1) {
75                         if (errno != EAGAIN) {
76                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
77                                 return -1;
78                         } else
79                                 return 0;
80                 }
81                 len -= res;
82                 data += res;
83         }
84         return 0;
85 }
86
87 static int conf_run(struct ast_channel *chan, int confno, int confflags)
88 {
89         int fd;
90         struct dahdi_confinfo dahdic;
91         struct ast_frame *f;
92         struct ast_channel *c;
93         struct ast_frame fr;
94         int outfd;
95         int ms;
96         int nfds;
97         int res;
98         int flags;
99         int retrydahdi;
100         int origfd;
101         int ret = -1;
102
103         DAHDI_BUFFERINFO bi;
104         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
105         char *buf = __buf + AST_FRIENDLY_OFFSET;
106
107         /* Set it into U-law mode (write) */
108         if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
109                 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
110                 goto outrun;
111         }
112
113         /* Set it into U-law mode (read) */
114         if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
115                 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
116                 goto outrun;
117         }
118         ast_indicate(chan, -1);
119         retrydahdi = strcasecmp(chan->tech->type, "DAHDI");
120 dahdiretry:
121         origfd = chan->fds[0];
122         if (retrydahdi) {
123                 fd = open("/dev/dahdi/pseudo", O_RDWR);
124                 if (fd < 0) {
125                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
126                         goto outrun;
127                 }
128                 /* Make non-blocking */
129                 flags = fcntl(fd, F_GETFL);
130                 if (flags < 0) {
131                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
132                         close(fd);
133                         goto outrun;
134                 }
135                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
136                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
137                         close(fd);
138                         goto outrun;
139                 }
140                 /* Setup buffering information */
141                 memset(&bi, 0, sizeof(bi));
142                 bi.bufsize = CONF_SIZE;
143                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
144                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
145                 bi.numbufs = 4;
146                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
147                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
148                         close(fd);
149                         goto outrun;
150                 }
151                 nfds = 1;
152         } else {
153                 /* XXX Make sure we're not running on a pseudo channel XXX */
154                 fd = chan->fds[0];
155                 nfds = 0;
156         }
157         memset(&dahdic, 0, sizeof(dahdic));
158         /* Check to see if we're in a conference... */
159         dahdic.chan = 0;        
160         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
161                 ast_log(LOG_WARNING, "Error getting conference\n");
162                 close(fd);
163                 goto outrun;
164         }
165         if (dahdic.confmode) {
166                 /* Whoa, already in a conference...  Retry... */
167                 if (!retrydahdi) {
168                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
169                         retrydahdi = 1;
170                         goto dahdiretry;
171                 }
172         }
173         memset(&dahdic, 0, sizeof(dahdic));
174         /* Add us to the conference */
175         dahdic.chan = 0;        
176         dahdic.confno = confno;
177         dahdic.confmode = DAHDI_CONF_MONITORBOTH;
178
179         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
180                 ast_log(LOG_WARNING, "Error setting conference\n");
181                 close(fd);
182                 goto outrun;
183         }
184         ast_debug(1, "Placed channel %s in DAHDI channel %d monitor\n", chan->name, confno);
185
186         for(;;) {
187                 outfd = -1;
188                 ms = -1;
189                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
190                 if (c) {
191                         if (c->fds[0] != origfd) {
192                                 if (retrydahdi) {
193                                         /* Kill old pseudo */
194                                         close(fd);
195                                 }
196                                 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
197                                 retrydahdi = 0;
198                                 goto dahdiretry;
199                         }
200                         f = ast_read(c);
201                         if (!f) 
202                                 break;
203                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
204                                 ret = 0;
205                                 ast_frfree(f);
206                                 break;
207                         } else if (fd != chan->fds[0]) {
208                                 if (f->frametype == AST_FRAME_VOICE) {
209                                         if (f->subclass == AST_FORMAT_ULAW) {
210                                                 /* Carefully write */
211                                                 careful_write(fd, f->data.ptr, f->datalen);
212                                         } else
213                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
214                                 }
215                         }
216                         ast_frfree(f);
217                 } else if (outfd > -1) {
218                         res = read(outfd, buf, CONF_SIZE);
219                         if (res > 0) {
220                                 memset(&fr, 0, sizeof(fr));
221                                 fr.frametype = AST_FRAME_VOICE;
222                                 fr.subclass = AST_FORMAT_ULAW;
223                                 fr.datalen = res;
224                                 fr.samples = res;
225                                 fr.data.ptr = buf;
226                                 fr.offset = AST_FRIENDLY_OFFSET;
227                                 if (ast_write(chan, &fr) < 0) {
228                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
229                                         /* break; */
230                                 }
231                         } else 
232                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
233                 }
234         }
235         if (fd != chan->fds[0])
236                 close(fd);
237         else {
238                 /* Take out of conference */
239                 /* Add us to the conference */
240                 dahdic.chan = 0;        
241                 dahdic.confno = 0;
242                 dahdic.confmode = 0;
243                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
244                         ast_log(LOG_WARNING, "Error setting conference\n");
245                 }
246         }
247
248 outrun:
249
250         return ret;
251 }
252
253 static int conf_exec(struct ast_channel *chan, void *data)
254 {
255         int res=-1;
256         int retrycnt = 0;
257         int confflags = 0;
258         int confno = 0;
259         char confstr[80] = "";
260         
261         if (!ast_strlen_zero(data)) {
262                 if ((sscanf(data, "DAHDI/%d", &confno) != 1) &&
263                     (sscanf(data, "%d", &confno) != 1)) {
264                         ast_log(LOG_WARNING, "DAHDIBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
265                         return 0;
266                 }
267         }
268         
269         if (chan->_state != AST_STATE_UP)
270                 ast_answer(chan);
271
272         while(!confno && (++retrycnt < 4)) {
273                 /* Prompt user for conference number */
274                 confstr[0] = '\0';
275                 res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
276                 if (res <0) goto out;
277                 if (sscanf(confstr, "%d", &confno) != 1)
278                         confno = 0;
279         }
280         if (confno) {
281                 /* XXX Should prompt user for pin if pin is required XXX */
282                 /* Run the conference */
283                 res = conf_run(chan, confno, confflags);
284         }
285 out:
286         /* Do the conference */
287         return res;
288 }
289
290 static int conf_exec_warn(struct ast_channel *chan, void *data)
291 {
292         ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", deprecated_app, app);
293         return conf_exec(chan, data);
294 }
295
296 static int unload_module(void)
297 {
298         return ast_unregister_application(app);
299 }
300
301 static int load_module(void)
302 {
303         ast_register_application(deprecated_app, conf_exec_warn, synopsis, descrip);
304         return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
305 }
306
307 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on DAHDI channel application");