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: Tilman Schmidt <tilman@imap.cc> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
b3251d8045
commit
e3628dd176
|
@ -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,
|
||||||
}
|
}
|
||||||
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)
|
||||||
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,
|
||||||
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 {
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
* 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,
|
||||||
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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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,
|
||||||
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,
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue