Merge tag 'phy-for-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux...
[linux-2.6-microblaze.git] / drivers / tty / n_gsm.c
index 0b1808e..fa92f72 100644 (file)
@@ -439,7 +439,7 @@ static u8 gsm_encode_modem(const struct gsm_dlci *dlci)
                modembits |= MDM_RTR;
        if (dlci->modem_tx & TIOCM_RI)
                modembits |= MDM_IC;
-       if (dlci->modem_tx & TIOCM_CD)
+       if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator)
                modembits |= MDM_DV;
        return modembits;
 }
@@ -448,7 +448,7 @@ static u8 gsm_encode_modem(const struct gsm_dlci *dlci)
  *     gsm_print_packet        -       display a frame for debug
  *     @hdr: header to print before decode
  *     @addr: address EA from the frame
- *     @cr: C/R bit from the frame
+ *     @cr: C/R bit seen as initiator
  *     @control: control including PF bit
  *     @data: following data bytes
  *     @dlen: length of data
@@ -548,7 +548,7 @@ static int gsm_stuff_frame(const u8 *input, u8 *output, int len)
  *     gsm_send        -       send a control frame
  *     @gsm: our GSM mux
  *     @addr: address for control frame
- *     @cr: command/response bit
+ *     @cr: command/response bit seen as initiator
  *     @control:  control byte including PF bit
  *
  *     Format up and transmit a control frame. These do not go via the
@@ -563,11 +563,15 @@ static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
        int len;
        u8 cbuf[10];
        u8 ibuf[3];
+       int ocr;
+
+       /* toggle C/R coding if not initiator */
+       ocr = cr ^ (gsm->initiator ? 0 : 1);
 
        switch (gsm->encoding) {
        case 0:
                cbuf[0] = GSM0_SOF;
-               cbuf[1] = (addr << 2) | (cr << 1) | EA;
+               cbuf[1] = (addr << 2) | (ocr << 1) | EA;
                cbuf[2] = control;
                cbuf[3] = EA;   /* Length of data = 0 */
                cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3);
@@ -577,7 +581,7 @@ static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
        case 1:
        case 2:
                /* Control frame + packing (but not frame stuffing) in mode 1 */
-               ibuf[0] = (addr << 2) | (cr << 1) | EA;
+               ibuf[0] = (addr << 2) | (ocr << 1) | EA;
                ibuf[1] = control;
                ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2);
                /* Stuffing may double the size worst case */
@@ -611,7 +615,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, 1, control);
+       gsm_send(gsm, addr, 0, control);
 }
 
 /**
@@ -1017,25 +1021,25 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
  *     @tty: virtual tty bound to the DLCI
  *     @dlci: DLCI to affect
  *     @modem: modem bits (full EA)
- *     @clen: command length
+ *     @slen: number of signal octets
  *
  *     Used when a modem control message or line state inline in adaption
  *     layer 2 is processed. Sort out the local modem state and throttles
  */
 
 static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
