aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/n_gsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/n_gsm.c')
-rw-r--r--drivers/tty/n_gsm.c116
1 files changed, 89 insertions, 27 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 1d92d2a84889..0b96b14bbfe1 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -271,6 +271,10 @@ static DEFINE_SPINLOCK(gsm_mux_lock);
static struct tty_driver *gsm_tty_driver;
+/* Save dlci open address */
+static int addr_open[256] = { 0 };
+/* Save dlci open count */
+static int addr_cnt;
/*
* This section of the driver logic implements the GSM encodings
* both the basic and the 'advanced'. Reliable transport is not
@@ -587,6 +591,10 @@ static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
return;
}
gsmld_output(gsm, cbuf, len);
+ if (!gsm->initiator) {
+ cr = cr & gsm->initiator;
+ control = control & ~PF;
+ }
gsm_print_packet("-->", addr, cr, control, NULL, 0);
}
@@ -601,7 +609,7 @@ static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
static inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
{
- gsm_send(gsm, addr, 0, control);
+ gsm_send(gsm, addr, 1, control);
}
/**
@@ -687,7 +695,7 @@ static void gsm_data_kick(struct gsm_mux *gsm, struct gsm_dlci *dlci)
print_hex_dump_bytes("gsm_data_kick: ",
DUMP_PREFIX_OFFSET,
gsm->txframe, len);
- if (gsmld_output(gsm, gsm->txframe, len) < 0)
+ if (gsmld_output(gsm, gsm->txframe, len) <= 0)
break;
/* FIXME: Can eliminate one SOF in many more cases */
gsm->tx_bytes -= msg->len;
@@ -1177,6 +1185,7 @@ static void gsm_control_rls(struct gsm_mux *gsm, const u8 *data, int clen)
}
static void gsm_dlci_begin_close(struct gsm_dlci *dlci);
+static void gsm_dlci_close(struct gsm_dlci *dlci);
/**
* gsm_control_message - DLCI 0 control processing
@@ -1195,15 +1204,28 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
{
u8 buf[1];
unsigned long flags;
+ struct gsm_dlci *dlci;
+ int i;
+ int address;
switch (command) {
case CMD_CLD: {
- struct gsm_dlci *dlci = gsm->dlci[0];
+ if (addr_cnt > 0) {
+ for (i = 0; i < addr_cnt; i++) {
+ address = addr_open[i];
+ dlci = gsm->dlci[address];
+ gsm_dlci_close(dlci);
+ addr_open[i] = 0;
+ }
+ }
/* Modem wishes to close down */
+ dlci = gsm->dlci[0];
if (dlci) {
dlci->dead = true;
gsm->dead = true;
- gsm_dlci_begin_close(dlci);
+ gsm_dlci_close(dlci);
+ addr_cnt = 0;
+ gsm_response(gsm, 0, UA|PF);
}
}
break;
@@ -1429,6 +1451,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
kfifo_reset(&dlci->fifo);
} else
dlci->gsm->dead = true;
+ /* Unregister gsmtty driver,report gsmtty dev remove uevent for user */
+ tty_unregister_device(gsm_tty_driver, dlci->addr);
wake_up(&dlci->gsm->event);
/* A DLCI 0 close is a MUX termination so we need to kick that
back to userspace somehow */
@@ -1450,6 +1474,8 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
dlci->state = DLCI_OPEN;
if (debug & 8)
pr_debug("DLCI %d goes open.\n", dlci->addr);
+ /* Register gsmtty driver,report gsmtty dev add uevent for user */
+ tty_register_device(gsm_tty_driver, dlci->addr, NULL);
wake_up(&dlci->gsm->event);
}
@@ -1748,6 +1774,7 @@ static void gsm_queue(struct gsm_mux *gsm)
struct gsm_dlci *dlci;
u8 cr;
int address;
+ int i, j, k, address_tmp;
/* We have to sneak a look at the packet body to do the FCS.
A somewhat layering violation in the spec */
@@ -1779,29 +1806,59 @@ static void gsm_queue(struct gsm_mux *gsm)
switch (gsm->control) {
case SABM|PF:
- if (cr == 0)
+ if (cr == 1)
goto invalid;
if (dlci == NULL)
dlci = gsm_dlci_alloc(gsm, address);
if (dlci == NULL)
return;
if (dlci->dead)
- gsm_response(gsm, address, DM);
+ gsm_response(gsm, address, DM|PF);
else {
- gsm_response(gsm, address, UA);
+ gsm_response(gsm, address, UA|PF);
gsm_dlci_open(dlci);
+ /* Save dlci open address */
+ if (address) {
+ addr_open[addr_cnt] = address;
+ addr_cnt++;
+ }
}
break;
case DISC|PF:
- if (cr == 0)
+ if (cr == 1)
goto invalid;
if (dlci == NULL || dlci->state == DLCI_CLOSED) {
- gsm_response(gsm, address, DM);
+ gsm_response(gsm, address, DM|PF);
return;
}
/* Real close complete */
- gsm_response(gsm, address, UA);
- gsm_dlci_close(dlci);
+ if (!address) {
+ if (addr_cnt > 0) {
+ for (i = 0; i < addr_cnt; i++) {
+ address = addr_open[i];
+ dlci = gsm->dlci[address];
+ gsm_dlci_close(dlci);
+ addr_open[i] = 0;
+ }
+ }
+ dlci = gsm->dlci[0];
+ gsm_dlci_close(dlci);
+ addr_cnt = 0;
+ gsm_response(gsm, 0, UA|PF);
+ } else {
+ gsm_response(gsm, address, UA|PF);
+ gsm_dlci_close(dlci);
+ /* clear dlci address */
+ for (j = 0; j < addr_cnt; j++) {
+ address_tmp = addr_open[j];
+ if (address_tmp == address) {
+ for (k = j; k < addr_cnt; k++)
+ addr_open[k] = addr_open[k+1];
+ addr_cnt--;
+ break;
+ }
+ }
+ }
break;
case UA:
case UA|PF:
@@ -2300,7 +2357,7 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
* configuration
*/
- if (need_close || need_restart) {
+ if (gsm->initiator && (need_close || need_restart)) {
int ret;
ret = gsm_disconnect(gsm);
@@ -2358,8 +2415,7 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
if (debug & 4)
print_hex_dump_bytes("gsmld_output: ", DUMP_PREFIX_OFFSET,
data, len);
- gsm->tty->ops->write(gsm->tty, data, len);
- return len;
+ return gsm->tty->ops->write(gsm->tty, data, len);
}
/**
@@ -2384,17 +2440,19 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
else {
/* Don't register device 0 - this is the control channel and not
a usable tty interface */
- base = mux_num_to_base(gsm); /* Base for this MUX */
- for (i = 1; i < NUM_DLCI; i++) {
- struct device *dev;
+ if (gsm->initiator) {
+ base = mux_num_to_base(gsm); /* Base for this MUX */
+ for (i = 1; i < NUM_DLCI; i++) {
+ struct device *dev;
- dev = tty_register_device(gsm_tty_driver,
+ dev = tty_register_device(gsm_tty_driver,
base + i, NULL);
- if (IS_ERR(dev)) {
- for (i--; i >= 1; i--)
- tty_unregister_device(gsm_tty_driver,
- base + i);
- return PTR_ERR(dev);
+ if (IS_ERR(dev)) {
+ for (i--; i >= 1; i--)
+ tty_unregister_device(gsm_tty_driver,
+ base + i);
+ return PTR_ERR(dev);
+ }
}
}
}
@@ -2416,8 +2474,10 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
int i;
WARN_ON(tty != gsm->tty);
- for (i = 1; i < NUM_DLCI; i++)
- tty_unregister_device(gsm_tty_driver, base + i);
+ if (gsm->initiator) {
+ for (i = 1; i < NUM_DLCI; i++)
+ tty_unregister_device(gsm_tty_driver, base + i);
+ }
gsm_cleanup_mux(gsm);
tty_kref_put(gsm->tty);
gsm->tty = NULL;
@@ -2651,7 +2711,7 @@ static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
base = mux_num_to_base(gsm);
return put_user(base + 1, (__u32 __user *)arg);
default:
- return n_tty_ioctl_helper(tty, file, cmd, arg);
+ return n_tty_ioctl_helper(tty, cmd, arg);
}
}
@@ -3000,6 +3060,7 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
{
struct gsm_dlci *dlci = tty->driver_data;
struct tty_port *port = &dlci->port;
+ struct gsm_mux *gsm = dlci->gsm;
port->count++;
tty_port_tty_set(port, tty);
@@ -3009,7 +3070,8 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
a DM straight back. This is ok as that will have caused a hangup */
tty_port_set_initialized(port, 1);
/* Start sending off SABM messages */
- gsm_dlci_begin_open(dlci);
+ if (gsm->initiator)
+ gsm_dlci_begin_open(dlci);
/* And wait for virtual carrier */
return tty_port_block_til_ready(port, tty, filp);
}