* Re: [PATCH RFC] tun: export underlying socket
From: Michael S. Tsirkin @ 2009-09-11 4:59 UTC (permalink / raw)
To: Paul Moore; +Cc: David Miller, netdev, herbert
In-Reply-To: <200909110017.27668.paul.moore@hp.com>
On Fri, Sep 11, 2009 at 12:17:27AM -0400, Paul Moore wrote:
> On Thursday 10 September 2009 08:59:29 am Michael S. Tsirkin wrote:
> > Tun device looks similar to a packet socket
> > in that both pass complete frames from/to userspace.
> >
> > This patch fills in enough fields in the socket underlying tun driver
> > to support sendmsg/recvmsg operations, and exports access to this socket
> > to modules.
> >
> > This way, code using raw sockets to inject packets
> > into a physical device, can support injecting
> > packets into host network stack almost without modification.
> >
> > First user of this interface will be vhost virtualization
> > accelerator.
>
> No comments on the code at this point - I'm just trying to understand the
> intended user right now which I'm assuming is the vhost-net bits you sent
> previously?
Yes - these now use raw socket, I'll add
something like
sock = tun_get_socket(file)
if (!IS_ERR(sock)) {
return sock;
}
> > Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> > ---
> >
> > This patch is on top of net-next master.
> > An alternative approach would be to add an ioctl to tun, to export the
> > underlying socket to userspace: a uniform way to work with a network
> > device and the host stack might be useful there, as well.
> > Kernel users could then do sockfd_lookup to get the socket.
> > I decided against it for now as it requires more code.
> > Please comment.
> >
> > drivers/net/tun.c | 78
> > +++++++++++++++++++++++++++++++++++++++++++---- include/linux/if_tun.h |
> > 14 ++++++++
> > 2 files changed, 85 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> > index 589a44a..76f5faa 100644
> > --- a/drivers/net/tun.c
> > +++ b/drivers/net/tun.c
> > @@ -151,6 +151,7 @@ static int tun_attach(struct tun_struct *tun, struct
> > file *file) err = 0;
> > tfile->tun = tun;
> > tun->tfile = tfile;
> > + tun->socket.file = file;
> > dev_hold(tun->dev);
> > sock_hold(tun->socket.sk);
> > atomic_inc(&tfile->count);
> > @@ -165,6 +166,7 @@ static void __tun_detach(struct tun_struct *tun)
> > /* Detach from net device */
> > netif_tx_lock_bh(tun->dev);
> > tun->tfile = NULL;
> > + tun->socket.file = NULL;
> > netif_tx_unlock_bh(tun->dev);
> >
> > /* Drop read queue */
> > @@ -750,7 +752,7 @@ static __inline__ ssize_t tun_put_user(struct
> > tun_struct *tun, len = min_t(int, skb->len, len);
> >
> > skb_copy_datagram_const_iovec(skb, 0, iv, total, len);
> > - total += len;
> > + total += skb->len;
> >
> > tun->dev->stats.tx_packets++;
> > tun->dev->stats.tx_bytes += len;
> > @@ -758,12 +760,10 @@ static __inline__ ssize_t tun_put_user(struct
> > tun_struct *tun, return total;
> > }
> >
> > -static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec
> > *iv, - unsigned long count, loff_t pos)
> > +static ssize_t tun_do_read(struct tun_struct *tun,
> > + struct kiocb *iocb, const struct iovec *iv,
> > + unsigned long count, int noblock)
> > {
> > - struct file *file = iocb->ki_filp;
> > - struct tun_file *tfile = file->private_data;
> > - struct tun_struct *tun = __tun_get(tfile);
> > DECLARE_WAITQUEUE(wait, current);
> > struct sk_buff *skb;
> > ssize_t len, ret = 0;
> > @@ -785,7 +785,7 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb,
> > const struct iovec *iv,
> >
> > /* Read frames from the queue */
> > if (!(skb=skb_dequeue(&tun->socket.sk->sk_receive_queue))) {
> > - if (file->f_flags & O_NONBLOCK) {
> > + if (noblock) {
> > ret = -EAGAIN;
> > break;
> > }
> > @@ -813,6 +813,21 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb,
> > const struct iovec *iv, remove_wait_queue(&tun->socket.wait, &wait);
> >
> > out:
> > + return ret;
> > +}
> > +
> > +static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec
> > *iv, + unsigned long count, loff_t pos)
> > +{
> > + struct file *file = iocb->ki_filp;
> > + struct tun_file *tfile = file->private_data;
> > + struct tun_struct *tun = __tun_get(tfile);
> > + ssize_t ret;
> > +
> > + if (!tun)
> > + return -EBADFD;
> > + ret = tun_do_read(tun, iocb, iv, count, file->f_flags & O_NONBLOCK);
> > + ret = min_t(ssize_t, ret, count);
> > tun_put(tun);
> > return ret;
> > }
> > @@ -865,6 +880,37 @@ static void tun_sock_destruct(struct sock *sk)
> > free_netdev(container_of(sk, struct tun_sock, sk)->tun->dev);
> > }
> >
> > +static int tun_sendmsg(struct kiocb *iocb, struct socket *sock,
> > + struct msghdr *m, size_t total_len)
> > +{
> > + struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
> > + return tun_get_user(tun, m->msg_iov, total_len,
> > + m->msg_flags & MSG_DONTWAIT);
> > +}
> > +
> > +static int tun_recvmsg(struct kiocb *iocb, struct socket *sock,
> > + struct msghdr *m, size_t total_len,
> > + int flags)
> > +{
> > + struct tun_struct *tun = container_of(sock, struct tun_struct, socket);
> > + int ret;
> > + if (flags & ~(MSG_DONTWAIT|MSG_TRUNC))
> > + return -EINVAL;
> > + ret = tun_do_read(tun, iocb, m->msg_iov, total_len,
> > + flags & MSG_DONTWAIT);
> > + if (ret > total_len) {
> > + m->msg_flags |= MSG_TRUNC;
> > + ret = flags & MSG_TRUNC ? ret : total_len;
> > + }
> > + return ret;
> > +}
> > +
> > +/* Ops structure to mimic raw sockets with tun */
> > +static const struct proto_ops tun_socket_ops = {
> > + .sendmsg = tun_sendmsg,
> > + .recvmsg = tun_recvmsg,
> > +};
> > +
> > static struct proto tun_proto = {
> > .name = "tun",
> > .owner = THIS_MODULE,
> > @@ -982,6 +1028,7 @@ static int tun_set_iff(struct net *net, struct file
> > *file, struct ifreq *ifr) goto err_free_dev;
> >
> > init_waitqueue_head(&tun->socket.wait);
> > + tun->socket.ops = &tun_socket_ops;
> > sock_init_data(&tun->socket, sk);
> > sk->sk_write_space = tun_sock_write_space;
> > sk->sk_sndbuf = INT_MAX;
> > @@ -1483,6 +1530,23 @@ static void tun_cleanup(void)
> > rtnl_link_unregister(&tun_link_ops);
> > }
> >
> > +/* Get an underlying socket object from tun file. Returns error unless
> > file is + * attached to a device. The returned object works like a packet
> > socket, it + * can be used for sock_sendmsg/sock_recvmsg. The caller is
> > responsible for + * holding a reference to the file for as long as the
> > socket is in use. */ +struct socket *tun_get_socket(struct file *file)
> > +{
> > + struct tun_struct *tun;
> > + if (file->f_op != &tun_fops)
> > + return ERR_PTR(-EINVAL);
> > + tun = tun_get(file);
> > + if (!tun)
> > + return ERR_PTR(-EBADFD);
> > + tun_put(tun);
> > + return &tun->socket;
> > +}
> > +EXPORT_SYMBOL_GPL(tun_get_socket);
> > +
> > module_init(tun_init);
> > module_exit(tun_cleanup);
> > MODULE_DESCRIPTION(DRV_DESCRIPTION);
> > diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h
> > index 3f5fd52..404abe0 100644
> > --- a/include/linux/if_tun.h
> > +++ b/include/linux/if_tun.h
> > @@ -86,4 +86,18 @@ struct tun_filter {
> > __u8 addr[0][ETH_ALEN];
> > };
> >
> > +#ifdef __KERNEL__
> > +#if defined(CONFIG_TUN) || defined(CONFIG_TUN_MODULE)
> > +struct socket *tun_get_socket(struct file *);
> > +#else
> > +#include <linux/err.h>
> > +#include <linux/errno.h>
> > +struct file;
> > +struct socket;
> > +static inline struct socket *tun_get_socket(struct file *f)
> > +{
> > + return ERR_PTR(-EINVAL);
> > +}
> > +#endif /* CONFIG_TUN */
> > +#endif /* __KERNEL__ */
> > #endif /* __IF_TUN_H */
> >
>
> --
> paul moore
> linux @ hp
^ permalink raw reply
* [PATCH 1/8] networking/fanotify: declare fanotify socket numbers
From: Eric Paris @ 2009-09-11 5:25 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
fanotify's user interface uses a custom socket (it doesn't use netlink
since work must be done in the context of the receive side of the socket)
This patch simply defines the fanotify socket number declarations. The
actual implementation of the socket is in a later patch.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
include/linux/socket.h | 5 ++++-
net/core/sock.c | 6 +++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 3b461df..e03f47b 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -195,7 +195,8 @@ struct ucred {
#define AF_ISDN 34 /* mISDN sockets */
#define AF_PHONET 35 /* Phonet sockets */
#define AF_IEEE802154 36 /* IEEE802154 sockets */
-#define AF_MAX 37 /* For now.. */
+#define AF_FANOTIFY 37 /* fscking all access sockets */
+#define AF_MAX 38 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -235,6 +236,7 @@ struct ucred {
#define PF_ISDN AF_ISDN
#define PF_PHONET AF_PHONET
#define PF_IEEE802154 AF_IEEE802154
+#define PF_FANOTIFY AF_FANOTIFY
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
@@ -306,6 +308,7 @@ struct ucred {
#define SOL_PNPIPE 275
#define SOL_RDS 276
#define SOL_IUCV 277
+#define SOL_FANOTIFY 278
/* IPX options */
#define IPX_TYPE 1
diff --git a/net/core/sock.c b/net/core/sock.c
index 30d5446..28a99bf 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -155,7 +155,7 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
"sk_lock-27" , "sk_lock-28" , "sk_lock-AF_CAN" ,
"sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV" ,
"sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN" , "sk_lock-AF_PHONET" ,
- "sk_lock-AF_IEEE802154",
+ "sk_lock-AF_IEEE802154", "sk_lock-AF_FANOTIFY",
"sk_lock-AF_MAX"
};
static const char *const af_family_slock_key_strings[AF_MAX+1] = {
@@ -171,7 +171,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-27" , "slock-28" , "slock-AF_CAN" ,
"slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_IUCV" ,
"slock-AF_RXRPC" , "slock-AF_ISDN" , "slock-AF_PHONET" ,
- "slock-AF_IEEE802154",
+ "slock-AF_IEEE802154", "slock=AF_FANOTIFY",
"slock-AF_MAX"
};
static const char *const af_family_clock_key_strings[AF_MAX+1] = {
@@ -187,7 +187,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-27" , "clock-28" , "clock-AF_CAN" ,
"clock-AF_TIPC" , "clock-AF_BLUETOOTH", "clock-AF_IUCV" ,
"clock-AF_RXRPC" , "clock-AF_ISDN" , "clock-AF_PHONET" ,
- "clock-AF_IEEE802154",
+ "clock-AF_IEEE802154", "clock-AF_FANOTIFY",
"clock-AF_MAX"
};
^ permalink raw reply related
* [PATCH 2/8] vfs: introduce FMODE_NONOTIFY
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
This is a new f_mode which can only be set by the kernel. It indicates
that the fd was opened by fanotify and should not cause future fanotify
events. This is needed to prevent fanotify livelock. An example of
obvious livelock is from fanotify close events.
Process A closes file1
This creates a close event for file1.
fanotify opens file1 for Listener X
Listener X deals with the event and closes its fd for file1.
This creates a close event for file1.
fanotify opens file1 for Listener X
Listener X deals with the event and closes its fd for file1.
This creates a close event for file1.
fanotify opens file1 for Listener X
Listener X deals with the event and closes its fd for file1.
notice a pattern?
The fix is to add the FMODE_NONOTIFY bit to the open filp done by the kernel
for fanotify. Thus when that file is used it will not generate future
events.
This patch simply defines the bit.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
include/linux/fs.h | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 02e111c..433db62 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -87,6 +87,9 @@ struct inodes_stat_t {
*/
#define FMODE_NOCMTIME ((__force fmode_t)2048)
+/* File was opened by fanotify and shouldn't generate fanotify events */
+#define FMODE_NONOTIFY ((__force fmode_t)4096)
+
/*
* The below are the various read and write types that we support. Some of
* them include behavioral modifiers that send information down to the
^ permalink raw reply related
* [PATCH 3/8] fanotify: fscking all notification system
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
fanotify is a novel file notification system which bases notification on
giving userspace both an event type (open, close, read, write) and an open
file descriptor to the object in question. This should address a number of
races and problems with other notification systems like inotify and dnotify
and should allow the future implementation of blocking or access controlled
notification. These are useful for on access scanners or hierachical storage
management schemes.
This patch just implements the basics of the fsnotify functions.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
fs/notify/Kconfig | 1
fs/notify/Makefile | 1
fs/notify/fanotify/Kconfig | 11 +++++
fs/notify/fanotify/Makefile | 1
fs/notify/fanotify/fanotify.c | 86 +++++++++++++++++++++++++++++++++++++++++
fs/notify/fanotify/fanotify.h | 12 ++++++
include/linux/Kbuild | 1
include/linux/fanotify.h | 40 +++++++++++++++++++
8 files changed, 153 insertions(+), 0 deletions(-)
create mode 100644 fs/notify/fanotify/Kconfig
create mode 100644 fs/notify/fanotify/Makefile
create mode 100644 fs/notify/fanotify/fanotify.c
create mode 100644 fs/notify/fanotify/fanotify.h
create mode 100644 include/linux/fanotify.h
diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig
index dffbb09..22c629e 100644
--- a/fs/notify/Kconfig
+++ b/fs/notify/Kconfig
@@ -3,3 +3,4 @@ config FSNOTIFY
source "fs/notify/dnotify/Kconfig"
source "fs/notify/inotify/Kconfig"
+source "fs/notify/fanotify/Kconfig"
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 0922cc8..396a387 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o
obj-y += dnotify/
obj-y += inotify/
+obj-y += fanotify/
diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig
new file mode 100644
index 0000000..70631ed
--- /dev/null
+++ b/fs/notify/fanotify/Kconfig
@@ -0,0 +1,11 @@
+config FANOTIFY
+ bool "Filesystem wide access notification"
+ select FSNOTIFY
+ default y
+ ---help---
+ Say Y here to enable fanotify suport. fanotify is a system wide
+ file access notification interface. Events are read from from a
+ socket and in doing so an fd is created in the reading process
+ which points to the same data as the one on which the event occured.
+
+ If unsure, say Y.
diff --git a/fs/notify/fanotify/Makefile b/fs/notify/fanotify/Makefile
new file mode 100644
index 0000000..e7d39c0
--- /dev/null
+++ b/fs/notify/fanotify/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_FANOTIFY) += fanotify.o
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
new file mode 100644
index 0000000..b5c2e32
--- /dev/null
+++ b/fs/notify/fanotify/fanotify.c
@@ -0,0 +1,86 @@
+#include <linux/fdtable.h>
+#include <linux/fsnotify_backend.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* UINT_MAX */
+#include <linux/net.h> /* struct socket */
+#include <linux/sched.h> /* task_struct */
+#include <linux/types.h>
+
+#include "fanotify.h"
+
+static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
+{
+ int ret;
+
+ BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
+ BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
+ BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
+ BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE);
+ BUILD_BUG_ON(FAN_OPEN != FS_OPEN);
+ BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
+ BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
+
+ ret = fsnotify_add_notify_event(group, event, NULL, NULL);
+
+ return ret;
+}
+
+static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode,
+ __u32 mask, void *data, int data_type)
+{
+ struct fsnotify_mark_entry *entry;
+ bool send;
+
+ /* sorry, fanotify only gives a damn about files and dirs */
+ if (!S_ISREG(inode->i_mode) &&
+ !S_ISDIR(inode->i_mode))
+ return false;
+
+ /* if we don't have enough info to send an event to userspace say no */
+ if ((data_type != FSNOTIFY_EVENT_FILE) &&
+ (data_type != FSNOTIFY_EVENT_PATH))
+ return false;
+
+ /* if this file was opened by fanotify don't send events about it */
+ if (data_type == FSNOTIFY_EVENT_FILE) {
+ struct file *file;
+
+ file = (struct file *)data;
+ if (file->f_mode & FMODE_NONOTIFY)
+ return false;
+ }
+
+ spin_lock(&inode->i_lock);
+ entry = fsnotify_find_mark_entry(group, inode);
+ spin_unlock(&inode->i_lock);
+ if (!entry)
+ return false;
+
+ /* if the event is for a child and this inode doesn't care about
+ * events on the child, don't send it! */
+ if ((mask & FS_EVENT_ON_CHILD) &&
+ !(entry->mask & FS_EVENT_ON_CHILD))
+ send = false;
+ else {
+ if (!(entry->mask & FS_EVENT_ON_CHILD) &&
+ (mask & FS_EVENT_ON_CHILD))
+ send = false;
+ else {
+ mask = (mask & ~FS_EVENT_ON_CHILD);
+ send = (entry->mask & mask);
+ }
+ }
+
+ /* find took a reference */
+ fsnotify_put_mark(entry);
+
+ return send;
+}
+
+const struct fsnotify_ops fanotify_ops = {
+ .handle_event = fanotify_handle_event,
+ .should_send_event = fanotify_should_send_event,
+ .free_group_priv = NULL,
+ .free_event_priv = NULL,
+ .freeing_mark = NULL,
+};
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
new file mode 100644
index 0000000..a8785c1
--- /dev/null
+++ b/fs/notify/fanotify/fanotify.h
@@ -0,0 +1,12 @@
+#include <linux/fanotify.h>
+#include <linux/fsnotify_backend.h>
+#include <linux/net.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+static inline bool fanotify_is_mask_valid(__u32 mask)
+{
+ if (mask & ~(FAN_ALL_INCOMING_EVENTS))
+ return false;
+ return true;
+}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index e7d84ff..b298c0e 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -206,6 +206,7 @@ unifdef-y += ethtool.h
unifdef-y += eventpoll.h
unifdef-y += signalfd.h
unifdef-y += ext2_fs.h
+unifdef-y += fanotify.h
unifdef-y += fb.h
unifdef-y += fcntl.h
unifdef-y += filter.h
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
new file mode 100644
index 0000000..b560f86
--- /dev/null
+++ b/include/linux/fanotify.h
@@ -0,0 +1,40 @@
+#ifndef _LINUX_FANOTIFY_H
+#define _LINUX_FANOTIFY_H
+
+#include <linux/types.h>
+
+/* the following events that user-space can register for */
+#define FAN_ACCESS 0x00000001 /* File was accessed */
+#define FAN_MODIFY 0x00000002 /* File was modified */
+#define FAN_CLOSE_WRITE 0x00000008 /* Unwrittable file closed */
+#define FAN_CLOSE_NOWRITE 0x00000010 /* Writtable file closed */
+#define FAN_OPEN 0x00000020 /* File was opened */
+
+#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
+
+/* FIXME currently Q's have no limit.... */
+#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
+
+/* helper events */
+#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
+
+/*
+ * All of the events - we build the list by hand so that we can add flags in
+ * the future and not break backward compatibility. Apps will get only the
+ * events that they originally wanted. Be sure to add new events here!
+ */
+#define FAN_ALL_EVENTS (FAN_ACCESS |\
+ FAN_MODIFY |\
+ FAN_CLOSE |\
+ FAN_OPEN)
+
+/*
+ * All legal FAN bits userspace can request (although possibly not all
+ * at the same time.
+ */
+#define FAN_ALL_INCOMING_EVENTS (FAN_ALL_EVENTS |\
+ FAN_EVENT_ON_CHILD)
+#ifdef __KERNEL__
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_FANOTIFY_H */
^ permalink raw reply related
* [PATCH 4/8] fanotify:drop notification if they exist in the outgoing queue
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
fanotify listeners get an open file descriptor to the object in question so
the ordering of operations is not as important as in other notification
systems. inotify will drop events if the last event in the event FIFO is
the same as the current event. This patch will drop fanotify events if
they are the same as another event anywhere in the event FIFO.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
fs/notify/fanotify/fanotify.c | 40 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 38 insertions(+), 2 deletions(-)
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index b5c2e32..f054300 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -8,6 +8,40 @@
#include "fanotify.h"
+static bool try_merge(struct fsnotify_event *old, struct fsnotify_event *new)
+{
+ if ((old->mask == new->mask) &&
+ (old->to_tell == new->to_tell) &&
+ (old->data_type == new->data_type)) {
+ switch (old->data_type) {
+ case (FSNOTIFY_EVENT_PATH):
+ if ((old->path.mnt == new->path.mnt) &&
+ (old->path.dentry == new->path.dentry))
+ return true;
+ case (FSNOTIFY_EVENT_NONE):
+ return true;
+ default:
+ BUG();
+ };
+ }
+ return false;
+}
+
+static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
+{
+ struct fsnotify_event_holder *holder;
+ struct fsnotify_event *test_event;
+
+ /* and the list better be locked by something too! */
+
+ list_for_each_entry_reverse(holder, list, event_list) {
+ test_event = holder->event;
+ if (try_merge(test_event, event))
+ return -EEXIST;
+ }
+
+ return 0;
+}
static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
{
int ret;
@@ -20,8 +54,10 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e
BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD);
BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
- ret = fsnotify_add_notify_event(group, event, NULL, NULL);
-
+ ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge);
+ /* -EEXIST means this event was merged with another, not that it was an error */
+ if (ret == -EEXIST)
+ ret = 0;
return ret;
}
^ permalink raw reply related
* [PATCH 5/8] fanotify: merge notification events with different masks
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
Instead of just merging fanotify events if they are exactly the same, merge
notification events with different masks. To do this we have to clone the
old event, update the mask in the new event with the new merged mask, and
put the new event in place of the old event.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
fs/notify/fanotify/fanotify.c | 24 ++++++++++++++++++------
1 files changed, 18 insertions(+), 6 deletions(-)
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index f054300..40ee137 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -10,8 +10,7 @@
static bool try_merge(struct fsnotify_event *old, struct fsnotify_event *new)
{
- if ((old->mask == new->mask) &&
- (old->to_tell == new->to_tell) &&
+ if ((old->to_tell == new->to_tell) &&
(old->data_type == new->data_type)) {
switch (old->data_type) {
case (FSNOTIFY_EVENT_PATH):
@@ -29,15 +28,28 @@ static bool try_merge(struct fsnotify_event *old, struct fsnotify_event *new)
static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
{
- struct fsnotify_event_holder *holder;
+ struct fsnotify_event_holder *test_holder, *prev;
struct fsnotify_event *test_event;
+ struct fsnotify_event *new_event;
+ int ret;
/* and the list better be locked by something too! */
- list_for_each_entry_reverse(holder, list, event_list) {
- test_event = holder->event;
- if (try_merge(test_event, event))
+ list_for_each_entry_safe_reverse(test_holder, prev, list, event_list) {
+ test_event = test_holder->event;
+ if (try_merge(test_event, event)) {
+ if (test_event->mask == event->mask)
+ return -EEXIST;
+ new_event = fsnotify_clone_event(test_event);
+ if (!new_event)
+ return 0;
+ new_event->mask = (test_event->mask | event->mask);
+ ret = fsnotify_replace_event(test_holder, new_event);
+ fsnotify_put_event(new_event); /* matches the ref from clone */
+ if (ret)
+ return ret;
return -EEXIST;
+ }
}
return 0;
^ permalink raw reply related
* [PATCH 6/8] fanotify: userspace socket
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
This patch implements an userspace interface for the fanotify notification
system. An fanotify socket is created in userspace and is 'bound' to an
address. That bind call actually creates the new fanotify listener much like
inotify_init() creates an inotify instance.
Requests for notification of events on certain fs objects is done using a
setsockopt() call. (not implemented in this patch) This setsockopt() call is
largely analogous to inotify_add_watch()
Events are retrieved from the kernel calling read on the bound socket.
This interface is designed to be forward looking as the kernel/userspace
interaction can be changed simply by implementing a new getsockopt option.
Macros are provided much like the netlink macros in order to allow of the
messages from the kernel to userspace to change in length in the future while
maintaining backwards compatibility.
This patch only implements the socket registration and the bind call. The
getsockopt() calls and data read call are implemented in later patches.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
fs/notify/fanotify/Makefile | 2 -
fs/notify/fanotify/af_fanotify.c | 152 ++++++++++++++++++++++++++++++++++++++
fs/notify/fanotify/af_fanotify.h | 21 +++++
fs/notify/fanotify/fanotify.h | 2 +
include/linux/fanotify.h | 19 +++++
5 files changed, 195 insertions(+), 1 deletions(-)
create mode 100644 fs/notify/fanotify/af_fanotify.c
create mode 100644 fs/notify/fanotify/af_fanotify.h
diff --git a/fs/notify/fanotify/Makefile b/fs/notify/fanotify/Makefile
index e7d39c0..1196005 100644
--- a/fs/notify/fanotify/Makefile
+++ b/fs/notify/fanotify/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_FANOTIFY) += fanotify.o
+obj-$(CONFIG_FANOTIFY) += fanotify.o af_fanotify.o
diff --git a/fs/notify/fanotify/af_fanotify.c b/fs/notify/fanotify/af_fanotify.c
new file mode 100644
index 0000000..d7bf658
--- /dev/null
+++ b/fs/notify/fanotify/af_fanotify.c
@@ -0,0 +1,152 @@
+#include <linux/errno.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/fsnotify_backend.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* UINT_MAX */
+#include <linux/mount.h> /* mntget() */
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "fanotify.h"
+#include "af_fanotify.h"
+
+static const struct proto_ops fanotify_proto_ops;
+
+static struct proto fanotify_proto = {
+ .name = "FANOTIFY",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct fanotify_sock),
+};
+
+static int fan_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ struct fanotify_sock *fan_sock;
+
+ /* FIXME maybe a new LSM hook? */
+ if (!capable(CAP_NET_RAW))
+ return -EPERM;
+
+ if (protocol != 0)
+ return -ESOCKTNOSUPPORT;
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ sock->state = SS_UNCONNECTED;
+
+ sk = sk_alloc(net, PF_FANOTIFY, GFP_KERNEL, &fanotify_proto);
+ if (sk == NULL)
+ return -ENOBUFS;
+
+ sock->ops = &fanotify_proto_ops;
+
+ sock_init_data(sock, sk);
+
+ sk->sk_family = PF_FANOTIFY;
+ sk_refcnt_debug_inc(sk);
+
+ fan_sock = fan_sk(sk);
+ fan_sock->group = NULL;
+
+ return 0;
+}
+
+static int fan_release(struct socket *sock)
+{
+ struct sock *sk;
+ struct fanotify_sock *fan_sock;
+
+ sk = sock->sk;
+ if (!sk)
+ return 0;
+
+ fan_sock = fan_sk(sk);
+
+ if (sock->state == SS_CONNECTED) {
+ sock->state = SS_UNCONNECTED;
+ fsnotify_put_group(fan_sock->group);
+ }
+
+ fan_sock->group = NULL;
+
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+ sk_refcnt_debug_release(sk);
+
+ sock_put(sk);
+
+ return 0;
+}
+
+static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+ struct fanotify_addr *fan_addr = (struct fanotify_addr *)addr;
+ struct fanotify_sock *fan_sock;
+
+ if (addr_len != sizeof(struct fanotify_addr))
+ return -EINVAL;
+
+ if (sock->state != SS_UNCONNECTED)
+ return -EINVAL;
+
+ if (!fanotify_is_mask_valid(fan_addr->mask))
+ return -EINVAL;
+
+ fan_sock = fan_sk(sock->sk);
+ fan_sock->group = fsnotify_obtain_group(fan_addr->mask, &fanotify_ops);
+
+ if (IS_ERR(fan_sock->group))
+ return PTR_ERR(fan_sock->group);
+
+ fan_sock->group->max_events = 16383;
+
+ sock->state = SS_CONNECTED;
+
+ return 0;
+}
+
+static const struct net_proto_family fanotify_family_ops = {
+ .family = PF_FANOTIFY,
+ .create = fan_sock_create,
+ .owner = THIS_MODULE,
+};
+
+static const struct proto_ops fanotify_proto_ops = {
+ .family = PF_FANOTIFY,
+ .owner = THIS_MODULE,
+ .release = fan_release,
+ .bind = fan_bind,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .getname = sock_no_getname,
+ .poll = sock_no_poll,
+ .ioctl = sock_no_ioctl,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+
+static int __init fanotify_init(void)
+{
+ if (proto_register(&fanotify_proto, 0))
+ panic("unable to register fanotify protocol with network stack\n");
+
+ sock_register(&fanotify_family_ops);
+
+ return 0;
+}
+device_initcall(fanotify_init);
diff --git a/fs/notify/fanotify/af_fanotify.h b/fs/notify/fanotify/af_fanotify.h
new file mode 100644
index 0000000..fff0e66
--- /dev/null
+++ b/fs/notify/fanotify/af_fanotify.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_AF_FANOTIFY_H
+#define _LINUX_AF_FANOTIFY_H
+
+#include <linux/fanotify.h>
+#include <net/sock.h>
+
+struct fanotify_sock {
+ struct sock sock;
+ struct fsnotify_group *group;
+};
+
+static inline struct fanotify_sock *fan_sk(struct sock *sock)
+{
+ struct fanotify_sock *fan_sock;
+
+ fan_sock = container_of(sock, struct fanotify_sock, sock);
+
+ return fan_sock;
+}
+
+#endif /* _LINUX_AF_NET_H */
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index a8785c1..6c7bf06 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -4,6 +4,8 @@
#include <linux/kernel.h>
#include <linux/types.h>
+extern const struct fsnotify_ops fanotify_ops;
+
static inline bool fanotify_is_mask_valid(__u32 mask)
{
if (mask & ~(FAN_ALL_INCOMING_EVENTS))
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index b560f86..4c1c6cd 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -1,6 +1,7 @@
#ifndef _LINUX_FANOTIFY_H
#define _LINUX_FANOTIFY_H
+#include <linux/socket.h>
#include <linux/types.h>
/* the following events that user-space can register for */
@@ -34,6 +35,24 @@
*/
#define FAN_ALL_INCOMING_EVENTS (FAN_ALL_EVENTS |\
FAN_EVENT_ON_CHILD)
+#ifndef SOL_FANOTIFY
+#define SOL_FANOTIFY 278
+#endif
+
+#ifndef AF_FANOTIFY
+#define AF_FANOTIFY 37
+#define PF_FANOTIFY AF_FANOTIFY
+#endif
+
+struct fanotify_addr {
+ sa_family_t family;
+ __u32 priority; /* unused */
+ __u32 mask_hi; /* unused */
+ __u32 mask; /* unused */
+ __u32 f_flags; /* unused */
+ __u32 unused[16];
+} __attribute__((packed));
+
#ifdef __KERNEL__
#endif /* __KERNEL__ */
^ permalink raw reply related
* [PATCH 7/8] fanotify: userspace can add and remove fsnotify inode marks
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
Using setsockopt a user can add or remove fsnotify marks on inodes. These
marks are used to determine which events for which inode are to be sent to
userspace. They are very similar in nature to inotify_add_watch and
inotify_rm_watch.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
fs/notify/fanotify/af_fanotify.c | 169 ++++++++++++++++++++++++++++++++++++++
include/linux/fanotify.h | 10 ++
2 files changed, 178 insertions(+), 1 deletions(-)
diff --git a/fs/notify/fanotify/af_fanotify.c b/fs/notify/fanotify/af_fanotify.c
index d7bf658..ac6aee1 100644
--- a/fs/notify/fanotify/af_fanotify.c
+++ b/fs/notify/fanotify/af_fanotify.c
@@ -17,6 +17,7 @@
#include "af_fanotify.h"
static const struct proto_ops fanotify_proto_ops;
+static struct kmem_cache *fanotify_mark_cache __read_mostly;
static struct proto fanotify_proto = {
.name = "FANOTIFY",
@@ -113,6 +114,170 @@ static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
return 0;
}
+static void fanotify_free_mark(struct fsnotify_mark_entry *entry)
+{
+ kmem_cache_free(fanotify_mark_cache, entry);
+}
+
+static int fanotify_remove_inode_mark(struct fsnotify_group *group,
+ struct fanotify_so_inode_mark *so_inode_mark)
+{
+ struct fsnotify_mark_entry *entry;
+ struct file *file;
+ struct inode *inode;
+ int fput_needed, ret = 0;
+
+ ret = -EBADF;
+ file = fget_light(so_inode_mark->fd, &fput_needed);
+ if (!file)
+ goto out;
+
+ inode = file->f_path.dentry->d_inode;
+
+ spin_lock(&inode->i_lock);
+ entry = fsnotify_find_mark_entry(group, inode);
+ spin_unlock(&inode->i_lock);
+
+ ret = -ENOENT;
+ if (!entry)
+ goto out_fput;
+
+ ret = 0;
+
+ fsnotify_destroy_mark_by_entry(entry);
+
+ /* matches the fsnotify_find_mark_entry() */
+ fsnotify_put_mark(entry);
+
+ fsnotify_recalc_group_mask(group);
+out_fput:
+ fput_light(file, fput_needed);
+out:
+ return ret;
+}
+
+static int fanotify_add_inode_mark(struct fsnotify_group *group,
+ struct fanotify_so_inode_mark *so_inode_mark)
+{
+ struct fsnotify_mark_entry *entry;
+ struct file *file;
+ struct inode *inode;
+ __u32 old_mask, new_mask;
+ int fput_needed, ret;
+
+ ret = -EINVAL;
+ if (!fanotify_is_mask_valid(so_inode_mark->mask))
+ goto out;
+
+ ret = -EBADF;
+ file = fget_light(so_inode_mark->fd, &fput_needed);
+ if (!file)
+ goto out;
+
+ inode = file->f_path.dentry->d_inode;
+
+ spin_lock(&inode->i_lock);
+ entry = fsnotify_find_mark_entry(group, inode);
+ spin_unlock(&inode->i_lock);
+
+ if (!entry) {
+ struct fsnotify_mark_entry *new_entry;
+
+ ret = -ENOMEM;
+ new_entry = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
+ if (!new_entry)
+ goto out_fput;
+
+ fsnotify_init_mark(new_entry, fanotify_free_mark);
+ ret = fsnotify_add_mark(new_entry, group, inode, 0);
+ if (ret) {
+ fanotify_free_mark(new_entry);
+ goto out_fput;
+ }
+
+ entry = new_entry;
+ }
+
+ ret = 0;
+
+ spin_lock(&entry->lock);
+ old_mask = entry->mask;
+ entry->mask |= so_inode_mark->mask;
+ new_mask = entry->mask;
+ spin_unlock(&entry->lock);
+
+ /* we made changes to a mask, update the group mask and the inode mask
+ * so things happen quickly. */
+ if (old_mask != new_mask) {
+ /* more bits in old than in new? */
+ int dropped = (old_mask & ~new_mask);
+ /* more bits in this entry than the inode's mask? */
+ int do_inode = (new_mask & ~inode->i_fsnotify_mask);
+ /* more bits in this entry than the group? */
+ int do_group = (new_mask & ~group->mask);
+
+ /* update the inode with this new entry */
+ if (dropped || do_inode)
+ fsnotify_recalc_inode_mask(inode);
+
+ /* update the group mask with the new mask */
+ if (dropped || do_group)
+ fsnotify_recalc_group_mask(group);
+ }
+
+ /* match the init or the find.... */
+ fsnotify_put_mark(entry);
+
+out_fput:
+ fput_light(file, fput_needed);
+out:
+ return ret;
+}
+
+static int fan_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int optlen)
+{
+ struct fanotify_sock *fan_sock;
+ struct fsnotify_group *group;
+ size_t copy_len;
+
+ union {
+ struct fanotify_so_inode_mark inode_mark;
+ } data;
+ int ret = 0;
+
+ if (sock->state != SS_CONNECTED)
+ return -EBADF;
+
+ if (level != SOL_FANOTIFY)
+ return -ENOPROTOOPT;
+
+ fan_sock = fan_sk(sock->sk);
+ group = fan_sock->group;
+
+ copy_len = min(optlen, (int)sizeof(data));
+ ret = copy_from_user(&data, optval, copy_len);
+ if (ret)
+ return ret;
+
+ switch (optname) {
+ case FANOTIFY_SET_MARK:
+ case FANOTIFY_REMOVE_MARK:
+ if (optlen < sizeof(struct fanotify_so_inode_mark))
+ return -ENOMEM;
+
+ if (optname == FANOTIFY_SET_MARK)
+ ret = fanotify_add_inode_mark(group, &data.inode_mark);
+ else if (optname == FANOTIFY_REMOVE_MARK)
+ ret = fanotify_remove_inode_mark(group, &data.inode_mark);
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ return ret;
+}
+
static const struct net_proto_family fanotify_family_ops = {
.family = PF_FANOTIFY,
.create = fan_sock_create,
@@ -132,7 +297,7 @@ static const struct proto_ops fanotify_proto_ops = {
.ioctl = sock_no_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
- .setsockopt = sock_no_setsockopt,
+ .setsockopt = fan_setsockopt,
.getsockopt = sock_no_getsockopt,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
@@ -142,6 +307,8 @@ static const struct proto_ops fanotify_proto_ops = {
static int __init fanotify_init(void)
{
+ fanotify_mark_cache = KMEM_CACHE(fsnotify_mark_entry, SLAB_PANIC);
+
if (proto_register(&fanotify_proto, 0))
panic("unable to register fanotify protocol with network stack\n");
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 4c1c6cd..6ecbcea 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -53,6 +53,16 @@ struct fanotify_addr {
__u32 unused[16];
} __attribute__((packed));
+/* struct used for FANOTIFY_SET_MARK */
+struct fanotify_so_inode_mark {
+ __s32 fd;
+ __u32 mask;
+} __attribute__((packed));
+
+/* fanotify setsockopt optvals */
+#define FANOTIFY_SET_MARK 1
+#define FANOTIFY_REMOVE_MARK 2
+
#ifdef __KERNEL__
#endif /* __KERNEL__ */
^ permalink raw reply related
* [PATCH 8/8] fanotify: send events to userspace over socket reads
From: Eric Paris @ 2009-09-11 5:26 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, netdev; +Cc: davem, viro, alan, hch
In-Reply-To: <20090911052558.32359.18075.stgit@paris.rdu.redhat.com>
fanotify sends event notification to userspace when userspace reads from the
fanotify socket. This patch implements the operations that happen at read
time. These include opening the file descriptor to the original object and
then filling the userspace buffer. The fd should be pollable to indicate when
it has data present and it should return how much data it has to send when the
FIONREAD ioctl is checked.
Signed-off-by: Eric Paris <eparis@redhat.com>
---
fs/notify/fanotify/af_fanotify.c | 228 ++++++++++++++++++++++++++++++++++++++
fs/notify/fanotify/fanotify.h | 5 +
include/linux/fanotify.h | 25 ++++
3 files changed, 256 insertions(+), 2 deletions(-)
diff --git a/fs/notify/fanotify/af_fanotify.c b/fs/notify/fanotify/af_fanotify.c
index ac6aee1..2ae871b 100644
--- a/fs/notify/fanotify/af_fanotify.c
+++ b/fs/notify/fanotify/af_fanotify.c
@@ -2,6 +2,7 @@
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fsnotify_backend.h>
+#include <linux/ima.h> /* ima_path_check */
#include <linux/init.h>
#include <linux/kernel.h> /* UINT_MAX */
#include <linux/mount.h> /* mntget() */
@@ -16,6 +17,8 @@
#include "fanotify.h"
#include "af_fanotify.h"
+#include <asm/ioctls.h>
+
static const struct proto_ops fanotify_proto_ops;
static struct kmem_cache *fanotify_mark_cache __read_mostly;
@@ -114,6 +117,36 @@ static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
return 0;
}
+static int fan_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct fanotify_sock *fan_sock;
+ struct fsnotify_group *group;
+ struct fsnotify_event_holder *holder;
+ void __user *p;
+ int ret = -ENOTTY;
+ size_t send_len = 0;
+
+ if (sock->state != SS_CONNECTED)
+ return -EBADF;
+
+ fan_sock = fan_sk(sock->sk);
+ group = fan_sock->group;
+
+ p = (void __user *) arg;
+
+ switch (cmd) {
+ case FIONREAD:
+ mutex_lock(&group->notification_mutex);
+ list_for_each_entry(holder, &group->notification_list, event_list)
+ send_len += FAN_EVENT_METADATA_LEN;
+ mutex_unlock(&group->notification_mutex);
+ ret = put_user(send_len, (int __user *) p);
+ break;
+ }
+
+ return ret;
+}
+
static void fanotify_free_mark(struct fsnotify_mark_entry *entry)
{
kmem_cache_free(fanotify_mark_cache, entry);
@@ -278,6 +311,197 @@ static int fan_setsockopt(struct socket *sock, int level, int optname,
return ret;
}
+/*
+ * Get an fsnotify notification event if one exists and is small
+ * enough to fit in "count". Return an error pointer if the count
+ * is not large enough.
+ *
+ * Called with the group->notification_mutex held.
+ */
+static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
+ size_t count)
+{
+ BUG_ON(!mutex_is_locked(&group->notification_mutex));
+
+ if (fsnotify_notify_queue_is_empty(group))
+ return NULL;
+
+ if (FAN_EVENT_METADATA_LEN > count)
+ return ERR_PTR(-EINVAL);
+
+ /* held the notification_mutex the whole time, so this is the
+ * same event we peeked above */
+ return fsnotify_remove_notify_event(group);
+}
+
+static int create_and_fill_fd(struct fsnotify_group *group,
+ struct fanotify_event_metadata *metadata,
+ struct fsnotify_event *event)
+{
+ int client_fd, err;
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+ struct file *new_file;
+
+ client_fd = get_unused_fd();
+ if (client_fd < 0)
+ return client_fd;
+
+ if (event->data_type != FSNOTIFY_EVENT_PATH) {
+ WARN_ON(1);
+ put_unused_fd(client_fd);
+ return -EINVAL;
+ }
+
+ /*
+ * we need a new file handle for the userspace program so it can read even if it was
+ * originally opened O_WRONLY.
+ */
+ dentry = dget(event->path.dentry);
+ mnt = mntget(event->path.mnt);
+ /* it's possible this event was an overflow event. in that case dentry and mnt
+ * are NULL; That's fine, just don't call dentry open */
+ if (dentry && mnt) {
+ err = ima_path_check(&event->path, MAY_READ, IMA_COUNT_UPDATE);
+ if (err)
+ new_file = ERR_PTR(err);
+ else
+ new_file = dentry_open(dentry, mnt, O_RDONLY | O_LARGEFILE,
+ current_cred());
+ } else
+ new_file = ERR_PTR(-EOVERFLOW);
+ if (IS_ERR(new_file)) {
+ /*
+ * we still send an event even if we can't open the file. this
+ * can happen when say tasks are gone and we try to open their
+ * /proc entries or we try to open a WRONLY file like in sysfs
+ * we just send the errno to userspace since there isn't much
+ * else we can do.
+ */
+ put_unused_fd(client_fd);
+ client_fd = PTR_ERR(new_file);
+ } else {
+ new_file->f_mode |= FMODE_NONOTIFY;
+ fd_install(client_fd, new_file);
+ }
+
+ metadata->fd = client_fd;
+
+ return 0;
+}
+
+static ssize_t fill_event_metadata(struct fsnotify_group *group,
+ struct fanotify_event_metadata *metadata,
+ struct fsnotify_event *event)
+{
+ pr_debug("%s: \n", __func__);
+
+ metadata->event_len = FAN_EVENT_METADATA_LEN;
+ metadata->vers = FANOTIFY_METADATA_VERSION;
+ metadata->mask = fanotify_outgoing_mask(event->mask);
+
+ return create_and_fill_fd(group, metadata, event);
+
+}
+
+static ssize_t copy_event_to_iov(struct fsnotify_group *group,
+ struct fsnotify_event *event,
+ struct iovec *iov)
+{
+ struct fanotify_event_metadata fanotify_event_metadata;
+ int ret;
+
+ pr_debug("%s: \n", __func__);
+
+ ret = fill_event_metadata(group, &fanotify_event_metadata, event);
+ if (ret)
+ return ret;
+
+ /* send the main event */
+ ret = memcpy_toiovec(iov, (unsigned char *)&fanotify_event_metadata,
+ FAN_EVENT_METADATA_LEN);
+ if (ret < 0)
+ return ret;
+
+ return FAN_EVENT_METADATA_LEN;
+}
+
+static ssize_t fan_recv_events(struct fsnotify_group *group, struct msghdr *msg,
+ int count, int nonblock)
+{
+ struct fsnotify_event *event;
+ int ret, len_sent = 0;
+ DEFINE_WAIT(wait);
+
+ pr_debug("%s: \n", __func__);
+
+ while (1) {
+ prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE);
+
+ mutex_lock(&group->notification_mutex);
+ event = get_one_event(group, count);
+ mutex_unlock(&group->notification_mutex);
+
+ if (event) {
+ ret = PTR_ERR(event);
+ if (IS_ERR(event))
+ break;
+
+ ret = copy_event_to_iov(group, event, msg->msg_iov);
+ fsnotify_put_event(event);
+ if (ret < 0)
+ break;
+ len_sent += ret;
+ count -= ret;
+ continue;
+ }
+
+ ret = -EAGAIN;
+ if (nonblock)
+ break;
+ ret = -EINTR;
+ if (signal_pending(current))
+ break;
+
+ if (len_sent)
+ break;
+
+ schedule();
+ }
+
+ finish_wait(&group->notification_waitq, &wait);
+ if (len_sent && ret != -EFAULT)
+ ret = len_sent;
+ return ret;
+}
+
+static int fan_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+{
+ struct fanotify_sock *fan_sock;
+ struct fsnotify_group *group;
+ int nonblock;
+
+ pr_debug("%s: \n", __func__);
+
+ if (sock->state != SS_CONNECTED)
+ return -EBADF;
+
+ if (size < FAN_EVENT_METADATA_LEN)
+ return -ENOMEM;
+
+ fan_sock = fan_sk(sock->sk);
+ group = fan_sock->group;
+
+ /* hey, nonblock no matter how they ask */
+ nonblock = !!(sock->file->f_flags & O_NONBLOCK);
+ nonblock |= !!(flags & MSG_DONTWAIT);
+
+ size = fan_recv_events(group, msg, size, nonblock);
+
+ return size;
+}
+
static const struct net_proto_family fanotify_family_ops = {
.family = PF_FANOTIFY,
.create = fan_sock_create,
@@ -294,13 +518,13 @@ static const struct proto_ops fanotify_proto_ops = {
.accept = sock_no_accept,
.getname = sock_no_getname,
.poll = sock_no_poll,
- .ioctl = sock_no_ioctl,
+ .ioctl = fan_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = fan_setsockopt,
.getsockopt = sock_no_getsockopt,
.sendmsg = sock_no_sendmsg,
- .recvmsg = sock_no_recvmsg,
+ .recvmsg = fan_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 6c7bf06..4a5c785 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -12,3 +12,8 @@ static inline bool fanotify_is_mask_valid(__u32 mask)
return false;
return true;
}
+
+static inline __u32 fanotify_outgoing_mask(__u32 mask)
+{
+ return mask & FAN_ALL_OUTGOING_EVENTS;
+}
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 6ecbcea..17f9550 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -35,6 +35,10 @@
*/
#define FAN_ALL_INCOMING_EVENTS (FAN_ALL_EVENTS |\
FAN_EVENT_ON_CHILD)
+
+#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\
+ FAN_Q_OVERFLOW)
+
#ifndef SOL_FANOTIFY
#define SOL_FANOTIFY 278
#endif
@@ -63,6 +67,27 @@ struct fanotify_so_inode_mark {
#define FANOTIFY_SET_MARK 1
#define FANOTIFY_REMOVE_MARK 2
+#define FANOTIFY_METADATA_VERSION 1
+
+struct fanotify_event_metadata {
+ __u32 event_len;
+ __u32 vers;
+ __s32 fd;
+ __u32 mask;
+} __attribute__((packed));
+
+
+/* Helper functions to deal with fanotify_event_metadata buffers */
+#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
+
+#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
+ (struct fanotify_event_metadata*)(((char *)(meta)) + \
+ (meta)->event_len))
+
+#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
+ (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
+ (long)(meta)->event_len <= (long)(len))
+
#ifdef __KERNEL__
#endif /* __KERNEL__ */
^ permalink raw reply related
* Re: [PATCH RFC] tun: export underlying socket
From: Michael S. Tsirkin @ 2009-09-11 5:36 UTC (permalink / raw)
To: Paul Moore; +Cc: David Miller, netdev, herbert
In-Reply-To: <20090911045943.GA1613@redhat.com>
On Fri, Sep 11, 2009 at 07:59:43AM +0300, Michael S. Tsirkin wrote:
> On Fri, Sep 11, 2009 at 12:17:27AM -0400, Paul Moore wrote:
> > On Thursday 10 September 2009 08:59:29 am Michael S. Tsirkin wrote:
> > > Tun device looks similar to a packet socket
> > > in that both pass complete frames from/to userspace.
> > >
> > > This patch fills in enough fields in the socket underlying tun driver
> > > to support sendmsg/recvmsg operations, and exports access to this socket
> > > to modules.
> > >
> > > This way, code using raw sockets to inject packets
> > > into a physical device, can support injecting
> > > packets into host network stack almost without modification.
> > >
> > > First user of this interface will be vhost virtualization
> > > accelerator.
> >
> > No comments on the code at this point - I'm just trying to understand the
> > intended user right now which I'm assuming is the vhost-net bits you sent
> > previously?
>
> Yes - these now use raw socket,
More specifically, vhost would then be patched with:
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index aeffb3a..b54f9d6 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -331,15 +331,26 @@ err:
return ERR_PTR(r);
}
+static struct socket *get_tun_socket(int fd)
+{
+ struct file *file = fget(fd);
+ if (!file)
+ return ERR_PTR(-EBADF);
+ return tun_get_socket(file);
+}
+
static struct socket *get_socket(int fd)
{
struct socket *sock;
sock = get_raw_socket(fd);
if (!IS_ERR(sock))
return sock;
+ sock = get_tun_socket(fd);
+ if (!IS_ERR(sock))
+ return sock;
return ERR_PTR(-ENOTSOCK);
}
static long vhost_net_set_socket(struct vhost_net *n, int fd)
{
struct socket *sock, *oldsock = NULL;
^ permalink raw reply related
* Re: [PATCH RFC] tun: export underlying socket
From: Eric Dumazet @ 2009-09-11 6:10 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: Paul Moore, David Miller, netdev, herbert
In-Reply-To: <20090911053610.GA10324@redhat.com>
Michael S. Tsirkin a écrit :
> On Fri, Sep 11, 2009 at 07:59:43AM +0300, Michael S. Tsirkin wrote:
>> On Fri, Sep 11, 2009 at 12:17:27AM -0400, Paul Moore wrote:
>>> On Thursday 10 September 2009 08:59:29 am Michael S. Tsirkin wrote:
>>>> Tun device looks similar to a packet socket
>>>> in that both pass complete frames from/to userspace.
>>>>
>>>> This patch fills in enough fields in the socket underlying tun driver
>>>> to support sendmsg/recvmsg operations, and exports access to this socket
>>>> to modules.
>>>>
>>>> This way, code using raw sockets to inject packets
>>>> into a physical device, can support injecting
>>>> packets into host network stack almost without modification.
>>>>
>>>> First user of this interface will be vhost virtualization
>>>> accelerator.
>>> No comments on the code at this point - I'm just trying to understand the
>>> intended user right now which I'm assuming is the vhost-net bits you sent
>>> previously?
>> Yes - these now use raw socket,
>
> More specifically, vhost would then be patched with:
>
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index aeffb3a..b54f9d6 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -331,15 +331,26 @@ err:
> return ERR_PTR(r);
> }
>
> +static struct socket *get_tun_socket(int fd)
> +{
> + struct file *file = fget(fd);
> + if (!file)
> + return ERR_PTR(-EBADF);
> + return tun_get_socket(file);
This would leak a reference on file, if it happens not being a tun file
> +}
> +
> static struct socket *get_socket(int fd)
> {
> struct socket *sock;
> sock = get_raw_socket(fd);
> if (!IS_ERR(sock))
> return sock;
> + sock = get_tun_socket(fd);
> + if (!IS_ERR(sock))
> + return sock;
> return ERR_PTR(-ENOTSOCK);
> }
>
> static long vhost_net_set_socket(struct vhost_net *n, int fd)
> {
> struct socket *sock, *oldsock = NULL;
^ permalink raw reply
* Re: iproute2 tools for 2.6.31
From: Eric Dumazet @ 2009-09-11 7:48 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <20090910120416.6fd03d1f@s6510>
Stephen Hemminger a écrit :
> I am putting together release for 2.6.31 based tools.
> The only open issue is how to deal with the error handling in commands
> that do monitoring filtering. Right now leaning towards the two socket
> solution.
>
> So if you have anything else that you have been waiting for,
> please drop me a note.
>
One thing that is IMHO strange is the output of sk information
on 64 bits (x86_64 for example)
# ss -e dst 55.225.18.6
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 55.225.18.96:9273 55.225.18.6:37405 timer:(keepalive,20min,0) ino:57807651 sk:36e40c80ffff8100
True sk pointer is ffff8100ffff8100, not 36e40c80ffff8100
ss/misc.c
printf(" sk:%08x", r->id.idiag_cookie[0]);
if (r->id.idiag_cookie[1] != 0)
printf("%08x", r->id.idiag_cookie[1]);
while kernel does :
r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
What do you think of following patch ?
[PATCH] ss: correct display of sk pointer
On 64bit arches, sk pointer was 32/32 reversed.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/misc/ss.c b/misc/ss.c
index 651fe3b..2447186 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -1393,9 +1393,10 @@ static int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f)
if (r->idiag_uid)
printf(" uid:%u", (unsigned)r->idiag_uid);
printf(" ino:%u", r->idiag_inode);
- printf(" sk:%08x", r->id.idiag_cookie[0]);
+ printf(" sk:");
if (r->id.idiag_cookie[1] != 0)
printf("%08x", r->id.idiag_cookie[1]);
+ printf("%08x", r->id.idiag_cookie[0]);
}
if (show_mem || show_tcpinfo) {
printf("\n\t");
^ permalink raw reply related
* Re: iproute2 tools for 2.6.31
From: Eric Dumazet @ 2009-09-11 7:51 UTC (permalink / raw)
Cc: Stephen Hemminger, netdev
In-Reply-To: <4AAA00B7.9030904@gmail.com>
Eric Dumazet a écrit :
> Stephen Hemminger a écrit :
>> I am putting together release for 2.6.31 based tools.
>> The only open issue is how to deal with the error handling in commands
>> that do monitoring filtering. Right now leaning towards the two socket
>> solution.
>>
>> So if you have anything else that you have been waiting for,
>> please drop me a note.
>>
>
> One thing that is IMHO strange is the output of sk information
> on 64 bits (x86_64 for example)
>
> # ss -e dst 55.225.18.6
> State Recv-Q Send-Q Local Address:Port Peer Address:Port
> ESTAB 0 0 55.225.18.96:9273 55.225.18.6:37405 timer:(keepalive,20min,0) ino:57807651 sk:36e40c80ffff8100
>
> True sk pointer is ffff8100ffff8100, not 36e40c80ffff8100
>
Oops I meant ffff810036e40c80, sorry for the copy/paste error ;)
>
>
> ss/misc.c
>
> printf(" sk:%08x", r->id.idiag_cookie[0]);
> if (r->id.idiag_cookie[1] != 0)
> printf("%08x", r->id.idiag_cookie[1]);
>
> while kernel does :
> r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
> r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
>
>
>
> What do you think of following patch ?
>
> [PATCH] ss: correct display of sk pointer
>
> On 64bit arches, sk pointer was 32/32 reversed.
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
>
> diff --git a/misc/ss.c b/misc/ss.c
> index 651fe3b..2447186 100644
> --- a/misc/ss.c
> +++ b/misc/ss.c
> @@ -1393,9 +1393,10 @@ static int tcp_show_sock(struct nlmsghdr *nlh, struct filter *f)
> if (r->idiag_uid)
> printf(" uid:%u", (unsigned)r->idiag_uid);
> printf(" ino:%u", r->idiag_inode);
> - printf(" sk:%08x", r->id.idiag_cookie[0]);
> + printf(" sk:");
> if (r->id.idiag_cookie[1] != 0)
> printf("%08x", r->id.idiag_cookie[1]);
> + printf("%08x", r->id.idiag_cookie[0]);
> }
> if (show_mem || show_tcpinfo) {
> printf("\n\t");
>
>
^ permalink raw reply
* [PATCH iproute2] ss: adds a space before congestion string
From: Eric Dumazet @ 2009-09-11 8:32 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: netdev
In-Reply-To: <20090910120416.6fd03d1f@s6510>
Stephen Hemminger a écrit :
> I am putting together release for 2.6.31 based tools.
> The only open issue is how to deal with the error handling in commands
> that do monitoring filtering. Right now leaning towards the two socket
> solution.
>
> So if you have anything else that you have been waiting for,
> please drop me a note.
Another patch would be nice too :
ss -io
ESTAB 0 0 55.225.18.16:52668 55.225.18.187:49531
sackbic wscale:2,6 rto:219 rtt:19.75/16.5 ato:40 cwnd:4 send 2.4Mbps rcv_space:5840
Note the 'sackbic' string, instead of sack bic ?
Thanks
[PATCH] ss: adds a space before congestion string
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/misc/ss.c b/misc/ss.c
index 651fe3b..9396468 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -1302,7 +1302,7 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r)
}
if (tb[INET_DIAG_CONG])
- printf("%s", (char *) RTA_DATA(tb[INET_DIAG_CONG]));
+ printf(" %s", (char *) RTA_DATA(tb[INET_DIAG_CONG]));
if (info->tcpi_options & TCPI_OPT_WSCALE)
printf(" wscale:%d,%d", info->tcpi_snd_wscale,
^ permalink raw reply related
* Re: [PATCH RFC] tun: export underlying socket
From: Michael S. Tsirkin @ 2009-09-11 9:44 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Paul Moore, David Miller, netdev, herbert
In-Reply-To: <4AA9E9D3.5070406@gmail.com>
On Fri, Sep 11, 2009 at 08:10:27AM +0200, Eric Dumazet wrote:
> Michael S. Tsirkin a écrit :
> > On Fri, Sep 11, 2009 at 07:59:43AM +0300, Michael S. Tsirkin wrote:
> >> On Fri, Sep 11, 2009 at 12:17:27AM -0400, Paul Moore wrote:
> >>> On Thursday 10 September 2009 08:59:29 am Michael S. Tsirkin wrote:
> >>>> Tun device looks similar to a packet socket
> >>>> in that both pass complete frames from/to userspace.
> >>>>
> >>>> This patch fills in enough fields in the socket underlying tun driver
> >>>> to support sendmsg/recvmsg operations, and exports access to this socket
> >>>> to modules.
> >>>>
> >>>> This way, code using raw sockets to inject packets
> >>>> into a physical device, can support injecting
> >>>> packets into host network stack almost without modification.
> >>>>
> >>>> First user of this interface will be vhost virtualization
> >>>> accelerator.
> >>> No comments on the code at this point - I'm just trying to understand the
> >>> intended user right now which I'm assuming is the vhost-net bits you sent
> >>> previously?
> >> Yes - these now use raw socket,
> >
> > More specifically, vhost would then be patched with:
> >
> > diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> > index aeffb3a..b54f9d6 100644
> > --- a/drivers/vhost/net.c
> > +++ b/drivers/vhost/net.c
> > @@ -331,15 +331,26 @@ err:
> > return ERR_PTR(r);
> > }
> >
> > +static struct socket *get_tun_socket(int fd)
> > +{
> > + struct file *file = fget(fd);
> > + if (!file)
> > + return ERR_PTR(-EBADF);
> > + return tun_get_socket(file);
>
> This would leak a reference on file, if it happens not being a tun file
Good catch, thanks! So it should be:
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index aeffb3a..e70f954 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -331,16 +331,30 @@ err:
return ERR_PTR(r);
}
+static struct socket *get_tun_socket(int fd)
+{
+ struct file *file = fget(fd);
+ struct socket *sock;
+ if (!file)
+ return ERR_PTR(-EBADF);
+ sock = tun_get_socket(file);
+ if (IS_ERR(sock))
+ fput(file);
+ return sock;
+}
+
static struct socket *get_socket(int fd)
{
struct socket *sock;
sock = get_raw_socket(fd);
if (!IS_ERR(sock))
return sock;
+ sock = get_tun_socket(fd);
+ if (!IS_ERR(sock))
+ return sock;
return ERR_PTR(-ENOTSOCK);
}
-
static long vhost_net_set_socket(struct vhost_net *n, int fd)
{
struct socket *sock, *oldsock = NULL;
^ permalink raw reply related
* [PATCH 2/2] ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface
From: Sebastian Haas @ 2009-09-11 10:54 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
greg-U8xfFu+wG4EAvxtiuMwx3w, oliver-fJ+pQTUTwRTk1uMJSBkQmQ,
wg-5Yr1BZd7O62+XT7JhA+gdA
In-Reply-To: <20090911105449.7815.65368.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
This patch adds support for one channel CAN/USB interace CPC-USB/ARM7 from
EMS Dr. Thomas Wuensche (http://www.ems-wuensche.com).
Signed-off-by: Sebastian Haas <haas-zsNKPWJ8Pib6hrUXjxyGrA@public.gmane.org>
---
drivers/net/can/Kconfig | 7
drivers/net/can/Makefile | 2
drivers/net/can/ems_usb.c | 1138 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1147 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/can/ems_usb.c
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 0900743..6ac5aa5 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -75,6 +75,13 @@ config CAN_EMS_PCI
CPC-PCIe and CPC-104P cards from EMS Dr. Thomas Wuensche
(http://www.ems-wuensche.de).
+config CAN_EMS_USB
+ tristate "EMS CPC-USB/ARM7 CAN/USB interface"
+ depends on USB && CAN_DEV
+ ---help---
+ This driver is for the one channel CPC-USB/ARM7 CAN/USB interface
+ from from EMS Dr. Thomas Wuensche (http://www.ems-wuensche.de).
+
config CAN_KVASER_PCI
tristate "Kvaser PCIcanx and Kvaser PCIcan PCI Cards"
depends on PCI && CAN_SJA1000
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 523a941..676a606 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -7,6 +7,8 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o
obj-$(CONFIG_CAN_DEV) += can-dev.o
can-dev-y := dev.o
+obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
+
obj-$(CONFIG_CAN_SJA1000) += sja1000/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/ems_usb.c b/drivers/net/can/ems_usb.c
new file mode 100644
index 0000000..84ab476
--- /dev/null
+++ b/drivers/net/can/ems_usb.c
@@ -0,0 +1,1138 @@
+/*
+ * CAN driver for EMS Dr. Thomas Wuensche CPC-USB/ARM7
+ *
+ * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+MODULE_AUTHOR("Sebastian Haas <haas-zsNKPWJ8Pib6hrUXjxyGrA@public.gmane.org>");
+MODULE_DESCRIPTION("CAN driver for EMS Dr. Thomas Wuensche CAN/USB interfaces");
+MODULE_LICENSE("GPL v2");
+
+/* Control-Values for CPC_Control() Command Subject Selection */
+#define CONTR_CAN_MESSAGE 0x04
+#define CONTR_CAN_STATE 0x0C
+#define CONTR_BUS_ERROR 0x1C
+
+/* Control Command Actions */
+#define CONTR_CONT_OFF 0
+#define CONTR_CONT_ON 1
+#define CONTR_ONCE 2
+
+/* Messages from CPC to PC */
+#define CPC_MSG_TYPE_CAN_FRAME 1 /* CAN data frame */
+#define CPC_MSG_TYPE_RTR_FRAME 8 /* CAN remote frame */
+#define CPC_MSG_TYPE_CAN_PARAMS 12 /* Actual CAN parameters */
+#define CPC_MSG_TYPE_CAN_STATE 14 /* CAN state message */
+#define CPC_MSG_TYPE_EXT_CAN_FRAME 16 /* Extended CAN data frame */
+#define CPC_MSG_TYPE_EXT_RTR_FRAME 17 /* Extended remote frame */
+#define CPC_MSG_TYPE_CONTROL 19 /* change interface behavior */
+#define CPC_MSG_TYPE_CONFIRM 20 /* command processed confirmation */
+#define CPC_MSG_TYPE_OVERRUN 21 /* overrun events */
+#define CPC_MSG_TYPE_CAN_FRAME_ERROR 23 /* detected bus errors */
+#define CPC_MSG_TYPE_ERR_COUNTER 25 /* RX/TX error counter */
+
+/* Messages from the PC to the CPC interface */
+#define CPC_CMD_TYPE_CAN_FRAME 1 /* CAN data frame */
+#define CPC_CMD_TYPE_CONTROL 3 /* control of interface behavior */
+#define CPC_CMD_TYPE_CAN_PARAMS 6 /* set CAN parameters */
+#define CPC_CMD_TYPE_RTR_FRAME 13 /* CAN remote frame */
+#define CPC_CMD_TYPE_CAN_STATE 14 /* CAN state message */
+#define CPC_CMD_TYPE_EXT_CAN_FRAME 15 /* Extended CAN data frame */
+#define CPC_CMD_TYPE_EXT_RTR_FRAME 16 /* Extended CAN remote frame */
+#define CPC_CMD_TYPE_CAN_EXIT 200 /* exit the CAN */
+
+#define CPC_CMD_TYPE_INQ_ERR_COUNTER 25 /* request the CAN error counters */
+#define CPC_CMD_TYPE_CLEAR_MSG_QUEUE 8 /* clear CPC_MSG queue */
+#define CPC_CMD_TYPE_CLEAR_CMD_QUEUE 28 /* clear CPC_CMD queue */
+
+#define CPC_CC_TYPE_SJA1000 2 /* Philips basic CAN controller */
+
+#define CPC_CAN_ECODE_ERRFRAME 0x01 /* Ecode type */
+
+/* Overrun types */
+#define CPC_OVR_EVENT_CAN 0x01
+#define CPC_OVR_EVENT_CANSTATE 0x02
+#define CPC_OVR_EVENT_BUSERROR 0x04
+
+/*
+ * If the CAN controller lost a message we indicate it with the highest bit
+ * set in the count field.
+ */
+#define CPC_OVR_HW 0x80
+
+/* Size of the "struct ems_cpc_msg" without the union */
+#define CPC_MSG_HEADER_LEN 11
+#define CPC_CAN_MSG_MIN_SIZE 5
+
+/* Define these values to match your devices */
+#define USB_CPCUSB_VENDOR_ID 0x12D6
+
+#define USB_CPCUSB_ARM7_PRODUCT_ID 0x0444
+
+/* Mode register NXP LPC2119/SJA1000 CAN Controller */
+#define SJA1000_MOD_NORMAL 0x00
+#define SJA1000_MOD_RM 0x01
+
+/* ECC register NXP LPC2119/SJA1000 CAN Controller */
+#define SJA1000_ECC_SEG 0x1F
+#define SJA1000_ECC_DIR 0x20
+#define SJA1000_ECC_ERR 0x06
+#define SJA1000_ECC_BIT 0x00
+#define SJA1000_ECC_FORM 0x40
+#define SJA1000_ECC_STUFF 0x80
+#define SJA1000_ECC_MASK 0xc0
+
+/* Status register content */
+#define SJA1000_SR_BS 0x80
+#define SJA1000_SR_ES 0x40
+
+#define SJA1000_DEFAULT_OUTPUT_CONTROL 0xDA
+
+#define EMS_USB_ARM7_CLOCK 8000000
+
+/*
+ * CAN-Message representation in a CPC_MSG. Message object type is
+ * CPC_MSG_TYPE_CAN_FRAME or CPC_MSG_TYPE_RTR_FRAME or
+ * CPC_MSG_TYPE_EXT_CAN_FRAME or CPC_MSG_TYPE_EXT_RTR_FRAME.
+ */
+struct cpc_can_msg {
+ u32 id;
+ u8 length;
+ u8 msg[8];
+};
+
+/* Representation of the CAN parameters for the SJA1000 controller */
+struct cpc_sja1000_params {
+ u8 mode;
+ u8 acc_code0;
+ u8 acc_code1;
+ u8 acc_code2;
+ u8 acc_code3;
+ u8 acc_mask0;
+ u8 acc_mask1;
+ u8 acc_mask2;
+ u8 acc_mask3;
+ u8 btr0;
+ u8 btr1;
+ u8 outp_contr;
+};
+
+/* CAN params message representation */
+struct cpc_can_params {
+ u8 cc_type;
+
+ /* Will support M16C CAN controller in the future */
+ union {
+ struct cpc_sja1000_params sja1000;
+ } cc_params;
+};
+
+/* Structure for confirmed message handling */
+struct cpc_confirm {
+ u8 error; /* error code */
+};
+
+/* Structure for overrun conditions */
+struct cpc_overrun {
+ u8 event;
+ u8 count;
+};
+
+/* SJA1000 CAN errors (compatible to NXP LPC2119) */
+struct cpc_sja1000_can_error {
+ u8 ecc;
+ u8 rxerr;
+ u8 txerr;
+};
+
+/* structure for CAN error conditions */
+struct cpc_can_error {
+ u8 ecode;
+
+ struct {
+ u8 cc_type;
+
+ /* Other controllers may also provide error code capture regs */
+ union {
+ struct cpc_sja1000_can_error sja1000;
+ } regs;
+ } cc;
+};
+
+/*
+ * Structure containing RX/TX error counter. This structure is used to request
+ * the values of the CAN controllers TX and RX error counter.
+ */
+struct cpc_can_err_counter {
+ u8 rx;
+ u8 tx;
+};
+
+/* Main message type used between library and application */
+struct __attribute__ ((packed)) ems_cpc_msg {
+ u8 type; /* type of message */
+ u8 length; /* length of data within union 'msg' */
+ u8 msgid; /* confirmation handle */
+ u32 ts_sec; /* timestamp in seconds */
+ u32 ts_nsec; /* timestamp in nano seconds */
+
+ union {
+ u8 generic[64];
+ struct cpc_can_msg can_msg;
+ struct cpc_can_params can_params;
+ struct cpc_confirm confirmation;
+ struct cpc_overrun overrun;
+ struct cpc_can_error error;
+ struct cpc_can_err_counter err_counter;
+ u8 can_state;
+ } msg;
+};
+
+/*
+ * Table of devices that work with this driver
+ * NOTE: This driver supports only CPC-USB/ARM7 (LPC2119) yet.
+ */
+static struct usb_device_id ems_usb_table[] = {
+ {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_ARM7_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ems_usb_table);
+
+#define RX_BUFFER_SIZE 64
+#define CPC_HEADER_SIZE 4
+
+#define MAX_RX_URBS 10
+#define MAX_TX_URBS CAN_ECHO_SKB_MAX
+
+struct ems_usb;
+
+struct ems_tx_urb_context {
+ struct ems_usb *dev;
+
+ u32 echo_index;
+ u8 dlc;
+};
+
+struct ems_usb {
+ struct can_priv can; /* must be the first member */
+ int open_time;
+
+ struct sk_buff *echo_skb[MAX_TX_URBS];
+
+ struct usb_device *udev;
+ struct net_device *netdev;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct ems_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct usb_anchor rx_submitted;
+
+ struct urb *intr_urb;
+
+ u8 *tx_msg_buffer;
+
+ u8 intr_in_buffer[4];
+ unsigned int free_slots; /* remember number of available slots */
+
+ struct ems_cpc_msg active_params; /* active controller parameters */
+};
+
+static void ems_usb_read_interrupt_callback(struct urb *urb)
+{
+ struct ems_usb *dev = urb->context;
+ struct net_device *netdev;
+ int err;
+
+ netdev = dev->netdev;
+
+ if (!netif_device_present(netdev))
+ return;
+
+ switch (urb->status) {
+ case 0:
+ dev->free_slots = dev->intr_in_buffer[1];
+ break;
+
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ dev_info(netdev->dev.parent, "Rx interrupt aborted %d\n",
+ urb->status);
+ break;
+ }
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (err == -ENODEV)
+ netif_device_detach(netdev);
+ else if (err)
+ dev_err(netdev->dev.parent,
+ "failed resubmitting intr urb: %d\n", err);
+
+ return;
+}
+
+static void ems_usb_rx_can_msg(struct ems_usb *dev, struct ems_cpc_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ int i;
+ struct net_device_stats *stats = &dev->netdev->stats;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+
+ skb->dev = dev->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+
+ cf->can_id = msg->msg.can_msg.id;
+ cf->can_dlc = min_t(u8, msg->msg.can_msg.length, 8);
+
+ if (msg->type == CPC_MSG_TYPE_EXT_CAN_FRAME
+ || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME)
+ cf->can_id |= CAN_EFF_FLAG;
+
+ if (msg->type == CPC_MSG_TYPE_RTR_FRAME
+ || msg->type == CPC_MSG_TYPE_EXT_RTR_FRAME) {
+ cf->can_id |= CAN_RTR_FLAG;
+ } else {
+ for (i = 0; i < cf->can_dlc; i++)
+ cf->data[i] = msg->msg.can_msg.msg[i];
+ }
+
+ netif_rx(skb);
+
+ dev->netdev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &dev->netdev->stats;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL)
+ return;
+
+ skb->dev = dev->netdev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+
+ cf->can_id = CAN_ERR_FLAG;
+ cf->can_dlc = CAN_ERR_DLC;
+
+ if (msg->type == CPC_MSG_TYPE_CAN_STATE) {
+ u8 state = msg->msg.can_state;
+
+ if (state & SJA1000_SR_BS) {
+ dev->can.state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ can_bus_off(dev->netdev);
+ } else if (state & SJA1000_SR_ES) {
+ dev->can.state = CAN_STATE_ERROR_WARNING;
+ dev->can.can_stats.error_warning++;
+ } else {
+ dev->can.state = CAN_STATE_ERROR_ACTIVE;
+ dev->can.can_stats.error_passive++;
+ }
+ } else if (msg->type == CPC_MSG_TYPE_CAN_FRAME_ERROR) {
+ u8 ecc = msg->msg.error.cc.regs.sja1000.ecc;
+ u8 txerr = msg->msg.error.cc.regs.sja1000.txerr;
+ u8 rxerr = msg->msg.error.cc.regs.sja1000.rxerr;
+
+ /* bus error interrupt */
+ dev->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ switch (ecc & SJA1000_ECC_MASK) {
+ case SJA1000_ECC_BIT:
+ cf->data[2] |= CAN_ERR_PROT_BIT;
+ break;
+ case SJA1000_ECC_FORM:
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ break;
+ case SJA1000_ECC_STUFF:
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ break;
+ default:
+ cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+ cf->data[3] = ecc & SJA1000_ECC_SEG;
+ break;
+ }
+
+ /* Error occured during transmission? */
+ if ((ecc & SJA1000_ECC_DIR) == 0)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+
+ if (dev->can.state == CAN_STATE_ERROR_WARNING ||
+ dev->can.state == CAN_STATE_ERROR_PASSIVE) {
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ } else if (msg->type == CPC_MSG_TYPE_OVERRUN) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+ }
+
+ netif_rx(skb);
+
+ dev->netdev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+/*
+ * callback for bulk IN urb
+ */
+static void ems_usb_read_bulk_callback(struct urb *urb)
+{
+ struct ems_usb *dev = urb->context;
+ struct net_device *netdev;
+ int retval;
+
+ netdev = dev->netdev;
+
+ if (!netif_device_present(netdev))
+ return;
+
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+
+ case -ENOENT:
+ return;
+
+ default:
+ dev_info(netdev->dev.parent, "Rx URB aborted (%d)\n",
+ urb->status);
+ goto resubmit_urb;
+ }
+
+ if (urb->actual_length > CPC_HEADER_SIZE) {
+ struct ems_cpc_msg *msg;
+ u8 *ibuf = urb->transfer_buffer;
+ u8 msg_count, again, start;
+
+ msg_count = ibuf[0] & ~0x80;
+ again = ibuf[0] & 0x80;
+
+ start = CPC_HEADER_SIZE;
+
+ while (msg_count) {
+ msg = (struct ems_cpc_msg *)&ibuf[start];
+
+ switch (msg->type) {
+ case CPC_MSG_TYPE_CAN_STATE:
+ /* Process CAN state changes */
+ ems_usb_rx_err(dev, msg);
+ break;
+
+ case CPC_MSG_TYPE_CAN_FRAME:
+ case CPC_MSG_TYPE_EXT_CAN_FRAME:
+ case CPC_MSG_TYPE_RTR_FRAME:
+ case CPC_MSG_TYPE_EXT_RTR_FRAME:
+ ems_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CPC_MSG_TYPE_CAN_FRAME_ERROR:
+ /* Process errorframe */
+ ems_usb_rx_err(dev, msg);
+ break;
+
+ case CPC_MSG_TYPE_OVERRUN:
+ /* Message lost while receiving */
+ ems_usb_rx_err(dev, msg);
+ break;
+ }
+
+ start += CPC_MSG_HEADER_LEN + msg->length;
+ msg_count--;
+
+ if (start > urb->transfer_buffer_length) {
+ dev_err(netdev->dev.parent, "format error\n");
+ break;
+ }
+ }
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2),
+ urb->transfer_buffer, RX_BUFFER_SIZE,
+ ems_usb_read_bulk_callback, dev);
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (retval == -ENODEV)
+ netif_device_detach(netdev);
+ else if (retval)
+ dev_err(netdev->dev.parent,
+ "failed resubmitting read bulk urb: %d\n", retval);
+
+ return;
+}
+
+/*
+ * callback for bulk IN urb
+ */
+static void ems_usb_write_bulk_callback(struct urb *urb)
+{
+ struct ems_tx_urb_context *context = urb->context;
+ struct ems_usb *dev;
+ struct net_device *netdev;
+
+ if (!context)
+ return;
+
+ dev = context->dev;
+ netdev = dev->netdev;
+
+ if (!netif_device_present(netdev))
+ return;
+
+ if (urb->status)
+ dev_info(netdev->dev.parent, "Tx URB aborted (%d)\n",
+ urb->status);
+
+ /* free up our allocated buffer */
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+
+ netdev->trans_start = jiffies;
+
+ /* transmission complete interrupt */
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes += context->dlc;
+
+ can_get_echo_skb(netdev, context->echo_index);
+
+ /* Release context */
+ context->echo_index = MAX_TX_URBS;
+
+ atomic_dec(&dev->active_tx_urbs);
+
+ if (netif_queue_stopped(netdev))
+ netif_wake_queue(netdev);
+}
+
+/*
+ * Send the given CPC command synchronously
+ */
+static int ems_usb_command_msg(struct ems_usb *dev, struct ems_cpc_msg *msg)
+{
+ int actual_length;
+
+ /* Copy payload */
+ memcpy(&dev->tx_msg_buffer[CPC_HEADER_SIZE], msg,
+ msg->length + CPC_MSG_HEADER_LEN);
+
+ /* Clear header */
+ memset(&dev->tx_msg_buffer[0], 0, CPC_HEADER_SIZE);
+
+ return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
+ &dev->tx_msg_buffer[0],
+ msg->length + CPC_MSG_HEADER_LEN + CPC_HEADER_SIZE,
+ &actual_length, 1000);
+}
+
+/*
+ * Change CAN controllers' mode register
+ */
+static int ems_usb_write_mode(struct ems_usb *dev, u8 mode)
+{
+ dev->active_params.msg.can_params.cc_params.sja1000.mode = mode;
+
+ return ems_usb_command_msg(dev, &dev->active_params);
+}
+
+/*
+ * Send a CPC_Control command to change behaviour when interface receives a CAN
+ * message, bus error or CAN state changed notifications.
+ */
+static int ems_usb_control_cmd(struct ems_usb *dev, u8 val)
+{
+ struct ems_cpc_msg cmd;
+
+ cmd.type = CPC_CMD_TYPE_CONTROL;
+ cmd.length = CPC_MSG_HEADER_LEN + 1;
+
+ cmd.msgid = 0;
+
+ cmd.msg.generic[0] = val;
+
+ return ems_usb_command_msg(dev, &cmd);
+}
+
+/*
+ * Start interface
+ */
+static int ems_usb_start(struct ems_usb *dev)
+{
+ struct net_device *netdev = dev->netdev;
+ int err, i;
+
+ dev->intr_in_buffer[0] = 0;
+ dev->free_slots = 15; /* initial size */
+
+ for (i = 0; i < MAX_RX_URBS; i++) {
+ struct urb *urb = NULL;
+ u8 *buf = NULL;
+
+ /* create a URB, and a buffer for it */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ dev_err(netdev->dev.parent,
+ "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = usb_buffer_alloc(dev->udev, RX_BUFFER_SIZE, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ dev_err(netdev->dev.parent,
+ "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2),
+ buf, RX_BUFFER_SIZE,
+ ems_usb_read_bulk_callback, dev);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ if (err == -ENODEV)
+ netif_device_detach(dev->netdev);
+
+ usb_unanchor_urb(urb);
+ usb_buffer_free(dev->udev, RX_BUFFER_SIZE, buf,
+ urb->transfer_dma);
+ break;
+ }
+
+ /* Drop reference, USB core will take care of freeing it */
+ usb_free_urb(urb);
+ }
+
+ /* Did we submit any URBs */
+ if (i == 0) {
+ dev_warn(netdev->dev.parent, "couldn't setup read URBs\n");
+ return err;
+ }
+
+ /* Warn if we've couldn't transmit all the URBs */
+ if (i < MAX_RX_URBS)
+ dev_warn(netdev->dev.parent, "rx performance may be slow\n");
+
+ /* Setup and start interrupt URB */
+ usb_fill_int_urb(dev->intr_urb, dev->udev,
+ usb_rcvintpipe(dev->udev, 1),
+ dev->intr_in_buffer,
+ sizeof(dev->intr_in_buffer),
+ ems_usb_read_interrupt_callback, dev, 1);
+
+ err = usb_submit_urb(dev->intr_urb, GFP_KERNEL);
+ if (err) {
+ if (err == -ENODEV)
+ netif_device_detach(dev->netdev);
+
+ dev_warn(netdev->dev.parent, "intr URB submit failed: %d\n",
+ err);
+
+ return err;
+ }
+
+ /* CPC-USB will transfer received message to host */
+ err = ems_usb_control_cmd(dev, CONTR_CAN_MESSAGE | CONTR_CONT_ON);
+ if (err)
+ goto failed;
+
+ /* CPC-USB will transfer CAN state changes to host */
+ err = ems_usb_control_cmd(dev, CONTR_CAN_STATE | CONTR_CONT_ON);
+ if (err)
+ goto failed;
+
+ /* CPC-USB will transfer bus errors to host */
+ err = ems_usb_control_cmd(dev, CONTR_BUS_ERROR | CONTR_CONT_ON);
+ if (err)
+ goto failed;
+
+ err = ems_usb_write_mode(dev, SJA1000_MOD_NORMAL);
+ if (err)
+ goto failed;
+
+ dev->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+
+failed:
+ if (err == -ENODEV)
+ netif_device_detach(dev->netdev);
+
+ dev_warn(netdev->dev.parent, "couldn't submit control: %d\n", err);
+
+ return err;
+}
+
+static void unlink_all_urbs(struct ems_usb *dev)
+{
+ int i;
+
+ usb_unlink_urb(dev->intr_urb);
+
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+ atomic_set(&dev->active_tx_urbs, 0);
+
+ for (i = 0; i < MAX_TX_URBS; i++)
+ dev->tx_contexts[i].echo_index = MAX_TX_URBS;
+}
+
+static int ems_usb_open(struct net_device *netdev)
+{
+ struct ems_usb *dev = netdev_priv(netdev);
+ int err;
+
+ err = ems_usb_write_mode(dev, SJA1000_MOD_RM);
+ if (err)
+ return err;
+
+ /* common open */
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ /* finally start device */
+ err = ems_usb_start(dev);
+ if (err) {
+ if (err == -ENODEV)
+ netif_device_detach(dev->netdev);
+
+ dev_warn(netdev->dev.parent, "couldn't start device: %d\n",
+ err);
+
+ close_candev(netdev);
+
+ return err;
+ }
+
+ dev->open_time = jiffies;
+
+ netif_start_queue(netdev);
+
+ return 0;
+}
+
+static int ems_usb_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct ems_usb *dev = netdev_priv(netdev);
+ struct ems_tx_urb_context *context = NULL;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct ems_cpc_msg *msg;
+ struct urb *urb;
+ u8 *buf;
+ int i, err;
+ size_t size = CPC_HEADER_SIZE + CPC_MSG_HEADER_LEN
+ + sizeof(struct cpc_can_msg);
+
+ /* create a URB, and a buffer for it, and copy the data to the URB */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ dev_err(netdev->dev.parent, "No memory left for URBs\n");
+ goto nomem;
+ }
+
+ buf = usb_buffer_alloc(dev->udev, size, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(netdev->dev.parent, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ goto nomem;
+ }
+
+ msg = (struct ems_cpc_msg *)&buf[CPC_HEADER_SIZE];
+
+ msg->msg.can_msg.id = cf->can_id & 0x1FFFFFFFU;
+ msg->msg.can_msg.length = cf->can_dlc;
+
+ if (cf->can_id & CAN_RTR_FLAG) {
+ msg->type = cf->can_id & CAN_EFF_FLAG ?
+ CPC_CMD_TYPE_EXT_RTR_FRAME : CPC_CMD_TYPE_RTR_FRAME;
+
+ msg->length = CPC_CAN_MSG_MIN_SIZE;
+ } else {
+ msg->type = cf->can_id & CAN_EFF_FLAG ?
+ CPC_CMD_TYPE_EXT_CAN_FRAME : CPC_CMD_TYPE_CAN_FRAME;
+
+ for (i = 0; i < cf->can_dlc; i++)
+ msg->msg.can_msg.msg[i] = cf->data[i];
+
+ msg->length = CPC_CAN_MSG_MIN_SIZE + cf->can_dlc;
+ }
+
+ for (i = 0; i < MAX_TX_URBS; i++) {
+ if (dev->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &dev->tx_contexts[i];
+ break;
+ }
+ }
+
+ /*
+ * May never happen! When this happens we'd more URBs in flight as
+ * allowed (MAX_TX_URBS).
+ */
+ if (!context) {
+ usb_unanchor_urb(urb);
+ usb_buffer_free(dev->udev, size, buf, urb->transfer_dma);
+
+ dev_warn(netdev->dev.parent, "couldn't find free context\n");
+
+ return NETDEV_TX_BUSY;
+ }
+
+ context->dev = dev;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), buf,
+ size, ems_usb_write_bulk_callback, context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &dev->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ usb_unanchor_urb(urb);
+ usb_buffer_free(dev->udev, size, buf, urb->transfer_dma);
+ dev_kfree_skb(skb);
+
+ if (err == -ENODEV) {
+ netif_device_detach(netdev);
+ } else {
+ dev_warn(netdev->dev.parent, "failed tx_urb %d\n", err);
+
+ stats->tx_dropped++;
+ }
+ } else {
+ atomic_inc(&dev->active_tx_urbs);
+
+ netdev->trans_start = jiffies;
+
+ /* Slow down tx path */
+ if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS ||
+ dev->free_slots < 5) {
+ netif_stop_queue(netdev);
+ return NETDEV_TX_OK;
+ }
+ }
+
+ /*
+ * Release our reference to this URB, the USB core will eventually free
+ * it entirely.
+ */
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+nomem:
+ if (skb)
+ dev_kfree_skb(skb);
+
+ stats->tx_dropped++;
+
+ return NETDEV_TX_OK;
+}
+
+static int ems_usb_close(struct net_device *netdev)
+{
+ struct ems_usb *dev = netdev_priv(netdev);
+
+ netif_stop_queue(netdev);
+
+ /* Stop polling */
+ unlink_all_urbs(dev);
+
+ /* Set CAN controller to reset mode */
+ if (ems_usb_write_mode(dev, SJA1000_MOD_RM))
+ dev_warn(netdev->dev.parent, "couldn't stop device");
+
+ close_candev(netdev);
+
+ dev->open_time = 0;
+
+ return 0;
+}
+
+static const struct net_device_ops ems_usb_netdev_ops = {
+ .ndo_open = ems_usb_open,
+ .ndo_stop = ems_usb_close,
+ .ndo_start_xmit = ems_usb_start_xmit,
+};
+
+static struct can_bittiming_const ems_usb_bittiming_const = {
+ .name = "ems_usb",
+ .tseg1_min = 1,
+ .tseg1_max = 16,
+ .tseg2_min = 1,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+ struct ems_usb *dev = netdev_priv(netdev);
+
+ if (!dev->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL))
+ dev_warn(netdev->dev.parent, "couldn't start device");
+
+ if (netif_queue_stopped(netdev))
+ netif_wake_queue(netdev);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int ems_usb_set_bittiming(struct net_device *netdev)
+{
+ struct ems_usb *dev = netdev_priv(netdev);
+ struct can_bittiming *bt = &dev->can.bittiming;
+ u8 btr0, btr1;
+
+ btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+ btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+ (((bt->phase_seg2 - 1) & 0x7) << 4);
+ if (dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ btr1 |= 0x80;
+
+ dev_info(netdev->dev.parent, "setting BTR0=0x%02x BTR1=0x%02x\n",
+ btr0, btr1);
+
+ dev->active_params.msg.can_params.cc_params.sja1000.btr0 = btr0;
+ dev->active_params.msg.can_params.cc_params.sja1000.btr1 = btr1;
+
+ return ems_usb_command_msg(dev, &dev->active_params);
+}
+
+
+static void init_params_sja1000(struct ems_cpc_msg *msg)
+{
+ struct cpc_sja1000_params *sja1000 =
+ &msg->msg.can_params.cc_params.sja1000;
+
+ msg->type = CPC_CMD_TYPE_CAN_PARAMS;
+ msg->length = sizeof(struct cpc_can_params);
+ msg->msgid = 0;
+
+ msg->msg.can_params.cc_type = CPC_CC_TYPE_SJA1000;
+
+ /* Acceptance filter open */
+ sja1000->acc_code0 = 0x00;
+ sja1000->acc_code1 = 0x00;
+ sja1000->acc_code2 = 0x00;
+ sja1000->acc_code3 = 0x00;
+
+ /* Acceptance filter open */
+ sja1000->acc_mask0 = 0xFF;
+ sja1000->acc_mask1 = 0xFF;
+ sja1000->acc_mask2 = 0xFF;
+ sja1000->acc_mask3 = 0xFF;
+
+ sja1000->btr0 = 0;
+ sja1000->btr1 = 0;
+
+ sja1000->outp_contr = SJA1000_DEFAULT_OUTPUT_CONTROL;
+ sja1000->mode = SJA1000_MOD_RM;
+}
+
+/*
+ * probe function for new CPC-USB devices
+ */
+static int ems_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct net_device *netdev;
+ struct ems_usb *dev;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(struct ems_usb));
+ if (!netdev) {
+ dev_err(netdev->dev.parent, "Couldn't alloc candev\n");
+ return -ENOMEM;
+ }
+
+ dev = netdev_priv(netdev);
+
+ dev->udev = interface_to_usbdev(intf);
+ dev->netdev = netdev;
+
+ dev->can.state = CAN_STATE_STOPPED;
+ dev->can.bittiming_const = &ems_usb_bittiming_const;
+ dev->can.do_set_bittiming = ems_usb_set_bittiming;
+ dev->can.do_set_mode = ems_usb_set_mode;
+
+ netdev->flags |= IFF_ECHO; /* we support local echo */
+
+ /*
+ * The device actually uses a 16MHz clock to generate the CAN clock
+ * but it expects SJA1000 bit settings based on 8MHz (is internally
+ * converted).
+ */
+ dev->can.clock.freq = EMS_USB_ARM7_CLOCK;
+
+ netdev->netdev_ops = &ems_usb_netdev_ops;
+
+ netdev->flags |= IFF_ECHO; /* we support local echo */
+
+ init_usb_anchor(&dev->rx_submitted);
+
+ init_usb_anchor(&dev->tx_submitted);
+ atomic_set(&dev->active_tx_urbs, 0);
+
+ for (i = 0; i < MAX_TX_URBS; i++)
+ dev->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->intr_urb) {
+ dev_err(netdev->dev.parent, "Couldn't alloc intr URB\n");
+ free_candev(netdev);
+ return -ENOMEM;
+ }
+
+ dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE +
+ sizeof(struct ems_cpc_msg), GFP_KERNEL);
+ if (!dev->tx_msg_buffer) {
+ dev_err(netdev->dev.parent, "Couldn't alloc Tx buffer\n");
+ free_candev(netdev);
+ usb_free_urb(dev->intr_urb);
+ return -ENOMEM;
+ }
+
+ usb_set_intfdata(intf, dev);
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ init_params_sja1000(&dev->active_params);
+
+ err = ems_usb_command_msg(dev, &dev->active_params);
+ if (err) {
+ dev_err(netdev->dev.parent,
+ "couldn't initialize controller: %d\n", err);
+ free_candev(netdev);
+ usb_free_urb(dev->intr_urb);
+ return err;
+ }
+
+ return register_candev(netdev);
+}
+
+/*
+ * called by the usb core when the device is removed from the system
+ */
+static void ems_usb_disconnect(struct usb_interface *intf)
+{
+ struct ems_usb *dev = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (dev) {
+ unregister_netdev(dev->netdev);
+ free_candev(dev->netdev);
+
+ unlink_all_urbs(dev);
+
+ usb_free_urb(dev->intr_urb);
+ }
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver ems_usb_driver = {
+ .name = "ems_usb",
+ .probe = ems_usb_probe,
+ .disconnect = ems_usb_disconnect,
+ .id_table = ems_usb_table,
+};
+
+static int __init ems_usb_init(void)
+{
+ int err;
+
+ printk(KERN_INFO "CPC-USB kernel driver loaded\n");
+
+ /* register this driver with the USB subsystem */
+ err = usb_register(&ems_usb_driver);
+
+ if (err) {
+ err("usb_register failed. Error number %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit ems_usb_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&ems_usb_driver);
+}
+
+module_init(ems_usb_init);
+module_exit(ems_usb_exit);
--
EMS Dr. Thomas Wuensche e.K.
Sonnenhang 3
85304 Ilmmuenster
HRA Neuburg a.d. Donau, HR-Nr. 70.106
Phone: +49-8441-490260
Fax : +49-8441-81860
http://www.ems-wuensche.com
^ permalink raw reply related
* [PATCH 1/2] cpc-usb: Removed driver from staging tree
From: Sebastian Haas @ 2009-09-11 10:54 UTC (permalink / raw)
To: netdev; +Cc: greg, wg, oliver, socketcan-core
This patch prepares replacing the staging driver cpc-usb with the new
developed ems_usb CAN driver.
Signed-off-by: Sebastian Haas <haas@ems-wuensche.com>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/cpc-usb/Kconfig | 4
drivers/staging/cpc-usb/Makefile | 3
drivers/staging/cpc-usb/TODO | 9
drivers/staging/cpc-usb/cpc-usb_drv.c | 1185 ---------------------------------
drivers/staging/cpc-usb/cpc.h | 440 ------------
drivers/staging/cpc-usb/cpc_int.h | 83 --
drivers/staging/cpc-usb/cpcusb.h | 86 --
drivers/staging/cpc-usb/sja2m16c.h | 41 -
drivers/staging/cpc-usb/sja2m16c_2.c | 452 -------------
11 files changed, 0 insertions(+), 2306 deletions(-)
delete mode 100644 drivers/staging/cpc-usb/Kconfig
delete mode 100644 drivers/staging/cpc-usb/Makefile
delete mode 100644 drivers/staging/cpc-usb/TODO
delete mode 100644 drivers/staging/cpc-usb/cpc-usb_drv.c
delete mode 100644 drivers/staging/cpc-usb/cpc.h
delete mode 100644 drivers/staging/cpc-usb/cpc_int.h
delete mode 100644 drivers/staging/cpc-usb/cpcusb.h
delete mode 100644 drivers/staging/cpc-usb/sja2m16c.h
delete mode 100644 drivers/staging/cpc-usb/sja2m16c_2.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 975ecdd..5e4b865 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -121,8 +121,6 @@ source "drivers/staging/serqt_usb2/Kconfig"
source "drivers/staging/vt6655/Kconfig"
-source "drivers/staging/cpc-usb/Kconfig"
-
source "drivers/staging/pata_rdc/Kconfig"
source "drivers/staging/udlfb/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 2241ae1..ede1599 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -42,6 +42,5 @@ obj-$(CONFIG_LINE6_USB) += line6/
obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
obj-$(CONFIG_VT6655) += vt6655/
-obj-$(CONFIG_USB_CPC) += cpc-usb/
obj-$(CONFIG_RDC_17F3101X) += pata_rdc/
obj-$(CONFIG_FB_UDL) += udlfb/
diff --git a/drivers/staging/cpc-usb/Kconfig b/drivers/staging/cpc-usb/Kconfig
deleted file mode 100644
index 2be0bc9..0000000
--- a/drivers/staging/cpc-usb/Kconfig
+++ /dev/null
@@ -1,4 +0,0 @@
-config USB_CPC
- tristate "CPC CAN USB driver"
- depends on USB && PROC_FS
- default n
diff --git a/drivers/staging/cpc-usb/Makefile b/drivers/staging/cpc-usb/Makefile
deleted file mode 100644
index 3f83170..0000000
--- a/drivers/staging/cpc-usb/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_USB_CPC) += cpc-usb.o
-
-cpc-usb-y := cpc-usb_drv.o sja2m16c_2.o
diff --git a/drivers/staging/cpc-usb/TODO b/drivers/staging/cpc-usb/TODO
deleted file mode 100644
index 000e8bb..0000000
--- a/drivers/staging/cpc-usb/TODO
+++ /dev/null
@@ -1,9 +0,0 @@
-Things to do for this driver to get merged into the main portion of the
-kernel:
- - checkpatch cleanups
- - sparse clean
- - remove proc code
- - tie into CAN socket interfaces if possible
- - figure out sane userspace api
-
-Send patches to Greg Kroah-Hartman <greg@kroah.com>
diff --git a/drivers/staging/cpc-usb/cpc-usb_drv.c b/drivers/staging/cpc-usb/cpc-usb_drv.c
deleted file mode 100644
index 9bf3f98..0000000
--- a/drivers/staging/cpc-usb/cpc-usb_drv.c
+++ /dev/null
@@ -1,1185 +0,0 @@
-/*
- * CPC-USB CAN Interface Kernel Driver
- *
- * Copyright (C) 2004-2009 EMS Dr. Thomas Wuensche
- *
- * 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; version 2 of the License.
- *
- * 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.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/smp_lock.h>
-#include <linux/completion.h>
-#include <asm/uaccess.h>
-#include <linux/usb.h>
-
-#include <linux/version.h>
-
-#include <linux/proc_fs.h>
-
-#include "cpc.h"
-
-#include "cpc_int.h"
-#include "cpcusb.h"
-
-#include "sja2m16c.h"
-
-/* Version Information */
-#define DRIVER_AUTHOR "Sebastian Haas <haas@ems-wuensche.com>"
-#define DRIVER_DESC "CPC-USB Driver for Linux Kernel 2.6"
-#define DRIVER_VERSION CPC_DRIVER_VERSION
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_LICENSE("GPL v2");
-
-/* Define these values to match your devices */
-#define USB_CPCUSB_VENDOR_ID 0x12D6
-
-#define USB_CPCUSB_M16C_PRODUCT_ID 0x0888
-#define USB_CPCUSB_LPC2119_PRODUCT_ID 0x0444
-
-#define CPC_USB_PROC_DIR CPC_PROC_DIR "cpc-usb"
-
-static struct proc_dir_entry *procDir;
-static struct proc_dir_entry *procEntry;
-
-/* Module parameters */
-static int debug;
-module_param(debug, int, S_IRUGO);
-
-/* table of devices that work with this driver */
-static struct usb_device_id cpcusb_table[] = {
- {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_M16C_PRODUCT_ID)},
- {USB_DEVICE(USB_CPCUSB_VENDOR_ID, USB_CPCUSB_LPC2119_PRODUCT_ID)},
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, cpcusb_table);
-
-/* use to prevent kernel panic if driver is unloaded
- * while a programm has still open the device
- */
-DECLARE_WAIT_QUEUE_HEAD(rmmodWq);
-atomic_t useCount;
-
-static CPC_USB_T *CPCUSB_Table[CPC_USB_CARD_CNT] = { 0 };
-static unsigned int CPCUsbCnt;
-
-/* prevent races between open() and disconnect() */
-static DECLARE_MUTEX(disconnect_sem);
-
-/* local function prototypes */
-static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count,
- loff_t *ppos);
-static ssize_t cpcusb_write(struct file *file, const char *buffer,
- size_t count, loff_t *ppos);
-static unsigned int cpcusb_poll(struct file *file, poll_table * wait);
-static int cpcusb_open(struct inode *inode, struct file *file);
-static int cpcusb_release(struct inode *inode, struct file *file);
-
-static int cpcusb_probe(struct usb_interface *interface,
- const struct usb_device_id *id);
-static void cpcusb_disconnect(struct usb_interface *interface);
-
-static void cpcusb_read_bulk_callback(struct urb *urb);
-static void cpcusb_write_bulk_callback(struct urb *urb);
-static void cpcusb_read_interrupt_callback(struct urb *urb);
-
-static int cpcusb_setup_intrep(CPC_USB_T *card);
-
-static struct file_operations cpcusb_fops = {
- /*
- * The owner field is part of the module-locking
- * mechanism. The idea is that the kernel knows
- * which module to increment the use-counter of
- * BEFORE it calls the device's open() function.
- * This also means that the kernel can decrement
- * the use-counter again before calling release()
- * or should the open() function fail.
- */
- .owner = THIS_MODULE,
-
- .read = cpcusb_read,
- .write = cpcusb_write,
- .poll = cpcusb_poll,
- .open = cpcusb_open,
- .release = cpcusb_release,
-};
-
-/*
- * usb class driver info in order to get a minor number from the usb core,
- * and to have the device registered with devfs and the driver core
- */
-static struct usb_class_driver cpcusb_class = {
- .name = "usb/cpc_usb%d",
- .fops = &cpcusb_fops,
- .minor_base = CPC_USB_BASE_MNR,
-};
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver cpcusb_driver = {
- .name = "cpc-usb",
- .probe = cpcusb_probe,
- .disconnect = cpcusb_disconnect,
- .id_table = cpcusb_table,
-};
-
-static int cpcusb_create_info_output(char *buf)
-{
- int i = 0, j;
-
- for (j = 0; j < CPC_USB_CARD_CNT; j++) {
- if (CPCUSB_Table[j]) {
- CPC_USB_T *card = CPCUSB_Table[j];
- CPC_CHAN_T *chan = card->chan;
-
- /* MINOR CHANNELNO BUSNO SLOTNO */
- i += sprintf(&buf[i], "%d %s\n", chan->minor,
- card->serialNumber);
- }
- }
-
- return i;
-}
-
-static int cpcusb_proc_read_info(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- int len = cpcusb_create_info_output(page);
-
- if (len <= off + count)
- *eof = 1;
- *start = page + off;
- len -= off;
- if (len > count)
- len = count;
- if (len < 0)
- len = 0;
-
- return len;
-}
-
-/*
- * Remove CPC-USB and cleanup
- */
-static inline void cpcusb_delete(CPC_USB_T *card)
-{
- if (card) {
- if (card->chan) {
- if (card->chan->buf)
- vfree(card->chan->buf);
-
- if (card->chan->CPCWait_q)
- kfree(card->chan->CPCWait_q);
-
- kfree(card->chan);
- }
-
- CPCUSB_Table[card->idx] = NULL;
- kfree(card);
- }
-}
-
-/*
- * setup the interrupt IN endpoint of a specific CPC-USB device
- */
-static int cpcusb_setup_intrep(CPC_USB_T *card)
-{
- int retval = 0;
- struct usb_endpoint_descriptor *ep;
-
- ep = &card->interface->altsetting[0].endpoint[card->num_intr_in].desc;
-
- card->intr_in_buffer[0] = 0;
- card->free_slots = 15; /* initial size */
-
- /* setup the urb */
- usb_fill_int_urb(card->intr_in_urb, card->udev,
- usb_rcvintpipe(card->udev, card->num_intr_in),
- card->intr_in_buffer,
- sizeof(card->intr_in_buffer),
- cpcusb_read_interrupt_callback,
- card,
- ep->bInterval);
-
- card->intr_in_urb->status = 0; /* needed! */
-
- /* submit the urb */
- retval = usb_submit_urb(card->intr_in_urb, GFP_KERNEL);
-
- if (retval)
- err("%s - failed submitting intr urb, error %d", __func__,
- retval);
-
- return retval;
-}
-
-static int cpcusb_open(struct inode *inode, struct file *file)
-{
- CPC_USB_T *card = NULL;
- struct usb_interface *interface;
- int subminor;
- int j, retval = 0;
-
- subminor = iminor(inode);
-
- /* prevent disconnects */
- down(&disconnect_sem);
-
- interface = usb_find_interface(&cpcusb_driver, subminor);
- if (!interface) {
- err("%s - error, can't find device for minor %d",
- __func__, subminor);
- retval = CPC_ERR_NO_INTERFACE_PRESENT;
- goto exit_no_device;
- }
-
- card = usb_get_intfdata(interface);
- if (!card) {
- retval = CPC_ERR_NO_INTERFACE_PRESENT;
- goto exit_no_device;
- }
-
- /* lock this device */
- down(&card->sem);
-
- /* increment our usage count for the driver */
- if (card->open) {
- dbg("device already opened");
- retval = CPC_ERR_CHANNEL_ALREADY_OPEN;
- goto exit_on_error;
- }
-
- /* save our object in the file's private structure */
- file->private_data = card;
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- usb_fill_bulk_urb(card->urbs[j].urb, card->udev,
- usb_rcvbulkpipe(card->udev, card->num_bulk_in),
- card->urbs[j].buffer, card->urbs[j].size,
- cpcusb_read_bulk_callback, card);
-
- retval = usb_submit_urb(card->urbs[j].urb, GFP_KERNEL);
-
- if (retval) {
- err("%s - failed submitting read urb, error %d",
- __func__, retval);
- retval = CPC_ERR_TRANSMISSION_FAILED;
- goto exit_on_error;
- }
- }
-
- info("%s - %d URB's submitted", __func__, j);
-
- ResetBuffer(card->chan);
-
- cpcusb_setup_intrep(card);
- card->open = 1;
-
- atomic_inc(&useCount);
-
-exit_on_error:
- /* unlock this device */
- up(&card->sem);
-
-exit_no_device:
- up(&disconnect_sem);
-
- return retval;
-}
-
-static unsigned int cpcusb_poll(struct file *file, poll_table * wait)
-{
- CPC_USB_T *card = (CPC_USB_T *) file->private_data;
- unsigned int retval = 0;
-
- if (!card) {
- err("%s - device object lost", __func__);
- return -EIO;
- }
-
- poll_wait(file, card->chan->CPCWait_q, wait);
-
- if (IsBufferNotEmpty(card->chan) || !(card->present))
- retval |= (POLLIN | POLLRDNORM);
-
- if (card->free_slots)
- retval |= (POLLOUT | POLLWRNORM);
-
- return retval;
-}
-
-static int cpcusb_release(struct inode *inode, struct file *file)
-{
- CPC_USB_T *card = (CPC_USB_T *) file->private_data;
- int j, retval = 0;
-
- if (card == NULL) {
- dbg("%s - object is NULL", __func__);
- return CPC_ERR_NO_INTERFACE_PRESENT;
- }
-
- /* lock our device */
- down(&card->sem);
-
- if (!card->open) {
- dbg("%s - device not opened", __func__);
- retval = CPC_ERR_NO_INTERFACE_PRESENT;
- goto exit_not_opened;
- }
-
- /* if device wasn't unplugged kill all urbs */
- if (card->present) {
- /* kill read urbs */
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- usb_kill_urb(card->urbs[j].urb);
- }
-
- /* kill irq urb */
- usb_kill_urb(card->intr_in_urb);
-
- /* kill write urbs */
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- if (atomic_read(&card->wrUrbs[j].busy)) {
- usb_kill_urb(card->wrUrbs[j].urb);
- wait_for_completion(&card->wrUrbs[j].finished);
- }
- }
- }
-
- atomic_dec(&useCount);
-
- /* last process detached */
- if (atomic_read(&useCount) == 0) {
- wake_up(&rmmodWq);
- }
-
- if (!card->present && card->open) {
- /* the device was unplugged before the file was released */
- up(&card->sem);
- cpcusb_delete(card);
- return 0;
- }
-
- card->open = 0;
-
-exit_not_opened:
- up(&card->sem);
-
- return 0;
-}
-
-static ssize_t cpcusb_read(struct file *file, char *buffer, size_t count,
- loff_t *ppos)
-{
- CPC_USB_T *card = (CPC_USB_T *) file->private_data;
- CPC_CHAN_T *chan;
- int retval = 0;
-
- if (count < sizeof(CPC_MSG_T))
- return CPC_ERR_UNKNOWN;
-
- /* check if can read from the given address */
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return CPC_ERR_UNKNOWN;
-
- /* lock this object */
- down(&card->sem);
-
- /* verify that the device wasn't unplugged */
- if (!card->present) {
- up(&card->sem);
- return CPC_ERR_NO_INTERFACE_PRESENT;
- }
-
- if (IsBufferEmpty(card->chan)) {
- retval = 0;
- } else {
- chan = card->chan;
-
-#if 0
- /* convert LPC2119 params back to SJA1000 params */
- if (card->deviceRevision >= 0x0200
- && chan->buf[chan->oidx].type == CPC_MSG_T_CAN_PRMS) {
- LPC2119_TO_SJA1000_Params(&chan->buf[chan->oidx]);
- }
-#endif
-
- if (copy_to_user(buffer, &chan->buf[chan->oidx], count) != 0) {
- retval = CPC_ERR_IO_TRANSFER;
- } else {
- chan->oidx = (chan->oidx + 1) % CPC_MSG_BUF_CNT;
- chan->WnR = 1;
- retval = sizeof(CPC_MSG_T);
- }
- }
-/* spin_unlock_irqrestore(&card->slock, flags); */
-
- /* unlock the device */
- up(&card->sem);
-
- return retval;
-}
-
-#define SHIFT 1
-static inline void cpcusb_align_buffer_alignment(unsigned char *buf)
-{
- /* CPC-USB uploads packed bytes. */
- CPC_MSG_T *cpc = (CPC_MSG_T *) buf;
- unsigned int i;
-
- for (i = 0; i < cpc->length + (2 * sizeof(unsigned long)); i++) {
- ((unsigned char *) &cpc->msgid)[1 + i] =
- ((unsigned char *) &cpc->msgid)[1 + SHIFT + i];
- }
-}
-
-static int cpc_get_buffer_count(CPC_CHAN_T *chan)
-{
- /* check the buffer parameters */
- if (chan->iidx == chan->oidx)
- return !chan->WnR ? CPC_MSG_BUF_CNT : 0;
- else if (chan->iidx >= chan->oidx)
- return (chan->iidx - chan->oidx) % CPC_MSG_BUF_CNT;
-
- return (chan->iidx + CPC_MSG_BUF_CNT - chan->oidx) % CPC_MSG_BUF_CNT;
-}
-
-static ssize_t cpcusb_write(struct file *file, const char *buffer,
- size_t count, loff_t *ppos)
-{
- CPC_USB_T *card = (CPC_USB_T *) file->private_data;
- CPC_USB_WRITE_URB_T *wrUrb = NULL;
-
- ssize_t bytes_written = 0;
- int retval = 0;
- int j;
-
- unsigned char *obuf = NULL;
- unsigned char type = 0;
- CPC_MSG_T *info = NULL;
-
- dbg("%s - entered minor %d, count = %zu, present = %d",
- __func__, card->minor, count, card->present);
-
- if (count > sizeof(CPC_MSG_T))
- return CPC_ERR_UNKNOWN;
-
- /* check if can read from the given address */
- if (!access_ok(VERIFY_READ, buffer, count))
- return CPC_ERR_UNKNOWN;
-
- /* lock this object */
- down(&card->sem);
-
- /* verify that the device wasn't unplugged */
- if (!card->present) {
- retval = CPC_ERR_NO_INTERFACE_PRESENT;
- goto exit;
- }
-
- /* verify that we actually have some data to write */
- if (count == 0) {
- dbg("%s - write request of 0 bytes", __func__);
- goto exit;
- }
-
- if (card->free_slots <= 5) {
- info = (CPC_MSG_T *) buffer;
-
- if (info->type != CPC_CMD_T_CLEAR_CMD_QUEUE
- || card->free_slots <= 0) {
- dbg("%s - send buffer full please try again %d",
- __func__, card->free_slots);
- retval = CPC_ERR_CAN_NO_TRANSMIT_BUF;
- goto exit;
- }
- }
-
- /* Find a free write urb */
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- if (!atomic_read(&card->wrUrbs[j].busy)) {
- wrUrb = &card->wrUrbs[j]; /* remember found URB */
- atomic_set(&wrUrb->busy, 1); /* lock this URB */
- init_completion(&wrUrb->finished); /* init completion */
- dbg("WR URB no. %d started", j);
- break;
- }
- }
-
- /* don't found write urb say error */
- if (!wrUrb) {
- dbg("%s - no free send urb available", __func__);
- retval = CPC_ERR_CAN_NO_TRANSMIT_BUF;
- goto exit;
- }
- dbg("URB write req");
-
- obuf = (unsigned char *) wrUrb->urb->transfer_buffer;
-
- /* copy the data from userspace into our transfer buffer;
- * this is the only copy required.
- */
- if (copy_from_user(&obuf[4], buffer, count) != 0) {
- atomic_set(&wrUrb->busy, 0); /* release urb */
- retval = CPC_ERR_IO_TRANSFER;
- goto exit;
- }
-
- /* check if it is a DRIVER information message, so we can
- * response to that message and not the USB
- */
- info = (CPC_MSG_T *) &obuf[4];
-
- bytes_written = 11 + info->length;
- if (bytes_written >= wrUrb->size) {
- retval = CPC_ERR_IO_TRANSFER;
- goto exit;
- }
-
- switch (info->type) {
- case CPC_CMD_T_CLEAR_MSG_QUEUE:
- ResetBuffer(card->chan);
- break;
-
- case CPC_CMD_T_INQ_MSG_QUEUE_CNT:
- retval = cpc_get_buffer_count(card->chan);
- atomic_set(&wrUrb->busy, 0);
-
- goto exit;
-
- case CPC_CMD_T_INQ_INFO:
- if (info->msg.info.source == CPC_INFOMSG_T_DRIVER) {
- /* release urb cause we'll use it for driver
- * information
- */
- atomic_set(&wrUrb->busy, 0);
- if (IsBufferFull(card->chan)) {
- retval = CPC_ERR_IO_TRANSFER;
- goto exit;
- }
-
- /* it is a driver information request message and we have
- * free rx slots to store the response
- */
- type = info->msg.info.type;
- info = &card->chan->buf[card->chan->iidx];
-
- info->type = CPC_MSG_T_INFO;
- info->msg.info.source = CPC_INFOMSG_T_DRIVER;
- info->msg.info.type = type;
-
- switch (type) {
- case CPC_INFOMSG_T_VERSION:
- info->length = strlen(CPC_DRIVER_VERSION) + 2;
- sprintf(info->msg.info.msg, "%s\n",
- CPC_DRIVER_VERSION);
- break;
-
- case CPC_INFOMSG_T_SERIAL:
- info->length = strlen(CPC_DRIVER_SERIAL) + 2;
- sprintf(info->msg.info.msg, "%s\n",
- CPC_DRIVER_SERIAL);
- break;
-
- default:
- info->length = 2;
- info->msg.info.type =
- CPC_INFOMSG_T_UNKNOWN_TYPE;
- }
-
- card->chan->WnR = 0;
- card->chan->iidx =
- (card->chan->iidx + 1) % CPC_MSG_BUF_CNT;
-
- retval = info->length;
- goto exit;
- }
- break;
- case CPC_CMD_T_CAN_PRMS:
- /* Check the controller type. If it's the new CPC-USB, make sure if these are SJA1000 params */
- if (info->msg.canparams.cc_type != SJA1000
- && info->msg.canparams.cc_type != M16C_BASIC
- && (card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID
- && info->msg.canparams.cc_type != SJA1000)) {
- /* don't forget to release the urb */
- atomic_set(&wrUrb->busy, 0);
- retval = CPC_ERR_WRONG_CONTROLLER_TYPE;
- goto exit;
- }
- break;
- }
-
- /* just convert the params if it is an old CPC-USB with M16C controller */
- if (card->productId == USB_CPCUSB_M16C_PRODUCT_ID) {
- /* if it is a parameter message convert it from SJA1000 controller
- * settings to M16C Basic controller settings
- */
- SJA1000_TO_M16C_BASIC_Params((CPC_MSG_T *) &obuf[4]);
- }
-
- /* don't forget the byte alignment */
- cpcusb_align_buffer_alignment(&obuf[4]);
-
- /* setup a the 4 byte header */
- obuf[0] = obuf[1] = obuf[2] = obuf[3] = 0;
-
- /* this urb was already set up, except for this write size */
- wrUrb->urb->transfer_buffer_length = bytes_written + 4;
-
- /* send the data out the bulk port */
- /* a character device write uses GFP_KERNEL,
- unless a spinlock is held */
- retval = usb_submit_urb(wrUrb->urb, GFP_KERNEL);
- if (retval) {
- atomic_set(&wrUrb->busy, 0); /* release urb */
- err("%s - failed submitting write urb, error %d",
- __func__, retval);
- } else {
- retval = bytes_written;
- }
-
-exit:
- /* unlock the device */
- up(&card->sem);
-
- dbg("%s - leaved", __func__);
-
- return retval;
-}
-
-/*
- * callback for interrupt IN urb
- */
-static void cpcusb_read_interrupt_callback(struct urb *urb)
-{
- CPC_USB_T *card = (CPC_USB_T *) urb->context;
- int retval;
- unsigned long flags;
-
- spin_lock_irqsave(&card->slock, flags);
-
- if (!card->present) {
- spin_unlock_irqrestore(&card->slock, flags);
- info("%s - no such device", __func__);
- return;
- }
-
- switch (urb->status) {
- case 0: /* success */
- card->free_slots = card->intr_in_buffer[1];
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* urb was killed */
- spin_unlock_irqrestore(&card->slock, flags);
- dbg("%s - intr urb killed", __func__);
- return;
- default:
- info("%s - nonzero urb status %d", __func__, urb->status);
- break;
- }
-
- retval = usb_submit_urb(urb, GFP_ATOMIC);
- if (retval) {
- err("%s - failed resubmitting intr urb, error %d",
- __func__, retval);
- }
-
- spin_unlock_irqrestore(&card->slock, flags);
- wake_up_interruptible(card->chan->CPCWait_q);
-
- return;
-}
-
-#define UN_SHIFT 1
-#define CPCMSG_HEADER_LEN_FIRMWARE 11
-static inline int cpcusb_unalign_and_copy_buffy(unsigned char *out,
- unsigned char *in)
-{
- unsigned int i, j;
-
- for (i = 0; i < 3; i++)
- out[i] = in[i];
-
- for (j = 0; j < (in[1] + (CPCMSG_HEADER_LEN_FIRMWARE - 3)); j++)
- out[j + i + UN_SHIFT] = in[j + i];
-
- return i + j;
-}
-
-/*
- * callback for bulk IN urb
- */
-static void cpcusb_read_bulk_callback(struct urb *urb)
-{
- CPC_USB_T *card = (CPC_USB_T *) urb->context;
- CPC_CHAN_T *chan;
- unsigned char *ibuf = urb->transfer_buffer;
- int retval, msgCnt, start, again = 0;
- unsigned long flags;
-
- if (!card) {
- err("%s - device object lost", __func__);
- return;
- }
-
- spin_lock_irqsave(&card->slock, flags);
-
- if (!card->present) {
- spin_unlock_irqrestore(&card->slock, flags);
- info("%s - no such device", __func__);
- return;
- }
-
- switch (urb->status) {
- case 0: /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* urb was killed */
- spin_unlock_irqrestore(&card->slock, flags);
- dbg("%s - read urb killed", __func__);
- return;
- default:
- info("%s - nonzero urb status %d", __func__, urb->status);
- break;
- }
-
- if (urb->actual_length) {
- msgCnt = ibuf[0] & ~0x80;
- again = ibuf[0] & 0x80;
-
- /* we have a 4 byte header */
- start = 4;
- chan = card->chan;
- while (msgCnt) {
- if (!(IsBufferFull(card->chan))) {
- start +=
- cpcusb_unalign_and_copy_buffy((unsigned char *)
- &chan->buf[chan->iidx], &ibuf[start]);
-
- if (start > urb->transfer_buffer_length) {
- err("%d > %d", start, urb->transfer_buffer_length);
- break;
- }
-
- chan->WnR = 0;
- chan->iidx = (chan->iidx + 1) % CPC_MSG_BUF_CNT;
- msgCnt--;
- } else {
- break;
- }
- }
- }
-
- usb_fill_bulk_urb(urb, card->udev,
- usb_rcvbulkpipe(card->udev, card->num_bulk_in),
- urb->transfer_buffer,
- urb->transfer_buffer_length,
- cpcusb_read_bulk_callback, card);
-
- retval = usb_submit_urb(urb, GFP_ATOMIC);
-
- if (retval) {
- err("%s - failed resubmitting read urb, error %d", __func__, retval);
- }
-
- spin_unlock_irqrestore(&card->slock, flags);
-
- wake_up_interruptible(card->chan->CPCWait_q);
-}
-
-/*
- * callback for bulk IN urb
- */
-static void cpcusb_write_bulk_callback(struct urb *urb)
-{
- CPC_USB_T *card = (CPC_USB_T *) urb->context;
- unsigned long flags;
- int j;
-
- spin_lock_irqsave(&card->slock, flags);
-
- /* find this urb */
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- if (card->wrUrbs[j].urb == urb) {
- dbg("URB found no. %d", j);
- /* notify anyone waiting that the write has finished */
- complete(&card->wrUrbs[j].finished);
- atomic_set(&card->wrUrbs[j].busy, 0);
- break;
- }
- }
-
- switch (urb->status) {
- case 0: /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* urb was killed */
- spin_unlock_irqrestore(&card->slock, flags);
- dbg("%s - write urb no. %d killed", __func__, j);
- return;
- default:
- info("%s - nonzero urb status %d", __func__, urb->status);
- break;
- }
-
- spin_unlock_irqrestore(&card->slock, flags);
-
- wake_up_interruptible(card->chan->CPCWait_q);
-}
-
-static inline int cpcusb_get_free_slot(void)
-{
- int i;
-
- for (i = 0; i < CPC_USB_CARD_CNT; i++) {
- if (!CPCUSB_Table[i])
- return i;
- }
-
- return -1;
-}
-
-/*
- * probe function for new CPC-USB devices
- */
-static int cpcusb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- CPC_USB_T *card = NULL;
- CPC_CHAN_T *chan = NULL;
-
- struct usb_device *udev = interface_to_usbdev(interface);
- struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
-
- int i, j, retval = -ENOMEM, slot;
-
- slot = cpcusb_get_free_slot();
- if (slot < 0) {
- info("No more devices supported");
- return -ENOMEM;
- }
-
- /* allocate memory for our device state and initialize it */
- card = kzalloc(sizeof(CPC_USB_T), GFP_KERNEL);
- if (!card) {
- err("Out of memory");
- return -ENOMEM;
- }
- CPCUSB_Table[slot] = card;
-
- /* allocate and initialize the channel struct */
- card->chan = kmalloc(sizeof(CPC_CHAN_T), GFP_KERNEL);
- if (!card->chan) {
- kfree(card);
- err("Out of memory");
- return -ENOMEM;
- }
-
- chan = card->chan;
- memset(chan, 0, sizeof(CPC_CHAN_T));
- ResetBuffer(chan);
-
- init_MUTEX(&card->sem);
- spin_lock_init(&card->slock);
-
- card->udev = udev;
- card->interface = interface;
- if (udev->descriptor.iSerialNumber) {
- usb_string(udev, udev->descriptor.iSerialNumber, card->serialNumber,
- 128);
- info("Serial %s", card->serialNumber);
- }
-
- card->productId = udev->descriptor.idProduct;
- info("Product %s",
- card->productId == USB_CPCUSB_LPC2119_PRODUCT_ID ?
- "CPC-USB/ARM7" : "CPC-USB/M16C");
-
- /* set up the endpoint information */
- /* check out the endpoints */
- /* use only the first bulk-in and bulk-out endpoints */
- iface_desc = &interface->altsetting[0];
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
-
- if (!card->num_intr_in &&
- (endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT)) {
- card->intr_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- card->num_intr_in = 1;
-
- if (!card->intr_in_urb) {
- err("No free urbs available");
- goto error;
- }
-
- dbg("intr_in urb %d", card->num_intr_in);
- }
-
- if (!card->num_bulk_in &&
- (endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
- card->num_bulk_in = 2;
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- card->urbs[j].size = endpoint->wMaxPacketSize;
- card->urbs[j].urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!card->urbs[j].urb) {
- err("No free urbs available");
- goto error;
- }
- card->urbs[j].buffer =
- usb_buffer_alloc(udev,
- card->urbs[j].size,
- GFP_KERNEL,
- &card->urbs[j].urb->transfer_dma);
- if (!card->urbs[j].buffer) {
- err("Couldn't allocate bulk_in_buffer");
- goto error;
- }
- }
- info("%s - %d reading URB's allocated",
- __func__, CPC_USB_URB_CNT);
- }
-
- if (!card->num_bulk_out &&
- !(endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
-
- card->num_bulk_out = 2;
-
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- card->wrUrbs[j].size =
- endpoint->wMaxPacketSize;
- card->wrUrbs[j].urb =
- usb_alloc_urb(0, GFP_KERNEL);
- if (!card->wrUrbs[j].urb) {
- err("No free urbs available");
- goto error;
- }
- card->wrUrbs[j].buffer = usb_buffer_alloc(udev,
- card->wrUrbs[j].size, GFP_KERNEL,
- &card->wrUrbs[j].urb->transfer_dma);
-
- if (!card->wrUrbs[j].buffer) {
- err("Couldn't allocate bulk_out_buffer");
- goto error;
- }
-
- usb_fill_bulk_urb(card->wrUrbs[j].urb, udev,
- usb_sndbulkpipe(udev, endpoint->bEndpointAddress),
- card->wrUrbs[j].buffer,
- card->wrUrbs[j].size,
- cpcusb_write_bulk_callback,
- card);
- }
-
- info("%s - %d writing URB's allocated", __func__, CPC_USB_URB_CNT);
- }
- }
-
- if (!(card->num_bulk_in && card->num_bulk_out)) {
- err("Couldn't find both bulk-in and bulk-out endpoints");
- goto error;
- }
-
- /* allow device read, write and ioctl */
- card->present = 1;
-
- /* we can register the device now, as it is ready */
- usb_set_intfdata(interface, card);
- retval = usb_register_dev(interface, &cpcusb_class);
-
- if (retval) {
- /* something prevented us from registering this driver */
- err("Not able to get a minor for this device.");
- usb_set_intfdata(interface, NULL);
- goto error;
- }
-
- card->chan->minor = card->minor = interface->minor;
-
- chan->buf = vmalloc(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT);
- if (chan->buf == NULL) {
- err("Out of memory");
- retval = -ENOMEM;
- goto error;
- }
- info("Allocated memory for %d messages (%lu kbytes)",
- CPC_MSG_BUF_CNT, (long unsigned int)(sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT) / 1000);
- memset(chan->buf, 0, sizeof(CPC_MSG_T) * CPC_MSG_BUF_CNT);
-
- ResetBuffer(chan);
-
- card->chan->CPCWait_q = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
- if (!card->chan->CPCWait_q) {
- err("Out of memory");
- retval = -ENOMEM;
- goto error;
- }
- init_waitqueue_head(card->chan->CPCWait_q);
-
- CPCUSB_Table[slot] = card;
- card->idx = slot;
- CPCUsbCnt++;
-
- /* let the user know what node this device is now attached to */
- info("Device now attached to USB-%d", card->minor);
- return 0;
-
-error:
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- if (card->urbs[j].buffer) {
- usb_buffer_free(card->udev, card->urbs[j].size,
- card->urbs[j].buffer,
- card->urbs[j].urb->transfer_dma);
- card->urbs[j].buffer = NULL;
- }
- if (card->urbs[j].urb) {
- usb_free_urb(card->urbs[j].urb);
- card->urbs[j].urb = NULL;
- }
- }
-
- cpcusb_delete(card);
- return retval;
-}
-
-/*
- * called by the usb core when the device is removed from the system
- */
-static void cpcusb_disconnect(struct usb_interface *interface)
-{
- CPC_USB_T *card = NULL;
- int minor, j;
-
- /* prevent races with open() */
- down(&disconnect_sem);
-
- card = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
-
- down(&card->sem);
-
- /* prevent device read, write and ioctl */
- card->present = 0;
-
- minor = card->minor;
-
- /* free all urbs and their buffers */
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- /* terminate an ongoing write */
- if (atomic_read(&card->wrUrbs[j].busy)) {
- usb_kill_urb(card->wrUrbs[j].urb);
- wait_for_completion(&card->wrUrbs[j].finished);
- }
- usb_buffer_free(card->udev, card->wrUrbs[j].size,
- card->wrUrbs[j].buffer,
- card->wrUrbs[j].urb->transfer_dma);
- usb_free_urb(card->wrUrbs[j].urb);
- }
- info("%d write URBs freed", CPC_USB_URB_CNT);
-
- /* free all urbs and their buffers */
- for (j = 0; j < CPC_USB_URB_CNT; j++) {
- usb_buffer_free(card->udev, card->urbs[j].size,
- card->urbs[j].buffer,
- card->urbs[j].urb->transfer_dma);
- usb_free_urb(card->urbs[j].urb);
- }
- info("%d read URBs freed", CPC_USB_URB_CNT);
- usb_free_urb(card->intr_in_urb);
-
- /* give back our minor */
- usb_deregister_dev(interface, &cpcusb_class);
-
- up(&card->sem);
-
- /* if the device is opened, cpcusb_release will clean this up */
- if (!card->open)
- cpcusb_delete(card);
- else
- wake_up_interruptible(card->chan->CPCWait_q);
-
- up(&disconnect_sem);
-
- CPCUsbCnt--;
- info("USB-%d now disconnected", minor);
-}
-
-static int __init CPCUsb_Init(void)
-{
- int result, i;
-
- info(DRIVER_DESC " v" DRIVER_VERSION);
- info("Build on " __DATE__ " at " __TIME__);
-
- for (i = 0; i < CPC_USB_CARD_CNT; i++)
- CPCUSB_Table[i] = 0;
-
- /* register this driver with the USB subsystem */
- result = usb_register(&cpcusb_driver);
- if (result) {
- err("usb_register failed. Error number %d", result);
- return result;
- }
-
- procDir = proc_mkdir(CPC_USB_PROC_DIR, NULL);
- if (!procDir) {
- err("Could not create proc entry");
- } else {
- procEntry = create_proc_read_entry("info", 0444, procDir,
- cpcusb_proc_read_info,
- NULL);
- if (!procEntry) {
- err("Could not create proc entry %s", CPC_USB_PROC_DIR "/info");
- remove_proc_entry(CPC_USB_PROC_DIR, NULL);
- procDir = NULL;
- }
- }
-
- return 0;
-}
-
-static void __exit CPCUsb_Exit(void)
-{
- wait_event(rmmodWq, !atomic_read(&useCount));
-
- /* deregister this driver with the USB subsystem */
- usb_deregister(&cpcusb_driver);
-
- if (procDir) {
- if (procEntry)
- remove_proc_entry("info", procDir);
- remove_proc_entry(CPC_USB_PROC_DIR, NULL);
- }
-}
-
-module_init(CPCUsb_Init);
-module_exit(CPCUsb_Exit);
diff --git a/drivers/staging/cpc-usb/cpc.h b/drivers/staging/cpc-usb/cpc.h
deleted file mode 100644
index ed8cb34..0000000
--- a/drivers/staging/cpc-usb/cpc.h
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * CPC CAN Interface Definitions
- *
- * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche
- *
- * 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.
- */
-#ifndef CPC_HEADER
-#define CPC_HEADER
-
-// the maximum length of the union members within a CPC_MSG
-// this value can be defined by the customer, but has to be
-// >= 64 bytes
-// however, if not defined before, we set a length of 64 byte
-#if !defined(CPC_MSG_LEN) || (CPC_MSG_LEN < 64)
-#undef CPC_MSG_LEN
-#define CPC_MSG_LEN 64
-#endif
-
-// check the operating system used
-#ifdef _WIN32 // running a Windows OS
-
-// define basic types on Windows platforms
-#ifdef _MSC_VER // Visual Studio
- typedef unsigned __int8 u8;
- typedef unsigned __int16 u16;
- typedef unsigned __int32 u32;
-#else // Borland Compiler
- typedef unsigned char u8;
- typedef unsigned short u16;
- typedef unsigned int u32;
-#endif
- // on Windows OS we use a byte alignment of 1
- #pragma pack(push, 1)
-
- // set the calling conventions for the library function calls
- #define CALL_CONV __stdcall
-#else
- // Kernel headers already define this types
- #ifndef __KERNEL__
- // define basic types
- typedef unsigned char u8;
- typedef unsigned short u16;
- typedef unsigned int u32;
- #endif
-
- // Linux does not use this calling convention
- #define CALL_CONV
-#endif
-
-// Transmission of events from CPC interfaces to PC can be individually
-// controlled per event type. Default state is: don't transmit
-// Control values are constructed by bit-or of Subject and Action
-// and passed to CPC_Control()
-
-// Control-Values for CPC_Control() Command Subject Selection
-#define CONTR_CAN_Message 0x04
-#define CONTR_Busload 0x08
-#define CONTR_CAN_State 0x0C
-#define CONTR_SendAck 0x10
-#define CONTR_Filter 0x14
-#define CONTR_CmdQueue 0x18 // reserved, do not use
-#define CONTR_BusError 0x1C
-
-// Control Command Actions
-#define CONTR_CONT_OFF 0
-#define CONTR_CONT_ON 1
-#define CONTR_SING_ON 2
-// CONTR_SING_ON doesn't change CONTR_CONT_ON state, so it should be
-// read as: transmit at least once
-
-// defines for confirmed request
-#define DO_NOT_CONFIRM 0
-#define DO_CONFIRM 1
-
-// event flags
-#define EVENT_READ 0x01
-#define EVENT_WRITE 0x02
-
-// Messages from CPC to PC contain a message object type field.
-// The following message types are sent by CPC and can be used in
-// handlers, others should be ignored.
-#define CPC_MSG_T_RESYNC 0 // Normally to be ignored
-#define CPC_MSG_T_CAN 1 // CAN data frame
-#define CPC_MSG_T_BUSLOAD 2 // Busload message
-#define CPC_MSG_T_STRING 3 // Normally to be ignored
-#define CPC_MSG_T_CONTI 4 // Normally to be ignored
-#define CPC_MSG_T_MEM 7 // Normally not to be handled
-#define CPC_MSG_T_RTR 8 // CAN remote frame
-#define CPC_MSG_T_TXACK 9 // Send acknowledge
-#define CPC_MSG_T_POWERUP 10 // Power-up message
-#define CPC_MSG_T_CMD_NO 11 // Normally to be ignored
-#define CPC_MSG_T_CAN_PRMS 12 // Actual CAN parameters
-#define CPC_MSG_T_ABORTED 13 // Command aborted message
-#define CPC_MSG_T_CANSTATE 14 // CAN state message
-#define CPC_MSG_T_RESET 15 // used to reset CAN-Controller
-#define CPC_MSG_T_XCAN 16 // XCAN data frame
-#define CPC_MSG_T_XRTR 17 // XCAN remote frame
-#define CPC_MSG_T_INFO 18 // information strings
-#define CPC_MSG_T_CONTROL 19 // used for control of interface/driver behaviour
-#define CPC_MSG_T_CONFIRM 20 // response type for confirmed requests
-#define CPC_MSG_T_OVERRUN 21 // response type for overrun conditions
-#define CPC_MSG_T_KEEPALIVE 22 // response type for keep alive conditions
-#define CPC_MSG_T_CANERROR 23 // response type for bus error conditions
-#define CPC_MSG_T_DISCONNECTED 24 // response type for a disconnected interface
-#define CPC_MSG_T_ERR_COUNTER 25 // RX/TX error counter of CAN controller
-
-#define CPC_MSG_T_FIRMWARE 100 // response type for USB firmware download
-
-// Messages from the PC to the CPC interface contain a command field
-// Most of the command types are wrapped by the library functions and have therefore
-// normally not to be used.
-// However, programmers who wish to circumvent the library and talk directly
-// to the drivers (mainly Linux programmers) can use the following
-// command types:
-
-#define CPC_CMD_T_CAN 1 // CAN data frame
-#define CPC_CMD_T_CONTROL 3 // used for control of interface/driver behaviour
-#define CPC_CMD_T_CAN_PRMS 6 // set CAN parameters
-#define CPC_CMD_T_CLEARBUF 8 // clears input queue; this is depricated, use CPC_CMD_T_CLEAR_MSG_QUEUE instead
-#define CPC_CMD_T_INQ_CAN_PARMS 11 // inquire actual CAN parameters
-#define CPC_CMD_T_FILTER_PRMS 12 // set filter parameter
-#define CPC_CMD_T_RTR 13 // CAN remote frame
-#define CPC_CMD_T_CANSTATE 14 // CAN state message
-#define CPC_CMD_T_XCAN 15 // XCAN data frame
-#define CPC_CMD_T_XRTR 16 // XCAN remote frame
-#define CPC_CMD_T_RESET 17 // used to reset CAN-Controller
-#define CPC_CMD_T_INQ_INFO 18 // miscellanous information strings
-#define CPC_CMD_T_OPEN_CHAN 19 // open a channel
-#define CPC_CMD_T_CLOSE_CHAN 20 // close a channel
-#define CPC_CMD_T_CNTBUF 21 // this is depricated, use CPC_CMD_T_INQ_MSG_QUEUE_CNT instead
-#define CPC_CMD_T_CAN_EXIT 200 // exit the CAN (disable interrupts; reset bootrate; reset output_cntr; mode = 1)
-
-#define CPC_CMD_T_INQ_MSG_QUEUE_CNT CPC_CMD_T_CNTBUF // inquires the count of elements in the message queue
-#define CPC_CMD_T_INQ_ERR_COUNTER 25 // request the CAN controllers error counter
-#define CPC_CMD_T_CLEAR_MSG_QUEUE CPC_CMD_T_CLEARBUF // clear CPC_MSG queue
-#define CPC_CMD_T_CLEAR_CMD_QUEUE 28 // clear CPC_CMD queue
-#define CPC_CMD_T_FIRMWARE 100 // reserved, must not be used
-#define CPC_CMD_T_USB_RESET 101 // reserved, must not be used
-#define CPC_CMD_T_WAIT_NOTIFY 102 // reserved, must not be used
-#define CPC_CMD_T_WAIT_SETUP 103 // reserved, must not be used
-#define CPC_CMD_T_ABORT 255 // Normally not to be used
-
-// definitions for CPC_MSG_T_INFO
-// information sources
-#define CPC_INFOMSG_T_UNKNOWN_SOURCE 0
-#define CPC_INFOMSG_T_INTERFACE 1
-#define CPC_INFOMSG_T_DRIVER 2
-#define CPC_INFOMSG_T_LIBRARY 3
-
-// information types
-#define CPC_INFOMSG_T_UNKNOWN_TYPE 0
-#define CPC_INFOMSG_T_VERSION 1
-#define CPC_INFOMSG_T_SERIAL 2
-
-// definitions for controller types
-#define PCA82C200 1 // Philips basic CAN controller, replaced by SJA1000
-#define SJA1000 2 // Philips basic CAN controller
-#define AN82527 3 // Intel full CAN controller
-#define M16C_BASIC 4 // M16C controller running in basic CAN (not full CAN) mode
-
-// channel open error codes
-#define CPC_ERR_NO_FREE_CHANNEL -1 // no more free space within the channel array
-#define CPC_ERR_CHANNEL_ALREADY_OPEN -2 // the channel is already open
-#define CPC_ERR_CHANNEL_NOT_ACTIVE -3 // access to a channel not active failed
-#define CPC_ERR_NO_DRIVER_PRESENT -4 // no driver at the location searched by the library
-#define CPC_ERR_NO_INIFILE_PRESENT -5 // the library could not find the inifile
-#define CPC_ERR_WRONG_PARAMETERS -6 // wrong parameters in the inifile
-#define CPC_ERR_NO_INTERFACE_PRESENT -7 // 1. The specified interface is not connected
- // 2. The interface (mostly CPC-USB) was disconnected upon operation
-#define CPC_ERR_NO_MATCHING_CHANNEL -8 // the driver couldn't find a matching channel
-#define CPC_ERR_NO_BUFFER_AVAILABLE -9 // the driver couldn't allocate buffer for messages
-#define CPC_ERR_NO_INTERRUPT -10 // the requested interrupt couldn't be claimed
-#define CPC_ERR_NO_MATCHING_INTERFACE -11 // no interface type related to this channel was found
-#define CPC_ERR_NO_RESOURCES -12 // the requested resources could not be claimed
-#define CPC_ERR_SOCKET -13 // error concerning TCP sockets
-
-// init error codes
-#define CPC_ERR_WRONG_CONTROLLER_TYPE -14 // wrong CAN controller type within initialization
-#define CPC_ERR_NO_RESET_MODE -15 // the controller could not be set into reset mode
-#define CPC_ERR_NO_CAN_ACCESS -16 // the CAN controller could not be accessed
-
-// transmit error codes
-#define CPC_ERR_CAN_WRONG_ID -20 // the provided CAN id is too big
-#define CPC_ERR_CAN_WRONG_LENGTH -21 // the provided CAN length is too long
-#define CPC_ERR_CAN_NO_TRANSMIT_BUF -22 // the transmit buffer was occupied
-#define CPC_ERR_CAN_TRANSMIT_TIMEOUT -23 // The message could not be sent within a
- // specified time
-
-// other error codes
-#define CPC_ERR_SERVICE_NOT_SUPPORTED -30 // the requested service is not supported by the interface
-#define CPC_ERR_IO_TRANSFER -31 // a transmission error down to the driver occurred
-#define CPC_ERR_TRANSMISSION_FAILED -32 // a transmission error down to the interface occurred
-#define CPC_ERR_TRANSMISSION_TIMEOUT -33 // a timeout occurred within transmission to the interface
-#define CPC_ERR_OP_SYS_NOT_SUPPORTED -35 // the operating system is not supported
-#define CPC_ERR_UNKNOWN -40 // an unknown error ocurred (mostly IOCTL errors)
-
-#define CPC_ERR_LOADING_DLL -50 // the library 'cpcwin.dll' could not be loaded
-#define CPC_ERR_ASSIGNING_FUNCTION -51 // the specified function could not be assigned
-#define CPC_ERR_DLL_INITIALIZATION -52 // the DLL was not initialized correctly
-#define CPC_ERR_MISSING_LICFILE -55 // the file containing the licenses does not exist
-#define CPC_ERR_MISSING_LICENSE -56 // a required license was not found
-
-// CAN state bit values. Ignore any bits not listed
-#define CPC_CAN_STATE_BUSOFF 0x80
-#define CPC_CAN_STATE_ERROR 0x40
-
-// Mask to help ignore undefined bits
-#define CPC_CAN_STATE_MASK 0xc0
-
-// CAN-Message representation in a CPC_MSG
-// Message object type is CPC_MSG_T_CAN or CPC_MSG_T_RTR
-// or CPC_MSG_T_XCAN or CPC_MSG_T_XRTR
-typedef struct CPC_CAN_MSG {
- u32 id;
- u8 length;
- u8 msg[8];
-} CPC_CAN_MSG_T;
-
-
-// representation of the CAN parameters for the PCA82C200 controller
-typedef struct CPC_PCA82C200_PARAMS {
- u8 acc_code; // Acceptance-code for receive, Standard: 0
- u8 acc_mask; // Acceptance-mask for receive, Standard: 0xff (everything)
- u8 btr0; // Bus-timing register 0
- u8 btr1; // Bus-timing register 1
- u8 outp_contr; // Output-control register
-} CPC_PCA82C200_PARAMS_T;
-
-// representation of the CAN parameters for the SJA1000 controller
-typedef struct CPC_SJA1000_PARAMS {
- u8 mode; // enables single or dual acceptance filtering
- u8 acc_code0; // Acceptance-code for receive, Standard: 0
- u8 acc_code1;
- u8 acc_code2;
- u8 acc_code3;
- u8 acc_mask0; // Acceptance-mask for receive, Standard: 0xff (everything)
- u8 acc_mask1;
- u8 acc_mask2;
- u8 acc_mask3;
- u8 btr0; // Bus-timing register 0
- u8 btr1; // Bus-timing register 1
- u8 outp_contr; // Output-control register
-} CPC_SJA1000_PARAMS_T;
-
-// representation of the CAN parameters for the M16C controller
-// in basic CAN mode (means no full CAN)
-typedef struct CPC_M16C_BASIC_PARAMS {
- u8 con0;
- u8 con1;
- u8 ctlr0;
- u8 ctlr1;
- u8 clk;
- u8 acc_std_code0;
- u8 acc_std_code1;
- u8 acc_ext_code0;
- u8 acc_ext_code1;
- u8 acc_ext_code2;
- u8 acc_ext_code3;
- u8 acc_std_mask0;
- u8 acc_std_mask1;
- u8 acc_ext_mask0;
- u8 acc_ext_mask1;
- u8 acc_ext_mask2;
- u8 acc_ext_mask3;
-} CPC_M16C_BASIC_PARAMS_T;
-
-// CAN params message representation
-typedef struct CPC_CAN_PARAMS {
- u8 cc_type; // represents the controller type
- union {
- CPC_M16C_BASIC_PARAMS_T m16c_basic;
- CPC_SJA1000_PARAMS_T sja1000;
- CPC_PCA82C200_PARAMS_T pca82c200;
- } cc_params;
-} CPC_CAN_PARAMS_T;
-
-// the following structures are slightly different for Windows and Linux
-// To be able to use the 'Select' mechanism with Linux the application
-// needs to know the devices file desciptor.
-// This mechanism is not implemented within Windows and the file descriptor
-// is therefore not needed
-#ifdef _WIN32
-
-// CAN init params message representation
-typedef struct CPC_INIT_PARAMS {
- CPC_CAN_PARAMS_T canparams;
-} CPC_INIT_PARAMS_T;
-
-#else// Linux
-
-// CHAN init params representation
-typedef struct CPC_CHAN_PARAMS {
- int fd;
-} CPC_CHAN_PARAMS_T;
-
-// CAN init params message representation
-typedef struct CPC_INIT_PARAMS {
- CPC_CHAN_PARAMS_T chanparams;
- CPC_CAN_PARAMS_T canparams;
-} CPC_INIT_PARAMS_T;
-
-#endif
-
-// structure for confirmed message handling
-typedef struct CPC_CONFIRM {
- u8 result; // error code
-} CPC_CONFIRM_T;
-
-// structure for information requests
-typedef struct CPC_INFO {
- u8 source; // interface, driver or library
- u8 type; // version or serial number
- char msg[CPC_MSG_LEN - 2]; // string holding the requested information
-} CPC_INFO_T;
-
-// OVERRUN ///////////////////////////////////////
-// In general two types of overrun may occur.
-// A hardware overrun, where the CAN controller
-// lost a message, because the interrupt was
-// not handled before the next messgae comes in.
-// Or a software overrun, where i.e. a received
-// message could not be stored in the CPC_MSG
-// buffer.
-
-// After a software overrun has occurred
-// we wait until we have CPC_OVR_GAP slots
-// free in the CPC_MSG buffer.
-#define CPC_OVR_GAP 10
-
-// Two types of software overrun may occur.
-// A received CAN message or a CAN state event
-// can cause an overrun.
-// Note: A CPC_CMD which would normally store
-// its result immediately in the CPC_MSG
-// queue may fail, because the message queue is full.
-// This will not generate an overrun message, but
-// will halt command execution, until this command
-// is able to store its message in the message queue.
-#define CPC_OVR_EVENT_CAN 0x01
-#define CPC_OVR_EVENT_CANSTATE 0x02
-#define CPC_OVR_EVENT_BUSERROR 0x04
-
-// If the CAN controller lost a message
-// we indicate it with the highest bit
-// set in the count field.
-#define CPC_OVR_HW 0x80
-
-// structure for overrun conditions
-typedef struct {
- u8 event;
- u8 count;
-} CPC_OVERRUN_T;
-
-// CAN errors ////////////////////////////////////
-// Each CAN controller type has different
-// registers to record errors.
-// Therefor a structure containing the specific
-// errors is set up for each controller here
-
-// SJA1000 error structure
-// see the SJA1000 datasheet for detailed
-// explanation of the registers
-typedef struct CPC_SJA1000_CAN_ERROR {
- u8 ecc; // error capture code register
- u8 rxerr; // RX error counter register
- u8 txerr; // TX error counter register
-} CPC_SJA1000_CAN_ERROR_T;
-
-// M16C error structure
-// see the M16C datasheet for detailed
-// explanation of the registers
-typedef struct CPC_M16C_CAN_ERROR {
- u8 tbd; // to be defined
-} CPC_M16C_CAN_ERROR_T;
-
-// structure for CAN error conditions
-#define CPC_CAN_ECODE_ERRFRAME 0x01
-typedef struct CPC_CAN_ERROR {
- u8 ecode;
- struct {
- u8 cc_type; // CAN controller type
- union {
- CPC_SJA1000_CAN_ERROR_T sja1000;
- CPC_M16C_CAN_ERROR_T m16c;
- } regs;
- } cc;
-} CPC_CAN_ERROR_T;
-
-// Structure containing RX/TX error counter.
-// This structure is used to request the
-// values of the CAN controllers TX and RX
-// error counter.
-typedef struct CPC_CAN_ERR_COUNTER {
- u8 rx;
- u8 tx;
-} CPC_CAN_ERR_COUNTER_T;
-
-// If this flag is set, transmissions from PC to CPC are protected against loss
-#define CPC_SECURE_TO_CPC 0x01
-
-// If this flag is set, transmissions from CPC to PC are protected against loss
-#define CPC_SECURE_TO_PC 0x02
-
-// If this flag is set, the CAN-transmit buffer is checked to be free before sending a message
-#define CPC_SECURE_SEND 0x04
-
-// If this flag is set, the transmission complete flag is checked
-// after sending a message
-// THIS IS CURRENTLY ONLY IMPLEMENTED IN THE PASSIVE INTERFACE DRIVERS
-#define CPC_SECURE_TRANSMIT 0x08
-
-// main message type used between library and application
-typedef struct CPC_MSG {
- u8 type; // type of message
- u8 length; // length of data within union 'msg'
- u8 msgid; // confirmation handle
- u32 ts_sec; // timestamp in seconds
- u32 ts_nsec; // timestamp in nano seconds
- union {
- u8 generic[CPC_MSG_LEN];
- CPC_CAN_MSG_T canmsg;
- CPC_CAN_PARAMS_T canparams;
- CPC_CONFIRM_T confirmation;
- CPC_INFO_T info;
- CPC_OVERRUN_T overrun;
- CPC_CAN_ERROR_T error;
- CPC_CAN_ERR_COUNTER_T err_counter;
- u8 busload;
- u8 canstate;
- } msg;
-} CPC_MSG_T;
-
-#ifdef _WIN32
-#pragma pack(pop) // reset the byte alignment
-#endif
-
-#endif // CPC_HEADER
diff --git a/drivers/staging/cpc-usb/cpc_int.h b/drivers/staging/cpc-usb/cpc_int.h
deleted file mode 100644
index a0d60c0..0000000
--- a/drivers/staging/cpc-usb/cpc_int.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * CPCLIB
- *
- * Copyright (C) 2000-2008 EMS Dr. Thomas Wuensche
- *
- * 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.
- *
- */
-#ifndef CPC_INT_H
-#define CPC_INT_H
-
-#include <linux/wait.h>
-
-#define CPC_MSG_BUF_CNT 1500
-
-#define CPC_PROC_DIR "driver/"
-
-#undef dbg
-#undef err
-#undef info
-
-/* Use our own dbg macro */
-#define dbg(format, arg...) do { if (debug) printk( KERN_INFO format "\n" , ## arg); } while (0)
-#define err(format, arg...) do { printk( KERN_INFO "ERROR " format "\n" , ## arg); } while (0)
-#define info(format, arg...) do { printk( KERN_INFO format "\n" , ## arg); } while (0)
-
-/* Macros help using of our buffers */
-#define IsBufferFull(x) (!(x)->WnR) && ((x)->iidx == (x)->oidx)
-#define IsBufferEmpty(x) ((x)->WnR) && ((x)->iidx == (x)->oidx)
-#define IsBufferNotEmpty(x) (!(x)->WnR) || ((x)->iidx != (x)->oidx)
-#define ResetBuffer(x) do { (x)->oidx = (x)->iidx=0; (x)->WnR = 1; } while(0);
-
-#define CPC_BufWriteAllowed ((chan->oidx != chan->iidx) || chan->WnR)
-
-typedef void (*chan_write_byte_t) (void *chan, unsigned int reg,
- unsigned char val);
-typedef unsigned char (*chan_read_byte_t) (void *chan, unsigned int reg);
-
-typedef struct CPC_CHAN {
- void __iomem * canBase; // base address of SJA1000
- chan_read_byte_t read_byte; // CAN controller read access routine
- chan_write_byte_t write_byte; // CAN controller write access routine
- CPC_MSG_T *buf; // buffer for CPC msg
- unsigned int iidx;
- unsigned int oidx;
- unsigned int WnR;
- unsigned int minor;
- unsigned int locked;
- unsigned int irqDisabled;
-
- unsigned char cpcCtrlCANMessage;
- unsigned char cpcCtrlCANState;
- unsigned char cpcCtrlBUSState;
-
- unsigned char controllerType;
-
- unsigned long ovrTimeSec;
- unsigned long ovrTimeNSec;
- unsigned long ovrLockedBuffer;
- CPC_OVERRUN_T ovr;
-
- /* for debugging only */
- unsigned int handledIrqs;
- unsigned int lostMessages;
-
- unsigned int sentStdCan;
- unsigned int sentExtCan;
- unsigned int sentStdRtr;
- unsigned int sentExtRtr;
-
- unsigned int recvStdCan;
- unsigned int recvExtCan;
- unsigned int recvStdRtr;
- unsigned int recvExtRtr;
-
- wait_queue_head_t *CPCWait_q;
-
- void *private;
-} CPC_CHAN_T;
-
-#endif
diff --git a/drivers/staging/cpc-usb/cpcusb.h b/drivers/staging/cpc-usb/cpcusb.h
deleted file mode 100644
index e5273dd..0000000
--- a/drivers/staging/cpc-usb/cpcusb.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* Header for CPC-USB Driver ********************
- * Copyright 1999, 2000, 2001
- *
- * Company: EMS Dr. Thomas Wuensche
- * Sonnenhang 3
- * 85304 Ilmmuenster
- * Phone: +49-8441-490260
- * Fax: +49-8441-81860
- * email: support@ems-wuensche.com
- * WWW: www.ems-wuensche.com
- */
-
-#ifndef CPCUSB_H
-#define CPCUSB_H
-
-#undef err
-#undef dbg
-#undef info
-
-/* Use our own dbg macro */
-#define dbg(format, arg...) do { if (debug) printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0)
-#define info(format, arg...) do { printk(KERN_INFO "CPC-USB: " format "\n" , ## arg); } while (0)
-#define err(format, arg...) do { printk(KERN_INFO "CPC-USB(ERROR): " format "\n" , ## arg); } while (0)
-
-#define CPC_USB_CARD_CNT 4
-
-typedef struct CPC_USB_READ_URB {
- unsigned char *buffer; /* the buffer to send data */
- size_t size; /* the size of the send buffer */
- struct urb *urb; /* the urb used to send data */
-} CPC_USB_READ_URB_T;
-
-typedef struct CPC_USB_WRITE_URB {
- unsigned char *buffer; /* the buffer to send data */
- size_t size; /* the size of the send buffer */
- struct urb *urb; /* the urb used to send data */
- atomic_t busy; /* true if write urb is busy */
- struct completion finished; /* wait for the write to finish */
-} CPC_USB_WRITE_URB_T;
-
-#define CPC_USB_URB_CNT 10
-
-typedef struct CPC_USB {
- struct usb_device *udev; /* save off the usb device pointer */
- struct usb_interface *interface; /* the interface for this device */
- unsigned char minor; /* the starting minor number for this device */
- unsigned char num_ports; /* the number of ports this device has */
- int num_intr_in; /* number of interrupt in endpoints we have */
- int num_bulk_in; /* number of bulk in endpoints we have */
- int num_bulk_out; /* number of bulk out endpoints we have */
-
- CPC_USB_READ_URB_T urbs[CPC_USB_URB_CNT];
-
- unsigned char intr_in_buffer[4]; /* interrupt transfer buffer */
- struct urb *intr_in_urb; /* interrupt transfer urb */
-
- CPC_USB_WRITE_URB_T wrUrbs[CPC_USB_URB_CNT];
-
- int open; /* if the port is open or not */
- int present; /* if the device is not disconnected */
- struct semaphore sem; /* locks this structure */
-
- int free_slots; /* free send slots of CPC-USB */
- int idx;
-
- spinlock_t slock;
-
- char serialNumber[128]; /* serial number */
- int productId; /* product id to differ between M16C and LPC2119 */
- CPC_CHAN_T *chan;
-} CPC_USB_T;
-
-#define CPCTable CPCUSB_Table
-
-#define CPC_DRIVER_VERSION "0.724"
-#define CPC_DRIVER_SERIAL "not applicable"
-
-#define OBUF_SIZE 255 // 4096
-
-/* read timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */
-#define RD_NAK_TIMEOUT (10*HZ) /* Default number of X seconds to wait */
-#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */
-
-#define CPC_USB_BASE_MNR 0 /* CPC-USB start at minor 0 */
-
-#endif
diff --git a/drivers/staging/cpc-usb/sja2m16c.h b/drivers/staging/cpc-usb/sja2m16c.h
deleted file mode 100644
index 654bd3f..0000000
--- a/drivers/staging/cpc-usb/sja2m16c.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef _SJA2M16C_H
-#define _SJA2M16C_H
-
-#include "cpc.h"
-
-#define BAUDRATE_TOLERANCE_PERCENT 1
-#define SAMPLEPOINT_TOLERANCE_PERCENT 5
-#define SAMPLEPOINT_UPPER_LIMIT 88
-
-/* M16C parameters */
-struct FIELD_C0CONR {
- unsigned int brp:4;
- unsigned int sam:1;
- unsigned int pr:3;
- unsigned int dummy:8;
-};
-struct FIELD_C1CONR {
- unsigned int ph1:3;
- unsigned int ph2:3;
- unsigned int sjw:2;
- unsigned int dummy:8;
-};
-typedef union C0CONR {
- unsigned char c0con;
- struct FIELD_C0CONR bc0con;
-} C0CONR_T;
-typedef union C1CONR {
- unsigned char c1con;
- struct FIELD_C1CONR bc1con;
-} C1CONR_T;
-
-#define SJA_TSEG1 ((pParams->btr1 & 0x0f)+1)
-#define SJA_TSEG2 (((pParams->btr1 & 0x70)>>4)+1)
-#define SJA_BRP ((pParams->btr0 & 0x3f)+1)
-#define SJA_SJW ((pParams->btr0 & 0xc0)>>6)
-#define SJA_SAM ((pParams->btr1 & 0x80)>>7)
-int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2);
-int samplepoint_m16c(int brp, int pr, int ph1, int ph2);
-int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T *pMsg);
-
-#endif
diff --git a/drivers/staging/cpc-usb/sja2m16c_2.c b/drivers/staging/cpc-usb/sja2m16c_2.c
deleted file mode 100644
index bf0230f..0000000
--- a/drivers/staging/cpc-usb/sja2m16c_2.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/****************************************************************************
-*
-* Copyright (c) 2003,2004 by EMS Dr. Thomas Wuensche
-*
-* - All rights reserved -
-*
-* This code is provided "as is" without warranty of any kind, either
-* expressed or implied, including but not limited to the liability
-* concerning the freedom from material defects, the fitness for parti-
-* cular purposes or the freedom of proprietary rights of third parties.
-*
-*****************************************************************************
-* Module name.: cpcusb
-*****************************************************************************
-* Include file: cpc.h
-*****************************************************************************
-* Project.....: Windows Driver Development Kit
-* Filename....: sja2m16c.cpp
-* Authors.....: (GU) Gerhard Uttenthaler
-* (CS) Christian Schoett
-*****************************************************************************
-* Short descr.: converts baudrate between SJA1000 and M16C
-*****************************************************************************
-* Description.: handles the baudrate conversion from SJA1000 parameters to
-* M16C parameters
-*****************************************************************************
-* Address : EMS Dr. Thomas Wuensche
-* Sonnenhang 3
-* D-85304 Ilmmuenster
-* Tel. : +49-8441-490260
-* Fax. : +49-8441-81860
-* email: support@ems-wuensche.com
-*****************************************************************************
-* History
-*****************************************************************************
-* Version Date Auth Remark
-*
-* 01.00 ?? GU - initial release
-* 01.10 ?????????? CS - adapted to fit into the USB Windows driver
-* 02.00 18.08.2004 GU - improved the baudrate calculating algorithm
-* - implemented acceptance filtering
-* 02.10 10.09.2004 CS - adapted to fit into the USB Windows driver
-*****************************************************************************
-* ToDo's
-*****************************************************************************
-*/
-
-/****************************************************************************/
-/* I N C L U D E S
-*/
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/smp_lock.h>
-#include <linux/completion.h>
-#include <asm/uaccess.h>
-#include <linux/usb.h>
-
-#include "cpc.h"
-#include "cpc_int.h"
-#include "cpcusb.h"
-
-#include "sja2m16c.h"
-
-/*********************************************************************/
-int baudrate_m16c(int clk, int brp, int pr, int ph1, int ph2)
-{
- return (16000000 / (1 << clk)) / 2 / (brp + 1) / (1 + pr + 1 +
- ph1 + 1 + ph2 +
- 1);
-}
-
-
-/*********************************************************************/
-int samplepoint_m16c(int brp, int pr, int ph1, int ph2)
-{
- return (100 * (1 + pr + 1 + ph1 + 1)) / (1 + pr + 1 + ph1 + 1 +
- ph2 + 1);
-}
-
-
-/****************************************************************************
-* Function.....: SJA1000_TO_M16C_BASIC_Params
-*
-* Task.........: This routine converts SJA1000 CAN btr parameters into M16C
-* parameters based on the sample point and the error. In
-* addition it converts the acceptance filter parameters to
-* suit the M16C parameters
-*
-* Parameters...: None
-*
-* Return values: None
-*
-* Comments.....:
-*****************************************************************************
-* History
-*****************************************************************************
-* 19.01.2005 CS - modifed the conversion of SJA1000 filter params into
-* M16C params. Due to compatibility reasons with the
-* older 82C200 CAN controller the SJA1000
-****************************************************************************/
-int SJA1000_TO_M16C_BASIC_Params(CPC_MSG_T * in)
-{
- int sjaBaudrate;
- int sjaSamplepoint;
- int *baudrate_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7]
- int *samplepoint_error; // BRP[0..15], PR[0..7], PH1[0..7], PH2[0..7]
- int baudrate_error_merk;
- int clk, brp, pr, ph1, ph2;
- int clk_merk, brp_merk, pr_merk, ph1_merk, ph2_merk;
- int index;
- unsigned char acc_code0, acc_code1, acc_code2, acc_code3;
- unsigned char acc_mask0, acc_mask1, acc_mask2, acc_mask3;
- CPC_MSG_T * out;
- C0CONR_T c0con;
- C1CONR_T c1con;
- int tmpAccCode;
- int tmpAccMask;
-
- // we have to convert the parameters into M16C parameters
- CPC_SJA1000_PARAMS_T * pParams;
-
- // check if the type is CAN parameters and if we have to convert the given params
- if (in->type != CPC_CMD_T_CAN_PRMS
- || in->msg.canparams.cc_type != SJA1000)
- return 0;
- pParams =
- (CPC_SJA1000_PARAMS_T *) & in->msg.canparams.cc_params.sja1000;
- acc_code0 = pParams->acc_code0;
- acc_code1 = pParams->acc_code1;
- acc_code2 = pParams->acc_code2;
- acc_code3 = pParams->acc_code3;
- acc_mask0 = pParams->acc_mask0;
- acc_mask1 = pParams->acc_mask1;
- acc_mask2 = pParams->acc_mask2;
- acc_mask3 = pParams->acc_mask3;
-
-#ifdef _DEBUG_OUTPUT_CAN_PARAMS
- info("acc_code0: %2.2Xh\n", acc_code0);
- info("acc_code1: %2.2Xh\n", acc_code1);
- info("acc_code2: %2.2Xh\n", acc_code2);
- info("acc_code3: %2.2Xh\n", acc_code3);
- info("acc_mask0: %2.2Xh\n", acc_mask0);
- info("acc_mask1: %2.2Xh\n", acc_mask1);
- info("acc_mask2: %2.2Xh\n", acc_mask2);
- info("acc_mask3: %2.2Xh\n", acc_mask3);
-
-#endif /* */
- if (!
- (baudrate_error =
- (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) {
- err("Could not allocate memory\n");
- return -3;
- }
- if (!
- (samplepoint_error =
- (int *) vmalloc(sizeof(int) * 16 * 8 * 8 * 8 * 5))) {
- err("Could not allocate memory\n");
- vfree(baudrate_error);
- return -3;
- }
- memset(baudrate_error, 0xff, sizeof(baudrate_error));
- memset(samplepoint_error, 0xff, sizeof(baudrate_error));
- sjaBaudrate =
- 16000000 / 2 / SJA_BRP / (1 + SJA_TSEG1 + SJA_TSEG2);
- sjaSamplepoint =
- 100 * (1 + SJA_TSEG1) / (1 + SJA_TSEG1 + SJA_TSEG2);
- if (sjaBaudrate == 0) {
- vfree(baudrate_error);
- vfree(samplepoint_error);
- return -2;
- }
-
-#ifdef _DEBUG_OUTPUT_CAN_PARAMS
- info("\nStarting SJA CAN params\n");
- info("-------------------------\n");
- info("TS1 : %2.2Xh TS2 : %2.2Xh\n", SJA_TSEG1, SJA_TSEG2);
- info("BTR0 : %2.2Xh BTR1: %2.2Xh\n", pParams->btr0,
- pParams->btr1);
- info("Baudrate: %d.%dkBaud\n", sjaBaudrate / 1000,
- sjaBaudrate % 1000);
- info("Sample P: 0.%d\n", sjaSamplepoint);
- info("\n");
-
-#endif /* */
- c0con.bc0con.sam = SJA_SAM;
- c1con.bc1con.sjw = SJA_SJW;
-
- // calculate errors for all baudrates
- index = 0;
- for (clk = 0; clk < 5; clk++) {
- for (brp = 0; brp < 16; brp++) {
- for (pr = 0; pr < 8; pr++) {
- for (ph1 = 0; ph1 < 8; ph1++) {
- for (ph2 = 0; ph2 < 8; ph2++) {
- baudrate_error[index] =
- 100 *
- abs(baudrate_m16c
- (clk, brp, pr, ph1,
- ph2) -
- sjaBaudrate) /
- sjaBaudrate;
- samplepoint_error[index] =
- abs(samplepoint_m16c
- (brp, pr, ph1,
- ph2) -
- sjaSamplepoint);
-
-#if 0
- info
- ("Baudrate : %d kBaud\n",
- baudrate_m16c(clk,
- brp, pr,
- ph1,
- ph2));
- info
- ("Baudrate Error: %d\n",
- baudrate_error
- [index]);
- info
- ("Sample P Error: %d\n",
- samplepoint_error
- [index]);
- info
- ("clk : %d\n",
- clk);
-
-#endif /* */
- index++;
- }
- }
- }
- }
- }
-
- // mark all baudrate_error entries which are outer limits
- index = 0;
- for (clk = 0; clk < 5; clk++) {
- for (brp = 0; brp < 16; brp++) {
- for (pr = 0; pr < 8; pr++) {
- for (ph1 = 0; ph1 < 8; ph1++) {
- for (ph2 = 0; ph2 < 8; ph2++) {
- if ((baudrate_error[index]
- >
- BAUDRATE_TOLERANCE_PERCENT)
- ||
- (samplepoint_error
- [index] >
- SAMPLEPOINT_TOLERANCE_PERCENT)
- ||
- (samplepoint_m16c
- (brp, pr, ph1,
- ph2) >
- SAMPLEPOINT_UPPER_LIMIT))
- {
- baudrate_error
- [index] = -1;
- } else
- if (((1 + pr + 1 +
- ph1 + 1 + ph2 +
- 1) < 8)
- ||
- ((1 + pr + 1 +
- ph1 + 1 + ph2 +
- 1) > 25)) {
- baudrate_error
- [index] = -1;
- }
-
-#if 0
- else {
- info
- ("Baudrate : %d kBaud\n",
- baudrate_m16c
- (clk, brp, pr,
- ph1, ph2));
- info
- ("Baudrate Error: %d\n",
- baudrate_error
- [index]);
- info
- ("Sample P Error: %d\n",
- samplepoint_error
- [index]);
- }
-
-#endif /* */
- index++;
- }
- }
- }
- }
- }
-
- // find list of minimum of baudrate_error within unmarked entries
- clk_merk = brp_merk = pr_merk = ph1_merk = ph2_merk = 0;
- baudrate_error_merk = 100;
- index = 0;
- for (clk = 0; clk < 5; clk++) {
- for (brp = 0; brp < 16; brp++) {
- for (pr = 0; pr < 8; pr++) {
- for (ph1 = 0; ph1 < 8; ph1++) {
- for (ph2 = 0; ph2 < 8; ph2++) {
- if (baudrate_error[index]
- != -1) {
- if (baudrate_error
- [index] <
- baudrate_error_merk)
- {
- baudrate_error_merk
- =
- baudrate_error
- [index];
- brp_merk =
- brp;
- pr_merk =
- pr;
- ph1_merk =
- ph1;
- ph2_merk =
- ph2;
- clk_merk =
- clk;
-
-#if 0
- info
- ("brp: %2.2Xh pr: %2.2Xh ph1: %2.2Xh ph2: %2.2Xh\n",
- brp,
- pr,
- ph1,
- ph2);
- info
- ("Baudrate : %d kBaud\n",
- baudrate_m16c
- (clk,
- brp,
- pr,
- ph1,
- ph2));
- info
- ("Baudrate Error: %d\n",
- baudrate_error
- [index]);
- info
- ("Sample P Error: %d\n",
- samplepoint_error
- [index]);
-
-#endif /* */
- }
- }
- index++;
- }
- }
- }
- }
- }
- if (baudrate_error_merk == 100) {
- info("ERROR: Could not convert CAN init parameter\n");
- vfree(baudrate_error);
- vfree(samplepoint_error);
- return -1;
- }
-
- // setting m16c CAN parameter
- c0con.bc0con.brp = brp_merk;
- c0con.bc0con.pr = pr_merk;
- c1con.bc1con.ph1 = ph1_merk;
- c1con.bc1con.ph2 = ph2_merk;
-
-#ifdef _DEBUG_OUTPUT_CAN_PARAMS
- info("\nResulting M16C CAN params\n");
- info("-------------------------\n");
- info("clk : %2.2Xh\n", clk_merk);
- info("ph1 : %2.2Xh ph2: %2.2Xh\n", c1con.bc1con.ph1 + 1,
- c1con.bc1con.ph2 + 1);
- info("pr : %2.2Xh brp: %2.2Xh\n", c0con.bc0con.pr + 1,
- c0con.bc0con.brp + 1);
- info("sjw : %2.2Xh sam: %2.2Xh\n", c1con.bc1con.sjw,
- c0con.bc0con.sam);
- info("co1 : %2.2Xh co0: %2.2Xh\n", c1con.c1con, c0con.c0con);
- info("Baudrate: %d.%dBaud\n",
- baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr,
- c1con.bc1con.ph1, c1con.bc1con.ph2) / 1000,
- baudrate_m16c(clk_merk, c0con.bc0con.brp, c0con.bc0con.pr,
- c1con.bc1con.ph1, c1con.bc1con.ph2) % 1000);
- info("Sample P: 0.%d\n",
- samplepoint_m16c(c0con.bc0con.brp, c0con.bc0con.pr,
- c1con.bc1con.ph1, c1con.bc1con.ph2));
- info("\n");
-
-#endif /* */
- out = in;
- out->type = 6;
- out->length = sizeof(CPC_M16C_BASIC_PARAMS_T) + 1;
- out->msg.canparams.cc_type = M16C_BASIC;
- out->msg.canparams.cc_params.m16c_basic.con0 = c0con.c0con;
- out->msg.canparams.cc_params.m16c_basic.con1 = c1con.c1con;
- out->msg.canparams.cc_params.m16c_basic.ctlr0 = 0x4C;
- out->msg.canparams.cc_params.m16c_basic.ctlr1 = 0x00;
- out->msg.canparams.cc_params.m16c_basic.clk = clk_merk;
- out->msg.canparams.cc_params.m16c_basic.acc_std_code0 =
- acc_code0;
- out->msg.canparams.cc_params.m16c_basic.acc_std_code1 = acc_code1;
-
-// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1);
- tmpAccCode = (acc_code1 >> 5) + (acc_code0 << 3);
- out->msg.canparams.cc_params.m16c_basic.acc_std_code0 =
- (unsigned char) tmpAccCode;
- out->msg.canparams.cc_params.m16c_basic.acc_std_code1 =
- (unsigned char) (tmpAccCode >> 8);
-
-// info("code0: 0x%2.2X, code1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_code0, out->msg.canparams.cc_params.m16c_basic.acc_std_code1);
- out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 =
- ~acc_mask0;
- out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 =
- ~acc_mask1;
-
-// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1);
- tmpAccMask = ((acc_mask1) >> 5) + ((acc_mask0) << 3);
-
-// info("tmpAccMask: 0x%4.4X\n", tmpAccMask);
- out->msg.canparams.cc_params.m16c_basic.acc_std_mask0 =
- (unsigned char) ~tmpAccMask;
- out->msg.canparams.cc_params.m16c_basic.acc_std_mask1 =
- (unsigned char) ~(tmpAccMask >> 8);
-
-// info("mask0: 0x%2.2X, mask1: 0x%2.2X\n", out->msg.canparams.cc_params.m16c_basic.acc_std_mask0, out->msg.canparams.cc_params.m16c_basic.acc_std_mask1);
- out->msg.canparams.cc_params.m16c_basic.acc_ext_code0 =
- (unsigned char) tmpAccCode;
- out->msg.canparams.cc_params.m16c_basic.acc_ext_code1 =
- (unsigned char) (tmpAccCode >> 8);
- out->msg.canparams.cc_params.m16c_basic.acc_ext_code2 = acc_code2;
- out->msg.canparams.cc_params.m16c_basic.acc_ext_code3 = acc_code3;
- out->msg.canparams.cc_params.m16c_basic.acc_ext_mask0 =
- (unsigned char) ~tmpAccMask;
- out->msg.canparams.cc_params.m16c_basic.acc_ext_mask1 =
- (unsigned char) ~(tmpAccMask >> 8);
- out->msg.canparams.cc_params.m16c_basic.acc_ext_mask2 =
- ~acc_mask2;
- out->msg.canparams.cc_params.m16c_basic.acc_ext_mask3 =
- ~acc_mask3;
- vfree(baudrate_error);
- vfree(samplepoint_error);
- return 0;
-}
-
-
--
EMS Dr. Thomas Wuensche e.K.
Sonnenhang 3
85304 Ilmmuenster
HRA Neuburg a.d. Donau, HR-Nr. 70.106
Phone: +49-8441-490260
Fax : +49-8441-81860
http://www.ems-wuensche.com
^ permalink raw reply related
* Re: [PATCH 2/2] ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface
From: Wolfgang Grandegger @ 2009-09-11 11:36 UTC (permalink / raw)
To: Sebastian Haas
Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
netdev-u79uwXL29TY76Z2rM5mHXA, oliver-fJ+pQTUTwRTk1uMJSBkQmQ,
greg-U8xfFu+wG4EAvxtiuMwx3w
In-Reply-To: <20090911105455.7815.12354.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
Hi Sebastian,
Sebastian Haas wrote:
> This patch adds support for one channel CAN/USB interace CPC-USB/ARM7 from
> EMS Dr. Thomas Wuensche (http://www.ems-wuensche.com).
>
> Signed-off-by: Sebastian Haas <haas-zsNKPWJ8Pib6hrUXjxyGrA@public.gmane.org>
we reviewed the driver already on the Socket-CAN ML and it's OK from the
Socket-CAN point of view. I will do a quick (re-)check beginning of next
week. I would be nice to have this patch also reviewed by some Linux USB
experts. Therefore I suggest sending it to the relevant Linux-USB
mailing list as well.
Wolfgang.
^ permalink raw reply
* Re: [PATCH 2/2] ems_usb: Added support for EMS CPC-USB/ARM7 CAN/USB interface
From: Oliver Hartkopp @ 2009-09-11 12:07 UTC (permalink / raw)
To: Wolfgang Grandegger; +Cc: Sebastian Haas, netdev, socketcan-core, greg
In-Reply-To: <4AAA362F.30806@grandegger.com>
Wolfgang Grandegger wrote:
> Hi Sebastian,
>
> Sebastian Haas wrote:
>> This patch adds support for one channel CAN/USB interace CPC-USB/ARM7 from
>> EMS Dr. Thomas Wuensche (http://www.ems-wuensche.com).
>>
>> Signed-off-by: Sebastian Haas <haas@ems-wuensche.com>
>
> we reviewed the driver already on the Socket-CAN ML and it's OK from the
> Socket-CAN point of view.
Ack.
> I will do a quick (re-)check beginning of next
> week. I would be nice to have this patch also reviewed by some Linux USB
> experts. Therefore I suggest sending it to the relevant Linux-USB
> mailing list as well.
When there is feedback that would cause any changes (and a second posting), i
would suggest to put the driver under
drivers/net/can/usb/ems_usb.c
Regards,
Oliver
^ permalink raw reply
* Re: [iproute2] tc action mirred question
From: jamal @ 2009-09-11 12:25 UTC (permalink / raw)
To: Xiaofei Wu; +Cc: linux netdev
In-Reply-To: <204967.7557.qm@web111613.mail.gq1.yahoo.com>
On Wed, 2009-09-09 at 23:06 -0700, Xiaofei Wu wrote:
>
> It seems that I modify the dst MAC, src MAC of the packets, then transmit to 'lo' and mirror the packects to 'eth0'.
> (On 'lo', '2616 packets transmitted, 0 received, 100% packet loss' .) How to let 'lo' receive the packets?
By not modifying the packets. I am a little suprised that changing the
Mac address on lo has that effect.
Note it should work on ingress as i described because ingress doesnt
queue packets.
>
> But I want to only modify the dst MAC, src MAC of the mirroring packets, transmit them to next hop.
> (not modify the dst,src MAC of the packets to 'lo'). What should I do?
Ok, so modifying then mirroring wont work on ingress;->
One thing you can try is first to mirror lo->eth0, then pedit only
specific flow on eth0 that came from lo.
cheers,
jamal
^ permalink raw reply
* Re: [PATCH 8/8] fanotify: send events to userspace over socket reads
From: Daniel Walker @ 2009-09-11 14:08 UTC (permalink / raw)
To: Eric Paris; +Cc: linux-kernel, linux-fsdevel, netdev, davem, viro, alan, hch
In-Reply-To: <20090911052649.32359.18553.stgit@paris.rdu.redhat.com>
On Fri, 2009-09-11 at 01:26 -0400, Eric Paris wrote:
> fanotify sends event notification to userspace when userspace reads from the
> fanotify socket. This patch implements the operations that happen at read
> time. These include opening the file descriptor to the original object and
> then filling the userspace buffer. The fd should be pollable to indicate when
> it has data present and it should return how much data it has to send when the
> FIONREAD ioctl is checked.
>
This patch has one checkpatch error, could you fix that? .. Also your
whole series has several very long lines over 80 characters , you might
want to consider trimming those down to 80 or less.. If you run these
patches through checkpatch you should output like the following denoting
the issues,
ERROR: "(foo*)" should be "(foo *)"
#381: FILE: include/linux/fanotify.h:84:
+ (struct fanotify_event_metadata*)(((char *)(meta)) + \
WARNING: line over 80 characters
#384: FILE: include/linux/fanotify.h:87:
+#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
Daniel
^ permalink raw reply
* Re: [PATCH 8/8] fanotify: send events to userspace over socket reads
From: Eric Paris @ 2009-09-11 14:15 UTC (permalink / raw)
To: Daniel Walker; +Cc: linux-kernel, linux-fsdevel, netdev, davem, viro, alan, hch
In-Reply-To: <1252678130.30578.848.camel@desktop>
On Fri, 2009-09-11 at 07:08 -0700, Daniel Walker wrote:
> On Fri, 2009-09-11 at 01:26 -0400, Eric Paris wrote:
> > fanotify sends event notification to userspace when userspace reads from the
> > fanotify socket. This patch implements the operations that happen at read
> > time. These include opening the file descriptor to the original object and
> > then filling the userspace buffer. The fd should be pollable to indicate when
> > it has data present and it should return how much data it has to send when the
> > FIONREAD ioctl is checked.
> >
>
> This patch has one checkpatch error, could you fix that? .. Also your
> whole series has several very long lines over 80 characters , you might
> want to consider trimming those down to 80 or less.. If you run these
> patches through checkpatch you should output like the following denoting
> the issues,
>
> ERROR: "(foo*)" should be "(foo *)"
> #381: FILE: include/linux/fanotify.h:84:
> + (struct fanotify_event_metadata*)(((char *)(meta)) + \
Yes I'll clean this up. It was stolen straight from
include/linux/netlink.h NLMSG_NEXT with that exact spacing.
> WARNING: line over 80 characters
> #384: FILE: include/linux/fanotify.h:87:
> +#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
I will look at all of the my 80+ character lines again. This one in
particular, I will not break up. I might read a little too broadly in
CodingStyle where it says the "exception to this is where exceeding 80
columns significantly increases readability and does not hide
information".
-Eric
^ permalink raw reply
* Re: [PATCH 8/8] fanotify: send events to userspace over socket reads
From: Daniel Walker @ 2009-09-11 14:22 UTC (permalink / raw)
To: Eric Paris; +Cc: linux-kernel, linux-fsdevel, netdev, davem, viro, alan, hch
In-Reply-To: <1252678545.2305.4.camel@dhcp231-106.rdu.redhat.com>
On Fri, 2009-09-11 at 10:15 -0400, Eric Paris wrote:
> I will look at all of the my 80+ character lines again. This one in
> particular, I will not break up. I might read a little too broadly in
> CodingStyle where it says the "exception to this is where exceeding 80
> columns significantly increases readability and does not hide
> information".
Yeah, it's ok if you don't break up any, or just do some. The fewer long
lines tho the cleaner it is I think.
Daniel
^ permalink raw reply
* Re: [PATCH 00/14] pktgen update for net-next (2.6.32)
From: Jesper Dangaard Brouer @ 2009-09-11 14:29 UTC (permalink / raw)
To: robert
Cc: Stephen Hemminger, David Miller, Robert Olsson, netdev,
Thomas Gleixner
In-Reply-To: <Pine.LNX.4.64.0909090937550.9377@ask.diku.dk>
On Wed, 9 Sep 2009, Jesper Dangaard Brouer wrote:
> On Tue, 8 Sep 2009, robert@herjulf.net wrote:
>
>> > With patches:
>> > -------------
>> > tx_pkt_sz: 64 TX-pps: 9426724
>>
>> Impressive. 82599 seems fast. Didn't you report even higher number
>> with other CPU's?
>
> Yes, the Core i7 is even faster... but currently I'm not using it as a
> generator, its the router in my current setup.
>
> Once I have finished my slides and last tests for LinuxCon. I'll run some
> pktgen tests with the Core i7 and post the results.
Here are some quick test results:
One thing I noticed during the tests, is that using the machine during the
tests gets very sluggy with the pktgen patches applied.
Another thing/bug that sometimes happens is that pktgen stops working and
the ixgbe driver "says":
[ 231.195624] pktgen 2.72: Packet Generator for packet performance testing.
[ 233.065639] ixgbe 0000:07:00.1: master disable timed out
[ 234.677107] ixgbe 0000:07:00.1: master disable timed out
[ 236.284576] ixgbe 0000:07:00.1: master disable timed out
[ 237.892045] ixgbe 0000:07:00.1: master disable timed out
The tests, I did complete, gave the following results:
Core i7-920 (with DDR3 at 1600Mhz)
----------------------------------
Without patches (2.6.31-rc2):
tx_pkt_sz: 64 TX-Mbps: 5704.29 TX-pps: 11141193
tx_pkt_sz: 128 TX-Mbps: 8415.27 TX-pps: 8218035
tx_pkt_sz: 256 TX-Mbps: 9146.23 TX-pps: 4465932
With the pktgen patches (2.6.31-rc5):
tx_pkt_sz: 64 TX-Mbps: 5763.64 TX-pps: 11257313
tx_pkt_sz: 128 TX-Mbps: 8414.91 TX-pps: 8217709
tx_pkt_sz: 256 TX-Mbps: 9141.70 TX-pps: 4463720
Cheers,
Jesper Brouer
--
-------------------------------------------------------------------
MSc. Master of Computer Science
Dept. of Computer Science, University of Copenhagen
Author of http://www.adsl-optimizer.dk
-------------------------------------------------------------------
^ permalink raw reply
* Re: [PATCH 8/8] fanotify: send events to userspace over socket reads
From: Daniel Walker @ 2009-09-11 14:32 UTC (permalink / raw)
To: Eric Paris; +Cc: linux-kernel, linux-fsdevel, netdev, davem, viro, alan, hch
In-Reply-To: <1252678545.2305.4.camel@dhcp231-106.rdu.redhat.com>
On Fri, 2009-09-11 at 10:15 -0400, Eric Paris wrote:
> I will look at all of the my 80+ character lines again. This one in
> particular, I will not break up. I might read a little too broadly in
> CodingStyle where it says the "exception to this is where exceeding 80
> columns significantly increases readability and does not hide
> information".
There is a point when longer lines get too long tho .. Maybe over 100 is
too long.. Where if your terminal only displays 80 characters , and the
text is way over 80 the lines just get wrapped and all the intended
readability goes out the window.
Daniel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox