Merge branch 'gigaset_modem_response'
Tilman Schmidt says: ==================== isdn/gigaset: restructure modem response parser This series of patches restructures the Gigaset ISDN driver's modem response parser to improve code readability and conform better to the device's specification and actual behaviour. Could you please merge these through net-next? ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
c9231f8247
|
@ -389,22 +389,49 @@ zsau_resp[] =
|
||||||
{NULL, ZSAU_UNKNOWN}
|
{NULL, ZSAU_UNKNOWN}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* retrieve CID from parsed response
|
/* check for and remove fixed string prefix
|
||||||
* returns 0 if no CID, -1 if invalid CID, or CID value 1..65535
|
* If s starts with prefix terminated by a non-alphanumeric character,
|
||||||
|
* return pointer to the first character after that, otherwise return NULL.
|
||||||
*/
|
*/
|
||||||
static int cid_of_response(char *s)
|
static char *skip_prefix(char *s, const char *prefix)
|
||||||
{
|
{
|
||||||
int cid;
|
while (*prefix)
|
||||||
int rc;
|
if (*s++ != *prefix++)
|
||||||
|
return NULL;
|
||||||
|
if (isalnum(*s))
|
||||||
|
return NULL;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
if (s[-1] != ';')
|
/* queue event with CID */
|
||||||
return 0; /* no CID separator */
|
static void add_cid_event(struct cardstate *cs, int cid, int type,
|
||||||
rc = kstrtoint(s, 10, &cid);
|
void *ptr, int parameter)
|
||||||
if (rc)
|
{
|
||||||
return 0; /* CID not numeric */
|
unsigned long flags;
|
||||||
if (cid < 1 || cid > 65535)
|
unsigned next, tail;
|
||||||
return -1; /* CID out of range */
|
struct event_t *event;
|
||||||
return cid;
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -417,190 +444,188 @@ static int cid_of_response(char *s)
|
||||||
*/
|
*/
|
||||||
void gigaset_handle_modem_response(struct cardstate *cs)
|
void gigaset_handle_modem_response(struct cardstate *cs)
|
||||||
{
|
{
|
||||||
unsigned char *argv[MAX_REC_PARAMS + 1];
|
char *eoc, *psep, *ptr;
|
||||||
int params;
|
|
||||||
int i, j;
|
|
||||||
const struct resp_type_t *rt;
|
const struct resp_type_t *rt;
|
||||||
const struct zsau_resp_t *zr;
|
const struct zsau_resp_t *zr;
|
||||||
int curarg;
|
int cid, parameter;
|
||||||
unsigned long flags;
|
u8 type, value;
|
||||||
unsigned next, tail, head;
|
|
||||||
struct event_t *event;
|
|
||||||
int resp_code;
|
|
||||||
int param_type;
|
|
||||||
int abort;
|
|
||||||
size_t len;
|
|
||||||
int cid;
|
|
||||||
int rawstring;
|
|
||||||
|
|
||||||
len = cs->cbytes;
|
if (!cs->cbytes) {
|
||||||
if (!len) {
|
|
||||||
/* ignore additional LFs/CRs (M10x config mode or cx100) */
|
/* ignore additional LFs/CRs (M10x config mode or cx100) */
|
||||||
gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[0]);
|
gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cs->respdata[len] = 0;
|
cs->respdata[cs->cbytes] = 0;
|
||||||
argv[0] = cs->respdata;
|
|
||||||
params = 1;
|
|
||||||
if (cs->at_state.getstring) {
|
if (cs->at_state.getstring) {
|
||||||
/* getstring only allowed without cid at the moment */
|
/* state machine wants next line verbatim */
|
||||||
cs->at_state.getstring = 0;
|
cs->at_state.getstring = 0;
|
||||||
rawstring = 1;
|
ptr = kstrdup(cs->respdata, GFP_ATOMIC);
|
||||||
cid = 0;
|
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 {
|
} else {
|
||||||
/* parse line */
|
/* no valid CID: leave unchanged */
|
||||||
for (i = 0; i < len; i++)
|
cid = 0;
|
||||||
switch (cs->respdata[i]) {
|
|
||||||
case ';':
|
|
||||||
case ',':
|
|
||||||
case '=':
|
|
||||||
if (params > MAX_REC_PARAMS) {
|
|
||||||
dev_warn(cs->dev,
|
|
||||||
"too many parameters in response\n");
|
|
||||||
/* need last parameter (might be CID) */
|
|
||||||
params--;
|
|
||||||
}
|
|
||||||
argv[params++] = cs->respdata + i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rawstring = 0;
|
|
||||||
cid = params > 1 ? cid_of_response(argv[params - 1]) : 0;
|
|
||||||
if (cid < 0) {
|
|
||||||
gigaset_add_event(cs, &cs->at_state, RSP_INVAL,
|
|
||||||
NULL, 0, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 1; j < params; ++j)
|
|
||||||
argv[j][-1] = 0;
|
|
||||||
|
|
||||||
gig_dbg(DEBUG_EVENT, "CMD received: %s", argv[0]);
|
|
||||||
if (cid) {
|
|
||||||
--params;
|
|
||||||
gig_dbg(DEBUG_EVENT, "CID: %s", argv[params]);
|
|
||||||
}
|
|
||||||
gig_dbg(DEBUG_EVENT, "available params: %d", params - 1);
|
|
||||||
for (j = 1; j < params; j++)
|
|
||||||
gig_dbg(DEBUG_EVENT, "param %d: %s", j, argv[j]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&cs->ev_lock, flags);
|
gig_dbg(DEBUG_EVENT, "CMD received: %s", cs->respdata);
|
||||||
head = cs->ev_head;
|
if (cid)
|
||||||
tail = cs->ev_tail;
|
gig_dbg(DEBUG_EVENT, "CID: %d", cid);
|
||||||
|
|
||||||
abort = 1;
|
switch (rt->type) {
|
||||||
curarg = 0;
|
case RT_NOTHING:
|
||||||
while (curarg < params) {
|
/* check parameter separator */
|
||||||
next = (tail + 1) % MAX_EVENTS;
|
if (*eoc)
|
||||||
if (unlikely(next == head)) {
|
goto bad_param; /* extra parameter */
|
||||||
dev_err(cs->dev, "event queue full\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
event = cs->events + tail;
|
add_cid_event(cs, cid, rt->resp_code, NULL, 0);
|
||||||
event->at_state = NULL;
|
break;
|
||||||
event->cid = cid;
|
|
||||||
event->ptr = NULL;
|
|
||||||
event->arg = NULL;
|
|
||||||
tail = next;
|
|
||||||
|
|
||||||
if (rawstring) {
|
case RT_RING:
|
||||||
resp_code = RSP_STRING;
|
/* check parameter separator */
|
||||||
param_type = RT_STRING;
|
if (!*eoc)
|
||||||
} else {
|
eoc = NULL; /* no parameter */
|
||||||
for (rt = resp_type; rt->response; ++rt)
|
else if (*eoc++ != ',')
|
||||||
if (!strcmp(argv[curarg], rt->response))
|
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;
|
break;
|
||||||
|
|
||||||
if (!rt->response) {
|
|
||||||
event->type = RSP_NONE;
|
|
||||||
gig_dbg(DEBUG_EVENT,
|
|
||||||
"unknown modem response: '%s'\n",
|
|
||||||
argv[curarg]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resp_code = rt->resp_code;
|
/* all legal parameters are of type RT_STRING */
|
||||||
param_type = rt->type;
|
if (!psep || rt->type != RT_STRING) {
|
||||||
++curarg;
|
|
||||||
}
|
|
||||||
|
|
||||||
event->type = resp_code;
|
|
||||||
|
|
||||||
switch (param_type) {
|
|
||||||
case RT_NOTHING:
|
|
||||||
break;
|
|
||||||
case RT_RING:
|
|
||||||
if (!cid) {
|
|
||||||
dev_err(cs->dev,
|
|
||||||
"received RING without CID!\n");
|
|
||||||
event->type = RSP_INVAL;
|
|
||||||
abort = 1;
|
|
||||||
} else {
|
|
||||||
event->cid = 0;
|
|
||||||
event->parameter = cid;
|
|
||||||
abort = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RT_ZSAU:
|
|
||||||
if (curarg >= params) {
|
|
||||||
event->parameter = ZSAU_NONE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (zr = zsau_resp; zr->str; ++zr)
|
|
||||||
if (!strcmp(argv[curarg], zr->str))
|
|
||||||
break;
|
|
||||||
event->parameter = zr->code;
|
|
||||||
if (!zr->str)
|
|
||||||
dev_warn(cs->dev,
|
dev_warn(cs->dev,
|
||||||
"%s: unknown parameter %s after ZSAU\n",
|
"illegal RING parameter: '%s'\n",
|
||||||
__func__, argv[curarg]);
|
eoc);
|
||||||
++curarg;
|
return;
|
||||||
break;
|
|
||||||
case RT_STRING:
|
|
||||||
if (curarg < params) {
|
|
||||||
event->ptr = kstrdup(argv[curarg], GFP_ATOMIC);
|
|
||||||
if (!event->ptr)
|
|
||||||
dev_err(cs->dev, "out of memory\n");
|
|
||||||
++curarg;
|
|
||||||
}
|
}
|
||||||
gig_dbg(DEBUG_EVENT, "string==%s",
|
|
||||||
event->ptr ? (char *) event->ptr : "NULL");
|
|
||||||
break;
|
|
||||||
case RT_ZCAU:
|
|
||||||
event->parameter = -1;
|
|
||||||
if (curarg + 1 < params) {
|
|
||||||
u8 type, value;
|
|
||||||
|
|
||||||
i = kstrtou8(argv[curarg++], 16, &type);
|
/* skip parameter value separator */
|
||||||
j = kstrtou8(argv[curarg++], 16, &value);
|
if (*psep++ != '=')
|
||||||
if (i == 0 && j == 0)
|
goto bad_param;
|
||||||
event->parameter = (type << 8) | value;
|
|
||||||
} else
|
/* look up end of parameter */
|
||||||
curarg = params - 1;
|
eoc = strchr(psep, ',');
|
||||||
break;
|
if (eoc)
|
||||||
case RT_NUMBER:
|
*eoc++ = 0;
|
||||||
if (curarg >= params ||
|
|
||||||
kstrtoint(argv[curarg++], 10, &event->parameter))
|
/* retrieve parameter value */
|
||||||
event->parameter = -1;
|
ptr = kstrdup(psep, GFP_ATOMIC);
|
||||||
gig_dbg(DEBUG_EVENT, "parameter==%d", event->parameter);
|
|
||||||
|
/* 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;
|
break;
|
||||||
}
|
}
|
||||||
|
if (*eoc++ != '=')
|
||||||
|
goto bad_param;
|
||||||
|
|
||||||
if (resp_code == RSP_ZDLE)
|
/* look up parameter value */
|
||||||
cs->dle = event->parameter;
|
for (zr = zsau_resp; zr->str; ++zr)
|
||||||
|
if (!strcmp(eoc, zr->str))
|
||||||
|
break;
|
||||||
|
if (!zr->str)
|
||||||
|
goto bad_param;
|
||||||
|
|
||||||
if (abort)
|
add_cid_event(cs, cid, rt->resp_code, NULL, zr->code);
|
||||||
break;
|
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, ¶meter))
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
cs->ev_tail = tail;
|
|
||||||
spin_unlock_irqrestore(&cs->ev_lock, flags);
|
|
||||||
|
|
||||||
if (curarg != params)
|
|
||||||
gig_dbg(DEBUG_EVENT,
|
|
||||||
"invalid number of processed parameters: %d/%d",
|
|
||||||
curarg, params);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gigaset_handle_modem_response);
|
EXPORT_SYMBOL_GPL(gigaset_handle_modem_response);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue