casteriskpjsip

Asterisk (PBX) channel driver - read() callback is never triggered


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>

Solution

  • 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.