mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
can: slcan: extend the protocol with error info
It extends the protocol to receive the adapter CAN communication errors and forward them to the netdev upper levels. Link: https://lore.kernel.org/all/20220628163137.413025-12-dario.binacchi@amarulasolutions.com Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
parent
4de0e8efa0
commit
b32ff46685
1 changed files with 139 additions and 1 deletions
|
@ -175,7 +175,7 @@ int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on)
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
/* Send one completely decapsulated can_frame to the network layer */
|
/* Send one completely decapsulated can_frame to the network layer */
|
||||||
static void slc_bump(struct slcan *sl)
|
static void slc_bump_frame(struct slcan *sl)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct can_frame *cf;
|
struct can_frame *cf;
|
||||||
|
@ -254,6 +254,144 @@ decode_failed:
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* An error frame can contain more than one type of error.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
*
|
||||||
|
* e1a : len 1, errors: ACK error
|
||||||
|
* e3bcO: len 3, errors: Bit0 error, CRC error, Tx overrun error
|
||||||
|
*/
|
||||||
|
static void slc_bump_err(struct slcan *sl)
|
||||||
|
{
|
||||||
|
struct net_device *dev = sl->dev;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct can_frame *cf;
|
||||||
|
char *cmd = sl->rbuff;
|
||||||
|
bool rx_errors = false, tx_errors = false, rx_over_errors = false;
|
||||||
|
int i, len;
|
||||||
|
|
||||||
|
/* get len from sanitized ASCII value */
|
||||||
|
len = cmd[1];
|
||||||
|
if (len >= '0' && len < '9')
|
||||||
|
len -= '0';
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((len + SLC_CMD_LEN + 1) > sl->rcount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
skb = alloc_can_err_skb(dev, &cf);
|
||||||
|
|
||||||
|
if (skb)
|
||||||
|
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
|
||||||
|
|
||||||
|
cmd += SLC_CMD_LEN + 1;
|
||||||
|
for (i = 0; i < len; i++, cmd++) {
|
||||||
|
switch (*cmd) {
|
||||||
|
case 'a':
|
||||||
|
netdev_dbg(dev, "ACK error\n");
|
||||||
|
tx_errors = true;
|
||||||
|
if (skb) {
|
||||||
|
cf->can_id |= CAN_ERR_ACK;
|
||||||
|
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
netdev_dbg(dev, "Bit0 error\n");
|
||||||
|
tx_errors = true;
|
||||||
|
if (skb)
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_BIT0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
netdev_dbg(dev, "Bit1 error\n");
|
||||||
|
tx_errors = true;
|
||||||
|
if (skb)
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_BIT1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
netdev_dbg(dev, "CRC error\n");
|
||||||
|
rx_errors = true;
|
||||||
|
if (skb) {
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_BIT;
|
||||||
|
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
netdev_dbg(dev, "Form Error\n");
|
||||||
|
rx_errors = true;
|
||||||
|
if (skb)
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_FORM;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
netdev_dbg(dev, "Rx overrun error\n");
|
||||||
|
rx_over_errors = true;
|
||||||
|
rx_errors = true;
|
||||||
|
if (skb) {
|
||||||
|
cf->can_id |= CAN_ERR_CRTL;
|
||||||
|
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
netdev_dbg(dev, "Tx overrun error\n");
|
||||||
|
tx_errors = true;
|
||||||
|
if (skb) {
|
||||||
|
cf->can_id |= CAN_ERR_CRTL;
|
||||||
|
cf->data[1] = CAN_ERR_CRTL_TX_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
netdev_dbg(dev, "Stuff error\n");
|
||||||
|
rx_errors = true;
|
||||||
|
if (skb)
|
||||||
|
cf->data[2] |= CAN_ERR_PROT_STUFF;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (skb)
|
||||||
|
dev_kfree_skb(skb);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx_errors)
|
||||||
|
dev->stats.rx_errors++;
|
||||||
|
|
||||||
|
if (rx_over_errors)
|
||||||
|
dev->stats.rx_over_errors++;
|
||||||
|
|
||||||
|
if (tx_errors)
|
||||||
|
dev->stats.tx_errors++;
|
||||||
|
|
||||||
|
if (skb)
|
||||||
|
netif_rx(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void slc_bump(struct slcan *sl)
|
||||||
|
{
|
||||||
|
switch (sl->rbuff[0]) {
|
||||||
|
case 'r':
|
||||||
|
fallthrough;
|
||||||
|
case 't':
|
||||||
|
fallthrough;
|
||||||
|
case 'R':
|
||||||
|
fallthrough;
|
||||||
|
case 'T':
|
||||||
|
return slc_bump_frame(sl);
|
||||||
|
case 'e':
|
||||||
|
return slc_bump_err(sl);
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* parse tty input stream */
|
/* parse tty input stream */
|
||||||
static void slcan_unesc(struct slcan *sl, unsigned char s)
|
static void slcan_unesc(struct slcan *sl, unsigned char s)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue