cregit-Linux how code gets into the kernel

Release 4.11 drivers/isdn/gigaset/ev-layer.c

/*
 * Stuff used by all variants of the driver
 *
 * Copyright (c) 2001 by Stefan Eilers,
 *                       Hansjoerg Lipp <hjlipp@web.de>,
 *                       Tilman Schmidt <tilman@imap.cc>.
 *
 * =====================================================================
 *      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.
 * =====================================================================
 */

#include <linux/export.h>
#include "gigaset.h"

/* ========================================================== */
/* bit masks for pending commands */

#define PC_DIAL		0x001

#define PC_HUP		0x002

#define PC_INIT		0x004

#define PC_DLE0		0x008

#define PC_DLE1		0x010

#define PC_SHUTDOWN	0x020

#define PC_ACCEPT	0x040

#define PC_CID		0x080

#define PC_NOCID	0x100

#define PC_CIDMODE	0x200

#define PC_UMMODE	0x400

/* types of modem responses */

#define RT_NOTHING	0

#define RT_ZSAU		1

#define RT_RING		2

#define RT_NUMBER	3

#define RT_STRING	4

#define RT_ZCAU		6

/* Possible ASCII responses */

#define RSP_OK		0

#define RSP_ERROR	1

#define RSP_ZGCI	3

#define RSP_RING	4

#define RSP_ZVLS	5

#define RSP_ZCAU	6

/* responses with values to store in at_state */
/* - numeric */

#define RSP_VAR		100

#define RSP_ZSAU	(RSP_VAR + VAR_ZSAU)

#define RSP_ZDLE	(RSP_VAR + VAR_ZDLE)

#define RSP_ZCTP	(RSP_VAR + VAR_ZCTP)
/* - string */

#define RSP_STR		(RSP_VAR + VAR_NUM)

#define RSP_NMBR	(RSP_STR + STR_NMBR)

#define RSP_ZCPN	(RSP_STR + STR_ZCPN)

#define RSP_ZCON	(RSP_STR + STR_ZCON)

#define RSP_ZBC		(RSP_STR + STR_ZBC)

#define RSP_ZHLC	(RSP_STR + STR_ZHLC)


#define RSP_WRONG_CID	-2	
/* unknown cid in cmd */

#define RSP_INVAL	-6	
/* invalid response   */

#define RSP_NODEV	-9	
/* device not connected */


#define RSP_NONE	-19

#define RSP_STRING	-20

#define RSP_NULL	-21

#define RSP_INIT	-27

#define RSP_ANY		-26

#define RSP_LAST	-28

/* actions for process_response */

#define ACT_NOTHING		0

#define ACT_SETDLE1		1

#define ACT_SETDLE0		2

#define ACT_FAILINIT		3

#define ACT_HUPMODEM		4

#define ACT_CONFIGMODE		5

#define ACT_INIT		6

#define ACT_DLE0		7

#define ACT_DLE1		8

#define ACT_FAILDLE0		9

#define ACT_FAILDLE1		10

#define ACT_RING		11

#define ACT_CID			12

#define ACT_FAILCID		13

#define ACT_SDOWN		14

#define ACT_FAILSDOWN		15

#define ACT_DEBUG		16

#define ACT_WARN		17

#define ACT_DIALING		18

#define ACT_ABORTDIAL		19

#define ACT_DISCONNECT		20

#define ACT_CONNECT		21

#define ACT_REMOTEREJECT	22

#define ACT_CONNTIMEOUT		23

#define ACT_REMOTEHUP		24

#define ACT_ABORTHUP		25

#define ACT_ICALL		26

#define ACT_ACCEPTED		27

#define ACT_ABORTACCEPT		28

#define ACT_TIMEOUT		29

#define ACT_GETSTRING		30

#define ACT_SETVER		31

#define ACT_FAILVER		32

#define ACT_GOTVER		33

#define ACT_TEST		34

#define ACT_ERROR		35

#define ACT_ABORTCID		36

#define ACT_ZCAU		37

#define ACT_NOTIFY_BC_DOWN	38

#define ACT_NOTIFY_BC_UP	39

#define ACT_DIAL		40

#define ACT_ACCEPT		41

#define ACT_HUP			43

#define ACT_IF_LOCK		44

#define ACT_START		45

#define ACT_STOP		46

#define ACT_FAKEDLE0		47

#define ACT_FAKEHUP		48

#define ACT_FAKESDOWN		49

#define ACT_SHUTDOWN		50

#define ACT_PROC_CIDMODE	51

#define ACT_UMODESET		52

#define ACT_FAILUMODE		53

#define ACT_CMODESET		54

#define ACT_FAILCMODE		55

#define ACT_IF_VER		56

#define ACT_CMD			100

/* at command sequences */

#define SEQ_NONE	0

#define SEQ_INIT	100

#define SEQ_DLE0	200

#define SEQ_DLE1	250

#define SEQ_CID		300

#define SEQ_NOCID	350

#define SEQ_HUP		400

#define SEQ_DIAL	600

#define SEQ_ACCEPT	720

#define SEQ_SHUTDOWN	500

#define SEQ_CIDMODE	10

#define SEQ_UMMODE	11


/* 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid),
 * 400: hup, 500: reset, 600: dial, 700: ring */

struct reply_t gigaset_tab_nocid[] =
{
/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
 * action, command */

/* initialize device, set cid mode if possible */
	{RSP_INIT,	 -1,  -1, SEQ_INIT,	100,  1, {ACT_TIMEOUT} },

	{EV_TIMEOUT,	100, 100, -1,		101,  3, {0},	"Z\r"},
	{RSP_OK,	101, 103, -1,		120,  5, {ACT_GETSTRING},
								"+GMR\r"},

	{EV_TIMEOUT,	101, 101, -1,		102,  5, {0},	"Z\r"},
	{RSP_ERROR,	101, 101, -1,		102,  5, {0},	"Z\r"},

	{EV_TIMEOUT,	102, 102, -1,		108,  5, {ACT_SETDLE1},
								"^SDLE=0\r"},
	{RSP_OK,	108, 108, -1,		104, -1},
	{RSP_ZDLE,	104, 104,  0,		103,  5, {0},	"Z\r"},
	{EV_TIMEOUT,	104, 104, -1,		  0,  0, {ACT_FAILINIT} },
	{RSP_ERROR,	108, 108, -1,		  0,  0, {ACT_FAILINIT} },

	{EV_TIMEOUT,	108, 108, -1,		105,  2, {ACT_SETDLE0,
							  ACT_HUPMODEM,
							  ACT_TIMEOUT} },
	{EV_TIMEOUT,	105, 105, -1,		103,  5, {0},	"Z\r"},

	{RSP_ERROR,	102, 102, -1,		107,  5, {0},	"^GETPRE\r"},
	{RSP_OK,	107, 107, -1,		  0,  0, {ACT_CONFIGMODE} },
	{RSP_ERROR,	107, 107, -1,		  0,  0, {ACT_FAILINIT} },
	{EV_TIMEOUT,	107, 107, -1,		  0,  0, {ACT_FAILINIT} },

	{RSP_ERROR,	103, 103, -1,		  0,  0, {ACT_FAILINIT} },
	{EV_TIMEOUT,	103, 103, -1,		  0,  0, {ACT_FAILINIT} },

	{RSP_STRING,	120, 120, -1,		121, -1, {ACT_SETVER} },

