canfd: add support for CAN FD in PF_CAN core

- handle ETH_P_CAN and ETH_P_CANFD skbuffs
- update sanity checks for CAN and CAN FD
- make sure the CAN frame can pass the selected CAN netdevice on send
- bump core version and abi version to indicate the new CAN FD support

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Oliver Hartkopp 2012-06-13 20:33:02 +02:00 committed by Marc Kleine-Budde
parent 7c9416365c
commit 8b01939f35
2 changed files with 84 additions and 29 deletions

View file

@ -17,10 +17,10 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#define CAN_VERSION "20090105" #define CAN_VERSION "20120528"
/* increment this number each time you change some user-space interface */ /* increment this number each time you change some user-space interface */
#define CAN_ABI_VERSION "8" #define CAN_ABI_VERSION "9"
#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION #define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION

View file

@ -221,30 +221,46 @@ static int can_create(struct net *net, struct socket *sock, int protocol,
* -ENOBUFS on full driver queue (see net_xmit_errno()) * -ENOBUFS on full driver queue (see net_xmit_errno())
* -ENOMEM when local loopback failed at calling skb_clone() * -ENOMEM when local loopback failed at calling skb_clone()
* -EPERM when trying to send on a non-CAN interface * -EPERM when trying to send on a non-CAN interface
* -EMSGSIZE CAN frame size is bigger than CAN interface MTU
* -EINVAL when the skb->data does not contain a valid CAN frame * -EINVAL when the skb->data does not contain a valid CAN frame
*/ */
int can_send(struct sk_buff *skb, int loop) int can_send(struct sk_buff *skb, int loop)
{ {
struct sk_buff *newskb = NULL; struct sk_buff *newskb = NULL;
struct can_frame *cf = (struct can_frame *)skb->data; struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
int err; int err = -EINVAL;
if (skb->len != sizeof(struct can_frame) || cf->can_dlc > 8) { if (skb->len == CAN_MTU) {
kfree_skb(skb); skb->protocol = htons(ETH_P_CAN);
return -EINVAL; if (unlikely(cfd->len > CAN_MAX_DLEN))
goto inval_skb;
} else if (skb->len == CANFD_MTU) {
skb->protocol = htons(ETH_P_CANFD);
if (unlikely(cfd->len > CANFD_MAX_DLEN))
goto inval_skb;
} else
goto inval_skb;
/*
* Make sure the CAN frame can pass the selected CAN netdevice.
* As structs can_frame and canfd_frame are similar, we can provide
* CAN FD frames to legacy CAN drivers as long as the length is <= 8
*/
if (unlikely(skb->len > skb->dev->mtu && cfd->len > CAN_MAX_DLEN)) {
err = -EMSGSIZE;
goto inval_skb;
} }
if (skb->dev->type != ARPHRD_CAN) { if (unlikely(skb->dev->type != ARPHRD_CAN)) {
kfree_skb(skb); err = -EPERM;
return -EPERM; goto inval_skb;
} }
if (!(skb->dev->flags & IFF_UP)) { if (unlikely(!(skb->dev->flags & IFF_UP))) {
kfree_skb(skb); err = -ENETDOWN;
return -ENETDOWN; goto inval_skb;
} }
skb->protocol = htons(ETH_P_CAN);
skb_reset_network_header(skb); skb_reset_network_header(skb);
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
@ -301,6 +317,10 @@ int can_send(struct sk_buff *skb, int loop)
can_stats.tx_frames_delta++; can_stats.tx_frames_delta++;
return 0; return 0;
inval_skb:
kfree_skb(skb);
return err;
} }
EXPORT_SYMBOL(can_send); EXPORT_SYMBOL(can_send);
@ -633,24 +653,11 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
return matches; return matches;
} }
static int can_rcv(struct sk_buff *skb, struct net_device *dev, static void can_receive(struct sk_buff *skb, struct net_device *dev)
struct packet_type *pt, struct net_device *orig_dev)
{ {
struct dev_rcv_lists *d; struct dev_rcv_lists *d;
struct can_frame *cf = (struct can_frame *)skb->data;
int matches; int matches;
if (!net_eq(dev_net(dev), &init_net))
goto drop;
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != sizeof(struct can_frame) ||
cf->can_dlc > 8,
"PF_CAN: dropped non conform skbuf: "
"dev type %d, len %d, can_dlc %d\n",
dev->type, skb->len, cf->can_dlc))
goto drop;
/* update statistics */ /* update statistics */
can_stats.rx_frames++; can_stats.rx_frames++;
can_stats.rx_frames_delta++; can_stats.rx_frames_delta++;
@ -674,7 +681,49 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
can_stats.matches++; can_stats.matches++;
can_stats.matches_delta++; can_stats.matches_delta++;
} }
}
static int can_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
if (unlikely(!net_eq(dev_net(dev), &init_net)))
goto drop;
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != CAN_MTU ||
cfd->len > CAN_MAX_DLEN,
"PF_CAN: dropped non conform CAN skbuf: "
"dev type %d, len %d, datalen %d\n",
dev->type, skb->len, cfd->len))
goto drop;
can_receive(skb, dev);
return NET_RX_SUCCESS;
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
static int canfd_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
if (unlikely(!net_eq(dev_net(dev), &init_net)))
goto drop;
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
skb->len != CANFD_MTU ||
cfd->len > CANFD_MAX_DLEN,
"PF_CAN: dropped non conform CAN FD skbuf: "
"dev type %d, len %d, datalen %d\n",
dev->type, skb->len, cfd->len))
goto drop;
can_receive(skb, dev);
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
drop: drop:
@ -808,10 +857,14 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg,
static struct packet_type can_packet __read_mostly = { static struct packet_type can_packet __read_mostly = {
.type = cpu_to_be16(ETH_P_CAN), .type = cpu_to_be16(ETH_P_CAN),
.dev = NULL,
.func = can_rcv, .func = can_rcv,
}; };
static struct packet_type canfd_packet __read_mostly = {
.type = cpu_to_be16(ETH_P_CANFD),
.func = canfd_rcv,
};
static const struct net_proto_family can_family_ops = { static const struct net_proto_family can_family_ops = {
.family = PF_CAN, .family = PF_CAN,
.create = can_create, .create = can_create,
@ -853,6 +906,7 @@ static __init int can_init(void)
sock_register(&can_family_ops); sock_register(&can_family_ops);
register_netdevice_notifier(&can_netdev_notifier); register_netdevice_notifier(&can_netdev_notifier);
dev_add_pack(&can_packet); dev_add_pack(&can_packet);
dev_add_pack(&canfd_packet);
return 0; return 0;
} }
@ -867,6 +921,7 @@ static __exit void can_exit(void)
can_remove_proc(); can_remove_proc();
/* protocol unregister */ /* protocol unregister */
dev_remove_pack(&canfd_packet);
dev_remove_pack(&can_packet); dev_remove_pack(&can_packet);
unregister_netdevice_notifier(&can_netdev_notifier); unregister_netdevice_notifier(&can_netdev_notifier);
sock_unregister(PF_CAN); sock_unregister(PF_CAN);