summaryrefslogtreecommitdiffstats
path: root/drivers/net/hamradio/mkiss.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/hamradio/mkiss.c')
-rw-r--r--drivers/net/hamradio/mkiss.c188
1 files changed, 151 insertions, 37 deletions
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
index d9fe64b46f4b..3e9accf137e7 100644
--- a/drivers/net/hamradio/mkiss.c
+++ b/drivers/net/hamradio/mkiss.c
@@ -14,13 +14,14 @@
*
* Copyright (C) Hans Alblas PE1AYX <hans@esrac.ele.tue.nl>
* Copyright (C) 2004, 05 Ralf Baechle DL5RB <ralf@linux-mips.org>
+ * Copyright (C) 2004, 05 Thomas Osterried DL9SAU <thomas@x-berg.in-berlin.de>
*/
-
#include <linux/config.h>
#include <linux/module.h>
#include <asm/system.h>
#include <linux/bitops.h>
#include <asm/uaccess.h>
+#include <linux/crc16.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
@@ -39,11 +40,6 @@
#include <net/ax25.h>
-#ifdef CONFIG_INET
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#endif
-
#define AX_MTU 236
/* SLIP/KISS protocol characters. */
@@ -80,9 +76,13 @@ struct mkiss {
int mode;
int crcmode; /* MW: for FlexNet, SMACK etc. */
-#define CRC_MODE_NONE 0
-#define CRC_MODE_FLEX 1
-#define CRC_MODE_SMACK 2
+ int crcauto; /* CRC auto mode */
+
+#define CRC_MODE_NONE 0
+#define CRC_MODE_FLEX 1
+#define CRC_MODE_SMACK 2
+#define CRC_MODE_FLEX_TEST 3
+#define CRC_MODE_SMACK_TEST 4
atomic_t refcnt;
struct semaphore dead_sem;
@@ -151,6 +151,21 @@ static int check_crc_flex(unsigned char *cp, int size)
return 0;
}
+static int check_crc_16(unsigned char *cp, int size)
+{
+ unsigned short crc = 0x0000;
+
+ if (size < 3)
+ return -1;
+
+ crc = crc16(0, cp, size);
+
+ if (crc != 0x0000)
+ return -1;
+
+ return 0;
+}
+
/*
* Standard encapsulation
*/
@@ -237,19 +252,42 @@ static void ax_bump(struct mkiss *ax)
spin_lock_bh(&ax->buflock);
if (ax->rbuff[0] > 0x0f) {
- if (ax->rbuff[0] & 0x20) {
- ax->crcmode = CRC_MODE_FLEX;
+ if (ax->rbuff[0] & 0x80) {
+ if (check_crc_16(ax->rbuff, ax->rcount) < 0) {
+ ax->stats.rx_errors++;
+ spin_unlock_bh(&ax->buflock);
+
+ return;
+ }
+ if (ax->crcmode != CRC_MODE_SMACK && ax->crcauto) {
+ printk(KERN_INFO
+ "mkiss: %s: Switchting to crc-smack\n",
+ ax->dev->name);
+ ax->crcmode = CRC_MODE_SMACK;
+ }
+ ax->rcount -= 2;
+ *ax->rbuff &= ~0x80;
+ } else if (ax->rbuff[0] & 0x20) {
if (check_crc_flex(ax->rbuff, ax->rcount) < 0) {
- ax->stats.rx_errors++;
+ ax->stats.rx_errors++;
+ spin_unlock_bh(&ax->buflock);
return;
}
+ if (ax->crcmode != CRC_MODE_FLEX && ax->crcauto) {
+ printk(KERN_INFO
+ "mkiss: %s: Switchting to crc-flexnet\n",
+ ax->dev->name);
+ ax->crcmode = CRC_MODE_FLEX;
+ }
ax->rcount -= 2;
- /* dl9sau bugfix: the trailling two bytes flexnet crc
- * will not be passed to the kernel. thus we have
- * to correct the kissparm signature, because it
- * indicates a crc but there's none
+
+ /*
+ * dl9sau bugfix: the trailling two bytes flexnet crc
+ * will not be passed to the kernel. thus we have to
+ * correct the kissparm signature, because it indicates
+ * a crc but there's none
*/
- *ax->rbuff &= ~0x20;
+ *ax->rbuff &= ~0x20;
}
}
spin_unlock_bh(&ax->buflock);
@@ -352,10 +390,8 @@ static void ax_changedmtu(struct mkiss *ax)
"MTU change cancelled.\n",
ax->dev->name);
dev->mtu = ax->mtu;
- if (xbuff != NULL)
- kfree(xbuff);
- if (rbuff != NULL)
- kfree(rbuff);
+ kfree(xbuff);
+ kfree(rbuff);
return;
}
@@ -417,20 +453,69 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len)
p = icp;
spin_lock_bh(&ax->buflock);
- switch (ax->crcmode) {
- unsigned short crc;
+ if ((*p & 0x0f) != 0) {
+ /* Configuration Command (kissparms(1).
+ * Protocol spec says: never append CRC.
+ * This fixes a very old bug in the linux
+ * kiss driver. -- dl9sau */
+ switch (*p & 0xff) {
+ case 0x85:
+ /* command from userspace especially for us,
+ * not for delivery to the tnc */
+ if (len > 1) {
+ int cmd = (p[1] & 0xff);
+ switch(cmd) {
+ case 3:
+ ax->crcmode = CRC_MODE_SMACK;
+ break;
+ case 2:
+ ax->crcmode = CRC_MODE_FLEX;
+ break;
+ case 1:
+ ax->crcmode = CRC_MODE_NONE;
+ break;
+ case 0:
+ default:
+ ax->crcmode = CRC_MODE_SMACK_TEST;
+ cmd = 0;
+ }
+ ax->crcauto = (cmd ? 0 : 1);
+ printk(KERN_INFO "mkiss: %s: crc mode %s %d\n", ax->dev->name, (len) ? "set to" : "is", cmd);
+ }
+ spin_unlock_bh(&ax->buflock);
+ netif_start_queue(dev);
- case CRC_MODE_FLEX:
- *p |= 0x20;
- crc = calc_crc_flex(p, len);
- count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2);
- break;
+ return;
+ default:
+ count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+ }
+ } else {
+ unsigned short crc;
+ switch (ax->crcmode) {
+ case CRC_MODE_SMACK_TEST:
+ ax->crcmode = CRC_MODE_FLEX_TEST;
+ printk(KERN_INFO "mkiss: %s: Trying crc-smack\n", ax->dev->name);
+ // fall through
+ case CRC_MODE_SMACK:
+ *p |= 0x80;
+ crc = swab16(crc16(0, p, len));
+ count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2);
+ break;
+ case CRC_MODE_FLEX_TEST:
+ ax->crcmode = CRC_MODE_NONE;
+ printk(KERN_INFO "mkiss: %s: Trying crc-flexnet\n", ax->dev->name);
+ // fall through
+ case CRC_MODE_FLEX:
+ *p |= 0x20;
+ crc = calc_crc_flex(p, len);
+ count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2);
+ break;
+
+ default:
+ count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+ }
+ }
- default:
- count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
- break;
- }
-
set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags);
actual = ax->tty->driver->write(ax->tty, ax->xbuff, count);
ax->stats.tx_packets++;
@@ -439,8 +524,6 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len)
ax->dev->trans_start = jiffies;
ax->xleft = count - actual;
ax->xhead = ax->xbuff + actual;
-
- spin_unlock_bh(&ax->buflock);
}
/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
@@ -622,7 +705,7 @@ static void ax_setup(struct net_device *dev)
* best way to fix this is to use a rwlock in the tty struct, but for now we
* use a single global rwlock for all ttys in ppp line discipline.
*/
-static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(disc_data_lock);
static struct mkiss *mkiss_get(struct tty_struct *tty)
{
@@ -643,6 +726,8 @@ static void mkiss_put(struct mkiss *ax)
up(&ax->dead_sem);
}
+static int crc_force = 0; /* Can be overridden with insmod */
+
static int mkiss_open(struct tty_struct *tty)
{
struct net_device *dev;
@@ -682,6 +767,33 @@ static int mkiss_open(struct tty_struct *tty)
if (register_netdev(dev))
goto out_free_buffers;
+ /* after register_netdev() - because else printk smashes the kernel */
+ switch (crc_force) {
+ case 3:
+ ax->crcmode = CRC_MODE_SMACK;
+ printk(KERN_INFO "mkiss: %s: crc mode smack forced.\n",
+ ax->dev->name);
+ break;
+ case 2:
+ ax->crcmode = CRC_MODE_FLEX;
+ printk(KERN_INFO "mkiss: %s: crc mode flexnet forced.\n",
+ ax->dev->name);
+ break;
+ case 1:
+ ax->crcmode = CRC_MODE_NONE;
+ printk(KERN_INFO "mkiss: %s: crc mode disabled.\n",
+ ax->dev->name);
+ break;
+ case 0:
+ /* fall through */
+ default:
+ crc_force = 0;
+ printk(KERN_INFO "mkiss: %s: crc mode is auto.\n",
+ ax->dev->name);
+ ax->crcmode = CRC_MODE_SMACK_TEST;
+ }
+ ax->crcauto = (crc_force ? 0 : 1);
+
netif_start_queue(dev);
/* Done. We have linked the TTY line to a channel. */
@@ -765,7 +877,6 @@ static int mkiss_ioctl(struct tty_struct *tty, struct file *file,
case SIOCSIFHWADDR: {
char addr[AX25_ADDR_LEN];
-printk(KERN_INFO "In SIOCSIFHWADDR");
if (copy_from_user(&addr,
(void __user *) arg, AX25_ADDR_LEN)) {
@@ -864,6 +975,7 @@ out:
}
static struct tty_ldisc ax_ldisc = {
+ .owner = THIS_MODULE,
.magic = TTY_LDISC_MAGIC,
.name = "mkiss",
.open = mkiss_open,
@@ -904,6 +1016,8 @@ static void __exit mkiss_exit_driver(void)
MODULE_AUTHOR("Ralf Baechle DL5RB <ralf@linux-mips.org>");
MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
+MODULE_PARM(crc_force, "i");
+MODULE_PARM_DESC(crc_force, "crc [0 = auto | 1 = none | 2 = flexnet | 3 = smack]");
MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_AX25);