{
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]);
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 */
}
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);
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;
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;
#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";
static struct kmem_cache *cgw_cache __read_mostly;
#define CGW_SK_MAGIC ((void *)(¬ifier))
-#define CGW_CS_DISABLED 42
/* structure that contains the (on-the-fly) CAN frame modifications */
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;
};
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 */
/* 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 */
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) {
/* 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)
/* 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;
}
}