Review Board 1.7.16


Add app_v110 to accept v.110 data calls

Review Request #1394 - Created Aug. 26, 2011 and updated

dwmw2
ASTERISK-14185
Reviewers
asterisk-dev
Asterisk
This adds a new application app_v110 which accepts data calls from a GSM mobile phone.
Asterisk 1.8.5.0+app_v110, ISDN BRI on British Telecom, Nokia N97 GSM phone...

AT+CBST=71,0,1
OK
ATD01799xxxxxx
CONNECT
Fedora release 15 (Lovelock)
Kernel 2.6.40.3-0.fc15.i686 on an i686 (/dev/pts/34)

obelisk.infradead.org login: dwmw2
Password: 
Last login: Sat Aug 27 01:02:52 2011 from 07976xxxxxx
[dwmw2@obelisk ~]$ 

Diff revision 1 (Latest)

  1. /trunk/apps/app_v110.c: Loading...
  2. /trunk/channels/chan_dahdi.c: Loading...
/trunk/apps/app_v110.c
New File

    
   
1
/*

    
   
2
 * Asterisk -- An open source telephony toolkit.

    
   
3
 *

    
   
4
 * Copyright (c) 2005-2011, David Woodhouse

    
   
5
 *

    
   
6
 * David Woodhouse <dwmw2@infradead.org>

    
   
7
 * Generic app execution and other stuff by Moises Silva <moises.silva@gmail.com>

    
   
8
 *

    
   
9
 * See http://www.asterisk.org for more information about

    
   
10
 * the Asterisk project. Please do not directly contact

    
   
11
 * any of the maintainers of this project for assistance;

    
   
12
 * the project provides a web site, mailing lists and IRC

    
   
13
 * channels for your use.

    
   
14
 *

    
   
15
 * This program is free software, distributed under the terms of

    
   
16
 * the GNU General Public License Version 2. See the LICENSE file

    
   
17
 * at the top of the source tree.

    
   
18
 */

    
   
19

   

    
   
20
/*! \file

    
   
21
 *

    
   
22
 * \brief V.110 application -- accept incoming v.110 call and spawn a custom application

    
   
23
 *

    
   
24
 * \author\verbatim David Woodhouse <dwmw2@infradead.org> \endverbatim

    
   
25
 *

    
   
26
 * \ingroup applications

    
   
27
 */

    
   
28

   

    
   
29
/*** MODULEINFO

    
   
30
	<defaultenabled>no</defaultenabled>

    
   
31
 ***/

    
   
32

   

    
   
33
#include "asterisk.h"

    
   
34

   

    
   
35
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")

    
   
36

   

    
   
37
#include "asterisk/file.h"

    
   
38
#include "asterisk/channel.h"

    
   
39
#include "asterisk/pbx.h"

    
   
40
#include "asterisk/module.h"

    
   
41
#include "asterisk/lock.h"

    
   
42
#include "asterisk/logger.h"

    
   
43
#include "asterisk/app.h"

    
   
44
#include "asterisk/transcap.h"

    
   
45

   

    
   
46
#include <sys/types.h>

    
   
47
#include <sys/wait.h>

    
   
48

   

    
   
49
/*** DOCUMENTATION

    
   
50
	<application name="V110" language="en_US">

    
   
51
		<synopsis>

    
   
52
			Accepts incoming V.110 data calls and spawns agetty or a custom command in a pseudo-tty.

    
   
53
		</synopsis>

    
   
54
		<syntax>

    
   
55
			<parameter name="command" required="false">

    
   
56
				<para>The name of the command to be run. If unspecified, <filename>/sbin/agetty</filename> will be used with suitable arguments.</para>

    
   
57
			</parameter>

    
   
58
			<parameter name="args..." requires="false">

    
   
59
				<para>Optional arguments to the login command, comma-separated</para>

    
   
60
			</parameter>

    
   
61
		</syntax>

    
   
62
		<description>

    
   
63
			<para>V110 handles incoming data calls. It creates a pseudo-tty and relays data

    
   
64
			from the incoming call to a child process which it spawns on the other end of

    
   
65
			the pseudo-tty.</para>

    
   
66

   

    
   
67
			<para>If invoked with no arguments, it will spawn a standard 'getty' process

    
   
68
			to handle a local login. If a command is specified, that command is run with

    
   
69
			its stdin, stderr and stdout directed to the pseudo-tty device.</para>

    
   
70

   

    
   
71
			<para>To dial in to this with a GSM mobile phone, issue the command

    
   
72
			<code>AT+CBST=71,0,1</code> to your phone before the standard <code>ATD</code>

    
   
73
			dial command.</para>

    
   
74
			<para>If used on an incoming call which is not a digital data call, the

    
   
75
			application will return success in order to allow control to pass to the

    
   
76
			next priority in the dialplan. If a call has been handled and has terminated,

    
   
77
			this application will hangup the call before returning</para>

    
   
78
		</description>

    
   
79
	</application>

    
   
80
 ***/

    
   
81

   

    
   
82

   

    
   
83
/*

    
   
84
  Testing mode: Send back exactly the same frames we received from the peer. On

    
   
85
  overloaded machines we *still* get the 'UUUUUUUUU' corruption sometimes, which

    
   
86
  seems to happen when Asterisk doesn't keep the TX channel full so it ends up

    
   
87
  being zeroes instead of empty V.110 frames. Would be nice if we could register

    
   
88
  a frame in advance with 'if you have nothing better to send, send this'.

    
   
89
*/

    
   
90
//#define V110_LOOPBACK

    
   
91

   

    
   
92
/*

    
   
93
  The generator does work, but makes us more susceptible to 'UUUUUUUUU' corruption.

    
   
94
*/

    
   
95
//#define USE_GENERATOR

    
   
96

   

    
   
97

   

    
   
98

   

    
   
99
static char *app = "V110";

    
   
100

   

    
   
101
#define URATE_EBITS	0 /* In E-bits or negotiated in-band */

    
   
102
#define URATE_600	1

    
   
103
#define URATE_1200	2

    
   
104
#define URATE_2400	3

    
   
105
#define URATE_3600	4

    
   
