diff --git a/intrepid.c b/intrepid.c index 6c5726b..330e2da 100644 --- a/intrepid.c +++ b/intrepid.c @@ -99,6 +99,14 @@ MODULE_VERSION(KO_VERSION); #define SIOCGCLIENTVEROK 0x3009 #define SIOCSBAUDRATE 0x300A +/* Flag bit encoded into intrepid_pending_tx_info.count when reporting a + * settings change to the usermode daemon (tx_box_index < 0). The low bits + * carry the arbitration bitrate, which bitrate_const caps at 1 Mbit/s, so + * the flag cannot collide with a real bitrate value. Only sent to daemons + * that announce v3.2+ via SIOCGCLIENTVEROK. + */ +#define INTREPID_BITRATE_FLAG_LISTENONLY BIT(30) + /* This is true until we have Ethernet support * It is used to stop the netif queues before we have to return NETDEV_TX_BUSY */ @@ -128,6 +136,8 @@ struct intrepid_netdevice { unsigned char *from_user; uint8_t tx_idx; int bitrate_changed; + u32 last_reported_bitrate; + int last_reported_listenonly; struct sk_buff *tx_skbs[MAX_TX]; }; @@ -369,6 +379,26 @@ static int intrepid_netdevice_stop(struct net_device *dev) static int intrepid_netdevice_open(struct net_device *dev) { + /* can_changelink() stores ctrlmode changes (e.g. listen-only) without + * calling into the driver, and requires the interface to be down, so + * ndo_open is the point where the accumulated CAN configuration is + * final. Report it to the usermode daemon, but only when it differs + * from what the daemon was already told, so repeated ifup/ifdown + * cycles do not re-apply unchanged settings to the hardware. + */ + if (dev->type == ARPHRD_CAN && VER_MIN_FROM_INT(client_version) > 1) { + struct intrepid_netdevice *ics = netdev_priv(dev); + u32 bitrate = ics->can.bittiming.bitrate; + int listenonly = (ics->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ? 1 : 0; + + if (bitrate != 0 && + (bitrate != ics->last_reported_bitrate || + listenonly != ics->last_reported_listenonly)) { + WRITE_ONCE(ics->bitrate_changed, 1); + wake_up_interruptible(&tx_wait); + } + } + netif_start_queue(dev); netif_carrier_on(dev); return 0; @@ -433,7 +463,7 @@ static int intrepid_set_bittiming(struct net_device *netdev) dev_dbg(&netdev->dev, "bitrate %d sample_point %d tq %d sjw %d phase1 %d phase2 %d prop %d brp %d", bt->bitrate, bt->sample_point, bt->tq, bt->sjw, bt->phase_seg1, bt->phase_seg2, bt->prop_seg, bt->brp); - dev->bitrate_changed = 1; + WRITE_ONCE(dev->bitrate_changed, 1); wake_up_interruptible(&tx_wait); return 0; } @@ -450,7 +480,7 @@ static int intrepid_set_data_bittiming(struct net_device *netdev) dev_dbg(&netdev->dev, "bitrate %d sample_point %d tq %d sjw %d phase1 %d phase2 %d prop %d brp %d", bt->bitrate, bt->sample_point, bt->tq, bt->sjw, bt->phase_seg1, bt->phase_seg2, bt->prop_seg, bt->brp); - dev->bitrate_changed = 1; + WRITE_ONCE(dev->bitrate_changed, 1); wake_up_interruptible(&tx_wait); return 0; } @@ -562,6 +592,8 @@ static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *r } ics->can.state = CAN_STATE_ERROR_ACTIVE; ics->can.ctrlmode_supported = CAN_CTRLMODE_FD; + if (VER_MIN_FROM_INT(client_version) > 1) + ics->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; spin_lock_init(&ics->lock); @@ -1060,6 +1092,12 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long #else ics->can.data_bittiming.bitrate = info.baudrates[1]; #endif + /* Values seeded by the daemon are already applied to the + * hardware; record them so the next ifup does not + * re-report an unchanged configuration. + */ + ics->last_reported_bitrate = info.baudrates[0]; + ics->last_reported_listenonly = 0; break; } case SIOCSADDETHIF: { @@ -1186,15 +1224,20 @@ static int check_bitrate_change(struct intrepid_pending_tx_info *info) continue; ics = netdev_priv(net_devices[i]); - if (ics->bitrate_changed) { + if (xchg(&ics->bitrate_changed, 0)) { info->tx_box_index = -(i + 1); info->count = ics->can.bittiming.bitrate; + ics->last_reported_bitrate = ics->can.bittiming.bitrate; + ics->last_reported_listenonly = 0; + if (ics->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + info->count |= INTREPID_BITRATE_FLAG_LISTENONLY; + ics->last_reported_listenonly = 1; + } #if KERNEL_CAN_PRIV_FD info->bytes = ics->can.fd.data_bittiming.bitrate; #else info->bytes = ics->can.data_bittiming.bitrate; #endif - ics->bitrate_changed = 0; return 1; } } @@ -1263,7 +1306,7 @@ static unsigned int intrepid_dev_poll(struct file *fp, poll_table *wait) continue; struct intrepid_netdevice *ics = netdev_priv(net_devices[i]); - if (ics->bitrate_changed) + if (READ_ONCE(ics->bitrate_changed)) return POLLIN | POLLRDNORM; } }