]> rtime.felk.cvut.cz Git - socketcan-devel.git/commitdiff
Added checksum functionality and some documentation in gw.h
authorhartkopp <hartkopp@030b6a49-0b11-0410-94ab-b0dab22257f2>
Mon, 12 Apr 2010 14:41:14 +0000 (14:41 +0000)
committerhartkopp <hartkopp@030b6a49-0b11-0410-94ab-b0dab22257f2>
Mon, 12 Apr 2010 14:41:14 +0000 (14:41 +0000)
git-svn-id: svn://svn.berlios.de//socketcan/trunk@1166 030b6a49-0b11-0410-94ab-b0dab22257f2

can-utils/cangw.c
kernel/2.6/include/socketcan/can/gw.h
kernel/2.6/net/can/gw.c

index d8757e3c01bcf07b3838ca9fe9cbeeab2981224d..8f99f9b6b4eb0d006ba10bade552cba8c6f3c0d1 100644 (file)
@@ -135,16 +135,17 @@ void print_cs_xor(struct cgw_csum_xor *cs_xor)
 {
        printf("-x %d:%d:%d:%02X ",
               cs_xor->from_idx, cs_xor->to_idx,
-              cs_xor->result_idx, cs_xor->prefix_value);
+              cs_xor->result_idx, cs_xor->init_xor_val);
 }
 
 void print_cs_crc8(struct cgw_csum_crc8 *cs_crc8)
 {
        int i;
 
-       printf("-c %d:%d:%d:",
+       printf("-c %d:%d:%d:%02X:%02X:",
               cs_crc8->from_idx, cs_crc8->to_idx,
-              cs_crc8->result_idx);
+              cs_crc8->result_idx, cs_crc8->init_crc_val,
+              cs_crc8->final_xor_val);
 
        for (i = 0; i < 256; i++)
                printf("%02X", cs_crc8->crctab[i]);
@@ -246,18 +247,25 @@ int parse_mod(char *optarg, struct modattr *modmsg)
                ptr++;
        }
 
-       if ((sscanf(++ptr, "%lx.%hhd.%16s",
-                   (long unsigned int *)&modmsg->cf.can_id,
-                   (unsigned char *)&modmsg->cf.can_dlc, hexdata) != 3) ||
-           (modmsg->cf.can_dlc > 8))
+       if (sscanf(++ptr, "%lx.%hhx.%16s",
+                  (long unsigned int *)&modmsg->cf.can_id,
+                  (unsigned char *)&modmsg->cf.can_dlc, hexdata) != 3)
                return 5;
 
-       if (strlen(hexdata) != 16)
+       /* 4-bit masks can have values from 0 to 0xF */ 
+       if (modmsg->cf.can_dlc > 0xF)
                return 6;
 
-       if (b64hex(hexdata, &modmsg->cf.data[0], 8))
+       /* but when setting CAN_DLC the value has to be limited to 8 */
+       if (modmsg->instruction == CGW_MOD_SET && modmsg->cf.can_dlc > 8)
                return 7;
 
+       if (strlen(hexdata) != 16)
+               return 8;
+
+       if (b64hex(hexdata, &modmsg->cf.data[0], 8))
+               return 9;
+
        return 0; /* ok */
 }
 