106
#define URATE_4800	5

    
   
107
#define URATE_7200	6

    
   
108
#define URATE_8000	7

    
   
109
#define URATE_9600	8

    
   
110
#define URATE_14400	9	/* isdn4linux abuses this for 38400 */

    
   
111
#define URATE_16000	10

    
   
112
#define URATE_19200	11

    
   
113
#define URATE_32000	12

    
   
114

   

    
   
115
#define URATE_48000	14

    
   
116
#define URATE_56000	15

    
   
117
#define URATE_135	21 /* 134.5 */

    
   
118
#define URATE_100	22

    
   
119
#define URATE_75_1200	23 /* 75 forward, 1200 back */

    
   
120
#define URATE_1200_75	24 /* 1200 forward, 75 back */

    
   
121
#define URATE_50	25

    
   
122
#define URATE_75	26

    
   
123
#define URATE_110	27

    
   
124
#define URATE_150	28

    
   
125
#define URATE_200	29

    
   
126
#define URATE_300	30

    
   
127
#define URATE_12000	31

    
   
128

   

    
   
129
static char *rates[] = {

    
   
130
	[URATE_600] = "600",

    
   
131
	[URATE_1200] = "1200",

    
   
132
	[URATE_2400] = "2400",

    
   
133
	[URATE_3600] = "3600",

    
   
134
	[URATE_4800] = "4800",

    
   
135
	[URATE_7200] = "7200",

    
   
136
	[URATE_8000] = "8000",

    
   
137
	[URATE_9600] = "9600",

    
   
138
	[URATE_14400] = "14400",

    
   
139
	[URATE_16000] = "16000",

    
   
140
	[URATE_19200] = "19200",

    
   
141
	[URATE_32000] = "32000",

    
   
142
	[URATE_48000] = "48000",

    
   
143
	[URATE_56000] = "56000",

    
   
144
	[URATE_135] = "135",

    
   
145
	[URATE_100] = "100",

    
   
146
	[URATE_75_1200] = "75",

    
   
147
	[URATE_1200_75] = "1200",

    
   
148
	[URATE_50] = "50",

    
   
149
	[URATE_75] = "75",

    
   
150
	[URATE_110] = "110",

    
   
151
	[URATE_150] = "150",

    
   
152
	[URATE_200] = "200",

    
   
153
	[URATE_300] = "300",

    
   
154
	[URATE_12000] = "12000",

    
   
155
};

    
   
156

   

    
   
157
#define IBUF_LEN 8192

    
   
158
#define OBUF_LEN 1024

    
   
159
#define OBUF_THRESH 16

    
   
160

   

    
   
161
struct v110_state {

    
   
162
	/* Input v.110 frame buffer */

    
   
163
	unsigned char vframe_in[10];

    
   
164
	unsigned vframe_in_len;

    
   
165

   

    
   
166
	/* Input data buffer */

    
   
167
	unsigned char ibuf[IBUF_LEN];

    
   
168
	unsigned ibufend;

    
   
169
	unsigned ibufstart;

    
   
170
	unsigned nextibit;

    
   
171

   

    
   
172
	/* Output data buffer */

    
   
173
	unsigned char obuf[OBUF_LEN];

    
   
174
	unsigned obufend;

    
   
175
	unsigned obufstart;

    
   
176
	unsigned nextobit;

    
   
177

   

    
   
178
	/* Output v.110 frame buffer */

    
   
179
	unsigned nextoline;

    
   
180
	unsigned last_frame_empty;

    
   
181

   

    
   
182
	int bufwarning;

    
   
183
	unsigned char cts, rts, sbit;

    
   
184
	int synccount;

    
   
185
	int eofcount;

    
   
186

   

    
   
187
	/* frame data */

    
   
188
	struct ast_frame f;

    
   
189
	unsigned char friendly[AST_FRIENDLY_OFFSET];

    
   
190
	unsigned char fdata[4096];

    
   
191

   

    
   
192
	int urate;

    
   
193
	/* hooks to call depending on the rate */

    
   
194
	void (*input_frame)(struct v110_state *, struct ast_frame *);

    
   
195
	void (*fill_outframe)(struct v110_state *, int);

    
   
196
};

    
   
197

   

    
   
198
void v110_input_frame_x4(struct v110_state *vs, struct ast_frame *);

    
   
199
void v110_input_frame_x2(struct v110_state *vs, struct ast_frame *);

    
   
200
void v110_input_frame_x1(struct v110_state *vs, struct ast_frame *);

    
   
201
void v110_fill_outframe_x4(struct v110_state *vs, int);

    
   
202
void v110_fill_outframe_x2(struct v110_state *vs, int);

    
   
203
void v110_fill_outframe_x1(struct v110_state *vs, int);

    
   
204

   

    
   
205
static pid_t loginpty(struct v110_state *vs, struct ast_channel *chan, const char *data, int *ptyfd);

    
   
206

   

    
   
207
/* Helper for reversing bit-order of data bytes. */

    
   
208
static const unsigned char bit_reverse_table[256] =

    
   
209
{

    
   
210
	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,

    
   
211
	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,

    
   
212
	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,

    
   
213
	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,

    
   
214
	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,

    
   
215
	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,

    
   
216
	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,

    
   
217
	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,

    
   
218
	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,

    
   
219
	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,

    
   
220
	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,

    
   
221
	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,

    
   
222
	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,

    
   
223
	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,

    
   
224
	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,

    
   
225
	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,

    
   
226
	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,

    
   
227
	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,

    
   
228
	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,

    
   
229
	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,

    
   
230
	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,

    
   
231
	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,

    
   
232
	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,

    
   
233
	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,

    
   
234
	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,

    
   
235
	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,

    
   
236
	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,

    
   
237
	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,

    
   
238
	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,

    
   
239
	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,

    
   
240
	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,

    
   
241
	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff

    
   
242
};

    
   
243

   

    
   
244
static int v110_generate(struct ast_channel *chan, void *data, int len, int samples)

    
   
245
{

    
   
246
	struct v110_state *vs = data;

    
   
247
	int want = samples;

    
   
248
	int res = 0;

    
   
249

   

    
   
250
	while (want > 4000) {

    
   
251
		vs->last_frame_empty = 0;

    
   
252
		vs->fill_outframe(vs, 4000);

    
   
253
		if (ast_write(chan, &vs->f)) {

    
   
254
			res = -1;

    
   
255
			goto out;

    
   
256
		}

    
   
257
		want -= 4000;

    
   
258
	}

    
   
259

   

    
   
260
	/* If the previous Asterisk frame contained only full

    
   
261
	   V.110 frames, and all of them were empty. And if we

    
   
262
	   have no data to send this time either, and want an

    
   
263
	   Asterisk frame of the same size... just re-use the

    
   
264
	   previous one in vs->f. */

    
   
265
	if (vs->nextoline == 10 && !vs->sbit && !vs->nextobit &&

    
   
266
	    vs->obufstart == vs->obufend) {

    
   
267
		if (vs->last_frame_empty && want == vs->f.datalen)

    
   
268
			goto write_frame;

    
   
269

   

    
   
270
		/* Perhaps next time... */

    
   
271
		vs->last_frame_empty = 1;

    
   
272
	} else {

    
   
273
		/* Not starting at the beginning of a V.110 frame. We

    
   
274
		   definitely can't re-use this Asterisk frame */

    
   
275
		vs->last_frame_empty = 0;

    
   
276
	}

    
   
277
	vs->fill_outframe(vs, want);

    
   
278

   

    
   
279
 write_frame:

    
   
280
	if (ast_write(chan, &vs->f))

    
   
281
		res = -1;

    
   
282
 out:

    
   
283
	return res;

    
   
284
}

    
   
285

   

    
   
286
#ifdef V110_LOOPBACK

    
   
287
#undef USE_GENERATOR

    
   
288
#endif

    
   
289

   

    
   
290
#ifdef USE_GENERATOR

    
   
291
static void *v110_gen_alloc(struct ast_channel *chan, void *data)

    
   
292
{

    
   
293
	return data;

    
   
294
}

    
   
295

   

    
   
296
static void v110_gen_release(struct ast_channel *chan, void *data)

    
   
297
{

    
   
298
}

    
   
299

   

    
   
300
static struct ast_generator v110_gen = {

    
   
301
	.alloc = v110_gen_alloc,

    
   
302
	.release = v110_gen_release,

    
   
303
	.generate = v110_generate

    
   
304
};

    
   
305
#endif

    
   
306

   

    
   
307
static int v110_exec(struct ast_channel *chan, const char *data)

    
   
