From: Ralf Baechle DL5RB (qdelrah.smrk@kerailya.tunkki.fi)
Date: Tue Nov 02 2004 - 22:31:07 EET
Below another 6pack patch which I've got sitting in my repository for a
while already. I've got one positive report about it so far but would
like to give a little more testing before sending this upstream. The
patch does:
o Update my callsign
o Cleanup the internal for synchronization with a 6pack TNC a little bit
so it won't result in heaps and piles of messages in syslogs as it used
to do.
o Does paranoia checks for the maximum allowable packet size on transmit.
There was a slight chance this might have resulted in a crash in just
the right configuration.
o Verifies the address family for the SIOCSIFHWADDR network interface
ioctl.
o Tries to do proper locking so nothing will access dev->dev_addr in
midflight while changing the layer 2 address.
o Uses an intermediate buffer for the SIOCSIFHWADDR ioctl so an error in
copy_from_user can't result in a half-modified MAC address.
o Swaps the arguments of decode_prio_command, decode_std_command and
decode_data such that for consistency the struct sixpack pointer will be
the first argument like everywhere else.
73 de DL5RB op Ralf
-- Loc. JN47BS / CQ 14 / ITU 28 / DOK A21--- drivers/net/hamradio/6pack.c 2004-10-25 17:07:35.000000000 +0200 +++ drivers/net/hamradio/6pack.c 2004-11-02 19:51:04.738359534 +0100 @@ -4,7 +4,7 @@ * kernel's AX.25 protocol layers. * * Authors: Andreas Könsgen <dic.wchzltv@gulftel.com> - * Ralf Baechle DO1GRB <nwkagt.pomjjwivb@olynet.com> + * Ralf Baechle DL5RB <aytqmroh.cypfnk@chwarzno.oslink.pl> * * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by * @@ -119,11 +119,10 @@ unsigned char status1; unsigned char status2; unsigned char tx_enable; - unsigned char tnc_ok; + unsigned char tnc_state; struct timer_list tx_t; struct timer_list resync_t; - atomic_t refcnt; struct semaphore dead_sem; spinlock_t lock; @@ -134,7 +133,6 @@ static void sp_start_tx_timer(struct sixpack *); static void sixpack_decode(struct sixpack *, unsigned char[], int); static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); -static int sixpack_init(struct net_device *dev); /* * perform the persistence/slottime algorithm for CSMA access. If the @@ -187,6 +185,11 @@ goto out_drop; } + if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */ + msg = "oversized transmit packet!"; + goto out_drop; + } + if (p[0] > 5) { msg = "invalid KISS command"; goto out_drop; @@ -249,8 +252,8 @@ out_drop: sp->stats.tx_dropped++; netif_start_queue(sp->dev); - printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg); - return; + if (net_ratelimit()) + printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg); } /* Encapsulate an IP datagram and kick it into a TTY queue. */ @@ -313,10 +316,20 @@ return &sp->stats; } -static int sp_set_dev_mac_address(struct net_device *dev, void *addr) +static int sp_set_mac_address(struct net_device *dev, void *addr) { - struct sockaddr *sa = addr; - memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); + struct sockaddr_ax25 *sa = addr; + + if (sa->sax25_family != AF_AX25) + return -EINVAL; + + if (!sa->sax25_ndigis) + return -EINVAL; + + spin_lock_irq(&dev->xmit_lock); + memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); + spin_unlock_irq(&dev->xmit_lock); + return 0; } @@ -337,7 +350,6 @@ {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1}; /* Finish setting up the DEVICE info. */ - dev->init = sixpack_init; dev->mtu = SIXP_MTU; dev->hard_start_xmit = sp_xmit; dev->open = sp_open_dev; @@ -345,7 +357,7 @@ dev->stop = sp_close; dev->hard_header = sp_header; dev->get_stats = sp_get_stats; - dev->set_mac_address = sp_set_dev_mac_address; + dev->set_mac_address = sp_set_mac_address; dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->addr_len = AX25_ADDR_LEN; dev->type = ARPHRD_AX25; @@ -359,51 +371,9 @@ SET_MODULE_OWNER(dev); - /* New-style flags. */ dev->flags = 0; } -/* Find a free 6pack channel, and link in this `tty' line. */ -static inline struct sixpack *sp_alloc(void) -{ - struct sixpack *sp = NULL; - struct net_device *dev = NULL; - - dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup); - if (!dev) - return NULL; - - sp = netdev_priv(dev); - sp->dev = dev; - - spin_lock_init(&sp->lock); - - if (register_netdev(dev)) - goto out_free; - - return sp; - -out_free: - printk(KERN_WARNING "sp_alloc() - register_netdev() failure.\n"); - - free_netdev(dev); - - return NULL; -} - -/* Free a 6pack channel. */ -static inline void sp_free(struct sixpack *sp) -{ - void * tmp; - - /* Free all 6pack frame buffers. */ - if ((tmp = xchg(&sp->rbuff, NULL)) != NULL) - kfree(tmp); - if ((tmp = xchg(&sp->xbuff, NULL)) != NULL) - kfree(tmp); -} - - /* Send one completely decapsulated IP datagram to the IP layer. */ /* @@ -482,6 +452,8 @@ struct sixpack *sp = sp_get(tty); int actual; + if (!sp) + return; if (sp->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ @@ -492,7 +464,7 @@ goto out; } - if (sp->tx_enable == 1) { + if (sp->tx_enable) { actual = tty->driver->write(tty, sp->xhead, sp->xleft); sp->xleft -= actual; sp->xhead += actual; @@ -504,80 +476,6 @@ /* ----------------------------------------------------------------------- */ -/* Open the low-level part of the 6pack channel. */ -static int sp_open(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - char *rbuff, *xbuff = NULL; - int err = -ENOBUFS; - unsigned long len; - - /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */ - - len = dev->mtu * 2; - - rbuff = kmalloc(len + 4, GFP_KERNEL); - if (rbuff == NULL) - goto err_exit; - - xbuff = kmalloc(len + 4, GFP_KERNEL); - if (xbuff == NULL) - goto err_exit; - - spin_lock_bh(&sp->lock); - - if (sp->tty == NULL) - return -ENODEV; - - /* - * Allocate the 6pack frame buffers: - * - * rbuff Receive buffer. - * xbuff Transmit buffer. - */ - - rbuff = xchg(&sp->rbuff, rbuff); - xbuff = xchg(&sp->xbuff, xbuff); - - sp->mtu = AX25_MTU + 73; - sp->buffsize = len; - sp->rcount = 0; - sp->rx_count = 0; - sp->rx_count_cooked = 0; - sp->xleft = 0; - - sp->flags = 0; /* Clear ESCAPE & ERROR flags */ - - sp->duplex = 0; - sp->tx_delay = SIXP_TXDELAY; - sp->persistence = SIXP_PERSIST; - sp->slottime = SIXP_SLOTTIME; - sp->led_state = 0x60; - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - sp->tnc_ok = 0; - sp->tx_enable = 0; - - netif_start_queue(dev); - - init_timer(&sp->tx_t); - init_timer(&sp->resync_t); - - spin_unlock_bh(&sp->lock); - - err = 0; - -err_exit: - if (xbuff) - kfree(xbuff); - if (rbuff) - kfree(rbuff); - - return err; -} - - static int sixpack_receive_room(struct tty_struct *tty) { return 65536; /* We can handle an infinite amount of data. :-) */ @@ -629,14 +527,45 @@ * decode_prio_command */ +#define TNC_UNINITIALIZED 0 +#define TNC_UNSYNC_STARTUP 1 +#define TNC_UNSYNCED 2 +#define TNC_IN_SYNC 3 + +static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) +{ + char *msg; + + switch (new_tnc_state) { + default: /* gcc oh piece-o-crap ... */ + case TNC_UNSYNC_STARTUP: + msg = "Synchronizing with TNC"; + break; + case TNC_UNSYNCED: + msg = "Lost synchronization with TNC\n"; + break; + case TNC_IN_SYNC: + msg = "Found TNC"; + break; + } + + sp->tnc_state = new_tnc_state; + printk(KERN_INFO "%s: %s\n", sp->dev->name, msg); +} + +static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) +{ + int old_tnc_state = sp->tnc_state; + + if (old_tnc_state != new_tnc_state) + __tnc_set_sync_state(sp, new_tnc_state); +} + static void resync_tnc(unsigned long channel) { struct sixpack *sp = (struct sixpack *) channel; - struct net_device *dev = sp->dev; static char resync_cmd = 0xe8; - printk(KERN_INFO "%s: resyncing TNC\n", dev->name); - /* clear any data that might have been received */ sp->rx_count = 0; @@ -647,7 +576,6 @@ sp->status = 1; sp->status1 = 1; sp->status2 = 0; - sp->tnc_ok = 0; /* resync the TNC */ @@ -659,9 +587,9 @@ /* 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; + 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); } @@ -669,6 +597,8 @@ { unsigned char inbyte = 0xe8; + tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP); + sp->tty->driver->write(sp->tty, &inbyte, 1); del_timer(&sp->resync_t); @@ -689,31 +619,91 @@ */ static int sixpack_open(struct tty_struct *tty) { + char *rbuff = NULL, *xbuff = NULL; + struct net_device *dev; struct sixpack *sp; + unsigned long len; int err = 0; if (!capable(CAP_NET_ADMIN)) return -EPERM; - sp = sp_alloc(); - if (!sp) { + dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup); + if (!dev) { err = -ENOMEM; goto out; } - sp->tty = tty; + sp = netdev_priv(dev); + sp->dev = dev; + + spin_lock_init(&sp->lock); atomic_set(&sp->refcnt, 1); init_MUTEX_LOCKED(&sp->dead_sem); - /* Perform the low-level 6pack initialization. */ - if ((err = sp_open(sp->dev))) - goto out; + /* !!! 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) { + err = -ENOBUFS; + goto out_free; + } + + spin_lock_bh(&sp->lock); + + 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; + sp->xleft = 0; + + sp->flags = 0; /* Clear ESCAPE & ERROR flags */ + + sp->duplex = 0; + sp->tx_delay = SIXP_TXDELAY; + sp->persistence = SIXP_PERSIST; + sp->slottime = SIXP_SLOTTIME; + sp->led_state = 0x60; + sp->status = 1; + sp->status1 = 1; + sp->status2 = 0; + sp->tx_enable = 0; + + netif_start_queue(dev); + + init_timer(&sp->tx_t); + init_timer(&sp->resync_t); + + spin_unlock_bh(&sp->lock); /* Done. We have linked the TTY line to a channel. */ tty->disc_data = sp; + /* Now we're ready to register. */ + if (register_netdev(dev)) + goto out_free; + tnc_init(sp); + return 0; + +out_free: + kfree(xbuff); + kfree(rbuff); + + if (dev) + free_netdev(dev); + out: return err; } @@ -727,7 +717,7 @@ */ static void sixpack_close(struct tty_struct *tty) { - struct sixpack *sp = (struct sixpack *) tty->disc_data; + struct sixpack *sp; write_lock(&disc_data_lock); sp = tty->disc_data; @@ -743,16 +733,14 @@ if (!atomic_dec_and_test(&sp->refcnt)) down(&sp->dead_sem); + unregister_netdev(sp->dev); + del_timer(&sp->tx_t); del_timer(&sp->resync_t); - sp_free(sp); - unregister_netdev(sp->dev); -} - -static int sp_set_mac_address(struct net_device *dev, void __user *addr) -{ - return copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN) ? -EFAULT : 0; + /* Free all 6pack frame buffers. */ + kfree(sp->rbuff); + kfree(sp->xbuff); } /* Perform I/O control on an active 6pack channel. */ @@ -760,6 +748,7 @@ unsigned int cmd, unsigned long arg) { struct sixpack *sp = sp_get(tty); + struct net_device *dev = sp->dev; unsigned int tmp, err; if (!sp) @@ -767,8 +756,8 @@ switch(cmd) { case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, sp->dev->name, - strlen(sp->dev->name) + 1) ? -EFAULT : 0; + err = copy_to_user((void *) arg, dev->name, + strlen(dev->name) + 1) ? -EFAULT : 0; break; case SIOCGIFENCAP: @@ -782,16 +771,30 @@ } sp->mode = tmp; - sp->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */ - sp->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3; - sp->dev->type = ARPHRD_AX25; + dev->addr_len = AX25_ADDR_LEN; + dev->hard_header_len = AX25_KISS_HEADER_LEN + + AX25_MAX_HEADER_LEN + 3; + dev->type = ARPHRD_AX25; err = 0; break; - case SIOCSIFHWADDR: - err = sp_set_mac_address(sp->dev, (void __user *) arg); + case SIOCSIFHWADDR: { + char addr[AX25_ADDR_LEN]; + + if (copy_from_user(&addr, + (void __user *) arg, AX25_ADDR_LEN)) { + err = -EFAULT; + break; + } + + spin_lock_irq(&dev->xmit_lock); + memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN); + spin_unlock_irq(&dev->xmit_lock); + + err = 0; break; + } /* Allow stty to read, but not set, the serial port */ case TCGETS: @@ -800,7 +803,7 @@ break; default: - return -ENOIOCTLCMD; + err = -ENOIOCTLCMD; } sp_put(sp); @@ -808,7 +811,6 @@ return err; } -/* Fill in our line protocol discipline */ static struct tty_ldisc sp_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, @@ -823,8 +825,10 @@ /* Initialize 6pack control device -- register 6pack line discipline */ -static char msg_banner[] __initdata = KERN_INFO "AX.25: 6pack driver, " SIXPACK_VERSION "\n"; -static char msg_regfail[] __initdata = KERN_ERR "6pack: can't register line discipline (err = %d)\n"; +static char msg_banner[] __initdata = KERN_INFO \ + "AX.25: 6pack driver, " SIXPACK_VERSION "\n"; +static char msg_regfail[] __initdata = KERN_ERR \ + "6pack: can't register line discipline (err = %d)\n"; static int __init sixpack_init_driver(void) { @@ -839,7 +843,8 @@ return status; } -static const char msg_unregfail[] __exitdata = KERN_ERR "6pack: can't unregister line discipline (err = %d)\n"; +static const char msg_unregfail[] __exitdata = KERN_ERR \ + "6pack: can't unregister line discipline (err = %d)\n"; static void __exit sixpack_exit_driver(void) { @@ -849,22 +854,6 @@ printk(msg_unregfail, ret); } -/* Initialize the 6pack driver. Called by DDI. */ -static int sixpack_init(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (sp == NULL) /* Allocation failed ?? */ - return -ENODEV; - - /* Set up the "6pack Control Block". (And clear statistics) */ - - memset(sp, 0, sizeof (struct sixpack)); - sp->dev = dev; - - return 0; -} - /* encode an AX.25 packet into 6pack */ static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, @@ -905,7 +894,7 @@ /* decode 4 sixpack-encoded bytes into 3 data bytes */ -static void decode_data(unsigned char inbyte, struct sixpack *sp) +static void decode_data(struct sixpack *sp, unsigned char inbyte) { unsigned char *buf; @@ -927,7 +916,7 @@ /* identify and execute a 6pack priority command byte */ -static void decode_prio_command(unsigned char cmd, struct sixpack *sp) +static void decode_prio_command(struct sixpack *sp, unsigned char cmd) { unsigned char channel; int actual; @@ -971,11 +960,11 @@ /* if the state byte has been received, the TNC is present, so the resync timer can be reset. */ - if (sp->tnc_ok == 1) { + 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; + 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); } @@ -984,7 +973,7 @@ /* identify and execute a standard 6pack command byte */ -static void decode_std_command(unsigned char cmd, struct sixpack *sp) +static void decode_std_command(struct sixpack *sp, unsigned char cmd) { unsigned char checksum = 0, rest = 0, channel; short i; @@ -1005,7 +994,7 @@ rest = sp->rx_count; if (rest != 0) for (i = rest; i <= 3; i++) - decode_data(0, sp); + decode_data(sp, 0); if (rest == 2) sp->rx_count_cooked -= 2; else if (rest == 3) @@ -1033,7 +1022,7 @@ /* decode a 6pack packet */ static void -sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count) +sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count) { unsigned char inbyte; int count1; @@ -1041,16 +1030,15 @@ for (count1 = 0; count1 < count; count1++) { inbyte = pre_rbuff[count1]; if (inbyte == SIXP_FOUND_TNC) { - printk(KERN_INFO "6pack: TNC found.\n"); - sp->tnc_ok = 1; + tnc_set_sync_state(sp, TNC_IN_SYNC); del_timer(&sp->resync_t); } if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) - decode_prio_command(inbyte, sp); + decode_prio_command(sp, inbyte); else if ((inbyte & SIXP_STD_CMD_MASK) != 0) - decode_std_command(inbyte, sp); + decode_std_command(sp, inbyte); else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) - decode_data(inbyte, sp); + decode_data(sp, inbyte); } } - To unsubscribe from this list: send the line "unsubscribe linux-hams" in the body of a message to terhi.victor@logonet.com More majordomo info at http://vger.kernel.org/majordomo-info.html
This archive was generated by hypermail 2b30 : Tue Nov 02 2004 - 21:44:04 EET