diff options
author | Tilman Schmidt <tilman@imap.cc> | 2010-02-22 13:09:22 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-26 01:24:23 -0800 |
commit | 63e055d1c6e3a5f6d370cc841d621d5fa4d5d834 (patch) | |
tree | 80f645c85bbc012d3b1b45ab66ee7dd8938de49b /drivers/isdn/gigaset | |
parent | 2ac2ed5f2dfc97ae9ed9f446ad6e064fa821ef6d (diff) | |
download | linux-63e055d1c6e3a5f6d370cc841d621d5fa4d5d834.tar.gz linux-63e055d1c6e3a5f6d370cc841d621d5fa4d5d834.tar.bz2 linux-63e055d1c6e3a5f6d370cc841d621d5fa4d5d834.zip |
bas_gigaset: collapse CR/LF at end of AT response
Copy the mechanism from ser_/usb_gigaset to avoid producing
spurious empty responses for CR/LF sequences from the device.
Add a comment in all drivers documenting that behaviour.
Correct an off by one error that might result in a one byte
buffer overflow when receiving an unexpectedly long reply.
Impact: minor bugfix
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/isdn/gigaset')
-rw-r--r-- | drivers/isdn/gigaset/asyncdata.c | 2 | ||||
-rw-r--r-- | drivers/isdn/gigaset/gigaset.h | 4 | ||||
-rw-r--r-- | drivers/isdn/gigaset/isocdata.c | 44 |
3 files changed, 35 insertions, 15 deletions
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index ccb2a7b7c41d..e913beb63f24 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -40,6 +40,8 @@ static inline int muststuff(unsigned char c) * Append received bytes to the command response buffer and forward them * line by line to the response handler. Exit whenever a mode/state change * might have occurred. + * Note: Received lines may be terminated by CR, LF, or CR LF, which will be + * removed before passing the line to the response handler. * Return value: * number of processed bytes */ diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index e963a6c2e86d..c9ccf7de2275 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -38,7 +38,7 @@ #define GIG_COMPAT {0, 4, 0, 0} #define MAX_REC_PARAMS 10 /* Max. number of params in response string */ -#define MAX_RESP_SIZE 512 /* Max. size of a response string */ +#define MAX_RESP_SIZE 511 /* Max. size of a response string */ #define MAX_EVENTS 64 /* size of event queue */ @@ -498,7 +498,7 @@ struct cardstate { spinlock_t ev_lock; /* current modem response */ - unsigned char respdata[MAX_RESP_SIZE]; + unsigned char respdata[MAX_RESP_SIZE+1]; unsigned cbytes; /* private data of hardware drivers */ diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index 85394a6ebae8..16fd3bd48883 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -905,29 +905,49 @@ void gigaset_isoc_receive(unsigned char *src, unsigned count, /* == data input =========================================================== */ +/* process a block of received bytes in command mode (mstate != MS_LOCKED) + * Append received bytes to the command response buffer and forward them + * line by line to the response handler. + * Note: Received lines may be terminated by CR, LF, or CR LF, which will be + * removed before passing the line to the response handler. + */ static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; unsigned cbytes = cs->cbytes; + unsigned char c; while (numbytes--) { - /* copy next character, check for end of line */ - switch (cs->respdata[cbytes] = *src++) { - case '\r': + c = *src++; + switch (c) { case '\n': - /* end of line */ - gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", - __func__, cbytes); - if (cbytes >= MAX_RESP_SIZE - 1) - dev_warn(cs->dev, "response too large\n"); + if (cbytes == 0 && cs->respdata[0] == '\r') { + /* collapse LF with preceding CR */ + cs->respdata[0] = 0; + break; + } + /* --v-- fall through --v-- */ + case '\r': + /* end of message line, pass to response handler */ + if (cbytes >= MAX_RESP_SIZE) { + dev_warn(cs->dev, "response too large (%d)\n", + cbytes); + cbytes = MAX_RESP_SIZE; + } cs->cbytes = cbytes; + gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response", + cbytes, cs->respdata); gigaset_handle_modem_response(cs); cbytes = 0; + + /* store EOL byte for CRLF collapsing */ + cs->respdata[0] = c; break; default: - /* advance in line buffer, checking for overflow */ - if (cbytes < MAX_RESP_SIZE - 1) - cbytes++; + /* append to line buffer if possible */ + if (cbytes < MAX_RESP_SIZE) + cs->respdata[cbytes] = c; + cbytes++; } } @@ -958,8 +978,6 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) numbytes, src); gigaset_if_receive(inbuf->cs, src, numbytes); } else { - gigaset_dbg_buffer(DEBUG_CMD, "received response", - numbytes, src); cmd_loop(src, numbytes, inbuf); } |