308
{

    
   
309
	int res = -1;

    
   
310
	int apppid = -1;

    
   
311
	int appstatus = 0;

    
   
312
	int urate = -1;

    
   
313
	int primelen = 0;

    
   
314
	const char *urate_var = NULL;

    
   
315
	struct ast_frame *f = NULL;

    
   
316
	struct v110_state *vs = NULL;

    
   
317
	int ptyfd = -1;

    
   
318

   

    
   
319
	if (!(chan->transfercapability & AST_TRANS_CAP_DIGITAL)) {

    
   
320
	  ast_verb(3, "Not an async V.110 call; capabilities = [%d]\n", chan->transfercapability);

    
   
321
		/* Return success to allow the dialplan to move on to the next priority */

    
   
322
		return 0;

    
   
323
	}

    
   
324

   

    
   
325
	/* mISDN puts it into MISDN_URATE variable. Dahdi doesn't seem to tell us at all. */

    
   
326
	urate_var = pbx_builtin_getvar_helper(chan, "MISDN_URATE");

    
   
327
	if (urate_var) {

    
   
328
		urate = atoi(urate_var);

    
   
329
	} else {

    
   
330
		urate_var = pbx_builtin_getvar_helper(chan, "ISDN_V110_URATE");

    
   
331
		if (urate_var)

    
   
332
		urate = atoi(urate_var);

    
   
333
	}

    
   
334
	if (urate == -1) {

    
   
335
		ast_log(LOG_NOTICE, "V.110 assuming 9600 rate, use ISDN_V110_URATE variable to override\n");

    
   
336
		urate = URATE_9600;

    
   
337
	}

    
   
338

   

    
   
339
	vs = ast_calloc(1, sizeof(*vs));

    
   
340
	if (!vs) {

    
   
341
		ast_log(LOG_ERROR, "Allocation of V.110 data structure failed\n");

    
   
342
		res = -1;

    
   
343
		goto out;

    
   
344
	}

    
   
345

   

    
   
346
	if (!strcasecmp(chan->tech->type, "mISDN")) {

    
   
347
		/* why isn't transfercapability digital enough for mISDN channel driver?? */

    
   
348
		pbx_builtin_setvar_helper(chan, "MISDN_DIGITAL_TRANS", "1");

    
   
349
	}

    
   
350
	/* we could restrict usage to DAHDI or mISDN channels, but, the truth is that this application only cares about

    
   
351
	   the provided V.110 bit stream, if some other channel can provide that, good for them */

    
   
352

   

    
   
353
	/* TODO: Waste bits between characters instead of relying on flow control */

    
   
354
	switch (urate) {

    
   
355
	case URATE_1200:

    
   
356
	case URATE_2400:

    
   
357
	case URATE_4800:

    
   
358
	case URATE_7200:

    
   
359
	case URATE_8000:

    
   
360
	case URATE_9600:

    
   
361
		vs->input_frame = v110_input_frame_x4;

    
   
362
		vs->fill_outframe = v110_fill_outframe_x4;

    
   
363
		primelen = 200;

    
   
364
		break;

    
   
365

   

    
   
366
	case URATE_12000:

    
   
367
	case URATE_16000:

    
   
368
	case URATE_19200:

    
   
369
		vs->input_frame = v110_input_frame_x2;

    
   
370
		vs->fill_outframe = v110_fill_outframe_x2;

    
   
371
		primelen = 200;

    
   
372
		break;

    
   
373

   

    
   
374
	case URATE_14400: /* NB. isdn4linux 38400 */

    
   
375
	case URATE_32000:

    
   
376
		vs->input_frame = v110_input_frame_x1;

    
   
377
		vs->fill_outframe = v110_fill_outframe_x1;

    
   
378
		primelen = 200;

    
   
379
		break;

    
   
380

   

    
   
381
	default:

    
   
382
		ast_log(LOG_NOTICE, "V.110 call at rate %d not supported\n", urate);

    
   
383
		goto out;

    
   
384
	}

    
   
385

   

    
   
386
	vs->urate = urate;

    
   
387
	vs->nextoline = 10;

    
   
388
	vs->rts = 1;

    
   
389
	vs->sbit = 1;

    
   
390
	vs->cts = 1;

    
   
391
	vs->synccount = 10;

    
   
392
	vs->bufwarning = 5;

    
   
393
	vs->f.data.ptr = vs->fdata;

    
   
394
	vs->f.offset = AST_FRIENDLY_OFFSET;

    
   
395
	vs->f.subclass.codec = chan->readformat;

    
   
396
	vs->f.frametype = AST_FRAME_VOICE;

    
   
397

   

    
   
398
	apppid = loginpty(vs, chan, data, &ptyfd);

    
   
399
	if (apppid == -1) {

    
   
400
		goto out;

    
   
401
	}

    
   
402

   

    
   
403
	ast_verb(3, "Accepting V.110 call\n");

    
   
404
	if (chan->_state != AST_STATE_UP) {

    
   
405
		ast_answer(chan);

    
   
406
	}

    
   
407

   

    
   
408
	v110_generate(chan, vs, 0, primelen);

    
   
409

   

    
   
410
#ifdef USE_GENERATOR

    
   
411
	res = ast_activate_generator(chan, &v110_gen, vs);

    
   
412
	if (res) {

    
   
413
		ast_log(LOG_ERROR, "Failed to activate generator on '%s'\n", chan->name);

    
   
414
		goto out;

    
   
415
	}

    
   
416
#endif

    
   
417
	ast_set_priority(1);

    
   
418

   

    
   
419
	res = 0;

    
   
420
	while (ast_waitfor(chan, -1) > -1) {

    
   
421
		int r, want;

    
   
422

   

    
   
423
		f = ast_read(chan);

    
   
424
		if (!f) {

    
   
425
			break;

    
   
426
		}

    
   
427

   

    
   
428
		if (f->frametype != AST_FRAME_VOICE) {

    
   
429
			ast_frfree(f);

    
   
430
			continue;

    
   
431
		}

    
   
432

   

    
   
433
		f->delivery.tv_sec = 0;

    
   
434
		f->delivery.tv_usec = 0;

    
   
435

   

    
   
436
		vs->input_frame(vs, f);

    
   
437

   

    
   
438
#ifndef V110_LOOPBACK

    
   
439
		/* normal operation */

    
   
440
#ifndef USE_GENERATOR

    
   
441
		if (v110_generate(chan, vs, 0, f->datalen)) {

    
   
442
			res = -1;

    
   
443
			break;

    
   
444
		}

    
   
445
#endif /* GENERATOR */

    
   
446

   

    
   
447
#else

    
   
448
		/* loopback mode for testing */

    
   
449
		if (0 && f->datalen >= 8)

    
   
450
			ast_log(LOG_NOTICE, "Received frame: %02x %02x %02x %02x %02x %02x %02x %02x\n",

    
   
451
				((unsigned char *)f->data.ptr)[0],

    
   
452
				((unsigned char *)f->data.ptr)[1],

    
   
453
				((unsigned char *)f->data.ptr)[2],

    
   
454
				((unsigned char *)f->data.ptr)[3],

    
   
455
				((unsigned char *)f->data.ptr)[4],

    
   
456
				((unsigned char *)f->data.ptr)[5],

    
   
457
				((unsigned char *)f->data.ptr)[6],

    
   
458
				((unsigned char *)f->data.ptr)[7]);

    
   
459
		if (ast_write(chan, f)) {

    
   
460
			res = -1;

    
   
461
			break;

    
   
462
		}

    
   
463
#endif

    
   
464

   

    
   
465
		/* Flush v.110 incoming buffer */

    
   
466
		if (vs->ibufend > vs->ibufstart) {

    
   
467
			want = vs->ibufend - vs->ibufstart;

    
   
468
		} else if (vs->ibufend < vs->ibufstart) {

    
   
469
			want = IBUF_LEN - vs->ibufstart;

    
   
470
		} else {

    
   
471
			want = 0;

    
   
472
		}

    
   
473
		if (want) {

    
   
474
			r = write(ptyfd, &vs->ibuf[vs->ibufstart], want);

    
   
475
			/* Ignore EPIPE too; we'll detect EOF on input */

    
   
476
			if (r < 0 && (errno == EAGAIN || errno == EPIPE)) {

    
   
477
				r = 0;

    
   
478
			}

    
   
479
			if (r < 0) {

    
   
480
				ast_log(LOG_DEBUG, "Error writing to pty: %s\n", strerror(errno));

    
   
481
				res = -1;

    
   
482
				goto out;

    
   
483
			}

    
   
484
			vs->ibufstart += r;

    
   
485
			if (vs->ibufstart == IBUF_LEN) {

    
   
486
				vs->ibufstart = 0;

    
   
487
			}

    
   
488

   

    
   
489
			/* Set flow control state. */

    
   
490
			if (r < want) {

    
   
491
				vs->rts = 1;

    
   
492
			} else {

    
   
493
				vs->rts = 0;

    
   
494
			}

    
   
495
		}

    
   
496

   

    
   
497
		/* Replenish v.110 outgoing buffer */

    
   
498
		if (vs->obufend >= vs->obufstart) {

    
   
499
			if (vs->obufend - vs->obufstart < OBUF_THRESH) {

    
   
500
				want = OBUF_LEN - vs->obufend - !vs->obufstart;

    
   
501
			} else {

    
   
502
				want = 0;

    
   
503
			}

    
   
504
		} else {

    
   
505
			if (vs->obufstart + OBUF_LEN - vs->obufend < OBUF_THRESH) {

    
   
506
				want = vs->obufstart - vs->obufend - 1;

    
   
507
			} else {

    
   
508
				want = 0;

    
   
509
			}

    
   
510
		}

    
   
511
		if (want) {

    
   
512
			r = read(ptyfd, &vs->obuf[vs->obufend], want);

    
   
513
			if (r < 0) {

    
   
514
				/* Don't abort on read error, until we've flushed data */

    
   
515
				if (errno == EAGAIN || vs->obufstart != vs->obufend) {

    
   
516
					r = 0;

    
   
517
				} else {

    
   
518
					/* It's expected that we get error when the other end closes the pipe or pty */

    
   
519
					ast_log(LOG_DEBUG, "Error reading from pty: %s\n", strerror(errno));

    
   
520
					res = -1;

    
   
521
					goto out;

    
   
522
				}

    
   
523
			}

    
   
524
			vs->obufend += r;

    
   
525
			if (vs->obufend == OBUF_LEN) {

    
   
526
				vs->obufend = 0;

    
   
527
			}

    
   
528
		}

    
   
529
	}

    
   
530
out:

    
   
531
	ast_log(LOG_DEBUG, "Exiting app_v110\n");

    
   
532

   

    
   
533
#ifdef USE_GENERATOR

    
   
534
	ast_deactivate_generator(chan);

    
   
535
#endif

    
   
536

   

    
   
537
	vs->sbit = 1;

    
   
538
	v110_generate(chan, vs, 0, primelen);

    
   
539

   

    
   
540
	/* free structures */

    
   
541
	if (vs) {

    
   
542
		ast_free(vs);

    
   
543
	}

    
   
544
	if (f) {

    
   
545
		ast_frfree(f);

    
   
546
	}

    
   
547

   

    
   
548
	ast_softhangup (chan, AST_SOFTHANGUP_SHUTDOWN);

    
   
549

   

    
   
550
	/* cleanup custom app */

    
   
551
	if (apppid > -1) {

    
   
552
		close(ptyfd);

    
   
553
		ast_log(LOG_DEBUG, "Reaping child pid %d\n", apppid);

    
   
554
		kill(apppid, SIGTERM);

    
   
555
		waitpid(apppid, &appstatus, 0);

    
   
556
		ast_log(LOG_DEBUG, "Child status = %d\n", WEXITSTATUS(appstatus));

    
   
557
	}

    
   
558

   

    
   
559
	return res;

    
   
560
}

    
   
