diff options
Diffstat (limited to 'drivers/net/hamradio/6pack.c')
| -rw-r--r-- | drivers/net/hamradio/6pack.c | 314 |
1 files changed, 95 insertions, 219 deletions
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 1450e33fc250..885992951e8a 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * 6pack.c This module implements the 6pack protocol for kernel-based * devices like TTY. It interfaces between a raw TTY and the @@ -13,7 +14,7 @@ */ #include <linux/module.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/string.h> #include <linux/mm.h> @@ -34,10 +35,7 @@ #include <linux/ip.h> #include <linux/tcp.h> #include <linux/semaphore.h> -#include <linux/compat.h> -#include <linux/atomic.h> - -#define SIXPACK_VERSION "Revision: 0.3.0" +#include <linux/refcount.h> /* sixpack priority commands */ #define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ @@ -68,9 +66,9 @@ #define SIXP_DAMA_OFF 0 /* default level 2 parameters */ -#define SIXP_TXDELAY (HZ/4) /* in 1 s */ +#define SIXP_TXDELAY 25 /* 250 ms */ #define SIXP_PERSIST 50 /* in 256ths */ -#define SIXP_SLOTTIME (HZ/10) /* in 1 s */ +#define SIXP_SLOTTIME 10 /* 100 ms */ #define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */ #define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */ @@ -88,20 +86,17 @@ struct sixpack { struct net_device *dev; /* easy for intr handling */ /* These are pointers to the malloc()ed frame buffers. */ - unsigned char *rbuff; /* receiver buffer */ int rcount; /* received chars counter */ unsigned char *xbuff; /* transmitter buffer */ unsigned char *xhead; /* next byte to XMIT */ int xleft; /* bytes left in XMIT queue */ - unsigned char raw_buf[4]; - unsigned char cooked_buf[400]; + u8 raw_buf[4]; + u8 cooked_buf[400]; unsigned int rx_count; unsigned int rx_count_cooked; - - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ + spinlock_t rxlock; unsigned long flags; /* Flag values/ mode etc */ unsigned char mode; /* 6pack mode */ @@ -112,22 +107,20 @@ struct sixpack { unsigned char slottime; unsigned char duplex; unsigned char led_state; - unsigned char status; - unsigned char status1; + u8 status; + u8 status1; unsigned char status2; unsigned char tx_enable; unsigned char tnc_state; struct timer_list tx_t; struct timer_list resync_t; - atomic_t refcnt; - struct semaphore dead_sem; spinlock_t lock; }; #define AX25_6PACK_HEADER_LEN 0 -static void sixpack_decode(struct sixpack *, unsigned char[], int); +static void sixpack_decode(struct sixpack *, const u8 *, size_t); static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); /* @@ -136,9 +129,9 @@ static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); * Note that in case of DAMA operation, the data is not sent here. */ -static void sp_xmit_on_air(unsigned long channel) +static void sp_xmit_on_air(struct timer_list *t) { - struct sixpack *sp = (struct sixpack *) channel; + struct sixpack *sp = timer_container_of(sp, t, tx_t); int actual, when = sp->slottime; static unsigned char random; @@ -166,12 +159,7 @@ static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) unsigned char *msg, *p = icp; int actual, count; - if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */ - msg = "oversized transmit packet!"; - goto out_drop; - } - - if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */ + if (len > AX25_MTU + 73) { msg = "oversized transmit packet!"; goto out_drop; } @@ -229,7 +217,7 @@ static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) sp->xleft = count; sp->xhead = sp->xbuff; sp->status2 = count; - sp_xmit_on_air((unsigned long)sp); + sp_xmit_on_air(&sp->tx_t); } return; @@ -247,6 +235,9 @@ static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev) { struct sixpack *sp = netdev_priv(dev); + if (skb->protocol == htons(ETH_P_IP)) + return ax25_ip_xmit(skb); + spin_lock_bh(&sp->lock); /* We were not busy, so we are now... :-) */ netif_stop_queue(dev); @@ -284,45 +275,19 @@ static int sp_close(struct net_device *dev) return 0; } -/* Return the frame type ID */ -static int sp_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, const void *daddr, - const void *saddr, unsigned len) -{ -#ifdef CONFIG_INET - if (type != ETH_P_AX25) - return ax25_hard_header(skb, dev, type, daddr, saddr, len); -#endif - return 0; -} - static int sp_set_mac_address(struct net_device *dev, void *addr) { struct sockaddr_ax25 *sa = addr; netif_tx_lock_bh(dev); netif_addr_lock(dev); - memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); + __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; } -static int sp_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - return ax25_rebuild_header(skb); -#else - return 0; -#endif -} - -static const struct header_ops sp_header_ops = { - .create = sp_header, - .rebuild = sp_rebuild_header, -}; - static const struct net_device_ops sp_netdev_ops = { .ndo_open = sp_open_dev, .ndo_stop = sp_close, @@ -334,10 +299,9 @@ static void sp_setup(struct net_device *dev) { /* Finish setting up the DEVICE info. */ dev->netdev_ops = &sp_netdev_ops; - dev->destructor = free_netdev; dev->mtu = SIXP_MTU; dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->header_ops = &sp_header_ops; + dev->header_ops = &ax25_header_ops; dev->addr_len = AX25_ADDR_LEN; dev->type = ARPHRD_AX25; @@ -345,7 +309,7 @@ static void sp_setup(struct net_device *dev) /* Only activated in AX.25 mode */ memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, &ax25_defaddr, AX25_ADDR_LEN); + dev_addr_set(dev, (u8 *)&ax25_defaddr); dev->flags = 0; } @@ -361,16 +325,16 @@ static void sp_bump(struct sixpack *sp, char cmd) { struct sk_buff *skb; int count; - unsigned char *ptr; + u8 *ptr; count = sp->rcount + 1; sp->dev->stats.rx_bytes += count; - if ((skb = dev_alloc_skb(count)) == NULL) + if ((skb = dev_alloc_skb(count + 1)) == NULL) goto out_mem; - ptr = skb_put(skb, count); + ptr = skb_put(skb, count + 1); *ptr++ = cmd; /* KISS command */ memcpy(ptr, sp->cooked_buf + 1, count); @@ -388,41 +352,12 @@ out_mem: /* ----------------------------------------------------------------------- */ /* - * We have a potential race on dereferencing tty->disc_data, because the tty - * layer provides no locking at all - thus one cpu could be running - * sixpack_receive_buf while another calls sixpack_close, which zeroes - * tty->disc_data and frees the memory that sixpack_receive_buf is using. The - * 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 DEFINE_RWLOCK(disc_data_lock); - -static struct sixpack *sp_get(struct tty_struct *tty) -{ - struct sixpack *sp; - - read_lock(&disc_data_lock); - sp = tty->disc_data; - if (sp) - atomic_inc(&sp->refcnt); - read_unlock(&disc_data_lock); - - return sp; -} - -static void sp_put(struct sixpack *sp) -{ - if (atomic_dec_and_test(&sp->refcnt)) - up(&sp->dead_sem); -} - -/* * Called by the TTY driver when there's room for more data. If we have * more packets to send, we send them here. */ static void sixpack_write_wakeup(struct tty_struct *tty) { - struct sixpack *sp = sp_get(tty); + struct sixpack *sp = tty->disc_data; int actual; if (!sp) @@ -434,7 +369,7 @@ static void sixpack_write_wakeup(struct tty_struct *tty) clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); sp->tx_enable = 0; netif_wake_queue(sp->dev); - goto out; + return; } if (sp->tx_enable) { @@ -442,37 +377,30 @@ static void sixpack_write_wakeup(struct tty_struct *tty) sp->xleft -= actual; sp->xhead += actual; } - -out: - sp_put(sp); } /* ----------------------------------------------------------------------- */ /* * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when + * This function is called by the tty module in the kernel when * a block of 6pack data has been received, which can now be decapsulated * and sent on to some IP layer for further processing. */ -static void sixpack_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp, + const u8 *fp, size_t count) { struct sixpack *sp; - unsigned char buf[512]; - int count1; + size_t count1; if (!count) return; - sp = sp_get(tty); + sp = tty->disc_data; if (!sp) return; - memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf)); - /* Read the characters out of the buffer */ - count1 = count; while (count) { count--; @@ -482,9 +410,8 @@ static void sixpack_receive_buf(struct tty_struct *tty, continue; } } - sixpack_decode(sp, buf, count1); + sixpack_decode(sp, cp, count1); - sp_put(sp); tty_unthrottle(tty); } @@ -527,9 +454,9 @@ static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) __tnc_set_sync_state(sp, new_tnc_state); } -static void resync_tnc(unsigned long channel) +static void resync_tnc(struct timer_list *t) { - struct sixpack *sp = (struct sixpack *) channel; + struct sixpack *sp = timer_container_of(sp, t, resync_t); static char resync_cmd = 0xe8; /* clear any data that might have been received */ @@ -551,12 +478,7 @@ static void resync_tnc(unsigned long channel) /* Start resync timer again -- the TNC might be still absent */ - - del_timer(&sp->resync_t); - sp->resync_t.data = (unsigned long) sp; - sp->resync_t.function = resync_tnc; - sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); } static inline int tnc_init(struct sixpack *sp) @@ -567,11 +489,7 @@ static inline int tnc_init(struct sixpack *sp) sp->tty->ops->write(sp->tty, &inbyte, 1); - del_timer(&sp->resync_t); - sp->resync_t.data = (unsigned long) sp; - sp->resync_t.function = resync_tnc; - sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); return 0; } @@ -585,7 +503,7 @@ static inline int tnc_init(struct sixpack *sp) */ static int sixpack_open(struct tty_struct *tty) { - char *rbuff = NULL, *xbuff = NULL; + char *xbuff = NULL; struct net_device *dev; struct sixpack *sp; unsigned long len; @@ -596,7 +514,8 @@ static int sixpack_open(struct tty_struct *tty) if (tty->ops->write == NULL) return -EOPNOTSUPP; - dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup); + dev = alloc_netdev(sizeof(struct sixpack), "sp%d", NET_NAME_UNKNOWN, + sp_setup); if (!dev) { err = -ENOMEM; goto out; @@ -606,17 +525,14 @@ static int sixpack_open(struct tty_struct *tty) sp->dev = dev; spin_lock_init(&sp->lock); - atomic_set(&sp->refcnt, 1); - sema_init(&sp->dead_sem, 0); + spin_lock_init(&sp->rxlock); /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */ len = dev->mtu * 2; - rbuff = kmalloc(len + 4, GFP_KERNEL); xbuff = kmalloc(len + 4, GFP_KERNEL); - - if (rbuff == NULL || xbuff == NULL) { + if (xbuff == NULL) { err = -ENOBUFS; goto out_free; } @@ -625,11 +541,8 @@ static int sixpack_open(struct tty_struct *tty) sp->tty = tty; - sp->rbuff = rbuff; sp->xbuff = xbuff; - sp->mtu = AX25_MTU + 73; - sp->buffsize = len; sp->rcount = 0; sp->rx_count = 0; sp->rx_count_cooked = 0; @@ -649,11 +562,9 @@ static int sixpack_open(struct tty_struct *tty) netif_start_queue(dev); - init_timer(&sp->tx_t); - sp->tx_t.function = sp_xmit_on_air; - sp->tx_t.data = (unsigned long) sp; + timer_setup(&sp->tx_t, sp_xmit_on_air, 0); - init_timer(&sp->resync_t); + timer_setup(&sp->resync_t, resync_tnc, 0); spin_unlock_bh(&sp->lock); @@ -662,7 +573,8 @@ static int sixpack_open(struct tty_struct *tty) tty->receive_room = 65536; /* Now we're ready to register. */ - if (register_netdev(dev)) + err = register_netdev(dev); + if (err) goto out_free; tnc_init(sp); @@ -671,10 +583,8 @@ static int sixpack_open(struct tty_struct *tty) out_free: kfree(xbuff); - kfree(rbuff); - if (dev) - free_netdev(dev); + free_netdev(dev); out: return err; @@ -691,35 +601,34 @@ static void sixpack_close(struct tty_struct *tty) { struct sixpack *sp; - write_lock_bh(&disc_data_lock); sp = tty->disc_data; - tty->disc_data = NULL; - write_unlock_bh(&disc_data_lock); if (!sp) return; - /* - * We have now ensured that nobody can start using ap from now on, but - * we have to wait for all existing users to finish. + tty->disc_data = NULL; + + /* We must stop the queue to avoid potentially scribbling + * on the free buffers. The sp->dead completion is not sufficient + * to protect us from sp->xbuff access. */ - if (!atomic_dec_and_test(&sp->refcnt)) - down(&sp->dead_sem); + netif_stop_queue(sp->dev); unregister_netdev(sp->dev); - del_timer(&sp->tx_t); - del_timer(&sp->resync_t); + timer_delete_sync(&sp->tx_t); + timer_delete_sync(&sp->resync_t); - /* Free all 6pack frame buffers. */ - kfree(sp->rbuff); + /* Free all 6pack frame buffers after unreg. */ kfree(sp->xbuff); + + free_netdev(sp->dev); } /* Perform I/O control on an active 6pack channel. */ -static int sixpack_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) +static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg) { - struct sixpack *sp = sp_get(tty); + struct sixpack *sp = tty->disc_data; struct net_device *dev; unsigned int tmp, err; @@ -752,92 +661,56 @@ static int sixpack_ioctl(struct tty_struct *tty, struct file *file, err = 0; break; - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; + case SIOCSIFHWADDR: { + char addr[AX25_ADDR_LEN]; - if (copy_from_user(&addr, - (void __user *) arg, AX25_ADDR_LEN)) { + if (copy_from_user(&addr, + (void __user *)arg, AX25_ADDR_LEN)) { err = -EFAULT; break; } netif_tx_lock_bh(dev); - memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN); + __dev_addr_set(dev, &addr, AX25_ADDR_LEN); netif_tx_unlock_bh(dev); - err = 0; break; } - default: - err = tty_mode_ioctl(tty, file, cmd, arg); + err = tty_mode_ioctl(tty, cmd, arg); } - sp_put(sp); - return err; } -#ifdef CONFIG_COMPAT -static long sixpack_compat_ioctl(struct tty_struct * tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case SIOCGIFNAME: - case SIOCGIFENCAP: - case SIOCSIFENCAP: - case SIOCSIFHWADDR: - return sixpack_ioctl(tty, file, cmd, - (unsigned long)compat_ptr(arg)); - } - - return -ENOIOCTLCMD; -} -#endif - static struct tty_ldisc_ops sp_ldisc = { .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, + .num = N_6PACK, .name = "6pack", .open = sixpack_open, .close = sixpack_close, .ioctl = sixpack_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = sixpack_compat_ioctl, -#endif .receive_buf = sixpack_receive_buf, .write_wakeup = sixpack_write_wakeup, }; /* Initialize 6pack control device -- register 6pack line discipline */ -static const char msg_banner[] __initconst = KERN_INFO \ - "AX.25: 6pack driver, " SIXPACK_VERSION "\n"; -static const char msg_regfail[] __initconst = KERN_ERR \ - "6pack: can't register line discipline (err = %d)\n"; - static int __init sixpack_init_driver(void) { int status; - printk(msg_banner); - /* Register the provided line protocol discipline */ - if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0) - printk(msg_regfail, status); + status = tty_register_ldisc(&sp_ldisc); + if (status) + pr_err("6pack: can't register line discipline (err = %d)\n", status); return status; } -static const char msg_unregfail[] = KERN_ERR \ - "6pack: can't unregister line discipline (err = %d)\n"; - static void __exit sixpack_exit_driver(void) { - int ret; - - if ((ret = tty_unregister_ldisc(N_6PACK))) - printk(msg_unregfail, ret); + tty_unregister_ldisc(&sp_ldisc); } /* encode an AX.25 packet into 6pack */ @@ -880,9 +753,9 @@ static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, /* decode 4 sixpack-encoded bytes into 3 data bytes */ -static void decode_data(struct sixpack *sp, unsigned char inbyte) +static void decode_data(struct sixpack *sp, u8 inbyte) { - unsigned char *buf; + u8 *buf; if (sp->rx_count != 3) { sp->raw_buf[sp->rx_count++] = inbyte; @@ -890,6 +763,12 @@ static void decode_data(struct sixpack *sp, unsigned char inbyte) return; } + if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) { + pr_err("6pack: cooked buffer overrun, data loss\n"); + sp->rx_count = 0; + return; + } + buf = sp->raw_buf; sp->cooked_buf[sp->rx_count_cooked++] = buf[0] | ((buf[1] << 2) & 0xc0); @@ -902,12 +781,10 @@ static void decode_data(struct sixpack *sp, unsigned char inbyte) /* identify and execute a 6pack priority command byte */ -static void decode_prio_command(struct sixpack *sp, unsigned char cmd) +static void decode_prio_command(struct sixpack *sp, u8 cmd) { - unsigned char channel; - int actual; + ssize_t actual; - channel = cmd & SIXP_CHN_MASK; if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ /* RX and DCD flags can only be set in the same prio command, @@ -946,25 +823,19 @@ static void decode_prio_command(struct sixpack *sp, unsigned char cmd) /* if the state byte has been received, the TNC is present, so the resync timer can be reset. */ - if (sp->tnc_state == TNC_IN_SYNC) { - del_timer(&sp->resync_t); - sp->resync_t.data = (unsigned long) sp; - sp->resync_t.function = resync_tnc; - sp->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); - } + if (sp->tnc_state == TNC_IN_SYNC) + mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT); sp->status1 = cmd & SIXP_PRIO_DATA_MASK; } /* identify and execute a standard 6pack command byte */ -static void decode_std_command(struct sixpack *sp, unsigned char cmd) +static void decode_std_command(struct sixpack *sp, u8 cmd) { - unsigned char checksum = 0, rest = 0, channel; + u8 checksum = 0, rest = 0; short i; - channel = cmd & SIXP_CHN_MASK; switch (cmd & SIXP_CMD_MASK) { /* normal command */ case SIXP_SEOF: if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) { @@ -977,6 +848,7 @@ static void decode_std_command(struct sixpack *sp, unsigned char cmd) sp->led_state = 0x60; /* fill trailing bytes with zeroes */ sp->tty->ops->write(sp->tty, &sp->led_state, 1); + spin_lock_bh(&sp->rxlock); rest = sp->rx_count; if (rest != 0) for (i = rest; i <= 3; i++) @@ -994,6 +866,7 @@ static void decode_std_command(struct sixpack *sp, unsigned char cmd) sp_bump(sp, 0); } sp->rx_count_cooked = 0; + spin_unlock_bh(&sp->rxlock); } break; case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n"); @@ -1008,23 +881,26 @@ static void decode_std_command(struct sixpack *sp, unsigned char cmd) /* decode a 6pack packet */ static void -sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count) +sixpack_decode(struct sixpack *sp, const u8 *pre_rbuff, size_t count) { - unsigned char inbyte; - int count1; + size_t count1; + u8 inbyte; for (count1 = 0; count1 < count; count1++) { inbyte = pre_rbuff[count1]; if (inbyte == SIXP_FOUND_TNC) { tnc_set_sync_state(sp, TNC_IN_SYNC); - del_timer(&sp->resync_t); + timer_delete(&sp->resync_t); } if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) decode_prio_command(sp, inbyte); else if ((inbyte & SIXP_STD_CMD_MASK) != 0) decode_std_command(sp, inbyte); - else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) + else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { + spin_lock_bh(&sp->rxlock); decode_data(sp, inbyte); + spin_unlock_bh(&sp->rxlock); + } } } |