@@ -515,7 +523,7 @@ int main(int argc, char **argv)
                case 'x':
                        if (sscanf(optarg, "%hhd:%hhd:%hhd:%hhx",
                                   &cs_xor.from_idx, &cs_xor.to_idx,
-                                  &cs_xor.result_idx, &cs_xor.prefix_value) == 4) {
+                                  &cs_xor.result_idx, &cs_xor.init_xor_val) == 4) {
                                have_cs_xor = 1;
                        } else {
                                printf("Bad XOR checksum definition '%s'.\n", optarg);
@@ -524,9 +532,10 @@ int main(int argc, char **argv)
                        break;
 
                case 'c':
-                       if ((sscanf(optarg, "%hhd:%hhd:%hhd:%512s",
+                       if ((sscanf(optarg, "%hhd:%hhd:%hhd:%hhx:%hhx:%512s",
                                    &cs_crc8.from_idx, &cs_crc8.to_idx,
-                                   &cs_crc8.result_idx, crc8tab) == 4) &&
+                                   &cs_crc8.result_idx, &cs_crc8.init_crc_val,
+                                   &cs_crc8.final_xor_val, crc8tab) == 6) &&
                            (strlen(crc8tab) == 512) &&
                            (b64hex(crc8tab, (unsigned char *)&cs_crc8.crctab, 256) == 0)) {
                                have_cs_crc8 = 1;
@@ -616,12 +625,12 @@ int main(int argc, char **argv)
        if (have_filter)
                addattr_l(&req.nh, sizeof(req), CGW_FILTER, &filter, sizeof(filter));
 
-       if (have_cs_xor)
-               addattr_l(&req.nh, sizeof(req), CGW_CS_XOR, &cs_xor, sizeof(cs_xor));
-
        if (have_cs_crc8)
                addattr_l(&req.nh, sizeof(req), CGW_CS_CRC8, &cs_crc8, sizeof(cs_crc8));
 
+       if (have_cs_xor)
+               addattr_l(&req.nh, sizeof(req), CGW_CS_XOR, &cs_xor, sizeof(cs_xor));
+
        /*
         * a better example code
         * modmsg.modtype = CGW_MOD_ID;
index f8ead84eab8e93edd530951d430a95087a1e7181..2cf3fb7280f63e4d3466536547800ef8379bfaf3 100644 (file)
@@ -77,13 +77,15 @@ struct cgw_csum_xor {
        __s8 from_idx;
        __s8 to_idx;
        __s8 result_idx;
-       __u8 prefix_value;
+       __u8 init_xor_val;
 } __attribute__ ((packed));
 
 struct cgw_csum_crc8 {
        __s8 from_idx;
        __s8 to_idx;
        __s8 result_idx;
+       __u8 init_crc_val;
+       __u8 final_xor_val;
        __u8 crctab[256];
 } __attribute__ ((packed));
 
@@ -110,14 +112,34 @@ struct cgw_csum_crc8 {
  * <u8> affected CAN frame elements
  *
  * CGW_CS_XOR (length 4 bytes):
- * Set a simple XOR checksum starting with the initial prefix-value into
+ * Set a simple XOR checksum starting with an initial value into
  * data[result-idx] using data[start-idx] .. data[end-idx]
  *
- * CGW_CS_CRC8 (length 259 bytes):
- * Set a CRC8 value into data[result-idx] using a given 256 byte CRC8 table and
- * a defined input data[start-idx] .. data[end-idx]
+ * The XOR checksum is calculated like this:
  *
- * Remark: The attribute data is a linear buffer. Beware of sending structs!
+ * xor = init_xor_val
+ * 
+ * for (i = from_idx .. to_idx)
+ *      xor ^= can_frame.data[i]
+ *
+ * can_frame.data[ result_idx ] = xor
+ *
+ * CGW_CS_CRC8 (length 261 bytes):
+ * Set a CRC8 value into data[result-idx] using a given 256 byte CRC8 table,
+ * a given initial value and a defined input data[start-idx] .. data[end-idx].
+ * Finally the result value is XOR'ed with the final_xor_val.
+ *
+ * The CRC8 checksum is calculated like this:
+ *
+ * crc = init_crc_val
+ * 
+ * for (i = from_idx .. to_idx)
+ *      crc = crctab[ crc ^ can_frame.data[i] ]
+ *
+ * can_frame.data[ result_idx ] = crc ^ final_xor_val
+ *
+ * Remark: In general the attribute data is a linear buffer.
+ *         Beware of sending unpacked or aligned structs!
  */
 
 #endif
index 33d8502b64cf5358009c4901ee1a58f03883f8b3..5ec8fa8f51af9c67d35ab526b0e8590dc4515ccd 100644 (file)
@@ -62,7 +62,7 @@
 #include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
 RCSID("$Id$");
 
-#define CAN_GW_VERSION "20100410"
+#define CAN_GW_VERSION "20100412"
 static __initdata const char banner[] =
        KERN_INFO "can: netlink gateway (rev " CAN_GW_VERSION ")\n";
 
@@ -78,7 +78,6 @@ static struct notifier_block notifier;
 static struct kmem_cache *cgw_cache __read_mostly;
 
 #define CGW_SK_MAGIC ((void *)(&notifier))
-#define CGW_CS_DISABLED 42
 
 /* structure that contains the (on-the-fly) CAN frame modifications */
 struct cf_mod {
@@ -102,6 +101,10 @@ struct cf_mod {
                struct cgw_csum_xor xor;
                struct cgw_csum_crc8 crc8;
        } csum;
+       struct {
+               void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
+               void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
+       } csumfunc;
 };
 
 
@@ -190,14 +193,100 @@ static int cgw_chk_csum_parms(s8 fr, s8 to, s8 re)
                return -EINVAL;
 } 
 
-static void cgw_csum_do_xor(struct can_frame *cf, struct cgw_csum_xor *xor)
+static inline int calc_idx(int idx, int rx_dlc)
+{
+       if (idx < 0)
+               return rx_dlc + idx;
+       else
+               return idx;
+}
+
+static void cgw_csum_xor_rel(struct can_frame *cf, struct cgw_csum_xor *xor)
+{
+       int from = calc_idx(xor->from_idx, cf->can_dlc);
+       int to = calc_idx(xor->to_idx, cf->can_dlc);
+       int res = calc_idx(xor->result_idx, cf->can_dlc);
+       u8 val = xor->init_xor_val;
+       int i;
+
+       if (from < 0 || to < 0 || res < 0)
+               return;
+
+       if (from <= to) {
+               for (i = from; i <= to; i++)
+                       val ^= cf->data[i]; 
+       } else {
+               for (i = from; i >= to; i--)
+                       val ^= cf->data[i]; 
+       }
+
+       cf->data[res] = val;
+}
+
+static void cgw_csum_xor_pos(struct can_frame *cf, struct cgw_csum_xor *xor)
+{
+       u8 val = xor->init_xor_val;
+       int i;
+
+       for (i = xor->from_idx; i <= xor->to_idx; i++)
+               val ^= cf->data[i];
+
+       cf->data[xor->result_idx] = val;
+}
+
+static void cgw_csum_xor_neg(struct can_frame *cf, struct cgw_csum_xor *xor)
 {
-       /* TODO: perform checksum update */
+       u8 val = xor->init_xor_val;
+       int i;
+
+       for (i = xor->from_idx; i >= xor->to_idx; i--)
+               val ^= cf->data[i];
+
+       cf->data[xor->result_idx] = val;
+}
+
+static void cgw_csum_crc8_rel(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
+{
+       int from = calc_idx(crc8->from_idx, cf->can_dlc);
+       int to = calc_idx(crc8->to_idx, cf->can_dlc);
+       int res = calc_idx(crc8->result_idx, cf->can_dlc);
+       u8 crc = crc8->init_crc_val;
+       int i;
+
+       if (from < 0 || to < 0 || res < 0)
+               return;
+
+       if (from <= to) {
+               for (i = crc8->from_idx; i <= crc8->to_idx; i++)
+                       crc = crc8->crctab[crc^cf->data[i]];
+       } else {
+               for (i = crc8->from_idx; i >= crc8->to_idx; i--)
+                       crc = crc8->crctab[crc^cf->data[i]];
+       }
+
+       cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
 }
 
-static void cgw_csum_do_crc8(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
+static void cgw_csum_crc8_pos(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
 {
-       /* TODO: perform checksum update */
+       u8 crc = crc8->init_crc_val;
+       int i;
+
+       for (i = crc8->from_idx; i <= crc8->to_idx; i++)
+               crc = crc8->crctab[crc^cf->data[i]];
+
+       cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
+}
+
+static void cgw_csum_crc8_neg(struct can_frame *cf, struct cgw_csum_crc8 *crc8)
+{
+       u8 crc = crc8->init_crc_val;
+       int i;
+
+       for (i = crc8->from_idx; i >= crc8->to_idx; i--)
+               crc = crc8->crctab[crc^cf->data[i]];
+
+       cf->data[crc8->result_idx] = crc^crc8->final_xor_val;
 }
 
 /* the receive & process & send function */
@@ -246,11 +335,11 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
 
        /* check for checksum updates when the CAN frame has been modified */
        if (modidx) {
-               if (gwj->mod.csum.xor.from_idx != CGW_CS_DISABLED)
-                       cgw_csum_do_xor(cf, &gwj->mod.csum.xor);
+               if (gwj->mod.csumfunc.crc8)
+                       (*gwj->mod.csumfunc.crc8)(cf, &gwj->mod.csum.crc8);
 
-               if (gwj->mod.csum.crc8.from_idx != CGW_CS_DISABLED)
-                       cgw_csum_do_crc8(cf, &gwj->mod.csum.crc8);
+               if (gwj->mod.csumfunc.xor)
+                       (*gwj->mod.csumfunc.xor)(cf, &gwj->mod.csum.xor);
        }
 
        /* clear the skb timestamp if not configured the other way */
@@ -381,22 +470,22 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj)
                        nlh->nlmsg_len += NLA_HDRLEN + NLA_ALIGN(sizeof(mb));
        }
 
-       if (gwj->mod.csum.xor.from_idx != CGW_CS_DISABLED) {
-               if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
-                           &gwj->mod.csum.xor) < 0)
+       if (gwj->mod.csumfunc.crc8) {
+               if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
+                           &gwj->mod.csum.crc8) < 0)
                        goto cancel;
                else
                        nlh->nlmsg_len += NLA_HDRLEN + \
-                               NLA_ALIGN(CGW_CS_XOR_LEN);
+                               NLA_ALIGN(CGW_CS_CRC8_LEN);
        }
 