	{EV_TIMEOUT,	120, 121, -1,		  0,  0, {ACT_FAILVER,
							  ACT_INIT} },
	{RSP_ERROR,	120, 121, -1,		  0,  0, {ACT_FAILVER,
							  ACT_INIT} },
	{RSP_OK,	121, 121, -1,		  0,  0, {ACT_GOTVER,
							  ACT_INIT} },
	{RSP_NONE,	121, 121, -1,		120,  0, {ACT_GETSTRING} },

/* leave dle mode */
	{RSP_INIT,	  0,   0, SEQ_DLE0,	201,  5, {0},	"^SDLE=0\r"},
	{RSP_OK,	201, 201, -1,		202, -1},
	{RSP_ZDLE,	202, 202,  0,		  0,  0, {ACT_DLE0} },
	{RSP_NODEV,	200, 249, -1,		  0,  0, {ACT_FAKEDLE0} },
	{RSP_ERROR,	200, 249, -1,		  0,  0, {ACT_FAILDLE0} },
	{EV_TIMEOUT,	200, 249, -1,		  0,  0, {ACT_FAILDLE0} },

/* enter dle mode */
	{RSP_INIT,	  0,   0, SEQ_DLE1,	251,  5, {0},	"^SDLE=1\r"},
	{RSP_OK,	251, 251, -1,		252, -1},
	{RSP_ZDLE,	252, 252,  1,		  0,  0, {ACT_DLE1} },
	{RSP_ERROR,	250, 299, -1,		  0,  0, {ACT_FAILDLE1} },
	{EV_TIMEOUT,	250, 299, -1,		  0,  0, {ACT_FAILDLE1} },

/* incoming call */
	{RSP_RING,	 -1,  -1, -1,		 -1, -1, {ACT_RING} },

/* get cid */
	{RSP_INIT,	  0,   0, SEQ_CID,	301,  5, {0},	"^SGCI?\r"},
	{RSP_OK,	301, 301, -1,		302, -1},
	{RSP_ZGCI,	302, 302, -1,		  0,  0, {ACT_CID} },
	{RSP_ERROR,	301, 349, -1,		  0,  0, {ACT_FAILCID} },
	{EV_TIMEOUT,	301, 349, -1,		  0,  0, {ACT_FAILCID} },

/* enter cid mode */
	{RSP_INIT,	  0,   0, SEQ_CIDMODE,	150,  5, {0},	"^SGCI=1\r"},
	{RSP_OK,	150, 150, -1,		  0,  0, {ACT_CMODESET} },
	{RSP_ERROR,	150, 150, -1,		  0,  0, {ACT_FAILCMODE} },
	{EV_TIMEOUT,	150, 150, -1,		  0,  0, {ACT_FAILCMODE} },

/* leave cid mode */
	{RSP_INIT,	  0,   0, SEQ_UMMODE,	160,  5, {0},	"Z\r"},
	{RSP_OK,	160, 160, -1,		  0,  0, {ACT_UMODESET} },
	{RSP_ERROR,	160, 160, -1,		  0,  0, {ACT_FAILUMODE} },
	{EV_TIMEOUT,	160, 160, -1,		  0,  0, {ACT_FAILUMODE} },

/* abort getting cid */
	{RSP_INIT,	  0,   0, SEQ_NOCID,	  0,  0, {ACT_ABORTCID} },

/* reset */
	{RSP_INIT,	  0,   0, SEQ_SHUTDOWN,	504,  5, {0},	"Z\r"},
	{RSP_OK,	504, 504, -1,		  0,  0, {ACT_SDOWN} },
	{RSP_ERROR,	501, 599, -1,		  0,  0, {ACT_FAILSDOWN} },
	{EV_TIMEOUT,	501, 599, -1,		  0,  0, {ACT_FAILSDOWN} },
	{RSP_NODEV,	501, 599, -1,		  0,  0, {ACT_FAKESDOWN} },

	{EV_PROC_CIDMODE, -1, -1, -1,		 -1, -1, {ACT_PROC_CIDMODE} },
	{EV_IF_LOCK,	 -1,  -1, -1,		 -1, -1, {ACT_IF_LOCK} },
	{EV_IF_VER,	 -1,  -1, -1,		 -1, -1, {ACT_IF_VER} },
	{EV_START,	 -1,  -1, -1,		 -1, -1, {ACT_START} },
	{EV_STOP,	 -1,  -1, -1,		 -1, -1, {ACT_STOP} },
	{EV_SHUTDOWN,	 -1,  -1, -1,		 -1, -1, {ACT_SHUTDOWN} },

/* misc. */
	{RSP_ERROR,	 -1,  -1, -1,		 -1, -1, {ACT_ERROR} },
	{RSP_ZCAU,	 -1,  -1, -1,		 -1, -1, {ACT_ZCAU} },
	{RSP_NONE,	 -1,  -1, -1,		 -1, -1, {ACT_DEBUG} },
	{RSP_ANY,	 -1,  -1, -1,		 -1, -1, {ACT_WARN} },
	{RSP_LAST}
};

/* 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring,
 * 400: hup, 750: accepted icall */

struct reply_t gigaset_tab_cid[] =
{
/* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout,
 * action, command */

/* dial */
	{EV_DIAL,	 -1,  -1, -1,		 -1, -1, {ACT_DIAL} },
	{RSP_INIT,	  0,   0, SEQ_DIAL,	601,  5, {ACT_CMD + AT_BC} },
	{RSP_OK,	601, 601, -1,		603,  5, {ACT_CMD + AT_PROTO} },
	{RSP_OK,	603, 603, -1,		604,  5, {ACT_CMD + AT_TYPE} },
	{RSP_OK,	604, 604, -1,		605,  5, {ACT_CMD + AT_MSN} },
	{RSP_NULL,	605, 605, -1,		606,  5, {ACT_CMD + AT_CLIP} },
	{RSP_OK,	605, 605, -1,		606,  5, {ACT_CMD + AT_CLIP} },
	{RSP_NULL,	606, 606, -1,		607,  5, {ACT_CMD + AT_ISO} },
	{RSP_OK,	606, 606, -1,		607,  5, {ACT_CMD + AT_ISO} },
	{RSP_OK,	607, 607, -1,		608,  5, {0},	"+VLS=17\r"},
	{RSP_OK,	608, 608, -1,		609, -1},
	{RSP_ZSAU,	609, 609, ZSAU_PROCEEDING, 610, 5, {ACT_CMD + AT_DIAL} },
	{RSP_OK,	610, 610, -1,		650,  0, {ACT_DIALING} },

	{RSP_ERROR,	601, 610, -1,		  0,  0, {ACT_ABORTDIAL} },
	{EV_TIMEOUT,	601, 610, -1,		  0,  0, {ACT_ABORTDIAL} },

/* optional dialing responses */
	{EV_BC_OPEN,	650, 650, -1,		651, -1},
	{RSP_ZVLS,	609, 651, 17,		 -1, -1, {ACT_DEBUG} },
	{RSP_ZCTP,	610, 651, -1,		 -1, -1, {ACT_DEBUG} },
	{RSP_ZCPN,	610, 651, -1,		 -1, -1, {ACT_DEBUG} },
	{RSP_ZSAU,	650, 651, ZSAU_CALL_DELIVERED, -1, -1, {ACT_DEBUG} },

/* connect */
	{RSP_ZSAU,	650, 650, ZSAU_ACTIVE,	800, -1, {ACT_CONNECT} },
	{RSP_ZSAU,	651, 651, ZSAU_ACTIVE,	800, -1, {ACT_CONNECT,
							  ACT_NOTIFY_BC_UP} },
	{RSP_ZSAU,	750, 750, ZSAU_ACTIVE,	800, -1, {ACT_CONNECT} },
	{RSP_ZSAU,	751, 751, ZSAU_ACTIVE,	800, -1, {ACT_CONNECT,
							  ACT_NOTIFY_BC_UP} },
	{EV_BC_OPEN,	800, 800, -1,		800, -1, {ACT_NOTIFY_BC_UP} },

/* remote hangup */
	{RSP_ZSAU,	650, 651, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT} },
	{RSP_ZSAU,	750, 751, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },
	{RSP_ZSAU,	800, 800, ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP} },

/* hangup */
	{EV_HUP,	 -1,  -1, -1,		 -1, -1, {ACT_HUP} },
	{RSP_INIT,	 -1,  -1, SEQ_HUP,	401,  5, {0},	"+VLS=0\r"},
	{RSP_OK,	401, 401, -1,		402,  5},
	{RSP_ZVLS,	402, 402,  0,		403,  5},
	{RSP_ZSAU,	403, 403, ZSAU_DISCONNECT_REQ, -1, -1, {ACT_DEBUG} },
	{RSP_ZSAU,	403, 403, ZSAU_NULL,	  0,  0, {ACT_DISCONNECT} },
	{RSP_NODEV,	401, 403, -1,		  0,  0, {ACT_FAKEHUP} },
	{RSP_ERROR,	401, 401, -1,		  0,  0, {ACT_ABORTHUP} },
	{EV_TIMEOUT,	401, 403, -1,		  0,  0, {ACT_ABORTHUP} },

	{EV_BC_CLOSED,	  0,   0, -1,		  0, -1, {ACT_NOTIFY_BC_DOWN} },

