struct ast_channel_tech custom_tech = {
.type = "Custom",
.description = "Custom Channel Driver",
.requester = custom_request,
.call = custom_call,
.hangup = custom_hangup,
.answer = custom_answer,
.read = custom_read,
.write = custom_write,
};
loading of the module:
static int load_module(void)
{
ast_log(LOG_NOTICE, "Loading Custom Channel Driver\n");
if (!(custom_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
return AST_MODULE_LOAD_DECLINE;
}
ast_format_cap_append_by_type(custom_tech.capabilities, AST_MEDIA_TYPE_AUDIO);
ast_format_cap_append(custom_tech.capabilities, ast_format_ulaw, 0);
if (ast_channel_register(&custom_tech)) {
ast_log(LOG_ERROR, "Unable to register channel class Custom");
ao2_ref(custom_tech.capabilities, -1);
custom_tech.capabilities = NULL;
return AST_MODULE_LOAD_DECLINE;
}
ast_log(LOG_NOTICE, "Custom Channel Driver Loaded\n");
return AST_MODULE_LOAD_SUCCESS;
}
dialplan (extensions.conf):
[softphone]
; 1) Outgoing calls from Softphone to GSM via custom trunk
exten => _X.,1,NoOp(Outgoing: dialing PSTN for ${EXTEN} from Softphone)
same => n,Dial(CUSTOM/${EXTEN})
same => n,NoOp(Call ended with status ${DIALSTATUS})
same => n,Hangup()
; 2) Incoming calls from GSM (Custom driver) -> Softphone
exten => 1000,1,NoOp(Incoming call to Softphone)
same => n,NoOp(>>> Attempting Dial(PJSIP/test))
same => n,Dial(PJSIP/test)
same => n,NoOp(>> Dial completed with status: ${DIALSTATUS})
same => n,Hangup()
'1)' works fine when I initiate a call from the softphone to, for example, a GSM phone
'2)' works also fine but for only the other way of GSM to Softphone.
Except for the .read()
, all the callbacks are working fine. The callback .write()
does write ast_frame
data to the audio device. The only issue I'm facing is that .read()
is never called. I do see in the logs of asterisk that it creates a simple_bridge
when i pick up the incoming call (from GSM to Softphone):
incoming_call()
-> is a thread that listens on the hardware for incoming calls and creates a new channel (via new_channel()
) once a incoming call is happening and start it via ast_pbx_start()
.
incoming_call: incoming_call() incoming call from number '001987654321'
new_channel: Channel number and type set: 'CUSTOM/001987654321-1748263972.0'
incoming_call: Successfully started PBX on new call from 001987654321
-- Executing [1000@linphone:1] NoOp("CUSTOM/001987654321-1748263972.0", "Incoming GSM call — answer and dial Linphone") in new stack
-- Executing [1000@linphone:2] NoOp("CUSTOM/001987654321-1748263972.0", ">>> Attempting Dial(PJSIP/test)") in new stack
-- Executing [1000@linphone:3] Dial("CUSTOM/001987654321-1748263972.0", "PJSIP/test") in new stack
-- Called PJSIP/test
-- PJSIP/test-00000000 is ringing
-- PJSIP/test-00000000 answered CUSTOM/001987654321-1748263972.0
custom_answer: Custom answer called for CUSTOM/001987654321-1748263972.0.
custom_answer: Channel state '4'
custom_indicate: Custom indicate called for CUSTOM/001987654321-1748263972.0 (condition: -1).
-- Channel PJSIP/test-00000000 joined 'simple_bridge' basic-bridge <5ca01042-1cee-4003-8eb5-17003f410e5c>
-- Channel CUSTOM/001987654321-1748263972.0 joined 'simple_bridge' basic-bridge <5ca01042-1cee-4003-8eb5-17003f410e5c>
the .read()
callback is triggered by setting up a file descriptor on the channel:
...
fd = open("/tmp/somefile", O_RDONLY | O_CREAT | O_NONBLOCK);
...
ast_channel_set_fd(channel, 0, fd);
ast_channel_set_fd(channel, 1, fd);
...
see for example the quectel channel driver
unsetting a file descriptor:
ast_channel_set_fd(channel, 0, -1);
ast_channel_set_fd(channel, 1, -1);
the second parameter which
is the index of the file descriptor. See this for more information. Quote:
It's the index into the channel's array of file descriptors. So if "which" is 3, then channel->fds[3] would be set to the fd you pass in as the "fd" parameter.