-       if (gwj->mod.csum.crc8.from_idx != CGW_CS_DISABLED) {
-               if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
-                           &gwj->mod.csum.crc8) < 0)
+       if (gwj->mod.csumfunc.xor) {
+               if (nla_put(skb, CGW_CS_XOR, CGW_CS_XOR_LEN,
+                           &gwj->mod.csum.xor) < 0)
                        goto cancel;
                else
                        nlh->nlmsg_len += NLA_HDRLEN + \
-                               NLA_ALIGN(CGW_CS_CRC8_LEN);
+                               NLA_ALIGN(CGW_CS_XOR_LEN);
        }
 
        if (gwj->gwtype == CGW_TYPE_CAN_CAN) {
@@ -464,8 +553,6 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
 
        /* initialize modification & checksum data space */
        memset(mod, 0, sizeof(*mod)); 
-       mod->csum.xor.from_idx = CGW_CS_DISABLED;
-       mod->csum.crc8.from_idx = CGW_CS_DISABLED;
 
        err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX, NULL);
        if (err < 0)
@@ -544,26 +631,58 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
        /* check for checksum operations after CAN frame modifications */
        if (modidx) {
 
-               if (tb[CGW_CS_XOR] &&
-                   nla_len(tb[CGW_CS_XOR]) == CGW_CS_XOR_LEN) {
-                       nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
-                                  CGW_CS_XOR_LEN);
-                       err = cgw_chk_csum_parms(mod->csum.xor.from_idx,
-                                                mod->csum.xor.to_idx,
-                                                mod->csum.xor.result_idx);
+               if (tb[CGW_CS_CRC8] &&
+                   nla_len(tb[CGW_CS_CRC8]) == CGW_CS_CRC8_LEN) {
+
+                       struct cgw_csum_crc8 *c = (struct cgw_csum_crc8 *)\
+                               nla_data(tb[CGW_CS_CRC8]);
+
+                       err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
+                                                c->result_idx);
                        if (err)
                                return err;
-               }
 
-               if (tb[CGW_CS_CRC8] &&
-                   nla_len(tb[CGW_CS_CRC8]) == CGW_CS_CRC8_LEN) {
                        nla_memcpy(&mod->csum.crc8, tb[CGW_CS_CRC8],
                                   CGW_CS_CRC8_LEN);
-                       err = cgw_chk_csum_parms(mod->csum.crc8.from_idx,
-                                                mod->csum.crc8.to_idx,
-                                                mod->csum.crc8.result_idx);
+
+                       /*
+                        * select dedicated processing function to reduce
+                        * runtime operations in receive hot path.
+                        */
+                       if (c->from_idx < 0 || c->to_idx < 0 ||
+                           c->result_idx < 0)
+                               mod->csumfunc.crc8 = cgw_csum_crc8_rel;
+                       else if (c->from_idx <= c->to_idx)
+                               mod->csumfunc.crc8 = cgw_csum_crc8_pos;
+                       else
+                               mod->csumfunc.crc8 = cgw_csum_crc8_neg;
+               }
+
+               if (tb[CGW_CS_XOR] &&
+                   nla_len(tb[CGW_CS_XOR]) == CGW_CS_XOR_LEN) {
+
+                       struct cgw_csum_xor *c = (struct cgw_csum_xor *)\
+                               nla_data(tb[CGW_CS_XOR]);
+
+                       err = cgw_chk_csum_parms(c->from_idx, c->to_idx,
+                                                c->result_idx);
                        if (err)
                                return err;
+
+                       nla_memcpy(&mod->csum.xor, tb[CGW_CS_XOR],
+                                  CGW_CS_XOR_LEN);
+
+                       /*
+                        * select dedicated processing function to reduce
+                        * runtime operations in receive hot path.
+                        */
+                       if (c->from_idx < 0 || c->to_idx < 0 ||
+                           c->result_idx < 0)
+                               mod->csumfunc.xor = cgw_csum_xor_rel;
+                       else if (c->from_idx <= c->to_idx)
+                               mod->csumfunc.xor = cgw_csum_xor_pos;
+                       else
+                               mod->csumfunc.xor = cgw_csum_xor_neg;
                }
        }