/* ring */
	{RSP_ZBC,	700, 700, -1,		 -1, -1, {0} },
	{RSP_ZHLC,	700, 700, -1,		 -1, -1, {0} },
	{RSP_NMBR,	700, 700, -1,		 -1, -1, {0} },
	{RSP_ZCPN,	700, 700, -1,		 -1, -1, {0} },
	{RSP_ZCTP,	700, 700, -1,		 -1, -1, {0} },
	{EV_TIMEOUT,	700, 700, -1,		720, 720, {ACT_ICALL} },
	{EV_BC_CLOSED,	720, 720, -1,		  0, -1, {ACT_NOTIFY_BC_DOWN} },

/*accept icall*/
	{EV_ACCEPT,	 -1,  -1, -1,		 -1, -1, {ACT_ACCEPT} },
	{RSP_INIT,	720, 720, SEQ_ACCEPT,	721,  5, {ACT_CMD + AT_PROTO} },
	{RSP_OK,	721, 721, -1,		722,  5, {ACT_CMD + AT_ISO} },
	{RSP_OK,	722, 722, -1,		723,  5, {0},	"+VLS=17\r"},
	{RSP_OK,	723, 723, -1,		724,  5, {0} },
	{RSP_ZVLS,	724, 724, 17,		750, 50, {ACT_ACCEPTED} },
	{RSP_ERROR,	721, 729, -1,		  0,  0, {ACT_ABORTACCEPT} },
	{EV_TIMEOUT,	721, 729, -1,		  0,  0, {ACT_ABORTACCEPT} },
	{RSP_ZSAU,	700, 729, ZSAU_NULL,	  0,  0, {ACT_ABORTACCEPT} },
	{RSP_ZSAU,	700, 729, ZSAU_ACTIVE,	  0,  0, {ACT_ABORTACCEPT} },
	{RSP_ZSAU,	700, 729, ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT} },

	{EV_BC_OPEN,	750, 750, -1,		751, -1},
	{EV_TIMEOUT,	750, 751, -1,		  0,  0, {ACT_CONNTIMEOUT} },

/* B channel closed (general case) */
	{EV_BC_CLOSED,	 -1,  -1, -1,		 -1, -1, {ACT_NOTIFY_BC_DOWN} },

/* misc. */
	{RSP_ZCON,	 -1,  -1, -1,		 -1, -1, {ACT_DEBUG} },
	{RSP_ZCAU,	 -1,  -1, -1,		 -1, -1, {ACT_ZCAU} },
	{RSP_NONE,	 -1,  -1, -1,		 -1, -1, {ACT_DEBUG} },
	{RSP_ANY,	 -1,  -1, -1,		 -1, -1, {ACT_WARN} },
	{RSP_LAST}
};



static const struct resp_type_t {
	
char	*response;
	
int	resp_code;
	
int	type;
}

resp_type[] =
{
	{"OK",		RSP_OK,		RT_NOTHING},
	{"ERROR",	RSP_ERROR,	RT_NOTHING},
	{"ZSAU",	RSP_ZSAU,	RT_ZSAU},
	{"ZCAU",	RSP_ZCAU,	RT_ZCAU},
	{"RING",	RSP_RING,	RT_RING},
	{"ZGCI",	RSP_ZGCI,	RT_NUMBER},
	{"ZVLS",	RSP_ZVLS,	RT_NUMBER},
	{"ZCTP",	RSP_ZCTP,	RT_NUMBER},
	{"ZDLE",	RSP_ZDLE,	RT_NUMBER},
	{"ZHLC",	RSP_ZHLC,	RT_STRING},
	{"ZBC",		RSP_ZBC,	RT_STRING},
	{"NMBR",	RSP_NMBR,	RT_STRING},
	{"ZCPN",	RSP_ZCPN,	RT_STRING},
	{"ZCON",	RSP_ZCON,	RT_STRING},
	{NULL,		0,		0}
};


static const struct zsau_resp_t {
	
char	*str;
	
int	code;
}

zsau_resp[] =
{
	{"OUTGOING_CALL_PROCEEDING",	ZSAU_PROCEEDING},
	{"CALL_DELIVERED",		ZSAU_CALL_DELIVERED},
	{"ACTIVE",			ZSAU_ACTIVE},
	{"DISCONNECT_IND",		ZSAU_DISCONNECT_IND},
	{"NULL",			ZSAU_NULL},
	{"DISCONNECT_REQ",		ZSAU_DISCONNECT_REQ},
	{NULL,				ZSAU_UNKNOWN}
};

/* check for and remove fixed string prefix
 * If s starts with prefix terminated by a non-alphanumeric character,
 * return pointer to the first character after that, otherwise return NULL.
 */

static char *skip_prefix(char *s, const char *prefix) { while (*prefix) if (*s++ != *prefix++) return NULL; if (isalnum(*s)) return NULL; return s; }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt48100.00%1100.00%
Total48100.00%1100.00%