561

   

    
   
562
static void v110_process_frame(struct v110_state *vs)

    
   
563
{

    
   
564
	int octet;

    
   
565

   

    
   
566
	if (0) ast_log(LOG_NOTICE, "frame %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",

    
   
567
		vs->vframe_in[0], vs->vframe_in[1], vs->vframe_in[2],

    
   
568
		vs->vframe_in[3], vs->vframe_in[4], vs->vframe_in[5],

    
   
569
		vs->vframe_in[6], vs->vframe_in[7], vs->vframe_in[8],

    
   
570
		vs->vframe_in[9]);

    
   
571

   

    
   
572
	/* Check that line 5 (E-bits) starts '1011' (reversed). */

    
   
573
	if ((vs->vframe_in[5] & 0xf0) != 0xb0)

    
   
574
		return;

    
   
575

   

    
   
576
	/* Check that each other octet starts with '1' */

    
   
577
	if (!(vs->vframe_in[1] & vs->vframe_in[2] & vs->vframe_in[3] &

    
   
578
	      vs->vframe_in[4] & vs->vframe_in[6] & vs->vframe_in[7] &

    
   
579
	      vs->vframe_in[8] & vs->vframe_in[9] & 0x80))

    
   
580
		return;

    
   
581

   

    
   
582
	/* Extract flow control signal from last octet */

    
   
583
	if (vs->synccount) {

    
   
584
		if (!--vs->synccount) {

    
   
585
			ast_verb(3, "V.110 synchronisation achieved\n");

    
   
586
			vs->sbit = 0;

    
   
587
			vs->rts = 0;

    
   
588
		}

    
   
589
	} else

    
   
590
		vs->cts = vs->vframe_in[7] & 1;

    
   
591

   

    
   
592
	for (octet = 1; octet < 10; octet++) {

    
   
593
		unsigned char tmp;

    
   
594

   

    
   
595
		/* Skip E-bits in line 5 */

    
   
596
		if (octet == 5)

    
   
597
			continue;

    
   
598

   

    
   
599
		tmp = bit_reverse_table[vs->vframe_in[octet]] & 0x7e;

    
   
600

   

    
   
601
		/* Search for start bit if not yet found */

    
   
602
		if (!vs->nextibit) {

    
   
603

   

    
   
604
			/* First check for no zero bits. This will be common */

    
   
605
			if (tmp == 0x7e)

    
   
606
				continue;

    
   
607

   

    
   
608
			/* Check for start bit being last in the octet */

    
   
609
			if (tmp == 0x3e) {

    
   
610
				vs->nextibit = 1; /* Expecting first data bit now */

    
   
611
				vs->ibuf[vs->ibufend] = 0;

    
   
612
				continue;

    
   
613
			}

    
   
614

   

    
   
615
			/* Scan for the start bit, copy the data bits (of which

    
   
616
			   there will be at least one) into the next byte of ibuf */

    
   
617
			vs->nextibit = 7;

    
   
618
			do {

    
   
619
				tmp >>= 1;

    
   
620
				vs->nextibit--;

    
   
621
			} while (tmp & 1);

    
   
622

   

    
   
623
			/* Start bit is now (host's) LSB */

    
   
624
			vs->ibuf[vs->ibufend] = tmp >> 1;

    
   
625
			continue;

    
   
626
		}

    
   
627

   

    
   
628
		tmp >>= 1;

    
   
629

   

    
   
630
		if (vs->nextibit < 9) {

    
   
631
			/* Add next bits of incoming byte to ibuf */

    
   
632
			vs->ibuf[vs->ibufend] |= tmp << (vs->nextibit-1);

    
   
633

   

    
   
634

   

    
   
635
			if (vs->nextibit <= 3) {

    
   
636
				/* Haven't finished this byte (including stop) yet */

    
   
637
				vs->nextibit += 6;

    
   
638
				continue;

    
   
639
			}

    
   
640

   

    
   
641
			tmp >>= (9 - vs->nextibit);

    
   
642
		}

    
   
643

   

    
   
644
		/* Check for stop bit */

    
   
645
		if (tmp & 1) {

    
   
646
			unsigned newend = (vs->ibufend + 1) & (IBUF_LEN-1);

    
   
647

   

    
   
648
			if (newend == vs->ibufstart) {

    
   
649
				/* Buffer full. This shouldn't happen because we should

    
   
650
				   have asserted flow control long ago */

    
   
651
				if (vs->bufwarning) {

    
   
652
					vs->bufwarning--;

    
   
653
					ast_log(LOG_NOTICE, "incoming buffer full\n");

    
   
654
				}

    
   
655
				continue;

    
   
656
			} else

    
   
657
				vs->ibufend = newend;

    
   
658
		} else {

    
   
659
			ast_log(LOG_NOTICE, "No stop bit\n");

    
   
660
		}

    
   
661

   

    
   
662
		/* Now, scan for next start bit */

    
   
663
		tmp >>= 1;

    
   
664
		vs->nextibit -= 4;

    
   
665
		while (vs->nextibit && (tmp & 1)) {

    
   
666
			tmp >>= 1;

    
   
667
			vs->nextibit--;

    
   
668
		}

    
   
669
		if (vs->nextibit > 1)

    
   
670
			vs->ibuf[vs->ibufend] = tmp >> 1;

    
   
671

   

    
   
672
	}

    
   
673

   

    
   
674
}

    
   