-                                                       u32 modem, int clen)
+                                                       u32 modem, int slen)
 {
        int  mlines = 0;
        u8 brk = 0;
        int fc;
 
-       /* The modem status command can either contain one octet (v.24 signals)
-          or two octets (v.24 signals + break signals). The length field will
-          either be 2 or 3 respectively. This is specified in section
-          5.4.6.3.7 of the  27.010 mux spec. */
+       /* The modem status command can either contain one octet (V.24 signals)
+        * or two octets (V.24 signals + break signals). This is specified in
+        * section 5.4.6.3.7 of the 07.10 mux spec.
+        */
 
-       if (clen == 2)
+       if (slen == 1)
                modem = modem & 0x7f;
        else {
                brk = modem & 0x7f;
@@ -1092,6 +1096,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
        unsigned int brk = 0;
        struct gsm_dlci *dlci;
        int len = clen;
+       int slen;
        const u8 *dp = data;
        struct tty_struct *tty;
 
@@ -1111,6 +1116,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
                return;
        dlci = gsm->dlci[addr];
 
+       slen = len;
        while (gsm_read_ea(&modem, *dp++) == 0) {
                len--;
                if (len == 0)
@@ -1127,7 +1133,7 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
                modem |= (brk & 0x7f);
        }
        tty = tty_port_tty_get(&dlci->port);
-       gsm_process_modem(tty, dlci, modem, clen);
+       gsm_process_modem(tty, dlci, modem, slen);
        if (tty) {
                tty_wakeup(tty);
                tty_kref_put(tty);
@@ -1451,6 +1457,9 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
        if (dlci->addr != 0) {
                tty_port_tty_hangup(&dlci->port, false);
                kfifo_reset(&dlci->fifo);
+               /* Ensure that gsmtty_open() can return. */
+               tty_port_set_initialized(&dlci->port, 0);
+               wake_up_interruptible(&dlci->port.open_wait);
        } else
                dlci->gsm->dead = true;
        /* Unregister gsmtty driver,report gsmtty dev remove uevent for user */
@@ -1514,7 +1523,7 @@ static void gsm_dlci_t1(struct timer_list *t)
                        dlci->mode = DLCI_MODE_ADM;
                        gsm_dlci_open(dlci);
                } else {
-                       gsm_dlci_close(dlci);
+                       gsm_dlci_begin_close(dlci); /* prevent half open link */
                }
 
                break;
@@ -1593,6 +1602,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
        struct tty_struct *tty;
        unsigned int modem = 0;
        int len = clen;
+       int slen = 0;
 
        if (debug & 16)
                pr_debug("%d bytes for tty\n", len);
@@ -1605,12 +1615,14 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
        case 2:         /* Asynchronous serial with line state in each frame */
                while (gsm_read_ea(&modem, *data++) == 0) {
                        len--;
+                       slen++;
                        if (len == 0)
                                return;
                }
+               slen++;
                tty = tty_port_tty_get(port);
                if (tty) {
-                       gsm_process_modem(tty, dlci, modem, clen);
+                       gsm_process_modem(tty, dlci, modem, slen);
                        tty_kref_put(tty);
                }
                fallthrough;
@@ -1748,7 +1760,12 @@ static void gsm_dlci_release(struct gsm_dlci *dlci)
                gsm_destroy_network(dlci);
                mutex_unlock(&dlci->mutex);
 
-               tty_hangup(tty);
+               /* We cannot use tty_hangup() because in tty_kref_put() the tty
+                * driver assumes that the hangup queue is free and reuses it to
+                * queue release_one_tty() -> NULL pointer panic in
+                * process_one_work().
+                */
+               tty_vhangup(tty);
 
                tty_port_tty_set(&dlci->port, NULL);
                tty_kref_put(tty);
@@ -1800,10 +1817,10 @@ static void gsm_queue(struct gsm_mux *gsm)
                goto invalid;
 
        cr = gsm->address & 1;          /* C/R bit */
+       cr ^= gsm->initiator ? 0 : 1;   /* Flip so 1 always means command */
 
        gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len);
 
-       cr ^= 1 - gsm->initiator;       /* Flip so 1 always means command */
        dlci = gsm->dlci[address];
 
        switch (gsm->control) {
@@ -3234,9 +3251,9 @@ static void gsmtty_throttle(struct tty_struct *tty)
        if (dlci->state == DLCI_CLOSED)
                return;
        if (C_CRTSCTS(tty))
-               dlci->modem_tx &= ~TIOCM_DTR;
+               dlci->modem_tx &= ~TIOCM_RTS;
        dlci->throttled = true;
-       /* Send an MSC with DTR cleared */
+       /* Send an MSC with RTS cleared */
        gsmtty_modem_update(dlci, 0);
 }
 
@@ -3246,9 +3263,9 @@ static void gsmtty_unthrottle(struct tty_struct *tty)
        if (dlci->state == DLCI_CLOSED)
                return;
        if (C_CRTSCTS(tty))
-               dlci->modem_tx |= TIOCM_DTR;
+               dlci->modem_tx |= TIOCM_RTS;
        dlci->throttled = false;
-       /* Send an MSC with DTR set */
+       /* Send an MSC with RTS set */
        gsmtty_modem_update(dlci, 0);
 }