usock.c

来自「Linux下gsm/gprs modem的看守程序。支持短信发送与接受。」· C语言 代码 · 共 1,745 行 · 第 1/4 页

C
1,745
字号
/* gsmd unix domain socket handling * * (C) 2006-2007 by OpenMoko, Inc. * Written by Harald Welte <laforge@openmoko.org> * 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 <stdlib.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <errno.h>#include <ctype.h>#include <sys/socket.h>#include <sys/un.h>#include "gsmd.h"#include <gsmd/gsmd.h>#include <gsmd/usock.h>#include <gsmd/select.h>#include <gsmd/atcmd.h>#include <gsmd/usock.h>#include <gsmd/talloc.h>#include <gsmd/extrsp.h>#include <gsmd/ts0707.h>#include <gsmd/sms.h>static void *__ucmd_ctx, *__gu_ctx;struct gsmd_ucmd *ucmd_alloc(int extra_size){	return talloc_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: <err> */		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: <id1>,<dir>,<stat>,<mode>,<mpty>[,		 * <number>,<type>[,<alpha>]]		 * [<CR><LF>+CLCC: <id2>,<dir>,<stat>,<mode>,<mpty>[,		 * <number>,<type>[,<alpha>]]		 * [...]]]		 */		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: <id1>,<dir>,<stat>,<mode>,<mpty>[,		 * <number>,<type>[,<alpha>]]		 * [<CR><LF>+CLCC: <id2>,<dir>,<stat>,<mode>,<mpty>[,		 * <number>,<type>[,<alpha>]]		 * [...]]]		 */		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: <err> */		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: <status>,<class1>[,<number>,<type>		 * [,<subaddr>,<satype>[,<time>]]][		 * <CR><LF>+CCFC: <status>,<class2>[,<number>,<type>		 * [,<subaddr>,<satype>[,<time>]]]		 * [...]]		 */		gcfs.status = er->tokens[0].u.numeric;		gcfs.classx = er->tokens[1].u.numeric;		gcfs.addr.number[0] = '\0';	}	else if ( er->num_tokens == 4 &&			er->tokens[0].type == GSMD_ECMD_RTT_NUMERIC &&			er->tokens[1].type == GSMD_ECMD_RTT_NUMERIC &&			er->tokens[2].type == GSMD_ECMD_RTT_STRING &&			er->tokens[3].type == GSMD_ECMD_RTT_NUMERIC ) {				gcfs.status = er->tokens[0].u.numeric;		gcfs.classx = er->tokens[1].u.numeric;		strlcpy(gcfs.addr.number, er->tokens[2].u.string, GSMD_ADDR_MAXLEN+1);		gcfs.addr.type = er->tokens[3].u.numeric;	}	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_STRING &&			er->tokens[3].type == GSMD_ECMD_RTT_NUMERIC && 			er->tokens[4].type == GSMD_ECMD_RTT_EMPTY &&			er->tokens[5].type == GSMD_ECMD_RTT_EMPTY &&			er->tokens[6].type == GSMD_ECMD_RTT_NUMERIC ) {				gcfs.status = er->tokens[0].u.numeric;		gcfs.classx = er->tokens[1].u.numeric;		strlcpy(gcfs.addr.number, er->tokens[2].u.string, GSMD_ADDR_MAXLEN+1);		gcfs.addr.type = er->tokens[3].u.numeric;		gcfs.time = er->tokens[6].u.numeric;	}	else {		DEBUGP("Invalid Input : Parse error\n");		return -EINVAL;	}		talloc_free(er);	return gsmd_ucmd_submit(gu, GSMD_MSG_VOICECALL, GSMD_VOICECALL_FWD_STAT,			cmd->id, sizeof(gcfs), &gcfs);}static int usock_ringing_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp){        struct gsmd_user *gu = ctx;        /* If the incoming call answer/rejection succeeded then we        * know the modem isn't ringing and we update the state info.  */        if (cmd->ret == 0)                gu->gsmd->dev_state.ringing = 0;        return usock_cmd_cb(cmd, ctx, resp);}static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,				int len){	struct gsmd_atcmd *cmd = NULL;	struct gsmd_addr *ga;	struct gsmd_dtmf *gd;	struct gsmd_call_ctrl *gcc; 	struct gsmd_call_fwd_reg *gcfr;	char buf[64];	int atcmd_len;	int *reason;			switch (gph->msg_subtype) {	case GSMD_VOICECALL_DIAL:		if (len < sizeof(*gph) + sizeof(*ga))			return -EINVAL;		ga = (struct gsmd_addr *) ((void *)gph + sizeof(*gph));		ga->number[GSMD_ADDR_MAXLEN] = '\0';		cmd = atcmd_fill("ATD", 5 + strlen(ga->number),				 &usock_cmd_cb, gu, gph->id, NULL);		if (!cmd)			return -ENOMEM;		sprintf(cmd->buf, "ATD%s;", ga->number);		/* FIXME: number type! */		break;	case GSMD_VOICECALL_HANGUP:		/* ATH0 is not supported by QC, we hope ATH is supported by everone */		cmd = atcmd_fill("ATH", 4,                                gu->gsmd->dev_state.ringing ?                                usock_ringing_cb : usock_cmd_cb,                                gu, gph->id,NULL);                                /* This command is special because it needs to be sent to                * the MS even if a command is currently executing.  */                if (cmd) {                        return cancel_atcmd(gu->gsmd, cmd);                }		break;	case GSMD_VOICECALL_ANSWER:                cmd = atcmd_fill("ATA", 4, &usock_ringing_cb, gu, gph->id,NULL);		break;	case GSMD_VOICECALL_DTMF:		if (len < sizeof(*gph) + sizeof(*gd))			return -EINVAL;		gd = (struct gsmd_dtmf *) ((void *)gph + sizeof(*gph));		if (len < sizeof(*gph) + sizeof(*gd) + gd->len)			return -EINVAL;		/* FIXME: we don't yet support DTMF of multiple digits */		if (gd->len != 1)			return -EINVAL;		atcmd_len = 1 + strlen("AT+VTS=") + (gd->len * 2);		cmd = atcmd_fill("AT+VTS=", atcmd_len, &usock_cmd_cb,				 gu, gph->id, NULL);		if (!cmd)			return -ENOMEM;		sprintf(cmd->buf, "AT+VTS=%c;", gd->dtmf[0]);		break;	case GSMD_VOICECALL_GET_STAT:		cmd = atcmd_fill("AT+CLCC", 7+1, &voicecall_get_stat_cb, 				 gu, gph->id, NULL);		if (!cmd)			return -ENOMEM;		break;	case GSMD_VOICECALL_CTRL:		if (len < sizeof(*gph) + sizeof(*gcc))			return -EINVAL;		gcc = (struct gsmd_call_ctrl *) ((void *)gph + sizeof(*gph));		atcmd_len = 1 + strlen("AT+CHLD=") + 2;		cmd = atcmd_fill("AT+CHLD=", atcmd_len, &voicecall_ctrl_cb,				 gu, gph->id, NULL);		if (!cmd)			return -ENOMEM;		switch (gcc->proc) {			case GSMD_CALL_CTRL_R_HLDS:						case GSMD_CALL_CTRL_UDUB:							sprintf(cmd->buf, "AT+CHLD=%d", 0);				break;			case GSMD_CALL_CTRL_R_ACTS_A_HLD_WAIT:					sprintf(cmd->buf, "AT+CHLD=%d", 1);				break;			case GSMD_CALL_CTRL_R_ACT_X:				sprintf(cmd->buf, "AT+CHLD=%d%d", 1, gcc->idx);				break;			case GSMD_CALL_CTRL_H_ACTS_A_HLD_WAIT:				sprintf(cmd->buf, "AT+CHLD=%d", 2);				break;			case GSMD_CALL_CTRL_H_ACTS_EXCEPT_X:				sprintf(cmd->buf, "AT+CHLD=%d%d", 2, gcc->idx);				break;			case GSMD_CALL_CTRL_M_HELD:				sprintf(cmd->buf, "AT+CHLD=%d", 3);				break;			default:				return -EINVAL;		}		break;	case GSMD_VOICECALL_FWD_DIS:		if(len < sizeof(*gph) + sizeof(int))			return -EINVAL;				reason = (int *) ((void *)gph + sizeof(*gph));		sprintf(buf, "%d,0", *reason);		atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf);		cmd = atcmd_fill("AT+CCFC=", atcmd_len,				 &usock_cmd_cb, gu, gph->id, NULL);		if (!cmd)			return -ENOMEM;		sprintf(cmd->buf, "AT+CCFC=%s", buf);		break;	case GSMD_VOICECALL_FWD_EN:		if(len < sizeof(*gph) + sizeof(int))			return -EINVAL;				reason = (int *) ((void *)gph + sizeof(*gph));		sprintf(buf, "%d,1", *reason);		atcmd_len = 1 + strlen("AT+CCFC=") + strlen(buf);		cmd = atcmd_fill("AT+CCFC=", atcmd_len,				 &usock_cmd_cb, gu, gph->id, NULL);		if (!cmd)			return -ENOMEM;		sprintf(cmd->buf, "AT+CCFC=%s", buf);		break;	case GSMD_VOICECALL_FWD_STAT:		if(len < sizeof(*gph) + sizeof(int))			return -EINVAL;		

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?