/* queue event with CID */
static void add_cid_event(struct cardstate *cs, int cid, int type, void *ptr, int parameter) { unsigned long flags; unsigned next, tail; struct event_t *event; gig_dbg(DEBUG_EVENT, "queueing event %d for cid %d", type, cid); spin_lock_irqsave(&cs->ev_lock, flags); tail = cs->ev_tail; next = (tail + 1) % MAX_EVENTS; if (unlikely(next == cs->ev_head)) { dev_err(cs->dev, "event queue full\n"); kfree(ptr); } else { event = cs->events + tail; event->type = type; event->cid = cid; event->ptr = ptr; event->arg = NULL; event->parameter = parameter; event->at_state = NULL; cs->ev_tail = next; } spin_unlock_irqrestore(&cs->ev_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt165100.00%1100.00%
Total165100.00%1100.00%

/** * gigaset_handle_modem_response() - process received modem response * @cs: device descriptor structure. * * Called by asyncdata/isocdata if a block of data received from the * device must be processed as a modem command response. The data is * already in the cs structure. */
void gigaset_handle_modem_response(struct cardstate *cs) { char *eoc, *psep, *ptr; const struct resp_type_t *rt; const struct zsau_resp_t *zr; int cid, parameter; u8 type, value; if (!cs->cbytes) { /* ignore additional LFs/CRs (M10x config mode or cx100) */ gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[0]); return; } cs->respdata[cs->cbytes] = 0; if (cs->at_state.getstring) { /* state machine wants next line verbatim */ cs->at_state.getstring = 0; ptr = kstrdup(cs->respdata, GFP_ATOMIC); gig_dbg(DEBUG_EVENT, "string==%s", ptr ? ptr : "NULL"); add_cid_event(cs, 0, RSP_STRING, ptr, 0); return; } /* look up response type */ for (rt = resp_type; rt->response; ++rt) { eoc = skip_prefix(cs->respdata, rt->response); if (eoc) break; } if (!rt->response) { add_cid_event(cs, 0, RSP_NONE, NULL, 0); gig_dbg(DEBUG_EVENT, "unknown modem response: '%s'\n", cs->respdata); return; } /* check for CID */ psep = strrchr(cs->respdata, ';'); if (psep && !kstrtoint(psep + 1, 10, &cid) && cid >= 1 && cid <= 65535) { /* valid CID: chop it off */ *psep = 0; } else { /* no valid CID: leave unchanged */ cid = 0; } gig_dbg(DEBUG_EVENT, "CMD received: %s", cs->respdata); if (cid) gig_dbg(DEBUG_EVENT, "CID: %d", cid); switch (rt->type) { case RT_NOTHING: /* check parameter separator */ if (*eoc) goto bad_param; /* extra parameter */ add_cid_event(cs, cid, rt->resp_code, NULL, 0); break; case RT_RING: /* check parameter separator */ if (!*eoc) eoc = NULL; /* no parameter */ else if (*eoc++ != ',') goto bad_param; add_cid_event(cs, 0, rt->resp_code, NULL, cid); /* process parameters as individual responses */ while (eoc) { /* look up parameter type */ psep = NULL; for (rt = resp_type; rt->response; ++rt) { psep = skip_prefix(eoc, rt->response); if (psep) break; } /* all legal parameters are of type RT_STRING */ if (!psep || rt->type != RT_STRING) { dev_warn(cs->dev, "illegal RING parameter: '%s'\n", eoc); return; } /* skip parameter value separator */ if (*psep++ != '=') goto bad_param; /* look up end of parameter */ eoc = strchr(psep, ','); if (eoc) *eoc++ = 0; /* retrieve parameter value */ ptr = kstrdup(psep, GFP_ATOMIC); /* queue event */ add_cid_event(cs, cid, rt->resp_code, ptr, 0); } break; case RT_ZSAU: /* check parameter separator */ if (!*eoc) { /* no parameter */ add_cid_event(cs, cid, rt->resp_code, NULL, ZSAU_NONE); break; } if (*eoc++ != '=') goto bad_param; /* look up parameter value */ for (zr = zsau_resp; zr->str; ++zr) if (!strcmp(eoc, zr->str)) break; if (!zr->str) goto bad_param; add_cid_event(cs, cid, rt->resp_code, NULL, zr->code); break; case RT_STRING: /* check parameter separator */ if (*eoc++ != '=') goto bad_param; /* retrieve parameter value */ ptr = kstrdup(eoc, GFP_ATOMIC); /* queue event */ add_cid_event(cs, cid, rt->resp_code, ptr, 0); break; case RT_ZCAU: /* check parameter separators */ if (*eoc++ != '=') goto bad_param; psep = strchr(eoc, ','); if (!psep) goto bad_param; *psep++ = 0; /* decode parameter values */ if (kstrtou8(eoc, 16, &type) || kstrtou8(psep, 16, &value)) { *--psep = ','; goto bad_param; } parameter = (type << 8) | value; add_cid_event(cs, cid, rt->resp_code, NULL, parameter); break; case RT_NUMBER: /* check parameter separator */ if (*eoc++ != '=') goto bad_param; /* decode parameter value */ if (kstrtoint(eoc, 10, &parameter)) goto bad_param; /* special case ZDLE: set flag before queueing event */ if (rt->resp_code == RSP_ZDLE) cs->dle = parameter; add_cid_event(cs, cid, rt->resp_code, NULL, parameter); break; bad_param: /* parameter unexpected, incomplete or malformed */ dev_warn(cs->dev, "bad parameter in response '%s'\n", cs->respdata); add_cid_event(cs, cid, rt->resp_code, NULL, -1); break; default: dev_err(cs->dev, "%s: internal error on '%s'\n", __func__, cs->respdata); } }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt59170.95%872.73%
Hansjoerg Lipp22026.41%19.09%
Andy Shevchenko182.16%19.09%
Alexey Dobriyan40.48%19.09%
Total833100.00%11100.00%

EXPORT_SYMBOL_GPL(gigaset_handle_modem_response); /* disconnect_nobc * process closing of connection associated with given AT state structure * without B channel */
static void disconnect_nobc(struct at_state_t **at_state_p, struct cardstate *cs) { unsigned long flags; spin_lock_irqsave(&cs->lock, flags); ++(*at_state_p)->seq_index; /* revert to selected idle mode */ if (!cs->cidmode) { cs->at_state.pending_commands |= PC_UMMODE; gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE"); cs->commands_pending = 1; } /* check for and deallocate temporary AT state */ if (!list_empty(&(*at_state_p)->list)) { list_del(&(*at_state_p)->list); kfree(*at_state_p); *at_state_p = NULL; } spin_unlock_irqrestore(&cs->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt9378.15%150.00%
Hansjoerg Lipp2621.85%150.00%
Total119100.00%2100.00%

/* disconnect_bc * process closing of connection associated with given AT state structure * and B channel */
static void disconnect_bc(struct at_state_t *at_state, struct cardstate *cs, struct bc_state *bcs) { unsigned long flags; spin_lock_irqsave(&cs->lock, flags); ++at_state->seq_index; /* revert to selected idle mode */ if (!cs->cidmode) { cs->at_state.pending_commands |= PC_UMMODE; gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE"); cs->commands_pending = 1; } spin_unlock_irqrestore(&cs->lock, flags); /* invoke hardware specific handler */ cs->ops->close_bchannel(bcs); /* notify LL */ if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); gigaset_isdn_hupD(bcs); } }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt8468.85%787.50%
Hansjoerg Lipp3831.15%112.50%
Total122100.00%8100.00%

/* get_free_channel * get a free AT state structure: either one of those associated with the * B channels of the Gigaset device, or if none of those is available, * a newly allocated one with bcs=NULL * The structure should be freed by calling disconnect_nobc() after use. */
static inline struct at_state_t *get_free_channel(struct cardstate *cs, int cid) /* cids: >0: siemens-cid * 0: without cid * -1: no cid assigned yet */ { unsigned long flags; int i; struct at_state_t *ret; for (i = 0; i < cs->channels; ++i) if (gigaset_get_channel(cs->bcs + i) >= 0) { ret = &cs->bcs[i].at_state; ret->cid = cid; return ret; } spin_lock_irqsave(&cs->lock, flags); ret = kmalloc(sizeof(struct at_state_t), GFP_ATOMIC); if (ret) { gigaset_at_init(ret, NULL, cs, cid); list_add(&ret->list, &cs->temp_at_states); } spin_unlock_irqrestore(&cs->lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp14497.96%133.33%
Tilman Schmidt32.04%266.67%
Total147100.00%3100.00%


static void init_failed(struct cardstate *cs, int mode) { int i; struct at_state_t *at_state; cs->at_state.pending_commands &= ~PC_INIT; cs->mode = mode; cs->mstate = MS_UNINITIALIZED; gigaset_free_channels(cs); for (i = 0; i < cs->channels; ++i) { at_state = &cs->bcs[i].at_state; if (at_state->pending_commands & PC_CID) { at_state->pending_commands &= ~PC_CID; at_state->pending_commands |= PC_NOCID; cs->commands_pending = 1; } } }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp10397.17%150.00%
Tilman Schmidt32.83%150.00%
Total106100.00%2100.00%


static void schedule_init(struct cardstate *cs, int state) { if (cs->at_state.pending_commands & PC_INIT) { gig_dbg(DEBUG_EVENT, "not scheduling PC_INIT again"); return; } cs->mstate = state; cs->mode = M_UNKNOWN; gigaset_block_channels(cs); cs->at_state.pending_commands |= PC_INIT; gig_dbg(DEBUG_EVENT, "Scheduling PC_INIT"); cs->commands_pending = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp6083.33%125.00%
Tilman Schmidt1216.67%375.00%
Total72100.00%4100.00%

/* send an AT command * adding the "AT" prefix, cid and DLE encapsulation as appropriate */
static void send_command(struct cardstate *cs, const char *cmd, struct at_state_t *at_state) { int cid = at_state->cid; struct cmdbuf_t *cb; size_t buflen; buflen = strlen(cmd) + 12; /* DLE ( A T 1 2 3 4 5 <cmd> DLE ) \0 */ cb = kmalloc(sizeof(struct cmdbuf_t) + buflen, GFP_ATOMIC); if (!cb) { dev_err(cs->dev, "%s: out of memory\n", __func__); return; } if (cid > 0 && cid <= 65535) cb->len = snprintf(cb->buf, buflen, cs->dle ? "\020(AT%d%s\020)" : "AT%d%s", cid, cmd); else cb->len = snprintf(cb->buf, buflen, cs->dle ? "\020(AT%s\020)" : "AT%s", cmd); cb->offset = 0; cb->next = NULL; cb->wake_tasklet = NULL; cs->ops->write_cmd(cs, cb); }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt9858.33%375.00%
Hansjoerg Lipp7041.67%125.00%
Total168100.00%4100.00%


static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid) { struct at_state_t *at_state; int i; unsigned long flags; if (cid == 0) return &cs->at_state; for (i = 0; i < cs->channels; ++i) if (cid == cs->bcs[i].at_state.cid) return &cs->bcs[i].at_state; spin_lock_irqsave(&cs->lock, flags); list_for_each_entry(at_state, &cs->temp_at_states, list) if (cid == at_state->cid) { spin_unlock_irqrestore(&cs->lock, flags); return at_state; } spin_unlock_irqrestore(&cs->lock, flags); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp135100.00%1100.00%
Total135100.00%1100.00%


static void bchannel_down(struct bc_state *bcs) { if (bcs->chstate & CHS_B_UP) { bcs->chstate &= ~CHS_B_UP; gigaset_isdn_hupB(bcs); } if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); gigaset_isdn_hupD(bcs); } gigaset_free_channel(bcs); gigaset_bcs_reinit(bcs); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp7197.26%150.00%
Tilman Schmidt22.74%150.00%
Total73100.00%2100.00%


static void bchannel_up(struct bc_state *bcs) { if (bcs->chstate & CHS_B_UP) { dev_notice(bcs->cs->dev, "%s: B channel already up\n", __func__); return; } bcs->chstate |= CHS_B_UP; gigaset_isdn_connB(bcs); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp3780.43%133.33%
Tilman Schmidt919.57%266.67%
Total46100.00%3100.00%


static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index) { struct bc_state *bcs = at_state->bcs; struct cardstate *cs = at_state->cs; char **commands = data; unsigned long flags; int i; bcs->chstate |= CHS_NOTIFY_LL; spin_lock_irqsave(&cs->lock, flags); if (at_state->seq_index != seq_index) { spin_unlock_irqrestore(&cs->lock, flags); goto error; } spin_unlock_irqrestore(&cs->lock, flags); for (i = 0; i < AT_NUM; ++i) { kfree(bcs->commands[i]); bcs->commands[i] = commands[i]; } at_state->pending_commands |= PC_CID; gig_dbg(DEBUG_EVENT, "Scheduling PC_CID"); cs->commands_pending = 1; return; error: for (i = 0; i < AT_NUM; ++i) { kfree(commands[i]); commands[i] = NULL; } at_state->pending_commands |= PC_NOCID; gig_dbg(DEBUG_EVENT, "Scheduling PC_NOCID"); cs->commands_pending = 1; return; }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt11555.29%583.33%
Hansjoerg Lipp9344.71%116.67%
Total208100.00%6100.00%


static void start_accept(struct at_state_t *at_state) { struct cardstate *cs = at_state->cs; struct bc_state *bcs = at_state->bcs; int i; for (i = 0; i < AT_NUM; ++i) { kfree(bcs->commands[i]); bcs->commands[i] = NULL; } bcs->commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC); bcs->commands[AT_ISO] = kmalloc(9, GFP_ATOMIC); if (!bcs->commands[AT_PROTO] || !bcs->commands[AT_ISO]) { dev_err(at_state->cs->dev, "out of memory\n"); /* error reset */ at_state->pending_commands |= PC_HUP; gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP"); cs->commands_pending = 1; return; } snprintf(bcs->commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2); snprintf(bcs->commands[AT_ISO], 9, "^SISO=%u\r", bcs->channel + 1); at_state->pending_commands |= PC_ACCEPT; gig_dbg(DEBUG_EVENT, "Scheduling PC_ACCEPT"); cs->commands_pending = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt14470.94%480.00%
Hansjoerg Lipp5929.06%120.00%
Total203100.00%5100.00%


static void do_start(struct cardstate *cs) { gigaset_free_channels(cs); if (cs->mstate != MS_LOCKED) schedule_init(cs, MS_INIT); cs->isdn_up = 1; gigaset_isdn_start(cs); cs->waiting = 0; wake_up(&cs->waitqueue); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp4987.50%133.33%
Tilman Schmidt712.50%266.67%
Total56100.00%3100.00%


static void finish_shutdown(struct cardstate *cs) { if (cs->mstate != MS_LOCKED) { cs->mstate = MS_UNINITIALIZED; cs->mode = M_UNKNOWN; } /* Tell the LL that the device is not available .. */ if (cs->isdn_up) { cs->isdn_up = 0; gigaset_isdn_stop(cs); } /* The rest is done by cleanup_cs() in process context. */ cs->cmd_result = -ENODEV; cs->waiting = 0; wake_up(&cs->waitqueue); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp5168.00%116.67%
Tilman Schmidt2432.00%583.33%
Total75100.00%6100.00%


static void do_shutdown(struct cardstate *cs) { gigaset_block_channels(cs); if (cs->mstate == MS_READY) { cs->mstate = MS_SHUTDOWN; cs->at_state.pending_commands |= PC_SHUTDOWN; gig_dbg(DEBUG_EVENT, "Scheduling PC_SHUTDOWN"); cs->commands_pending = 1; } else finish_shutdown(cs); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp5084.75%125.00%
Tilman Schmidt915.25%375.00%
Total59100.00%4100.00%


static void do_stop(struct cardstate *cs) { unsigned long flags; spin_lock_irqsave(&cs->lock, flags); cs->connected = 0; spin_unlock_irqrestore(&cs->lock, flags); do_shutdown(cs); }

Contributors

PersonTokensPropCommitsCommitProp
Tilman Schmidt3065.22%150.00%
Hansjoerg Lipp1634.78%150.00%
Total46100.00%2100.00%

/* Entering cid mode or getting a cid failed: * try to initialize the device and try again. * * channel >= 0: getting cid for the channel failed * channel < 0: entering cid mode failed * * returns 0 on success, <0 on failure */
static int reinit_and_retry(struct cardstate *cs, int channel) { int i; if (--cs->retry_count <= 0) return -EFAULT; for (i = 0; i < cs->channels; ++i) if (cs->bcs[i].at_state.cid > 0) return -EBUSY; if (channel < 0) dev_warn(cs->dev, "Could not enter cid mode. Reinit device and try again.\n"); else { dev_warn(cs->dev, "Could not get a call id. Reinit device and try again.\n"); cs->bcs[channel].at_state.pending_commands |= PC_CID; } schedule_init(cs, MS_INIT); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp9785.09%133.33%
Tilman Schmidt1714.91%266.67%
Total114100.00%3100.00%


static int at_state_invalid(struct cardstate *cs, struct at_state_t *test_ptr) { unsigned long flags; unsigned channel; struct at_state_t *at_state; int retval = 0; spin_lock_irqsave(&cs->lock, flags); if (test_ptr == &cs->at_state) goto exit; list_for_each_entry(at_state, &cs->temp_at_states, list) if (at_state == test_ptr) goto exit; for (channel = 0; channel < cs->channels; ++channel) if (&cs->bcs[channel].at_state == test_ptr) goto exit; retval = 1; exit: spin_unlock_irqrestore(&cs->lock, flags); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp123100.00%1100.00%
Total123100.00%1100.00%


static void handle_icall(struct cardstate *cs, struct bc_state *bcs, struct at_state_t *at_state) { int retval; retval = gigaset_isdn_icall(at_state); switch (retval) { case ICALL_ACCEPT: break; default: dev_err(cs->dev, "internal error: disposition=%d\n", retval); /* --v-- fall through --v-- */ case ICALL_IGNORE: case ICALL_REJECT: /* hang up actively * Device doc says that would reject the call. * In fact it doesn't. */ at_state->pending_commands |= PC_HUP; cs->commands_pending = 1; break; } }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp6689.19%125.00%
Tilman Schmidt810.81%375.00%
Total74100.00%4100.00%


static int do_lock(struct cardstate *cs) { int mode; int i; switch (cs->mstate) { case MS_UNINITIALIZED: case MS_READY: if (cs->cur_at_seq || !list_empty(&cs->temp_at_states) || cs->at_state.pending_commands) return -EBUSY; for (i = 0; i < cs->channels; ++i) if (cs->bcs[i].at_state.pending_commands) return -EBUSY; if (gigaset_get_channels(cs) < 0) return -EBUSY; break; case MS_LOCKED: break; default: return -EBUSY; } mode = cs->mode; cs->mstate = MS_LOCKED; cs->mode = M_UNKNOWN; return mode; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp12896.97%133.33%
Tilman Schmidt43.03%266.67%
Total132100.00%3100.00%


static int do_unlock(struct cardstate *cs) { if (cs->mstate != MS_LOCKED) return -EINVAL; cs->mstate = MS_UNINITIALIZED; cs->mode = M_UNKNOWN; gigaset_free_channels(cs); if (cs->connected) schedule_init(cs, MS_INIT); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp5496.43%150.00%
Tilman Schmidt23.57%150.00%
Total56100.00%2100.00%


static void do_action(int action, struct cardstate *cs, struct bc_state *bcs, struct at_state_t **p_at_state, char **pp_command, int *p_genresp, int *p_resp_code, struct event_t *ev) { struct at_state_t *at_state = *p_at_state; struct bc_state *bcs2; unsigned long flags; int channel; unsigned char *s, *e; int i; unsigned long val; switch (action) { case ACT_NOTHING: break; case ACT_TIMEOUT: at_state->waiting = 1; break; case ACT_INIT: cs->at_state.pending_commands &= ~PC_INIT; cs->cur_at_seq = SEQ_NONE; cs->mode = M_UNIMODEM; spin_lock_irqsave(&cs->lock, flags); if (!cs->cidmode) { spin_unlock_irqrestore(&cs->lock, flags); gigaset_free_channels(cs); cs->mstate = MS_READY; break; } spin_unlock_irqrestore(&cs->lock, flags); cs->at_state.pending_commands |= PC_CIDMODE; gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE"); cs->commands_pending = 1; break; case ACT_FAILINIT: dev_warn(cs->dev, "Could not initialize the device.\n"); cs->dle = 0; init_failed(cs, M_UNKNOWN); cs->cur_at_seq = SEQ_NONE; break; case ACT_CONFIGMODE: init_failed(cs, M_CONFIG); cs->cur_at_seq = SEQ_NONE; break; case ACT_SETDLE1: cs->dle = 1; /* cs->inbuf[0].inputstate |= INS_command | INS_DLE_command; */ cs->inbuf[0].inputstate &= ~(INS_command | INS_DLE_command); break; case ACT_SETDLE0: cs->dle = 0; cs->inbuf[0].inputstate = (cs->inbuf[0].inputstate & ~INS_DLE_command) | INS_command; break; case ACT_CMODESET: if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) { gigaset_free_channels(cs); cs->mstate = MS_READY; } cs->mode = M_CID; cs->cur_at_seq = SEQ_NONE; break; case ACT_UMODESET: cs->mode = M_UNIMODEM; cs->cur_at_seq = SEQ_NONE; break; case ACT_FAILCMODE: cs->cur_at_seq = SEQ_NONE; if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) { init_failed(cs, M_UNKNOWN); break; } if (reinit_and_retry(cs, -1) < 0) schedule_init(cs, MS_RECOVER); break; case ACT_FAILUMODE: cs->cur_at_seq = SEQ_NONE; schedule_init(cs, MS_RECOVER); break; case ACT_HUPMODEM: /* send "+++" (hangup in unimodem mode) */ if (cs->connected) { struct cmdbuf_t *cb; cb = kmalloc(sizeof(struct cmdbuf_t) + 3, GFP_ATOMIC); if (!cb) { dev_err(cs->dev, "%s: out of memory\n", __func__); return; } memcpy(cb->buf, "+++", 3); cb->len = 3; cb->offset = 0; cb->next = NULL; cb->wake_tasklet = NULL; cs->ops->write_cmd(cs, cb); } break; case ACT_RING: /* get fresh AT state structure for new CID */ at_state = get_free_channel(cs, ev->parameter); if (!at_state) { dev_warn(cs->dev, "RING ignored: could not allocate channel structure\n"); break; } /* initialize AT state structure * note that bcs may be NULL if no B channel is free */ at_state->ConState = 700; for (i = 0; i < STR_NUM; ++i) { kfree(at_state->str_var[i]); at_state->str_var[i] = NULL; } at_state->int_var[VAR_ZCTP] = -1; spin_lock_irqsave(&cs->lock, flags); at_state->timer_expires = RING_TIMEOUT; at_state->timer_active = 1; spin_unlock_irqrestore(&cs->lock, flags); break; case ACT_ICALL: handle_icall(cs, bcs, at_state); break; case ACT_FAILSDOWN: dev_warn(cs->dev, "Could not shut down the device.\n"); /* fall through */ case ACT_FAKESDOWN: case ACT_SDOWN: cs->cur_at_seq = SEQ_NONE; finish_shutdown(cs); break; case ACT_CONNECT: if (cs->onechannel) { at_state->pending_commands |= PC_DLE1; cs->commands_pending = 1; break; } bcs->chstate |= CHS_D_UP; gigaset_isdn_connD(bcs); cs->ops->init_bchannel(bcs); break; case ACT_DLE1: cs->cur_at_seq = SEQ_NONE; bcs = cs->bcs + cs->curchannel; bcs->chstate |= CHS_D_UP; gigaset_isdn_connD(bcs); cs->ops->init_bchannel(bcs); break; case ACT_FAKEHUP: at_state->int_var[VAR_ZSAU] = ZSAU_NULL; /* fall through */ case ACT_DISCONNECT: cs->cur_at_seq = SEQ_NONE; at_state->cid = -1; if (!bcs) { disconnect_nobc(p_at_state, cs); } else if (cs->onechannel && cs->dle) { /* Check for other open channels not needed: * DLE only used for M10x with one B channel. */ at_state->pending_commands |= PC_DLE0; cs->commands_pending = 1; } else { disconnect_bc(at_state, cs, bcs); } break; case ACT_FAKEDLE0: at_state->int_var[VAR_ZDLE] = 0; cs->dle = 0; /* fall through */ case ACT_DLE0: cs->cur_at_seq = SEQ_NONE; bcs2 = cs->bcs + cs->curchannel; disconnect_bc(&bcs2->at_state, cs, bcs2); break; case ACT_ABORTHUP: cs->cur_at_seq = SEQ_NONE; dev_warn(cs->dev, "Could not hang up.\n"); at_state->cid = -1; if (!bcs) disconnect_nobc(p_at_state, cs); else if (cs->onechannel) at_state->pending_commands |= PC_DLE0; else disconnect_bc(at_state, cs, bcs); schedule_init(cs, MS_RECOVER); break; case ACT_FAILDLE0: cs->cur_at_seq = SEQ_NONE; dev_warn(cs->dev, "Error leaving DLE mode.\n"); cs->dle = 0; bcs2 = cs->bcs + cs->curchannel; disconnect_bc(&bcs2->at_state, cs, bcs2); schedule_init(cs, MS_RECOVER); break; case ACT_FAILDLE1: cs->cur_at_seq = SEQ_NONE; dev_warn(cs->dev, "Could not enter DLE mode. Trying to hang up.\n"); channel = cs->curchannel; cs->bcs[channel].at_state.pending_commands |= PC_HUP; cs->commands_pending = 1; break; case ACT_CID: /* got cid; start dialing */ cs->cur_at_seq = SEQ_NONE; channel = cs->curchannel; if (ev->parameter > 0 && ev->parameter <= 65535) { cs->bcs[channel].at_state.cid = ev->parameter; cs->bcs[channel].at_state.pending_commands |= PC_DIAL; cs->commands_pending = 1; break; } /* bad cid: fall through */ case ACT_FAILCID: cs->cur_at_seq = SEQ_NONE; channel = cs->curchannel; if (reinit_and_retry(cs, channel) < 0) { dev_warn(cs->dev, "Could not get a call ID. Cannot dial.\n"); bcs2 = cs->bcs + channel; disconnect_bc(&bcs2->at_state, cs, bcs2); } break; case ACT_ABORTCID: cs->cur_at_seq = SEQ_NONE; bcs2 = cs->bcs + cs->curchannel; disconnect_bc(&bcs2->at_state, cs, bcs2); break; case ACT_DIALING: case ACT_ACCEPTED: cs->cur_at_seq = SEQ_NONE; break; case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL procssng */ if (bcs) disconnect_bc(at_state, cs, bcs); else disconnect_nobc(p_at_state, cs); break; case ACT_ABORTDIAL: /* error/timeout during dial preparation */ cs->cur_at_seq = SEQ_NONE; at_state->pending_commands |= PC_HUP; cs->commands_pending = 1; break; case ACT_REMOTEREJECT: /* DISCONNECT_IND after dialling */ case ACT_CONNTIMEOUT: /* timeout waiting for ZSAU=ACTIVE */ case ACT_REMOTEHUP: /* DISCONNECT_IND with established connection */ at_state->pending_commands |= PC_HUP; cs->commands_pending = 1; break; case ACT_GETSTRING: /* warning: RING, ZDLE, ... are not handled properly anymore */ at_state->getstring = 1; break; case ACT_SETVER: if (!ev->ptr) { *p_genresp = 1; *p_resp_code = RSP_ERROR; break; } s = ev->ptr; if (!strcmp(s, "OK")) { /* OK without version string: assume old response */ *p_genresp = 1; *p_resp_code = RSP_NONE; break; } for (i = 0; i < 4; ++i) { val = simple_strtoul(s, (char **) &e, 10); if (val > INT_MAX || e == s) break; if (i == 3) { if (*e) break; } else if (*e != '.') break; else s = e + 1; cs->fwver[i] = val; } if (i != 4) { *p_genresp = 1; *p_resp_code = RSP_ERROR; break; } cs->gotfwver = 0; break; case ACT_GOTVER: if (cs->gotfwver == 0) { cs->gotfwver = 1; gig_dbg(DEBUG_EVENT, "firmware version %02d.%03d.%02d.%02d", cs->fwver[0], cs->fwver[1], cs->fwver[2], cs->fwver[3]); break; } /* fall through */ case ACT_FAILVER: cs->gotfwver = -1; dev_err(cs->dev, "could not read firmware version.\n"); break; case ACT_ERROR: gig_dbg(DEBUG_ANY, "%s: ERROR response in ConState %d", __func__, at_state->ConState); cs->cur_at_seq = SEQ_NONE; break; case ACT_DEBUG: gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d", __func__, ev->type, at_state->ConState); break; case ACT_WARN: dev_warn(cs->dev, "%s: resp_code %d in ConState %d!\n", __func__, ev->type, at_state->ConState); break; case ACT_ZCAU: dev_warn(cs->dev, "cause code %04x in connection state %d.\n", ev->parameter, at_state->ConState); break; /* events from the LL */ case ACT_DIAL: if (!ev->ptr) { *p_genresp = 1; *p_resp_code = RSP_ERROR; break; } start_dial(at_state, ev->ptr, ev->parameter); break; case ACT_ACCEPT: start_accept(at_state); break; case ACT_HUP: at_state->pending_commands |= PC_HUP; gig_dbg(DEBUG_EVENT, "Scheduling PC_HUP"); cs->commands_pending = 1; break; /* hotplug events */ case ACT_STOP: do_stop(cs); break; case ACT_START: do_start(cs); break; /* events from the interface */ case ACT_IF_LOCK: cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs); cs->waiting = 0; wake_up(&cs->waitqueue); break; case ACT_IF_VER: if (ev->parameter != 0) cs->cmd_result = -EINVAL; else if (cs->gotfwver != 1) { cs->cmd_result = -ENOENT; } else { memcpy(ev->arg, cs->fwver, sizeof cs->fwver); cs->cmd_result = 0; } cs->waiting = 0; wake_up(&cs->waitqueue); break; /* events from the proc file system */ case ACT_PROC_CIDMODE: spin_lock_irqsave(&cs->lock, flags); if (ev->parameter != cs->cidmode) { cs->cidmode = ev->parameter; if (ev->parameter) { cs->at_state.pending_commands |= PC_CIDMODE; gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE"); } else { cs->at_state.pending_commands |= PC_UMMODE; gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE"); } cs->commands_pending = 1; } spin_unlock_irqrestore(&cs->lock, flags); cs->waiting = 0; wake_up(&cs->waitqueue); break; /* events from the hardware drivers */ case ACT_NOTIFY_BC_DOWN: bchannel_down(bcs); break; case ACT_NOTIFY_BC_UP: bchannel_up(bcs); break; case ACT_SHUTDOWN: do_shutdown(cs); break; default: if (action >= ACT_CMD && action < ACT_CMD + AT_NUM) { *pp_command = at_state->bcs->commands[action - ACT_CMD]; if (!*pp_command) { *p_genresp = 1; *p_resp_code = RSP_NULL; } } else dev_err(cs->dev, "%s: action==%d!\n", __func__, action); } }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp164379.95%15.88%
Tilman Schmidt41220.05%1694.12%
Total2055100.00%17100.00%

/* State machine to do the calling and hangup procedure */
static void process_event(struct cardstate *cs, struct event_t *ev) { struct bc_state *bcs; char *p_command = NULL; struct reply_t *rep; int rcode; int genresp = 0; int resp_code = RSP_ERROR; struct at_state_t *at_state; int index; int curact; unsigned long flags; if (ev->cid >= 0) { at_state = at_state_from_cid(cs, ev->cid); if (!at_state) { gig_dbg(DEBUG_EVENT, "event %d for invalid cid %d", ev->type, ev->cid); gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID, NULL, 0, NULL); return; } } else { at_state = ev->at_state; if (at_state_invalid(cs, at_state)) { gig_dbg(DEBUG_EVENT, "event for invalid at_state %p", at_state); return; } } gig_dbg(DEBUG_EVENT, "connection state %d, event %d", at_state->ConState, ev->type); bcs = at_state->bcs; /* Setting the pointer to the dial array */ rep = at_state->replystruct; spin_lock_irqsave(&cs->lock, flags); if (ev->type == EV_TIMEOUT) { if (ev->parameter != at_state->timer_index || !at_state->timer_active) { ev->type = RSP_NONE; /* old timeout */ gig_dbg(DEBUG_EVENT, "old timeout"); } else { if (at_state->waiting) gig_dbg(DEBUG_EVENT, "stopped waiting"); else gig_dbg(DEBUG_EVENT, "timeout occurred"); } } spin_unlock_irqrestore(&cs->lock, flags); /* if the response belongs to a variable in at_state->int_var[VAR_XXXX] or at_state->str_var[STR_XXXX], set it */ if (ev->type >= RSP_VAR && ev->type < RSP_VAR + VAR_NUM) { index = ev->type - RSP_VAR; at_state->int_var[index] = ev->parameter; } else if (ev->type >= RSP_STR && ev->type < RSP_STR + STR_NUM) { index = ev->type - RSP_STR; kfree(at_state->str_var[index]); at_state->str_var[index] = ev->ptr; ev->ptr = NULL; /* prevent process_events() from deallocating ptr */ } if (ev->type == EV_TIMEOUT || ev->type == RSP_STRING) at_state->getstring = 0; /* Search row in dial array which matches modem response and current constate */ for (;; rep++) { rcode = rep->resp_code; if (rcode == RSP_LAST) { /* found nothing...*/ dev_warn(cs->dev, "%s: rcode=RSP_LAST: " "resp_code %d in ConState %d!\n", __func__, ev->type, at_state->ConState); return; } if ((rcode == RSP_ANY || rcode == ev->type) && ((int) at_state->ConState >= rep->min_ConState) && (rep->max_ConState < 0 || (int) at_state->ConState <= rep->max_ConState) && (rep->parameter < 0 || rep->parameter == ev->parameter)) break; } p_command = rep->command; at_state->waiting = 0; for (curact = 0; curact < MAXACT; ++curact) { /* The row tells us what we should do .. */ do_action(rep->action[curact], cs, bcs, &at_state, &p_command, &genresp, &resp_code, ev); if (!at_state) /* at_state destroyed by disconnect */ return; } /* Jump to the next con-state regarding the array */ if (rep->new_ConState >= 0) at_state->ConState = rep->new_ConState; if (genresp) { spin_lock_irqsave(&cs->lock, flags); at_state->timer_expires = 0; at_state->timer_active = 0; spin_unlock_irqrestore(&cs->lock, flags); gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL); } else { /* Send command to modem if not NULL... */ if (p_command) { if (cs->connected) send_command(cs, p_command, at_state); else gigaset_add_event(cs, at_state, RSP_NODEV, NULL, 0, NULL); } spin_lock_irqsave(&cs->lock, flags); if (!rep->timeout) { at_state->timer_expires = 0; at_state->timer_active = 0; } else if (rep->timeout > 0) { /* new timeout */ at_state->timer_expires = rep->timeout * 10; at_state->timer_active = 1; ++at_state->timer_index; } spin_unlock_irqrestore(&cs->lock, flags); } }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp67191.29%116.67%
Tilman Schmidt648.71%583.33%
Total735100.00%6100.00%


static void schedule_sequence(struct cardstate *cs, struct at_state_t *at_state, int sequence) { cs->cur_at_seq = sequence; gigaset_add_event(cs, at_state, RSP_INIT, NULL, sequence, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp40100.00%1100.00%
Total40100.00%1100.00%


static void process_command_flags(struct cardstate *cs) { struct at_state_t *at_state = NULL; struct bc_state *bcs; int i; int sequence; unsigned long flags; cs->commands_pending = 0; if (cs->cur_at_seq) { gig_dbg(DEBUG_EVENT, "not searching scheduled commands: busy"); return; } gig_dbg(DEBUG_EVENT, "searching scheduled commands"); sequence = SEQ_NONE; /* clear pending_commands and hangup channels on shutdown */ if (cs->at_state.pending_commands & PC_SHUTDOWN) { cs->at_state.pending_commands &= ~PC_CIDMODE; for (i = 0; i < cs->channels; ++i) { bcs = cs->bcs + i; at_state = &bcs->at_state; at_state->pending_commands &= ~(PC_DLE1 | PC_ACCEPT | PC_DIAL); if (at_state->cid > 0) at_state->pending_commands |= PC_HUP; if (at_state->pending_commands & PC_CID) { at_state->pending_commands |= PC_NOCID; at_state->pending_commands &= ~PC_CID; } } } /* clear pending_commands and hangup channels on reset */ if (cs->at_state.pending_commands & PC_INIT) { cs->at_state.pending_commands &= ~PC_CIDMODE; for (i = 0; i < cs->channels; ++i) { bcs = cs->bcs + i; at_state = &bcs->at_state; at_state->pending_commands &= ~(PC_DLE1 | PC_ACCEPT | PC_DIAL); if (at_state->cid > 0) at_state->pending_commands |= PC_HUP; if (cs->mstate == MS_RECOVER) { if (at_state->pending_commands & PC_CID) { at_state->pending_commands |= PC_NOCID; at_state->pending_commands &= ~PC_CID; } } } } /* only switch back to unimodem mode if no commands are pending and * no channels are up */ spin_lock_irqsave(&cs->lock, flags); if (cs->at_state.pending_commands == PC_UMMODE && !cs->cidmode && list_empty(&cs->temp_at_states) && cs->mode == M_CID) { sequence = SEQ_UMMODE; at_state = &cs->at_state; for (i = 0; i < cs->channels; ++i) { bcs = cs->bcs + i; if (bcs->at_state.pending_commands || bcs->at_state.cid > 0) { sequence = SEQ_NONE; break; } } } spin_unlock_irqrestore(&cs->lock, flags); cs->at_state.pending_commands &= ~PC_UMMODE; if (sequence != SEQ_NONE) { schedule_sequence(cs, at_state, sequence); return; } for (i = 0; i < cs->channels; ++i) { bcs = cs->bcs + i; if (bcs->at_state.pending_commands & PC_HUP) { if (cs->dle) { cs->curchannel = bcs->channel; schedule_sequence(cs, &cs->at_state, SEQ_DLE0); return; } bcs->at_state.pending_commands &= ~PC_HUP; if (bcs->at_state.pending_commands & PC_CID) { /* not yet dialing: PC_NOCID is sufficient */ bcs->at_state.pending_commands |= PC_NOCID; bcs->at_state.pending_commands &= ~PC_CID; } else { schedule_sequence(cs, &bcs->at_state, SEQ_HUP); return; } } if (bcs->at_state.pending_commands & PC_NOCID) { bcs->at_state.pending_commands &= ~PC_NOCID; cs->curchannel = bcs->channel; schedule_sequence(cs, &cs->at_state, SEQ_NOCID); return; } else if (bcs->at_state.pending_commands & PC_DLE0) { bcs->at_state.pending_commands &= ~PC_DLE0; cs->curchannel = bcs->channel; schedule_sequence(cs, &cs->at_state, SEQ_DLE0); return; } } list_for_each_entry(at_state, &cs->temp_at_states, list) if (at_state->pending_commands & PC_HUP) { at_state->pending_commands &= ~PC_HUP; schedule_sequence(cs, at_state, SEQ_HUP); return; } if (cs->at_state.pending_commands & PC_INIT) { cs->at_state.pending_commands &= ~PC_INIT; cs->dle = 0; cs->inbuf->inputstate = INS_command; schedule_sequence(cs, &cs->at_state, SEQ_INIT); return; } if (cs->at_state.pending_commands & PC_SHUTDOWN) { cs->at_state.pending_commands &= ~PC_SHUTDOWN; schedule_sequence(cs, &cs->at_state, SEQ_SHUTDOWN); return; } if (cs->at_state.pending_commands & PC_CIDMODE) { cs->at_state.pending_commands &= ~PC_CIDMODE; if (cs->mode == M_UNIMODEM) { cs->retry_count = 1; schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE); return; } } for (i = 0; i < cs->channels; ++i) { bcs = cs->bcs + i; if (bcs->at_state.pending_commands & PC_DLE1) { bcs->at_state.pending_commands &= ~PC_DLE1; cs->curchannel = bcs->channel; schedule_sequence(cs, &cs->at_state, SEQ_DLE1); return; } if (bcs->at_state.pending_commands & PC_ACCEPT) { bcs->at_state.pending_commands &= ~PC_ACCEPT; schedule_sequence(cs, &bcs->at_state, SEQ_ACCEPT); return; } if (bcs->at_state.pending_commands & PC_DIAL) { bcs->at_state.pending_commands &= ~PC_DIAL; schedule_sequence(cs, &bcs->at_state, SEQ_DIAL); return; } if (bcs->at_state.pending_commands & PC_CID) { switch (cs->mode) { case M_UNIMODEM: cs->at_state.pending_commands |= PC_CIDMODE; gig_dbg(DEBUG_EVENT, "Scheduling PC_CIDMODE"); cs->commands_pending = 1; return; case M_UNKNOWN: schedule_init(cs, MS_INIT); return; } bcs->at_state.pending_commands &= ~PC_CID; cs->curchannel = bcs->channel; cs->retry_count = 2; schedule_sequence(cs, &cs->at_state, SEQ_CID); return; } } }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp96093.93%114.29%
Tilman Schmidt626.07%685.71%
Total1022100.00%7100.00%


static void process_events(struct cardstate *cs) { struct event_t *ev; unsigned head, tail; int i; int check_flags = 0; int was_busy; unsigned long flags; spin_lock_irqsave(&cs->ev_lock, flags); head = cs->ev_head; for (i = 0; i < 2 * MAX_EVENTS; ++i) { tail = cs->ev_tail; if (tail == head) { if (!check_flags && !cs->commands_pending) break; check_flags = 0; spin_unlock_irqrestore(&cs->ev_lock, flags); process_command_flags(cs); spin_lock_irqsave(&cs->ev_lock, flags); tail = cs->ev_tail; if (tail == head) { if (!cs->commands_pending) break; continue; } } ev = cs->events + head; was_busy = cs->cur_at_seq != SEQ_NONE; spin_unlock_irqrestore(&cs->ev_lock, flags); process_event(cs, ev); spin_lock_irqsave(&cs->ev_lock, flags); kfree(ev->ptr); ev->ptr = NULL; if (was_busy && cs->cur_at_seq == SEQ_NONE) check_flags = 1; head = (head + 1) % MAX_EVENTS; cs->ev_head = head; } spin_unlock_irqrestore(&cs->ev_lock, flags); if (i == 2 * MAX_EVENTS) { dev_err(cs->dev, "infinite loop in process_events; aborting.\n"); } }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp19173.18%133.33%
Tilman Schmidt7026.82%266.67%
Total261100.00%3100.00%

/* tasklet scheduled on any event received from the Gigaset device * parameter: * data ISDN controller state structure */
void gigaset_handle_event(unsigned long data) { struct cardstate *cs = (struct cardstate *) data; /* handle incoming data on control/common channel */ if (cs->inbuf->head != cs->inbuf->tail) { gig_dbg(DEBUG_INTR, "processing new data"); cs->ops->handle_input(cs->inbuf); } process_events(cs); }

Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp6098.36%150.00%
Tilman Schmidt11.64%150.00%
Total61100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Hansjoerg Lipp817877.09%12.56%
Tilman Schmidt240622.68%3589.74%
Andy Shevchenko180.17%12.56%
Alexey Dobriyan40.04%12.56%
Paul Gortmaker30.03%12.56%
Total10609100.00%39100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.