This patch allows us to bypass the IrDA stack down to the IrLAP level. Sending and receiving frames is done through a character device. This is useful for e.g. doing real IrDA sniffing, testing external IrDA stacks and for Lirc (once I will add the framing disabling switch).
Signed-off-by: Samuel Ortiz <[EMAIL PROTECTED]> --- include/net/irda/irlap.h | 5 + include/net/irda/irlap_raw.h | 27 +++ net/irda/Kconfig | 10 ++ net/irda/Makefile | 1 + net/irda/irlap.c | 5 + net/irda/irlap_event.c | 5 + net/irda/irlap_frame.c | 6 + net/irda/irlap_raw.c | 358 ++++++++++++++++++++++++++++++++++++++++++ net/irda/irsysctl.c | 16 ++- 9 files changed, 432 insertions(+), 1 deletions(-) create mode 100644 include/net/irda/irlap_raw.h create mode 100644 net/irda/irlap_raw.c diff --git a/include/net/irda/irlap.h b/include/net/irda/irlap.h index e77eb88..1b24cfc 100644 --- a/include/net/irda/irlap.h +++ b/include/net/irda/irlap.h @@ -36,6 +36,7 @@ #include <net/irda/qos.h> /* struct qos_info */ #include <net/irda/discovery.h> /* discovery_t */ #include <net/irda/irlap_event.h> /* IRLAP_STATE, ... */ +#include <net/irda/irlap_raw.h> /* IRLAP raw definitions */ #include <net/irda/irmod.h> /* struct notify_t */ #define CONFIG_IRDA_DYNAMIC_WINDOW 1 @@ -208,6 +209,10 @@ struct irlap_cb { int xbofs_delay; /* Nr of XBOF's used to MTT */ int bofs_count; /* Negotiated extra BOFs */ int next_bofs; /* Negotiated extra BOFs after next frame */ +#ifdef CONFIG_IRDA_RAW + int raw_mode; + struct irlap_raw * irlap_raw; +#endif }; /* diff --git a/include/net/irda/irlap_raw.h b/include/net/irda/irlap_raw.h new file mode 100644 index 0000000..37a29dd --- /dev/null +++ b/include/net/irda/irlap_raw.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2007 Samuel Ortiz ([EMAIL PROTECTED]) + * + * 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. + * + */ + +#ifndef _IRLAP_RAW_H +#define _IRLAP_RAW_H + +#ifdef CONFIG_IRDA_RAW + +int irlap_raw_recv_frame(struct sk_buff *skb, struct net_device *dev); +int irlap_raw_register_device(struct net_device * dev); +int irlap_raw_unregister_device(struct net_device * dev); + +#else + +#define irlap_raw_recv_frame(skbuff, netdev) +#define irlap_raw_register_device(netdev) +#define irlap_raw_unregister_device(netdev) + +#endif + +#endif diff --git a/net/irda/Kconfig b/net/irda/Kconfig index 9efb17b..310a036 100644 --- a/net/irda/Kconfig +++ b/net/irda/Kconfig @@ -92,5 +92,15 @@ config IRDA_DEBUG If unsure, say Y (since it makes it easier to find the bugs). +config IRDA_RAW + bool "IrLAP raw" + depends on IRDA + help + Say Y if you want to bypass the IrDA stack, down to the IrLAP level. + This option allows you to send and receive IrLAP frames from + userspace, by writing and reading to a character device + (/dev/irdaX_raw). Note that the IrDA stack will be mostly disabled. + If unsure, say N. + source "drivers/net/irda/Kconfig" diff --git a/net/irda/Makefile b/net/irda/Makefile index d1366c2..11d5ffb 100644 --- a/net/irda/Makefile +++ b/net/irda/Makefile @@ -13,3 +13,4 @@ irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \ discovery.o parameters.o irmod.o irda-$(CONFIG_PROC_FS) += irproc.o irda-$(CONFIG_SYSCTL) += irsysctl.o +irda-$(CONFIG_IRDA_RAW) += irlap_raw.o diff --git a/net/irda/irlap.c b/net/irda/irlap.c index d93ebd1..656c949 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -173,6 +173,8 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, irlmp_register_link(self, self->saddr, &self->notify); + irlap_raw_register_device(dev); + return self; } EXPORT_SYMBOL(irlap_open); @@ -234,6 +236,9 @@ void irlap_close(struct irlap_cb *self) IRDA_DEBUG(1, "%s(), Didn't find myself!\n", __FUNCTION__); return; } + + irlap_raw_unregister_device(self->netdev); + __irlap_close(lap); } EXPORT_SYMBOL(irlap_close); diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index 7b6433f..94a1288 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -241,6 +241,11 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, if (!self || self->magic != LAP_MAGIC) return; +#ifdef CONFIG_IRDA_RAW + if (self->raw_mode) + return; +#endif + IRDA_DEBUG(3, "%s(), event = %s, state = %s\n", __FUNCTION__, irlap_event[event], irlap_state[self->state]); diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c index 0b04603..6906a72 100644 --- a/net/irda/irlap_frame.c +++ b/net/irda/irlap_frame.c @@ -1323,6 +1323,12 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, return -1; } +#ifdef CONFIG_IRDA_RAW + if (self->raw_mode) { + return irlap_raw_recv_frame(skb, dev); + } +#endif + /* We are no longer an "old" protocol, so we need to handle * share and non linear skbs. This should never happen, so * we don't need to be clever about it. Jean II */ diff --git a/net/irda/irlap_raw.c b/net/irda/irlap_raw.c new file mode 100644 index 0000000..5a4c409 --- /dev/null +++ b/net/irda/irlap_raw.c @@ -0,0 +1,358 @@ +/* + * net/irda/irlap_raw.c Raw IrLAP character device. + * Copyright (C) 2007 Samuel Ortiz ([EMAIL PROTECTED]) + * + * 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. + * + * From /dev/irdaN_raw, you can read and write raw IrLAP frames. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/miscdevice.h> +#include <linux/wait.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> +#include <net/irda/irlap.h> +#include <net/irda/irlap_raw.h> + +/* We keep the last 10 IrLAP frames */ +unsigned int sysctl_raw_ring_length = 10; + +struct irlap_raw { + struct list_head list; + char name[IFNAMSIZ + 8]; + struct miscdevice misc_device; + struct irlap_cb * irlap; + struct sk_buff_head skb_ring; + wait_queue_head_t rx_wait; + unsigned int refcount; + struct mutex rx_mutex; + struct mutex tx_mutex; + struct work_struct skb_ring_work; +}; + +static LIST_HEAD(irlap_raw_list); +static DEFINE_MUTEX(irlap_raw_mutex); + +static void irlap_raw_skb_ring_gc(struct work_struct *work) +{ + struct irlap_raw * raw = + container_of(work, struct irlap_raw, skb_ring_work); + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + if (mutex_lock_interruptible(&raw->rx_mutex)) + return; + + while(skb_queue_len(&raw->skb_ring) > sysctl_raw_ring_length) { + struct sk_buff * old_skb; + + IRDA_DEBUG(3, "%s(): We still have %d entries\n", + __FUNCTION__, skb_queue_len(&raw->skb_ring)); + + old_skb = skb_dequeue_tail(&raw->skb_ring); + dev_kfree_skb(old_skb); + } + + mutex_unlock(&raw->rx_mutex); +} + +int irlap_raw_recv_frame(struct sk_buff *skb, struct net_device *dev) +{ + struct irlap_cb * irlap; + struct irlap_raw * raw; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + irlap = dev->atalk_ptr; + if (irlap == NULL) + return -ENODEV; + + if (skb == NULL) + return -EINVAL; + + raw = irlap->irlap_raw; + if (raw == NULL) + return -ENODEV; + + skb_queue_head(&raw->skb_ring, skb); + + wake_up_interruptible(&raw->rx_wait); + + /* + * We call the "garbage collector" in process + * context, so that we can lock the skb removal + * against read(). + */ + if (skb_queue_len(&raw->skb_ring) > sysctl_raw_ring_length) { + IRDA_DEBUG(3, "%s(): We have %d entries, time to clean up\n", + __FUNCTION__, skb_queue_len(&raw->skb_ring)); + schedule_work(&raw->skb_ring_work); + } + + return 0; +} +EXPORT_SYMBOL(irlap_raw_recv_frame); + + +ssize_t irlap_raw_read(struct file * file, char __user * buf, + size_t count, loff_t * ppos) +{ + int ret = 0; + struct irlap_raw * raw; + struct sk_buff * frame; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + raw = file->private_data; + + ret = mutex_lock_interruptible(&raw->rx_mutex); + if (ret) + return ret; + + ret = wait_event_interruptible(raw->rx_wait, + !skb_queue_empty(&raw->skb_ring)); + if (ret < 0) + goto out; + + if (skb_queue_empty(&raw->skb_ring)) { + ret = -EINTR; + IRDA_DEBUG(3, "%s(): Queue is empty\n", __FUNCTION__); + goto out; + } + + frame = skb_dequeue_tail(&raw->skb_ring); + + if (frame == NULL) { + ret = -EFAULT; + goto out; + } + + if (copy_to_user(buf, frame->data, frame->len)) { + ret = -EFAULT; + goto out; + } + + ret = frame->len; + dev_kfree_skb(frame); + + out: + mutex_unlock(&raw->rx_mutex); + + IRDA_DEBUG(3, "%s(): Read %d bytes\n", __FUNCTION__, ret); + return ret; +} + +extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); +ssize_t irlap_raw_write(struct file * file, const char __user * buf, + size_t count, loff_t * ppos) +{ + int ret; + struct irlap_raw * raw; + struct sk_buff *tx_skb; + + IRDA_DEBUG(3, "%s(): Trying to write %d bytes\n", __FUNCTION__, count); + + raw = file->private_data; + + ret = mutex_lock_interruptible(&raw->tx_mutex); + if (ret) + return ret; + + if (count == 0) + goto out; + + tx_skb = alloc_skb(IRDA_SIR_MAX_FRAME, GFP_KERNEL); + if (tx_skb == NULL) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(skb_put(tx_skb, count), buf, count)) { + ret = -EFAULT; + goto out; + } + + irlap_queue_xmit(raw->irlap, tx_skb); + + ret = count; + out: + mutex_unlock(&raw->tx_mutex); + + IRDA_DEBUG(3, "%s(): Wrote %d bytes\n", __FUNCTION__, ret); + return ret; +} + +unsigned int irlap_raw_poll(struct file * file, + struct poll_table_struct * table) +{ + int ret = 0; + struct irlap_raw * raw; + + raw = file->private_data; + + poll_wait(file, &raw->rx_wait, table); + + if (!skb_queue_empty(&raw->skb_ring)) + ret = POLLIN | POLLRDNORM; + else + ret = POLLOUT | POLLWRNORM; + + return ret; +} + +int irlap_raw_open(struct inode * inode, struct file * file) +{ + unsigned int minor = iminor(inode); + int ret = -ENODEV; + struct irlap_raw * raw, * tmp; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + mutex_lock_interruptible(&irlap_raw_mutex); + list_for_each_entry_safe(raw, tmp, &irlap_raw_list, list) { + if (raw->misc_device.minor == minor) { + IRDA_DEBUG(3, "%s(): Found raw (minor: %d)\n", + __FUNCTION__, minor); + file->private_data = raw; + raw->irlap->raw_mode = 1; + raw->refcount++; + mutex_unlock(&irlap_raw_mutex); + return 0; + } + } + + mutex_unlock(&irlap_raw_mutex); + return ret; +} + +int irlap_raw_release(struct inode * inode, struct file * file) +{ + struct irlap_raw * raw; + + if (file->private_data) { + mutex_lock_interruptible(&irlap_raw_mutex); + + raw = file->private_data; + if (raw->refcount > 0) + raw->refcount--; + + if (raw->refcount == 0) { + skb_queue_purge(&raw->skb_ring); + raw->irlap->raw_mode = 0; + } + + file->private_data = NULL; + + mutex_unlock(&irlap_raw_mutex); + } + + return 0; +} + +static const struct file_operations irlap_raw_fops = { + .owner = THIS_MODULE, + .read = irlap_raw_read, + .write = irlap_raw_write, + .poll = irlap_raw_poll, + .open = irlap_raw_open, + .release = irlap_raw_release, +}; + + +int irlap_raw_register_device(struct net_device * dev) +{ + int ret = 0; + struct irlap_cb * irlap; + struct irlap_raw * raw; + + if (dev == NULL) + return -ENODEV; + + raw = kzalloc(sizeof(struct irlap_raw), GFP_KERNEL); + if (raw == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&raw->list); + INIT_WORK(&raw->skb_ring_work, irlap_raw_skb_ring_gc); + mutex_init(&raw->rx_mutex); + mutex_init(&raw->tx_mutex); + raw->refcount = 0; + skb_queue_head_init(&raw->skb_ring); + + sprintf(raw->name, "%s_raw", dev->name); + raw->misc_device.minor = MISC_DYNAMIC_MINOR; + raw->misc_device.name = raw->name; + raw->misc_device.fops = &irlap_raw_fops; + + list_add(&raw->list, &irlap_raw_list); + + irlap = dev->atalk_ptr; + if (irlap == NULL) { + ret = -ENODEV; + goto out_err; + } + + /* We'll enter raw mode whenever the device is open */ + irlap->raw_mode = 0; + irlap->irlap_raw = raw; + + init_waitqueue_head(&raw->rx_wait); + + raw->irlap = irlap; + + ret = misc_register(&raw->misc_device); + if (!ret) { + printk(KERN_INFO "Registered %s raw IrLAP device\n", raw->name); + return ret; + } + + out_err: + irlap->irlap_raw = NULL; + kfree(raw); + return ret; + +} +EXPORT_SYMBOL(irlap_raw_register_device); + + +int irlap_raw_unregister_device(struct net_device * dev) +{ + struct irlap_cb * irlap; + struct irlap_raw * raw; + + irlap = dev->atalk_ptr; + if (irlap == NULL) + return -ENODEV; + + raw = irlap->irlap_raw; + mutex_lock_interruptible(&irlap_raw_mutex); + if (raw->refcount) { + mutex_unlock(&irlap_raw_mutex); + return -EBUSY; + } + + printk(KERN_INFO "%s unregistered\n", raw->name); + + list_del(&raw->list); + misc_deregister(&raw->misc_device); + + mutex_unlock(&irlap_raw_mutex); + + kfree(raw); + + return 0; +} +EXPORT_SYMBOL(irlap_raw_unregister_device); diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index 2e968e7..a7e967d 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -35,7 +35,7 @@ enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS, DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME, MAX_TX_DATA_SIZE, MAX_TX_WINDOW, MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, - LAP_KEEPALIVE_TIME }; + LAP_KEEPALIVE_TIME, RAW_RING_LENGTH }; extern int sysctl_discovery; extern int sysctl_discovery_slots; @@ -50,6 +50,9 @@ extern int sysctl_max_tx_window; extern int sysctl_max_noreply_time; extern int sysctl_warn_noreply_time; extern int sysctl_lap_keepalive_time; +#ifdef CONFIG_IRDA_RAW +extern unsigned int sysctl_raw_ring_length; +#endif /* this is needed for the proc_dointvec_minmax - Jean II */ static int max_discovery_slots = 16; /* ??? */ @@ -237,6 +240,17 @@ static ctl_table irda_table[] = { .extra1 = &min_lap_keepalive_time, .extra2 = &max_lap_keepalive_time }, +#ifdef CONFIG_IRDA_RAW + { + .ctl_name = RAW_RING_LENGTH, + .procname = "raw_ring_length", + .data = &sysctl_raw_ring_length, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, +#endif + { .ctl_name = 0 } }; -- 1.5.0.2 - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html