/* * Copyright (C) 2005, 2006 Sebastian Smolorz * * * Copyright (C) 2006 Wolfgang Grandegger * * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; eitherer version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "rtcan_internal.h" #include "rtcan_socket.h" #include "rtcan_list.h" #include "rtcan_dev.h" #include "rtcan_raw.h" #if 0 void rtcan_raw_print_filter(struct rtcan_device *dev) { int i; struct rtcan_recv *r = dev->receivers; rtdm_printk("%s: recv_list=%p empty_list=%p free_entries=%d\n", dev->name, dev->recv_list, dev->empty_list, dev->free_entries); for (i = 0; i < RTCAN_MAX_RECEIVERS; i++, r++) { rtdm_printk("%2d %p sock=%p next=%p id=%x mask=%x\n", i, r, r->sock, r->next, r->can_filter.can_id, r->can_filter.can_mask); } } #else #define rtcan_raw_print_filter(dev) #endif static inline void rtcan_raw_mount_filter(can_filter_t *recv_filter, can_filter_t *filter) { if (filter->can_id & CAN_INV_FILTER) { recv_filter->can_id = filter->can_id & ~CAN_INV_FILTER; recv_filter->can_mask = filter->can_mask | CAN_INV_FILTER; } else { recv_filter->can_id = filter->can_id; recv_filter->can_mask = filter->can_mask & ~CAN_INV_FILTER; } /* Apply mask for fast filter check */ recv_filter->can_id &= recv_filter->can_mask; } int rtcan_raw_check_filter(struct rtcan_socket *sock, int ifindex, struct rtcan_filter_list *flist) { int old_ifindex = 0, old_flistlen_all = 0; int free_entries, i, begin, end; struct rtcan_device *dev; int flistlen; if (rtcan_flist_no_filter(flist)) return 0; /* Check if filter list has been defined by user */ flistlen = (flist) ? flist->flistlen : 1; /* Now we check if a reception list would overflow. This takes some * preparation, so let's go ... */ /* Check current bind status */ if (rtcan_sock_has_filter(sock)) { /* Socket is bound */ i = atomic_read(&sock->ifindex); if (i == 0) /* Socket was bound to ALL interfaces */ old_flistlen_all = sock->flistlen; else /* Socket was bound to only one interface */ old_ifindex = i; } if (ifindex) { /* We bind the socket to only one interface. */ begin = ifindex; end = ifindex; } else { /* Socket must be bound to all interfaces. */ begin = 1; end = RTCAN_MAX_DEVICES; } /* Check if there is space for the new binding */ for (i = begin; i <= end; i++) { if ((dev = rtcan_dev_get_by_index(i)) == NULL) continue; free_entries = dev->free_entries + old_flistlen_all; rtcan_dev_dereference(dev); if (i == old_ifindex) free_entries += sock->flistlen; /* Compare free list space to new filter list length */ if (free_entries < flistlen) return -ENOSPC; } return 0; } int rtcan_raw_add_filter(struct rtcan_socket *sock, int ifindex) { int i, j, begin, end; struct rtcan_recv *first, *last; struct rtcan_device *dev; /* Check if filter list has been defined by user */ int flistlen; if (rtcan_flist_no_filter(sock->flist)) { return 0; } flistlen = (sock->flist) ? sock->flist->flistlen : 0; if (ifindex) { /* We bind the socket to only one interface. */ begin = ifindex; end = ifindex; } else { /* Socket must be bound to all interfaces. */ begin = 1; end = RTCAN_MAX_DEVICES; } for (i = begin; i <= end; i++) { if ((dev = rtcan_dev_get_by_index(i)) == NULL) continue; /* Take first entry of empty list */ first = last = dev->empty_list; /* Check if filter list is empty */ if (flistlen) { /* Filter list is not empty */ /* Register first filter */ rtcan_raw_mount_filter(&last->can_filter, &sock->flist->flist[0]); last->match_count = 0; last->sock = sock; for (j = 1; j < flistlen; j++) { /* Register remaining filters */ last = last->next; rtcan_raw_mount_filter(&last->can_filter, &sock->flist->flist[j]); last->sock = sock; last->match_count = 0; } /* Decrease free entries counter by length of filter list */ dev->free_entries -= flistlen; } else { /* Filter list is empty. Socket must be bound to all CAN IDs. */ /* Fill list entry members */ last->can_filter.can_id = last->can_filter.can_mask = 0; last->sock = sock; last->match_count = 0; /* Decrease free entries counter by 1 * (one filter for all CAN frames) */ dev->free_entries--; } /* Set new empty list header */ dev->empty_list = last->next; /* Add new partial recv list to the head of reception list */ last->next = dev->recv_list; /* Adjust rececption list pointer */ dev->recv_list = first; rtcan_raw_print_filter(dev); rtcan_dev_dereference(dev); } return (flistlen) ? flistlen : 1; } void rtcan_raw_remove_filter(struct rtcan_socket *sock) { int i, j, begin, end; struct rtcan_recv *first, *next, *last; int ifindex = atomic_read(&sock->ifindex); struct rtcan_device *dev; if (!rtcan_sock_has_filter(sock)) /* nothing to do */ return; if (ifindex) { /* Socket was bound to one interface only. */ begin = ifindex; end = ifindex; } else { /* Socket was bound to all interfaces */ begin = 1; end = RTCAN_MAX_DEVICES; } for (i = begin; i <= end; i++) { if ((dev = rtcan_dev_get_by_index(i)) == NULL) continue; /* Search for first list entry pointing to this socket */ first = NULL; next = dev->recv_list; while (next->sock != sock) { first = next; next = first->next; } /* Now go to the end of the old filter list */ last = next; for (j = 1; j < sock->flistlen; j++) last = last->next; /* Detach found first list entry from reception list */ if (first) first->next = last->next; else dev->recv_list = last->next; /* Add partial list to the head of empty list */ last->next = dev->empty_list; /* Adjust empty list pointer */ dev->empty_list = next; /* Increase free entries counter by length of old filter list */ dev->free_entries += sock->flistlen; rtcan_raw_print_filter(dev); rtcan_dev_dereference(dev); } }