/* gsmd unix domain socket handling * * (C) 2006-2007 by OpenMoko, Inc. * Written by Harald Welte * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "gsmd.h" #include #include #include #include #include #include #include #include #include extern int loglevel; static void *__ucmd_ctx, *__gu_ctx; struct gsmd_ucmd *ucmd_alloc(int extra_size) { return talloc_zero_size(__ucmd_ctx, sizeof(struct gsmd_ucmd) + extra_size); } void usock_cmd_enqueue(struct gsmd_ucmd *ucmd, struct gsmd_user *gu) { DEBUGP("enqueueing usock cmd %p for user %p\n", ucmd, gu); /* add to per-user list of finished cmds */ llist_add_tail(&ucmd->list, &gu->finished_ucmds); /* mark socket of user as we-want-to-write */ gu->gfd.when |= GSMD_FD_WRITE; } /* callback for completed passthrough gsmd_atcmd's */ static int usock_cmd_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; int rlen = strlen(resp)+1; struct gsmd_ucmd *ucmd = ucmd_alloc(rlen); DEBUGP("entering(cmd=%p, gu=%p)\n", cmd, gu); if (!ucmd) return -ENOMEM; /* FIXME: pass error values back somehow */ ucmd->hdr.version = GSMD_PROTO_VERSION; ucmd->hdr.msg_type = GSMD_MSG_PASSTHROUGH; ucmd->hdr.msg_subtype = GSMD_PASSTHROUGH_RESP; ucmd->hdr.len = rlen; ucmd->hdr.id = cmd->id; memcpy(ucmd->buf, resp, ucmd->hdr.len); usock_cmd_enqueue(ucmd, gu); return 0; } typedef int usock_msg_handler(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, int len); static int usock_rcv_passthrough(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, int len) { struct gsmd_atcmd *cmd; cmd = atcmd_fill((char *)gph+sizeof(*gph), gph->len, &usock_cmd_cb, gu, gph->id, NULL); if (!cmd) return -ENOMEM; DEBUGP("submitting cmd=%p, gu=%p\n", cmd, gu); return atcmd_submit(gu->gsmd, cmd); } static int usock_rcv_event(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, int len) { u_int32_t *evtmask = (u_int32_t *) ((char *)gph + sizeof(*gph)); if (len < sizeof(*gph) + sizeof(u_int32_t)) return -EINVAL; if (gph->msg_subtype != GSMD_EVT_SUBSCRIPTIONS) return -EINVAL; gu->subscriptions = *evtmask; return 0; } static int voicecall_get_stat_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; struct gsmd_call_status gcs; struct gsm_extrsp *er; DEBUGP("resp: %s\n", resp); er = extrsp_parse(cmd, resp); if ( !er ) return -ENOMEM; gcs.is_last = (cmd->ret == 0 || cmd->ret == 4)? 1:0; if ( !strncmp(resp, "OK", 2) ) { /* No existing call */ gcs.idx = 0; } else if ( !strncmp(resp, "+CME", 4) ) { /* +CME ERROR: */ DEBUGP("+CME error\n"); gcs.idx = 0 - atoi(strpbrk(resp, "0123456789")); } else if ( er->num_tokens == 7 && er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[2].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[3].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[4].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[5].type == GSMD_ECMD_RTT_STRING && er->tokens[6].type == GSMD_ECMD_RTT_NUMERIC ) { /* * [+CLCC: ,,,,[, * ,[,]] * [+CLCC: ,,,,[, * ,[,]] * [...]]] */ gcs.idx = er->tokens[0].u.numeric; gcs.dir = er->tokens[1].u.numeric; gcs.stat = er->tokens[2].u.numeric; gcs.mode = er->tokens[3].u.numeric; gcs.mpty = er->tokens[4].u.numeric; strlcpy(gcs.number, er->tokens[5].u.string, GSMD_ADDR_MAXLEN+1); gcs.type = er->tokens[6].u.numeric; } else if ( er->num_tokens == 8 && er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[2].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[3].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[4].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[5].type == GSMD_ECMD_RTT_STRING && er->tokens[6].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[7].type == GSMD_ECMD_RTT_STRING ) { /* * [+CLCC: ,,,,[, * ,[,]] * [+CLCC: ,,,,[, * ,[,]] * [...]]] */ gcs.idx = er->tokens[0].u.numeric; gcs.dir = er->tokens[1].u.numeric; gcs.stat = er->tokens[2].u.numeric; gcs.mode = er->tokens[3].u.numeric; gcs.mpty = er->tokens[4].u.numeric; strlcpy(gcs.number, er->tokens[5].u.string, GSMD_ADDR_MAXLEN+1); gcs.type = er->tokens[6].u.numeric; strlcpy(gcs.alpha, er->tokens[7].u.string, GSMD_ALPHA_MAXLEN+1); } else { DEBUGP("Invalid Input : Parse error\n"); return -EINVAL; } talloc_free(er); return gsmd_ucmd_submit(gu, GSMD_MSG_VOICECALL, GSMD_VOICECALL_GET_STAT, cmd->id, sizeof(gcs), &gcs); } static int voicecall_ctrl_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; int ret = 0; DEBUGP("resp: %s\n", resp); if ( !strncmp(resp, "+CME", 4) ) { /* +CME ERROR: */ DEBUGP("+CME error\n"); ret = atoi(strpbrk(resp, "0123456789")); } return gsmd_ucmd_submit(gu, GSMD_MSG_VOICECALL, GSMD_VOICECALL_CTRL, cmd->id, sizeof(ret), &ret); } static int voicecall_fwd_stat_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; struct gsm_extrsp *er; struct gsmd_call_fwd_stat gcfs; DEBUGP("resp: %s\n", resp); er = extrsp_parse(cmd, resp); if ( !er ) return -ENOMEM; gcfs.is_last = (cmd->ret == 0 || cmd->ret == 4)? 1:0; if ( er->num_tokens == 2 && er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC && er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC ) { /* * +CCFC: ,[,, * [,,[,