aboutsummaryrefslogtreecommitdiffstats
path: root/net/can/af_can.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/can/af_can.c')
-rw-r--r--net/can/af_can.c63
1 files changed, 48 insertions, 15 deletions
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 7d4d2b3c137e..d8173e50cb87 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -319,23 +319,52 @@ static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
319 return n ? d : NULL; 319 return n ? d : NULL;
320} 320}
321 321
322/**
323 * find_rcv_list - determine optimal filterlist inside device filter struct
324 * @can_id: pointer to CAN identifier of a given can_filter
325 * @mask: pointer to CAN mask of a given can_filter
326 * @d: pointer to the device filter struct
327 *
328 * Description:
329 * Returns the optimal filterlist to reduce the filter handling in the
330 * receive path. This function is called by service functions that need
331 * to register or unregister a can_filter in the filter lists.
332 *
333 * A filter matches in general, when
334 *
335 * <received_can_id> & mask == can_id & mask
336 *
337 * so every bit set in the mask (even CAN_EFF_FLAG, CAN_RTR_FLAG) describe
338 * relevant bits for the filter.
339 *
340 * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
341 * filter for error frames (CAN_ERR_FLAG bit set in mask). For error frames
342 * there is a special filterlist and a special rx path filter handling.
343 *
344 * Return:
345 * Pointer to optimal filterlist for the given can_id/mask pair.
346 * Constistency checked mask.
347 * Reduced can_id to have a preprocessed filter compare value.
348 */
322static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, 349static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
323 struct dev_rcv_lists *d) 350 struct dev_rcv_lists *d)
324{ 351{
325 canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */ 352 canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
326 353
327 /* filter error frames */ 354 /* filter for error frames in extra filterlist */
328 if (*mask & CAN_ERR_FLAG) { 355 if (*mask & CAN_ERR_FLAG) {
329 /* clear CAN_ERR_FLAG in list entry */ 356 /* clear CAN_ERR_FLAG in filter entry */
330 *mask &= CAN_ERR_MASK; 357 *mask &= CAN_ERR_MASK;
331 return &d->rx[RX_ERR]; 358 return &d->rx[RX_ERR];
332 } 359 }
333 360
334 /* ensure valid values in can_mask */ 361 /* with cleared CAN_ERR_FLAG we have a simple mask/value filterpair */
335 if (*mask & CAN_EFF_FLAG) 362
336 *mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG); 363#define CAN_EFF_RTR_FLAGS (CAN_EFF_FLAG | CAN_RTR_FLAG)
337 else 364
338 *mask &= (CAN_SFF_MASK | CAN_RTR_FLAG); 365 /* ensure valid values in can_mask for 'SFF only' frame filtering */
366 if ((*mask & CAN_EFF_FLAG) && !(*can_id & CAN_EFF_FLAG))
367 *mask &= (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS);
339 368
340 /* reduce condition testing at receive time */ 369 /* reduce condition testing at receive time */
341 *can_id &= *mask; 370 *can_id &= *mask;
@@ -348,15 +377,19 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
348 if (!(*mask)) 377 if (!(*mask))
349 return &d->rx[RX_ALL]; 378 return &d->rx[RX_ALL];
350 379
351 /* use extra filterset for the subscription of exactly *ONE* can_id */ 380 /* extra filterlists for the subscription of a single non-RTR can_id */
352 if (*can_id & CAN_EFF_FLAG) { 381 if (((*mask & CAN_EFF_RTR_FLAGS) == CAN_EFF_RTR_FLAGS)
353 if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) { 382 && !(*can_id & CAN_RTR_FLAG)) {
354 /* RFC: a use-case for hash-tables in the future? */ 383
355 return &d->rx[RX_EFF]; 384 if (*can_id & CAN_EFF_FLAG) {
385 if (*mask == (CAN_EFF_MASK | CAN_EFF_RTR_FLAGS)) {
386 /* RFC: a future use-case for hash-tables? */
387 return &d->rx[RX_EFF];
388 }
389 } else {
390 if (*mask == (CAN_SFF_MASK | CAN_EFF_RTR_FLAGS))
391 return &d->rx_sff[*can_id];
356 } 392 }
357 } else {
358 if (*mask == CAN_SFF_MASK)
359 return &d->rx_sff[*can_id];
360 } 393 }
361 394
362 /* default: filter via can_id/can_mask */ 395 /* default: filter via can_id/can_mask */