675

   

    
   
676
/* We don't handle multiple multiplexed channels. Nobody really does */

    
   
677
void v110_input_frame_x4(struct v110_state *vs, struct ast_frame *f)

    
   
678
{

    
   
679
	int datalen = f->datalen;

    
   
680
	unsigned char *frame_data = f->data.ptr;

    
   
681

   

    
   
682
	while (datalen) {

    
   
683
		if (vs->vframe_in_len < 4) {

    
   
684
			/* Find zero octet in buffer */

    
   
685
			if ( (*frame_data) & 0xc0 ) {

    
   
686
				vs->vframe_in_len = 0;

    
   
687
				frame_data++;

    
   
688
				datalen--;

    
   
689
				continue;

    
   
690
			}

    
   
691
			/* Found a suitable byte. Add it. */

    
   
692
			if (++vs->vframe_in_len == 4)

    
   
693
				memset(vs->vframe_in, 0, 10);

    
   
694
			frame_data++;

    
   
695
			datalen--;

    
   
696
			continue;

    
   
697
		}

    
   
698
		/* Add in these two bits */

    
   
699
		vs->vframe_in[vs->vframe_in_len/4] |=

    
   
700
			((*frame_data) & 0xc0) >> ((vs->vframe_in_len & 3) * 2);

    
   
701

   

    
   
702
		vs->vframe_in_len++;

    
   
703
		frame_data++;

    
   
704
		datalen--;

    
   
705

   

    
   
706
		if (vs->vframe_in_len == 40) {

    
   
707
			v110_process_frame(vs);

    
   
708
			vs->vframe_in_len = 0;

    
   
709
		}

    
   
710
	}

    
   
711
}

    
   
712

   

    
   
713
void v110_input_frame_x2(struct v110_state *vs, struct ast_frame *f)

    
   
714
{

    
   
715
	int datalen = f->datalen;

    
   
716
	unsigned char *frame_data = f->data.ptr;

    
   
717

   

    
   
718
	while (datalen) {

    
   
719
		if (vs->vframe_in_len < 2) {

    
   
720
			/* Find zero octet in buffer */

    
   
721
			if ( (*frame_data) & 0xf0 ) {

    
   
722
				vs->vframe_in_len = 0;

    
   
723
				frame_data++;

    
   
724
				datalen--;

    
   
725
				continue;

    
   
726
			}

    
   
727
			/* Found a suitable byte. Add it. */

    
   
728
			if (++vs->vframe_in_len == 2)

    
   
729
				memset(vs->vframe_in, 0, 10);

    
   
730
			frame_data++;

    
   
731
			datalen--;

    
   
732
			continue;

    
   
733
		}

    
   
734
		/* Add in these four bits */

    
   
735
		vs->vframe_in[vs->vframe_in_len/2] |=

    
   
736
			((*frame_data) & 0xf0) >> ((vs->vframe_in_len & 1) * 4);

    
   
737

   

    
   
738
		vs->vframe_in_len++;

    
   
739
		frame_data++;

    
   
740
		datalen--;

    
   
741

   

    
   
742
		if (vs->vframe_in_len == 20) {

    
   
743
			v110_process_frame(vs);

    
   
744
			vs->vframe_in_len = 0;

    
   
745
		}

    
   
746
	}

    
   
747
}

    
   
748

   

    
   
749
void v110_input_frame_x1(struct v110_state *vs, struct ast_frame *f)

    
   
750
{

    
   
751
	int datalen = f->datalen;

    
   
752
	unsigned char *frame_data = f->data.ptr;

    
   
753

   

    
   
754
	while (datalen) {

    
   
755
		if (!vs->vframe_in_len) {

    
   
756
			/* Find zero octet in buffer */

    
   
757
			if ( (*frame_data)) {

    
   
758
				vs->vframe_in_len = 0;

    
   
759
				frame_data++;

    
   
760
				datalen--;

    
   
761
				continue;

    
   
762
			}

    
   
763
			/* Found a suitable byte. Add it. */

    
   
764
			vs->vframe_in_len++;

    
   
765
			memset(vs->vframe_in, 0, 10);

    
   
766
			frame_data++;

    
   
767
			datalen--;

    
   
768
			continue;

    
   
769
		}

    
   
770
		/* Add byte to frame */

    
   
771
		vs->vframe_in[vs->vframe_in_len] = *frame_data;

    
   
772

   

    
   
773
		vs->vframe_in_len++;

    
   
774
		frame_data++;

    
   
775
		datalen--;

    
   
776

   

    
   
777
		if (vs->vframe_in_len == 10) {

    
   
778
			v110_process_frame(vs);

    
   
779
			vs->vframe_in_len = 0;

    
   
780
		}

    
   
781
	}

    
   
782
}

    
   
783

   

    
   
784
/* Some bitmasks to ease calculation. */

    
   
785
static unsigned char helper1[] = { 0x81, 0x81, 0x81, 0xc1, 0xe1, 0xf1, 0xf9, 0xfd, 0xff };

    
   
786
static unsigned char helper2[] = { 0x81, 0x83, 0x87, 0x8f, 0x9f, 0xbf };

    
   
787

   

    
   
788
static unsigned char v110_getline(struct v110_state *vs)

    
   
789
{

    
   
790
	unsigned char octet;

    
   
791
	unsigned char tmp;

    
   
792
	int line = vs->nextoline++;

    
   
793
	int place = 2;

    
   
794

   

    
   
795
	if (line == 10) {

    
   
796
		vs->nextoline = 1;

    
   
797
		return 0x00; /* Header */

    
   
798
	} else if (line == 5) {

    
   
799
		return 0xbf; /* E-bits. 10111111 */

    
   
800
	} else if (line == 2 || line == 7) {

    
   
801
		octet = 0xfe | vs->rts;

    
   
802
	} else {

    
   
803
		octet = 0xfe | vs->sbit;

    
   
804
	}

    
   
805

   

    
   
806
	/* If we're already sending a byte, finish it */

    
   
807
	if (vs->nextobit) {

    
   
808
		vs->last_frame_empty = 0;

    
   
809

   

    
   
810
		/* Shift the data byte so that the bit we want is in bit 1 */

    
   
811
		tmp = vs->obuf[vs->obufstart] >> (vs->nextobit - 2);

    
   
812

   

    
   
813
		/* Mask in the bits we don't want to touch and the stop bit */

    
   
814
		tmp |= helper1[vs->nextobit - 1];

    
   
815

   

    
   
816
		/* Clear bits in the generated octet to match */

    
   
817
		octet &= bit_reverse_table[tmp];

    
   
818

   

    
   
819
		if (vs->nextobit < 4) {

    
   
820
			/* There's some of this byte left; possibly just the stop bit */

    
   
821
			vs->nextobit += 6;

    
   
822
			return octet;

    
   
823
		}

    
   
824

   

    
   
825
		/* We've finished this byte */

    
   
826
		vs->obufstart++;

    
   
827
		if (vs->obufstart == OBUF_LEN)

    
   
828
			vs->obufstart = 0;

    
   
829

   

    
   
830
		if (vs->nextobit < 5) {

    
   
831
			/* But there's still no room in this octet for any more */

    
   
832
			vs->nextobit = 0;

    
   
833
			return octet;

    
   
834
		}

    
   
835
		/* Work out where to put the next data byte */

    
   
836
		place = 12 - vs->nextobit;

    
   
837
		vs->nextobit = 0;

    
   
838
	} else {

    
   
839
		/* Nothing to follow; start bit of new byte at bit 1 */

    
   
840
		place = 2;

    
   
841
	}

    
   
842

   

    
   
843
	/* Honour flow control when starting new characters */

    
   
844
	if (vs->cts || vs->obufstart == vs->obufend)

    
   
845
		return octet;

    
   
846

   

    
   
847
	vs->last_frame_empty = 0;

    
   
848
	/* 'place' is the location within the octet to start the new

    
   
849
	   data byte. It'll be 2 unless we've already got the tail of

    
   
850
	   a previous data byte in this octet. If you're starting at it

    
   
851
	   and think there's an off-by-one error, remember the start bit

    
   
852
	   which is zero, and in bit (place-1). */

    
   
853
	tmp = vs->obuf[vs->obufstart] << place;

    
   
854

   

    
   
855
	/* The bits we don't want to touch */

    
   
856
	tmp |= helper2[place - 2];

    
   
857

   

    
   
858
	octet &= bit_reverse_table[tmp];

    
   
859
	vs->nextobit = 8 - place;

    
   
860

   

    
   
861
	return octet;

    
   
862
}

    
   
863

   

    
   
864
void v110_fill_outframe_x4(struct v110_state *vs, int datalen)

    
   
865
{

    
   
866
	unsigned char *pos = vs->f.data.ptr;

    
   
867

   

    
   
868
	if (datalen & 3) {

    
   
869
		vs->last_frame_empty = 0;

    
   
870
		datalen = (datalen + 3) & ~3;

    
   
871
	}

    
   
872

   

    
   
873
	vs->f.datalen = vs->f.samples = datalen;

    
   
874

   

    
   
875
	while (datalen) {

    
   
876
		unsigned char tmp = v110_getline(vs);

    
   
877
		pos[0] = 0x3f | (tmp & 0xc0);

    
   
878
		tmp <<= 2;

    
   
879
		pos[1] = 0x3f | (tmp & 0xc0);

    
   
880
		tmp <<= 2;

    
   
881
		pos[2] = 0x3f | (tmp & 0xc0);

    
   
882
		tmp <<= 2;

    
   
883
		pos[3] = 0x3f | tmp;

    
   
884
		pos += 4;

    
   
885
		datalen -= 4;

    
   
886
	}

    
   
887
}

    
   
888

   

    
   
889
void v110_fill_outframe_x2(struct v110_state *vs, int datalen)

    
   
890
{

    
   
891
	unsigned char *pos = vs->f.data.ptr;

    
   
892

   

    
   
893
	if (datalen & 1) {

    
   
894
		vs->last_frame_empty = 0;

    
   
895
		vs->f.datalen = datalen = datalen + 1;

    
   
896
	}

    
   
897

   

    
   
898
	vs->f.datalen = vs->f.samples = datalen;

    
   
899

   

    
   
900
	while (datalen) {

    
   
901
		unsigned char tmp = v110_getline(vs);

    
   
902
		pos[0] = 0x0f | (tmp & 0xf0);

    
   
903
		tmp <<= 4;

    
   
904
		pos[1] = 0x0f | tmp;

    
   
905
		pos += 2;

    
   
906
		datalen -= 2;

    
   
907
	}

    
   
908
}

    
   
909

   

    
   
