* isotp.c - ISO 15765-2 CAN transport protocol for protocol family CAN
*
* WARNING: This is ALPHA code for discussions and first tests that should
- * not be used in productive environments.
+ * not be used in production environments.
*
* In the discussion the Socket-API to the userspace or the ISO-TP socket
* options or the return values we may change! Current behaviour:
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
#include <linux/wait.h>
#include <linux/uio.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
-#include <linux/can.h>
-#include <linux/can/core.h>
-#include <linux/can/isotp.h>
+#include <socketcan/can.h>
+#include <socketcan/can/core.h>
+#include <socketcan/can/isotp.h>
#include <net/sock.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
#include <net/net_namespace.h>
#include "compat.h"
#endif
-#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+#include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */
RCSID("$Id$");
#define CAN_ISOTP_VERSION CAN_VERSION
MODULE_DESCRIPTION("PF_CAN isotp 15765-2 protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_ALIAS("can-proto-6");
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
#error This modules needs hrtimers (available since Kernel 2.6.22)
canid_t txid;
canid_t rxid;
ktime_t tx_gap;
+ ktime_t lastrxcf_tstamp;
struct hrtimer rxtimer, txtimer;
struct tasklet_struct txtsklet;
struct can_isotp_options opt;
struct can_isotp_fc_options rxfc, txfc;
+ __u32 force_tx_stmin;
+ __u32 force_rx_stmin;
struct tpcon rx, tx;
struct notifier_block notifier;
wait_queue_head_t wait;
/* reset blocksize counter */
so->rx.bs = 0;
+ /* reset last CF frame rx timestamp for rx stmin enforcement */
+ so->lastrxcf_tstamp = ktime_set(0,0);
+
/* start rx timeout watchdog */
hrtimer_start(&so->rxtimer, ktime_set(1,0), HRTIMER_MODE_REL);
return 0;
/* add transmission time for CAN frame N_As */
so->tx_gap = ktime_add_ns(so->tx_gap, so->opt.frame_txtime);
/* add waiting time for consecutive frames N_Cs */
- if (so->txfc.stmin < 0x80)
+ if (so->opt.flags & CAN_ISOTP_FORCE_TXSTMIN)
+ so->tx_gap = ktime_add_ns(so->tx_gap,
+ so->force_tx_stmin);
+ else if (so->txfc.stmin < 0x80)
so->tx_gap = ktime_add_ns(so->tx_gap,
so->txfc.stmin * 1000000);
else
if (so->rx.state != ISOTP_WAIT_DATA)
return 0;
+ /* drop if timestamp gap is less than force_rx_stmin nano secs */
+ if (so->opt.flags & CAN_ISOTP_FORCE_RXSTMIN) {
+
+ if (ktime_to_ns(ktime_sub(skb->tstamp, so->lastrxcf_tstamp)) <
+ so->force_rx_stmin)
+ return 0;
+
+ so->lastrxcf_tstamp = skb->tstamp;
+ }
+
hrtimer_cancel(&so->rxtimer);
if ((cf->data[ae] & 0x0F) != so->rx.sn) {
static int isotp_release(struct socket *sock)
{
struct sock *sk = sock->sk;
- struct isotp_sock *so = isotp_sk(sk);
+ struct isotp_sock *so;
+
+ if (!sk)
+ return 0;
+
+ so = isotp_sk(sk);
/* wait for complete transmission of current pdu */
wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
so->ifindex = 0;
so->bound = 0;
+ sock_orphan(sk);
+ sock->sk = NULL;
+
release_sock(sk);
sock_put(sk);
return 0;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+static int isotp_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+#else
static int isotp_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
+#endif
{
struct sock *sk = sock->sk;
struct isotp_sock *so = isotp_sk(sk);
return -EFAULT;
break;
+ case CAN_ISOTP_TX_STMIN:
+ if (optlen != sizeof(__u32))
+ return -EINVAL;
+
+ if (copy_from_user(&so->force_tx_stmin, optval, optlen))
+ return -EFAULT;
+ break;
+
+ case CAN_ISOTP_RX_STMIN:
+ if (optlen != sizeof(__u32))
+ return -EINVAL;
+
+ if (copy_from_user(&so->force_rx_stmin, optval, optlen))
+ return -EFAULT;
+ break;
+
default:
ret = -ENOPROTOOPT;
}
val = &so->rxfc;
break;
+ case CAN_ISOTP_TX_STMIN:
+ len = min_t(int, len, sizeof(__u32));
+ val = &so->force_tx_stmin;
+ break;
+
+ case CAN_ISOTP_RX_STMIN:
+ len = min_t(int, len, sizeof(__u32));
+ val = &so->force_rx_stmin;
+ break;
+
default:
return -ENOPROTOOPT;
}
}
-static struct proto_ops isotp_ops __read_mostly = {
+static const struct proto_ops isotp_ops = {
.family = PF_CAN,
.release = isotp_release,
.bind = isotp_bind,
.accept = sock_no_accept,
.getname = isotp_getname,
.poll = datagram_poll,
- .ioctl = NULL, /* use can_ioctl() from af_can.c */
+ .ioctl = can_ioctl, /* use can_ioctl() from af_can.c */
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = isotp_setsockopt,
.init = isotp_init,
};
-static struct can_proto isotp_can_proto __read_mostly = {
+static const struct can_proto isotp_can_proto = {
.type = SOCK_DGRAM,
.protocol = CAN_ISOTP,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
.capability = -1,
+#endif
.ops = &isotp_ops,
.prot = &isotp_proto,
};