Put breakpoint inside ast_read(), in channel.c:1372 ** NOT REACHED ** ======================= Put bkpt inside ast_playtones_start(), in indications.c:214 (call to ast_activate_generator() ) TAKEN: Breakpoint 2, ast_playtones_start (chan=0x8132b20, vol=0, playlst=0xbe3fd77c "!697+1336/1000|!0/100", interruptible=0) at indications.c:214 214 if (ast_activate_generator(chan, &playtones, &d)) { Firstly, this parses the playlist in its thrd parameter and parses it to build d (a struct playtones_def )... d.items[d.nitems].freq1 = freq1; d.items[d.nitems].freq2 = freq2; d.items[d.nitems].duration = time; d.items[d.nitems].modulate = modulate; d.nitems++; This calls ast_activate_generator (channel.c:849) passing it d as third parameter. This allocates chan->generatordata (a (struct playtones_state *) using gen->alloc which really is playtones_alloc()): chan->generatordata = gen->alloc(chan, params) So, chan->generatordata now points to a struct playtones_state whose fields reppos, nitems and items are set to the ones of the same names in params: (gdb) print ((struct playtones_state *)chan->generatordata)->reppos $17 = -1 (gdb) print ((struct playtones_state *)chan->generatordata)->nitems $16 = 2 (gdb) print *(struct playtones_state *)chan->generatordata $9 = {vol = 8192, reppos = -1, nitems = 2, items = 0x80f4900, npos = 0, pos = 0, origwfmt = 4, f = {frametype = 0, subclass = 0, datalen = 0, samples = 0, mallocd = 0, offset = 0, src = 0x0, data = 0x0, delivery = {tv_sec = 0, tv_usec = 0}, prev = 0x0, next = 0x0}, offset = '\000' , data = {0 }} (gdb) print ((struct playtones_state *)chan->generatordata)->items[0] $14 = {freq1 = 697, freq2 = 1336, duration = 1000, modulate = 0} (gdb) print ((struct playtones_state *)chan->generatordata)->items[1] $15 = {freq1 = 0, freq2 = 0, duration = 100, modulate = 0} It also sets the chan->generator field to gen (a struct made of pointers to playtones_alloc(), playtones_release() and playtones_generator() ), Calls to ast_prod(chan) and ast_settimeout(chan, 160, generator_force, chan) have no significant effects because, respectively, chan is already up (chan->_state == AST_STATE_UP) and ZAPTEL_OPTIMIZATIONS is undefined, so that the timer can't be used. ============== So, the problem is that the generator is correctly initialized, but never run because ast_read is never called. NOTE: that should be done by ast_safe_sleep if called for a delay > 0: channel.c:554: int ast_safe_sleep(struct ast_channel *chan, int ms) { struct ast_frame *f; while(ms > 0) { ms = ast_waitfor(chan, ms); if (ms <0) return -1; if (ms > 0) { f = ast_read(chan); <========= if (!f) return -1; ast_frfree(f); } } return 0; } ========== With SendDTMF, on the other hand, ast_safe_sleep() is called, but it finds chan->generator and chan->generatordata == NULL :-( This is because (??) it's called to process a "w" at app.c:283: interdigit pauses are processed directly through calls to the underlying ast_waitfor().