910
void v110_fill_outframe_x1(struct v110_state *vs, int datalen)

    
   
911
{

    
   
912
	unsigned char *pos = vs->f.data.ptr;

    
   
913

   

    
   
914
	vs->f.datalen = vs->f.samples = datalen;

    
   
915

   

    
   
916
	while (datalen) {

    
   
917
		*pos = v110_getline(vs);

    
   
918
		pos++;

    
   
919
		datalen--;

    
   
920
	}

    
   
921
}

    
   
922

   

    
   
923
pid_t loginpty(struct v110_state *vs, struct ast_channel *chan, const char *data, int *ptyfd)

    
   
924
{

    
   
925
	int master;

    
   
926
	pid_t pid = -1;

    
   
927
	char name[256];

    
   
928
	char *source = "<unknown>";

    
   
929
	int flags;

    
   
930
	char **child_argv = NULL;

    
   
931
	int child_argc = 0;

    
   
932

   

    
   
933
	if (data && *data) {

    
   
934
		char *arg, *userargs = ast_strdupa(data);

    
   
935

   

    
   
936
		while ( (arg = strsep(&userargs, ",")) && *arg) {

    
   
937
			child_argv = realloc(child_argv, sizeof(char *) * (child_argc + 2));

    
   
938
			if (!child_argv) {

    
   
939
				ast_log(LOG_ERROR, "Failed to allocate child arguments\n");

    
   
940
				goto out;

    
   
941
			}

    
   
942
			child_argv[child_argc++] = arg;

    
   
943
		}

    
   
944
		child_argv[child_argc] = NULL;

    
   
945
	} else {

    
   
946
		child_argv = malloc(sizeof(char *) * 8);

    
   
947
		if (!child_argv) {

    
   
948
			ast_log(LOG_ERROR, "Failed to allocate child arguments\n");

    
   
949
			goto out;

    
   
950
		}

    
   
951
		child_argv[child_argc++] = "/sbin/agetty";

    
   
952
		if (chan->caller.id.number.valid) {

    
   
953
			child_argv[child_argc++] = "-H";

    
   
954
			source = child_argv[child_argc++] = chan->caller.id.number.str;

    
   
955
		}

    
   
956
		child_argv[child_argc++] = rates[vs->urate];

    
   
957
		child_argv[child_argc++] = name;

    
   
958
		child_argv[child_argc++] = "vt100";

    
   
959
		child_argv[child_argc++] = NULL;

    
   
960
	}

    
   
961

   

    
   
962
	master = getpt();

    
   
963

   

    
   
964
	if (master < 0) {

    
   
965
		ast_log (LOG_NOTICE, "Failed to allocate pty: %s\n",

    
   
966
			 strerror(errno));

    
   
967
		goto out;

    
   
968
	}

    
   
969

   

    
   
970
	if (grantpt(master)) {

    
   
971
		ast_log(LOG_NOTICE, "grantpt() failed: %s\n", strerror(errno));

    
   
972
		close(master);

    
   
973
		goto out;

    
   
974
	}

    
   
975

   

    
   
976
	if (unlockpt(master)) {

    
   
977
		ast_log(LOG_NOTICE, "unlockpt() failed: %s\n", strerror(errno));

    
   
978
		close(master);

    
   
979
		goto out;

    
   
980
	}

    
   
981

   

    
   
982
	flags = fcntl(master, F_GETFL);

    
   
983
	fcntl(master, F_SETFL, flags | O_NONBLOCK);

    
   
984

   

    
   
985
	if (ptsname_r(master, name, sizeof(name))) {

    
   
986
		ast_log(LOG_NOTICE, "ptsname_r() failed: %s\n", strerror(errno));

    
   
987
		close(master);

    
   
988
		goto out;

    
   
989
        }

    
   
990

   

    
   
991
	if ((pid = ast_safe_fork(1)) < 0) {

    
   
992
		ast_log(LOG_WARNING, "Failed to fork() to launch login process %s\n", strerror(errno));

    
   
993
		close (master);

    
   
994
		goto out;

    
   
995
	}

    
   
996

   

    
   
997
	if (!pid) {

    
   
998
		ast_set_priority(0);

    
   
999

   

    
   
1000
		/* Close everything */

    
   
1001
		close(0);

    
   
1002
		ast_close_fds_above_n(0);

    
   
1003
		setsid();

    
   
1004

   

    
   
1005
		setenv ("TERM", "vt100", 1);

    
   
1006
		setenv ("V110_RATE", rates[vs->urate], 1);

    
   
1007
		setenv ("V110_SOURCE", source, 1);

    
   
1008
		setenv ("V110_PTY", name, 1);

    
   
1009

   

    
   
1010
		if (data && *data) {

    
   
1011
			/* If a command was specified, open the slave PTY for it */

    
   
1012
			int slave = open(name, O_RDWR);

    
   
1013
			if (slave < 0)

    
   
1014
				_exit(1);

    
   
1015

   

    
   
1016
			dup2(0, 1);

    
   
1017
			dup2(0, 2);

    
   
1018
		}

    
   
1019

   

    
   
1020
		execv(child_argv[0], child_argv);

    
   
1021

   

    
   
1022
		fprintf(stderr, "Failed to spawn child process: %s\n", strerror(errno));

    
   
1023
		_exit(1);

    
   
1024
	}

    
   
1025
	ast_verb(3, "Launched login process\n");

    
   
1026
	*ptyfd = master;

    
   
1027
   out:

    
   
1028
	free (child_argv);

    
   
1029
	return pid;

    
   
1030
}

    
   
1031

   

    
   
1032
static int load_module(void)

    
   
1033
{

    
   
1034
	return ast_register_application_xml(app, v110_exec) ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;

    
   
1035
}

    
   
1036

   

    
   
1037
static int unload_module(void)

    
   
1038
{

    
   
1039
	return ast_unregister_application(app);

    
   
1040
}

    
   
1041

   

    
   
1042
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "V.110 dialin");

    
   
1043

   
/trunk/channels/chan_dahdi.c
Revision 333428 New Change
 
  1. /trunk/apps/app_v110.c: Loading...
  2. /trunk/channels/chan_dahdi.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.