diff options
Diffstat (limited to 'drivers/tty/n_gsm.c')
-rw-r--r-- | drivers/tty/n_gsm.c | 89 |
1 files changed, 69 insertions, 20 deletions
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 739f522cb893..b3550ff9c494 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -339,6 +339,7 @@ struct gsm_mux { unsigned long bad_fcs; unsigned long malformed; unsigned long io_error; + unsigned long open_error; unsigned long bad_size; unsigned long unsupported; }; @@ -1450,15 +1451,16 @@ static int gsm_control_command(struct gsm_mux *gsm, int cmd, const u8 *data, int dlen) { struct gsm_msg *msg; + struct gsm_dlci *dlci = gsm->dlci[0]; - msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->dlci[0]->ftype); + msg = gsm_data_alloc(gsm, 0, dlen + 2, dlci->ftype); if (msg == NULL) return -ENOMEM; msg->data[0] = (cmd << 1) | CR | EA; /* Set C/R */ msg->data[1] = (dlen << 1) | EA; memcpy(msg->data + 2, data, dlen); - gsm_data_queue(gsm->dlci[0], msg); + gsm_data_queue(dlci, msg); return 0; } @@ -1477,14 +1479,15 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data, int dlen) { struct gsm_msg *msg; + struct gsm_dlci *dlci = gsm->dlci[0]; - msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->dlci[0]->ftype); + msg = gsm_data_alloc(gsm, 0, dlen + 2, dlci->ftype); if (msg == NULL) return; msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ msg->data[1] = (dlen << 1) | EA; memcpy(msg->data + 2, data, dlen); - gsm_data_queue(gsm->dlci[0], msg); + gsm_data_queue(dlci, msg); } /** @@ -1589,6 +1592,7 @@ static int gsm_process_negotiation(struct gsm_mux *gsm, unsigned int addr, if (debug & DBG_ERRORS) pr_info("%s unsupported I frame request in PN\n", __func__); + gsm->unsupported++; return -EINVAL; default: if (debug & DBG_ERRORS) @@ -1730,25 +1734,32 @@ static void gsm_control_negotiation(struct gsm_mux *gsm, unsigned int cr, struct gsm_dlci *dlci; struct gsm_dlci_param_bits *params; - if (dlen < sizeof(struct gsm_dlci_param_bits)) + if (dlen < sizeof(struct gsm_dlci_param_bits)) { + gsm->open_error++; return; + } /* Invalid DLCI? */ params = (struct gsm_dlci_param_bits *)data; addr = FIELD_GET(PN_D_FIELD_DLCI, params->d_bits); - if (addr == 0 || addr >= NUM_DLCI || !gsm->dlci[addr]) + if (addr == 0 || addr >= NUM_DLCI || !gsm->dlci[addr]) { + gsm->open_error++; return; + } dlci = gsm->dlci[addr]; /* Too late for parameter negotiation? */ - if ((!cr && dlci->state == DLCI_OPENING) || dlci->state == DLCI_OPEN) + if ((!cr && dlci->state == DLCI_OPENING) || dlci->state == DLCI_OPEN) { + gsm->open_error++; return; + } /* Process the received parameters */ if (gsm_process_negotiation(gsm, addr, cr, params) != 0) { /* Negotiation failed. Close the link. */ if (debug & DBG_ERRORS) pr_info("%s PN failed\n", __func__); + gsm->open_error++; gsm_dlci_close(dlci); return; } @@ -1768,6 +1779,7 @@ static void gsm_control_negotiation(struct gsm_mux *gsm, unsigned int cr, } else { if (debug & DBG_ERRORS) pr_info("%s PN in invalid state\n", __func__); + gsm->open_error++; } } @@ -1888,6 +1900,8 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, /* Optional unsupported commands */ case CMD_RPN: /* Remote port negotiation */ case CMD_SNC: /* Service negotiation command */ + gsm->unsupported++; + fallthrough; default: /* Reply to bad commands with an NSC */ buf[0] = command; @@ -2221,6 +2235,7 @@ static void gsm_dlci_t1(struct timer_list *t) dlci->retries--; mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); } else { + gsm->open_error++; gsm_dlci_begin_close(dlci); /* prevent half open link */ } break; @@ -2236,6 +2251,7 @@ static void gsm_dlci_t1(struct timer_list *t) dlci->mode = DLCI_MODE_ADM; gsm_dlci_open(dlci); } else { + gsm->open_error++; gsm_dlci_begin_close(dlci); /* prevent half open link */ } @@ -2444,8 +2460,10 @@ static void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len) data += dlen; /* Malformed command? */ - if (clen > len) + if (clen > len) { + dlci->gsm->malformed++; return; + } if (command & 1) gsm_control_message(dlci->gsm, command, data, clen); @@ -2532,6 +2550,8 @@ static int gsm_dlci_config(struct gsm_dlci *dlci, struct gsm_dlci_config *dc, in return -EINVAL; if (dc->k > 7) return -EINVAL; + if (dc->flags & ~GSM_FL_RESTART) /* allow future extensions */ + return -EINVAL; /* * See what is needed for reconfiguration @@ -2546,6 +2566,8 @@ static int gsm_dlci_config(struct gsm_dlci *dlci, struct gsm_dlci_config *dc, in /* Requires care */ if (dc->priority != dlci->prio) need_restart = true; + if (dc->flags & GSM_FL_RESTART) + need_restart = true; if ((open && gsm->wait_config) || need_restart) need_open = true; @@ -2753,12 +2775,16 @@ static void gsm_queue(struct gsm_mux *gsm) switch (gsm->control) { case SABM|PF: - if (cr == 1) + if (cr == 1) { + gsm->open_error++; goto invalid; + } if (dlci == NULL) dlci = gsm_dlci_alloc(gsm, address); - if (dlci == NULL) + if (dlci == NULL) { + gsm->open_error++; return; + } if (dlci->dead) gsm_response(gsm, address, DM|PF); else { @@ -3276,7 +3302,6 @@ static void gsm_copy_config_values(struct gsm_mux *gsm, static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c) { - int ret = 0; int need_close = 0; int need_restart = 0; @@ -3355,7 +3380,7 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c) * and removing from the mux array */ if (gsm->dead) { - ret = gsm_activate_mux(gsm); + int ret = gsm_activate_mux(gsm); if (ret) return ret; if (gsm->initiator) @@ -3374,6 +3399,7 @@ static void gsm_copy_config_ext_values(struct gsm_mux *gsm, static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce) { + bool need_restart = false; unsigned int i; /* @@ -3383,6 +3409,20 @@ static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce) for (i = 0; i < ARRAY_SIZE(ce->reserved); i++) if (ce->reserved[i]) return -EINVAL; + if (ce->flags & ~GSM_FL_RESTART) + return -EINVAL; + + /* Requires care */ + if (ce->flags & GSM_FL_RESTART) + need_restart = true; + + /* + * Close down what is needed, restart and initiate the new + * configuration. On the first time there is no DLCI[0] + * and closing or cleaning up is not necessary. + */ + if (need_restart) + gsm_cleanup_mux(gsm, true); /* * Setup the new configuration values @@ -3390,6 +3430,14 @@ static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce) gsm->wait_config = ce->wait_config ? true : false; gsm->keep_alive = ce->keep_alive; + if (gsm->dead) { + int ret = gsm_activate_mux(gsm); + if (ret) + return ret; + if (gsm->initiator) + gsm_dlci_begin_open(gsm->dlci[0]); + } + return 0; } @@ -3490,8 +3538,8 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) gsm->tty = NULL; } -static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, - const char *fp, int count) +static void gsmld_receive_buf(struct tty_struct *tty, const u8 *cp, + const u8 *fp, size_t count) { struct gsm_mux *gsm = tty->disc_data; char flags = TTY_NORMAL; @@ -3577,6 +3625,9 @@ static int gsmld_open(struct tty_struct *tty) { struct gsm_mux *gsm; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (tty->ops->write == NULL) return -EINVAL; @@ -3636,9 +3687,8 @@ static void gsmld_write_wakeup(struct tty_struct *tty) * This code must be sure never to sleep through a hangup. */ -static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, - unsigned char *buf, size_t nr, - void **cookie, unsigned long offset) +static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, u8 *buf, + size_t nr, void **cookie, unsigned long offset) { return -EOPNOTSUPP; } @@ -3658,7 +3708,7 @@ static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, */ static ssize_t gsmld_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t nr) + const u8 *buf, size_t nr) { struct gsm_mux *gsm = tty->disc_data; unsigned long flags; @@ -4254,8 +4304,7 @@ static void gsmtty_hangup(struct tty_struct *tty) gsm_dlci_begin_close(dlci); } -static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, - int len) +static ssize_t gsmtty_write(struct tty_struct *tty, const u8 *buf, size_t len) { int sent; struct gsm_dlci *dlci = tty->driver_data; |