Commit e3628dd1 authored by Tilman Schmidt's avatar Tilman Schmidt Committed by David S. Miller

isdn/gigaset: avoid copying AT commands twice

Change the Gigaset driver's internal write_cmd interface to accept a
cmdbuf structure instead of a string. This avoids copying formatted
AT commands a second time.

Impact: optimization
Signed-off-by: default avatarTilman Schmidt <tilman@imap.cc>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b3251d80
...@@ -1913,65 +1913,41 @@ static int start_cbsend(struct cardstate *cs) ...@@ -1913,65 +1913,41 @@ static int start_cbsend(struct cardstate *cs)
* USB transmission is started if necessary. * USB transmission is started if necessary.
* parameters: * parameters:
* cs controller state structure * cs controller state structure
* buf command string to send * cb command buffer structure
* len number of bytes to send (max. IF_WRITEBUF)
* wake_tasklet tasklet to run when transmission is completed
* (NULL if none)
* return value: * return value:
* number of bytes queued on success * number of bytes queued on success
* error code < 0 on error * error code < 0 on error
*/ */
static int gigaset_write_cmd(struct cardstate *cs, static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
const unsigned char *buf, int len,
struct tasklet_struct *wake_tasklet)
{ {
struct cmdbuf_t *cb;
unsigned long flags; unsigned long flags;
int rc; int rc;
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD, DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf); "CMD Transmit", cb->len, cb->buf);
if (len <= 0) {
/* nothing to do */
rc = 0;
goto notqueued;
}
/* translate "+++" escape sequence sent as a single separate command /* translate "+++" escape sequence sent as a single separate command
* into "close AT channel" command for error recovery * into "close AT channel" command for error recovery
* The next command will reopen the AT channel automatically. * The next command will reopen the AT channel automatically.
*/ */
if (len == 3 && !memcmp(buf, "+++", 3)) { if (cb->len == 3 && !memcmp(cb->buf, "+++", 3)) {
kfree(cb);
rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
goto notqueued; if (cb->wake_tasklet)
} tasklet_schedule(cb->wake_tasklet);
return rc < 0 ? rc : cb->len;
if (len > IF_WRITEBUF)
len = IF_WRITEBUF;
cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC);
if (!cb) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
rc = -ENOMEM;
goto notqueued;
} }
memcpy(cb->buf, buf, len);
cb->len = len;
cb->offset = 0;
cb->next = NULL;
cb->wake_tasklet = wake_tasklet;
spin_lock_irqsave(&cs->cmdlock, flags); spin_lock_irqsave(&cs->cmdlock, flags);
cb->prev = cs->lastcmdbuf; cb->prev = cs->lastcmdbuf;
if (cs->lastcmdbuf) if (cs->lastcmdbuf)
cs->lastcmdbuf->next = cb; cs->lastcmdbuf->next = cb;
else { else {
cs->cmdbuf = cb; cs->cmdbuf = cb;
cs->curlen = len; cs->curlen = cb->len;
} }
cs->cmdbytes += len; cs->cmdbytes += cb->len;
cs->lastcmdbuf = cb; cs->lastcmdbuf = cb;
spin_unlock_irqrestore(&cs->cmdlock, flags); spin_unlock_irqrestore(&cs->cmdlock, flags);
...@@ -1988,12 +1964,7 @@ static int gigaset_write_cmd(struct cardstate *cs, ...@@ -1988,12 +1964,7 @@ static int gigaset_write_cmd(struct cardstate *cs,
} }
rc = start_cbsend(cs); rc = start_cbsend(cs);
spin_unlock_irqrestore(&cs->lock, flags); spin_unlock_irqrestore(&cs->lock, flags);
return rc < 0 ? rc : len; return rc < 0 ? rc : cb->len;
notqueued: /* request handled without queuing */
if (wake_tasklet)
tasklet_schedule(wake_tasklet);
return rc;
} }
/* gigaset_write_room /* gigaset_write_room
......
...@@ -797,48 +797,27 @@ static void schedule_init(struct cardstate *cs, int state) ...@@ -797,48 +797,27 @@ static void schedule_init(struct cardstate *cs, int state)
static void send_command(struct cardstate *cs, const char *cmd, int cid, static void send_command(struct cardstate *cs, const char *cmd, int cid,
int dle, gfp_t kmallocflags) int dle, gfp_t kmallocflags)
{ {
size_t cmdlen, buflen; struct cmdbuf_t *cb;
char *cmdpos, *cmdbuf, *cmdtail; size_t buflen;
cmdlen = strlen(cmd); buflen = strlen(cmd) + 12; /* DLE ( A T 1 2 3 4 5 <cmd> DLE ) \0 */
buflen = 11 + cmdlen; cb = kmalloc(sizeof(struct cmdbuf_t) + buflen, kmallocflags);
if (unlikely(buflen <= cmdlen)) { if (!cb) {
dev_err(cs->dev, "integer overflow in buflen\n"); dev_err(cs->dev, "%s: out of memory\n", __func__);
return; return;
} }
if (cid > 0 && cid <= 65535)
cmdbuf = kmalloc(buflen, kmallocflags); cb->len = snprintf(cb->buf, buflen,
if (unlikely(!cmdbuf)) { dle ? "\020(AT%d%s\020)" : "AT%d%s",
dev_err(cs->dev, "out of memory\n"); cid, cmd);
return; else
} cb->len = snprintf(cb->buf, buflen,
dle ? "\020(AT%s\020)" : "AT%s",
cmdpos = cmdbuf + 9; cmd);
cmdtail = cmdpos + cmdlen; cb->offset = 0;
memcpy(cmdpos, cmd, cmdlen); cb->next = NULL;
cb->wake_tasklet = NULL;
if (cid > 0 && cid <= 65535) { cs->ops->write_cmd(cs, cb);
do {
*--cmdpos = '0' + cid % 10;
cid /= 10;
++cmdlen;
} while (cid);
}
cmdlen += 2;
*--cmdpos = 'T';
*--cmdpos = 'A';
if (dle) {
cmdlen += 4;
*--cmdpos = '(';
*--cmdpos = 0x10;
*cmdtail++ = 0x10;
*cmdtail++ = ')';
}
cs->ops->write_cmd(cs, cmdpos, cmdlen, NULL);
kfree(cmdbuf);
} }
static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid) static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid)
...@@ -1240,8 +1219,22 @@ static void do_action(int action, struct cardstate *cs, ...@@ -1240,8 +1219,22 @@ static void do_action(int action, struct cardstate *cs,
break; break;
case ACT_HUPMODEM: case ACT_HUPMODEM:
/* send "+++" (hangup in unimodem mode) */ /* send "+++" (hangup in unimodem mode) */
if (cs->connected) if (cs->connected) {
cs->ops->write_cmd(cs, "+++", 3, NULL); 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; break;
case ACT_RING: case ACT_RING:
/* get fresh AT state structure for new CID */ /* get fresh AT state structure for new CID */
......
...@@ -574,9 +574,7 @@ struct bas_bc_state { ...@@ -574,9 +574,7 @@ struct bas_bc_state {
struct gigaset_ops { struct gigaset_ops {
/* Called from ev-layer.c/interface.c for sending AT commands to the /* Called from ev-layer.c/interface.c for sending AT commands to the
device */ device */
int (*write_cmd)(struct cardstate *cs, int (*write_cmd)(struct cardstate *cs, struct cmdbuf_t *cb);
const unsigned char *buf, int len,
struct tasklet_struct *wake_tasklet);
/* Called from interface.c for additional device control */ /* Called from interface.c for additional device control */
int (*write_room)(struct cardstate *cs); int (*write_room)(struct cardstate *cs);
......
...@@ -339,7 +339,8 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file, ...@@ -339,7 +339,8 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file,
static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
{ {
struct cardstate *cs; struct cardstate *cs;
int retval = -ENODEV; struct cmdbuf_t *cb;
int retval;
cs = (struct cardstate *) tty->driver_data; cs = (struct cardstate *) tty->driver_data;
if (!cs) { if (!cs) {
...@@ -355,18 +356,39 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) ...@@ -355,18 +356,39 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
if (!cs->connected) { if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected"); gig_dbg(DEBUG_IF, "not connected");
retval = -ENODEV; retval = -ENODEV;
} else if (!cs->open_count) goto done;
}
if (!cs->open_count) {
dev_warn(cs->dev, "%s: device not opened\n", __func__); dev_warn(cs->dev, "%s: device not opened\n", __func__);
else if (cs->mstate != MS_LOCKED) { retval = -ENODEV;
goto done;
}
if (cs->mstate != MS_LOCKED) {
dev_warn(cs->dev, "can't write to unlocked device\n"); dev_warn(cs->dev, "can't write to unlocked device\n");
retval = -EBUSY; retval = -EBUSY;
} else { goto done;
retval = cs->ops->write_cmd(cs, buf, count, }
&cs->if_wake_tasklet); if (count <= 0) {
/* nothing to do */
retval = 0;
goto done;
} }
mutex_unlock(&cs->mutex); cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
if (!cb) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
retval = -ENOMEM;
goto done;
}
memcpy(cb->buf, buf, count);
cb->len = count;
cb->offset = 0;
cb->next = NULL;
cb->wake_tasklet = &cs->if_wake_tasklet;
retval = cs->ops->write_cmd(cs, cb);
done:
mutex_unlock(&cs->mutex);
return retval; return retval;
} }
......
...@@ -241,30 +241,13 @@ static void flush_send_queue(struct cardstate *cs) ...@@ -241,30 +241,13 @@ static void flush_send_queue(struct cardstate *cs)
* return value: * return value:
* number of bytes queued, or error code < 0 * number of bytes queued, or error code < 0
*/ */
static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
int len, struct tasklet_struct *wake_tasklet)
{ {
struct cmdbuf_t *cb;
unsigned long flags; unsigned long flags;
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD, DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf); "CMD Transmit", cb->len, cb->buf);
if (len <= 0)
return 0;
cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC);
if (!cb) {
dev_err(cs->dev, "%s: out of memory!\n", __func__);
return -ENOMEM;
}
memcpy(cb->buf, buf, len);
cb->len = len;
cb->offset = 0;
cb->next = NULL;
cb->wake_tasklet = wake_tasklet;
spin_lock_irqsave(&cs->cmdlock, flags); spin_lock_irqsave(&cs->cmdlock, flags);
cb->prev = cs->lastcmdbuf; cb->prev = cs->lastcmdbuf;
...@@ -272,9 +255,9 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, ...@@ -272,9 +255,9 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
cs->lastcmdbuf->next = cb; cs->lastcmdbuf->next = cb;
else { else {
cs->cmdbuf = cb; cs->cmdbuf = cb;
cs->curlen = len; cs->curlen = cb->len;
} }
cs->cmdbytes += len; cs->cmdbytes += cb->len;
cs->lastcmdbuf = cb; cs->lastcmdbuf = cb;
spin_unlock_irqrestore(&cs->cmdlock, flags); spin_unlock_irqrestore(&cs->cmdlock, flags);
...@@ -282,7 +265,7 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, ...@@ -282,7 +265,7 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
if (cs->connected) if (cs->connected)
tasklet_schedule(&cs->write_tasklet); tasklet_schedule(&cs->write_tasklet);
spin_unlock_irqrestore(&cs->lock, flags); spin_unlock_irqrestore(&cs->lock, flags);
return len; return cb->len;
} }
/* /*
......
...@@ -494,29 +494,13 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) ...@@ -494,29 +494,13 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
} }
/* Send command to device. */ /* Send command to device. */
static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
int len, struct tasklet_struct *wake_tasklet)
{ {
struct cmdbuf_t *cb;
unsigned long flags; unsigned long flags;
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ? gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD, DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf); "CMD Transmit", cb->len, cb->buf);
if (len <= 0)
return 0;
cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC);
if (!cb) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
return -ENOMEM;
}
memcpy(cb->buf, buf, len);
cb->len = len;
cb->offset = 0;
cb->next = NULL;
cb->wake_tasklet = wake_tasklet;
spin_lock_irqsave(&cs->cmdlock, flags); spin_lock_irqsave(&cs->cmdlock, flags);
cb->prev = cs->lastcmdbuf; cb->prev = cs->lastcmdbuf;
...@@ -524,9 +508,9 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, ...@@ -524,9 +508,9 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
cs->lastcmdbuf->next = cb; cs->lastcmdbuf->next = cb;
else { else {
cs->cmdbuf = cb; cs->cmdbuf = cb;
cs->curlen = len; cs->curlen = cb->len;
} }
cs->cmdbytes += len; cs->cmdbytes += cb->len;
cs->lastcmdbuf = cb; cs->lastcmdbuf = cb;
spin_unlock_irqrestore(&cs->cmdlock, flags); spin_unlock_irqrestore(&cs->cmdlock, flags);
...@@ -534,7 +518,7 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, ...@@ -534,7 +518,7 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
if (cs->connected) if (cs->connected)
tasklet_schedule(&cs->write_tasklet); tasklet_schedule(&cs->write_tasklet);
spin_unlock_irqrestore(&cs->lock, flags); spin_unlock_irqrestore(&cs->lock, flags);
return len; return cb->len;
} }
static int gigaset_write_room(struct cardstate *cs) static int gigaset_write_room(struct cardstate *cs)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment