Synchronize email addresses
[elmcan.git] / module / elmcan.c
index c3acc07f70f5da8cb1880e86ea8eeba09827742a..0ff9cff132e85c8e58e1227fb02a40c5eb0dd249 100644 (file)
@@ -4,7 +4,7 @@
  *
  * This file is derived from linux/drivers/net/can/slcan.c
  *
- * elmcan.c Author : Max Staudt <elmcan@enpas.org>
+ * elmcan.c Author : Max Staudt <max-linux@enpas.org>
  * slcan.c Author  : Oliver Hartkopp <socketcan@hartkopp.net>
  * slip.c Authors  : Laurence Culhane <loz@holmes.demon.co.uk>
  *                   Fred N. van Kempen <waltje@uwalt.nl.mugnet.org>
@@ -22,6 +22,7 @@
 
 #include <linux/atomic.h>
 #include <linux/bitops.h>
+#include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/if_ether.h>
@@ -45,6 +46,16 @@ MODULE_DESCRIPTION("ELM327 based CAN interface");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Max Staudt <max-linux@enpas.org>");
 
+/* If this is enabled, we'll try to make the best of the situation
+ * even if we receive unexpected characters on the line.
+ * No guarantees.
+ * Handle with care, it's likely your hardware is unreliable!
+ */
+static bool accept_flaky_uart = false;
+module_param_named(accept_flaky_uart, accept_flaky_uart, bool, 0444);
+MODULE_PARM_DESC(accept_flaky_uart, "Don't bail at the first invalid character. Behavior undefined.");
+
+
 /* Line discipline ID number */
 #ifndef N_ELMCAN
 #define N_ELMCAN 29
@@ -66,6 +77,7 @@ enum ELM_TODO {
        ELM_TODO_CANID_11BIT,
        ELM_TODO_CANID_29BIT_LOW,
        ELM_TODO_CANID_29BIT_HIGH,
+       ELM_TODO_CAN_CONFIG_PART2,
        ELM_TODO_CAN_CONFIG,
        ELM_TODO_RESPONSES,
        ELM_TODO_SILENT_MONITOR,
@@ -192,7 +204,6 @@ static void elm327_kick_into_cmd_mode(struct elmcan *elm)
                elm327_send(elm, ELM327_MAGIC_STRING, 1);
 
                elm->state = ELM_GETMAGICCHAR;
-               elm->rxfill = 0;
        }
 }
 
@@ -360,6 +371,15 @@ static inline void elm327_hw_failure(struct elmcan *elm)
   * (assumes elm->lock taken)                                  *
   ************************************************************************/
 
+static bool elm327_is_ready_char(char c)
+{
+       /* Bits 0xc0 are sometimes set (randomly), hence the mask.
+        * Probably bad hardware.
+        */
+       return (c & 0x3f) == ELM327_READY_CHAR;
+}
+
+
 static void elm327_parse_error(struct elmcan *elm, int len)
 {
        struct can_frame frame;
@@ -441,6 +461,23 @@ static int elm327_parse_frame(struct elmcan *elm, int len)
                }
        }
 
+       /* If we accept stray characters coming in:
+        * Check for stray characters on a payload line.
+        * No idea what causes this.
+        */
+       if (accept_flaky_uart
+           && hexlen < len
+           && !isdigit(elm->rxbuf[hexlen])
+           && !isupper(elm->rxbuf[hexlen])
+           && '<' != elm->rxbuf[hexlen]
+           && ' ' != elm->rxbuf[hexlen]) {
+               /* The line is likely garbled anyway, so bail.
+                * The main code will restart listening.
+                */
+               elm327_kick_into_cmd_mode(elm);
+               return 3;
+       }
+
        /* Use spaces in CAN ID to distinguish 29 or 11 bit address length.
         * No out-of-bounds access:
         * We use the fact that we can always read from elm->rxbuf.
@@ -553,8 +590,8 @@ static void elm327_parse_line(struct elmcan *elm, int len)
                                /* Parse an error line. */
                                elm327_parse_error(elm, len);
 
-                               /* After the error line, we expect a prompt. */
-                               elm->state = ELM_GETPROMPT;
+                               /* Start afresh. */
+                               elm327_kick_into_cmd_mode(elm);
                        }
                        break;
                default:
@@ -595,6 +632,10 @@ static void elm327_handle_prompt(struct elmcan *elm)
                                !(elm->can.ctrlmode & CAN_CTRLMODE_LISTENONLY));
 
                } else if (test_and_clear_bit(ELM_TODO_CAN_CONFIG, &elm->cmds_todo)) {
+                       sprintf(local_txbuf, "ATPC\r");
+                       set_bit(ELM_TODO_CAN_CONFIG_PART2, &elm->cmds_todo);
+
+               } else if (test_and_clear_bit(ELM_TODO_CAN_CONFIG_PART2, &elm->cmds_todo)) {
                        sprintf(local_txbuf, "ATPB%04X\r",
                                elm->can_config);
 
@@ -668,7 +709,7 @@ static void elm327_parse_rxbuf(struct elmcan *elm)
                                elm->state = ELM_GETPROMPT;
                                i++;
                                break;
-                       } else if (elm->rxbuf[i] == ELM327_READY_CHAR) {
+                       } else if (elm327_is_ready_char(elm->rxbuf[i])) {
                                elm327_send(elm, ELM327_MAGIC_STRING, 1);
                                i++;
                                break;
@@ -682,7 +723,7 @@ static void elm327_parse_rxbuf(struct elmcan *elm)
 
        case ELM_GETPROMPT:
                /* Wait for '>' */
-               if (elm->rxbuf[elm->rxfill - 1] == ELM327_READY_CHAR) {
+               if (elm327_is_ready_char(elm->rxbuf[elm->rxfill - 1])) {
                        elm327_handle_prompt(elm);
                }
 
@@ -706,7 +747,7 @@ static void elm327_parse_rxbuf(struct elmcan *elm)
                        return;
                } else if (len == elm->rxfill) {
                        if (elm->state == ELM_RECEIVING
-                               && elm->rxbuf[elm->rxfill - 1] == ELM327_READY_CHAR) {
+                               && elm327_is_ready_char(elm->rxbuf[elm->rxfill - 1])) {
                                /* The ELM327's AT ST response timeout ran out,
                                 * so we got a prompt.
                                 * Clear RX buffer and restart listening.
@@ -936,6 +977,14 @@ static void elmcan_ldisc_rx(struct tty_struct *tty,
        if (!elm)
                return;
 
+       spin_lock_bh(&elm->lock);
+       if (elm->hw_failure) {
+               spin_unlock_bh(&elm->lock);
+
+               put_elm(elm);
+               return;
+       }
+
        /* Read the characters out of the buffer */
        while (count-- && elm->rxfill < sizeof(elm->rxbuf)) {
                if (fp && *fp++) {
@@ -948,16 +997,52 @@ static void elmcan_ldisc_rx(struct tty_struct *tty,
                        put_elm(elm);
                        return;
                }
+
+               /* Ignore NUL characters, which the PIC microcontroller may
+                * inadvertently insert due to a known hardware bug.
+                * See ELM327 documentation, which refers to a Microchip PIC
+                * bug description.
+                */
                if (*cp != 0) {
+                       /* Check for stray characters on the UART line.
+                        * No idea what causes this.
+                        */
+                       if (!accept_flaky_uart
+                           && !isdigit(*cp)
+                           && !isupper(*cp)
+                           && ELM327_MAGIC_CHAR != *cp
+                           && ELM327_READY_CHAR != *cp
+                           && '<' != *cp
+                           && 'a' != *cp
+                           && 'b' != *cp
+                           && 'v' != *cp
+                           && '.' != *cp
+                           && '?' != *cp
+                           && '\r' != *cp
+                           && ' ' != *cp) {
+                               /* We've received an invalid character, so bail.
+                                * There's something wrong with the ELM327, or
+                                * with the UART line.
+                                */
+                               netdev_err(elm->dev,
+                                       "Received illegal character %02x.\n",
+                                       *cp);
+                               elm327_hw_failure(elm);
+                               spin_unlock_bh(&elm->lock);
+
+                               put_elm(elm);
+                               return;
+                       }
+
                        elm->rxbuf[elm->rxfill++] = *cp;
                }
+
                cp++;
        }
 
        if (count >= 0) {
                netdev_err(elm->dev, "Receive buffer overflowed. Bad chip or wiring?");
 
-               spin_lock_bh(&elm->lock);
                elm327_hw_failure(elm);
                spin_unlock_bh(&elm->lock);
 
@@ -965,7 +1050,6 @@ static void elmcan_ldisc_rx(struct tty_struct *tty,
                return;
        }
 
-       spin_lock_bh(&elm->lock);
        elm327_parse_rxbuf(elm);
        spin_unlock_bh(&elm->lock);
 
@@ -1037,19 +1121,30 @@ static void elmcan_ldisc_tx_wakeup(struct tty_struct *tty)
 
 
 
-/* Some fake bit timings to allow bitrate setting */
-static const struct can_bittiming_const elmcan_bittiming_const = {
-       .name = "elmcan",
-       .tseg1_min = 1,
-       .tseg1_max = 1,
-       .tseg2_min = 0,
-       .tseg2_max = 0,
-       .sjw_max = 1,
-       .brp_min = 1,
-       .brp_max = 500,
-       .brp_inc = 1,
+/* ELM327 can only handle bitrates that are integer divisors of 500 kHz,
+ * or 7/8 of that. Divisors are 1 to 64.
+ * Currently we don't implement support for 7/8 rates.
+ */
+static const u32 elmcan_bitrate_const[64] = {
+       7812, 7936, 8064, 8196, 8333, 8474, 8620, 8771,
+       8928, 9090, 9259, 9433, 9615, 9803, 10000, 10204,
+       10416, 10638, 10869, 11111, 11363, 11627, 11904, 12195,
+       12500, 12820, 13157, 13513, 13888, 14285, 14705, 15151,
+       15625, 16129, 16666, 17241, 17857, 18518, 19230, 20000,
+       20833, 21739, 22727, 23809, 25000, 26315, 27777, 29411,
+       31250, 33333, 35714, 38461, 41666, 45454, 50000, 55555,
+       62500, 71428, 83333, 100000, 125000, 166666, 250000, 500000
 };
 
+/* Dummy function to claim we're changing the bitrate.
+ * We actually do this when opening the net device.
+ */
+static int elmcan_do_set_bittiming(struct net_device *netdev)
+{
+       return 0;
+}
+
+
 /*
  * Open the high-level part of the elmcan channel.
  * This function is called by the TTY module when the
@@ -1085,8 +1180,9 @@ static int elmcan_ldisc_open(struct tty_struct *tty)
 
        /* Configure CAN metadata */
        elm->can.state = CAN_STATE_STOPPED;
-       elm->can.clock.freq = 1000000;
-       elm->can.bittiming_const = &elmcan_bittiming_const;
+       elm->can.bitrate_const = elmcan_bitrate_const;
+       elm->can.bitrate_const_cnt = ARRAY_SIZE(elmcan_bitrate_const);
+       elm->can.do_set_bittiming = elmcan_do_set_bittiming;
        elm->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY;
 
        /* Configure netlink interface */