📄 mkiss.c
字号:
count = kiss_esc(p, (unsigned char *)ax->xbuff, len); } } spin_unlock_bh(&ax->buflock); set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); actual = ax->tty->driver->write(ax->tty, ax->xbuff, count); ax->stats.tx_packets++; ax->stats.tx_bytes += actual; ax->dev->trans_start = jiffies; ax->xleft = count - actual; ax->xhead = ax->xbuff + actual;}/* Encapsulate an AX.25 packet and kick it into a TTY queue. */static int ax_xmit(struct sk_buff *skb, struct net_device *dev){ struct mkiss *ax = netdev_priv(dev); if (!netif_running(dev)) { printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); return 1; } if (netif_queue_stopped(dev)) { /* * May be we must check transmitter timeout here ? * 14 Oct 1994 Dmitry Gorodchanin. */ if (time_before(jiffies, dev->trans_start + 20 * HZ)) { /* 20 sec timeout not reached */ return 1; } printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, (ax->tty->driver->chars_in_buffer(ax->tty) || ax->xleft) ? "bad line quality" : "driver error"); ax->xleft = 0; clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); netif_start_queue(dev); } /* We were not busy, so we are now... :-) */ if (skb != NULL) { netif_stop_queue(dev); ax_encaps(dev, skb->data, skb->len); kfree_skb(skb); } return 0;}static int ax_open_dev(struct net_device *dev){ struct mkiss *ax = netdev_priv(dev); if (ax->tty == NULL) return -ENODEV; return 0;}#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)/* Return the frame type ID */static int ax_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len){#ifdef CONFIG_INET if (type != htons(ETH_P_AX25)) return ax25_hard_header(skb, dev, type, daddr, saddr, len);#endif return 0;}static int ax_rebuild_header(struct sk_buff *skb){#ifdef CONFIG_INET return ax25_rebuild_header(skb);#else return 0;#endif}#endif /* CONFIG_{AX25,AX25_MODULE} *//* Open the low-level part of the AX25 channel. Easy! */static int ax_open(struct net_device *dev){ struct mkiss *ax = netdev_priv(dev); unsigned long len; if (ax->tty == NULL) return -ENODEV; /* * Allocate the frame buffers: * * rbuff Receive buffer. * xbuff Transmit buffer. */ len = dev->mtu * 2; /* * allow for arrival of larger UDP packets, even if we say not to * also fixes a bug in which SunOS sends 512-byte packets even with * an MSS of 128 */ if (len < 576 * 2) len = 576 * 2; if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) goto norbuff; if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) goto noxbuff; ax->mtu = dev->mtu + 73; ax->buffsize = len; ax->rcount = 0; ax->xleft = 0; ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */ spin_lock_init(&ax->buflock); return 0;noxbuff: kfree(ax->rbuff);norbuff: return -ENOMEM;}/* Close the low-level part of the AX25 channel. Easy! */static int ax_close(struct net_device *dev){ struct mkiss *ax = netdev_priv(dev); if (ax->tty) clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); netif_stop_queue(dev); return 0;}static struct net_device_stats *ax_get_stats(struct net_device *dev){ struct mkiss *ax = netdev_priv(dev); return &ax->stats;}static void ax_setup(struct net_device *dev){ static char ax25_bcast[AX25_ADDR_LEN] = {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1}; static char ax25_test[AX25_ADDR_LEN] = {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1}; /* Finish setting up the DEVICE info. */ dev->mtu = AX_MTU; dev->hard_start_xmit = ax_xmit; dev->open = ax_open_dev; dev->stop = ax_close; dev->get_stats = ax_get_stats; dev->set_mac_address = ax_set_mac_address; dev->hard_header_len = 0; dev->addr_len = 0; dev->type = ARPHRD_AX25; dev->tx_queue_len = 10; dev->hard_header = ax_header; dev->rebuild_header = ax_rebuild_header; memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); dev->flags = IFF_BROADCAST | IFF_MULTICAST;}/* * 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 mkiss *mkiss_get(struct tty_struct *tty){ struct mkiss *ax; read_lock(&disc_data_lock); ax = tty->disc_data; if (ax) atomic_inc(&ax->refcnt); read_unlock(&disc_data_lock); return ax;}static void mkiss_put(struct mkiss *ax){ if (atomic_dec_and_test(&ax->refcnt)) 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; struct mkiss *ax; int err; if (!capable(CAP_NET_ADMIN)) return -EPERM; dev = alloc_netdev(sizeof(struct mkiss), "ax%d", ax_setup); if (!dev) { err = -ENOMEM; goto out; } ax = netdev_priv(dev); ax->dev = dev; spin_lock_init(&ax->buflock); atomic_set(&ax->refcnt, 1); init_MUTEX_LOCKED(&ax->dead_sem); ax->tty = tty; tty->disc_data = ax; if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); /* Restore default settings */ dev->type = ARPHRD_AX25; /* Perform the low-level AX25 initialization. */ if ((err = ax_open(ax->dev))) { goto out_free_netdev; } 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. */ return 0;out_free_buffers: kfree(ax->rbuff); kfree(ax->xbuff);out_free_netdev: free_netdev(dev);out: return err;}static void mkiss_close(struct tty_struct *tty){ struct mkiss *ax; write_lock(&disc_data_lock); ax = tty->disc_data; tty->disc_data = NULL; write_unlock(&disc_data_lock); if (ax == 0) 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. */ if (!atomic_dec_and_test(&ax->refcnt)) down(&ax->dead_sem); unregister_netdev(ax->dev); /* Free all AX25 frame buffers. */ kfree(ax->rbuff); kfree(ax->xbuff); ax->tty = NULL;}/* Perform I/O control on an active ax25 channel. */static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct mkiss *ax = mkiss_get(tty); struct net_device *dev = ax->dev; unsigned int tmp, err; /* First make sure we're connected. */ if (ax == NULL) return -ENXIO; switch (cmd) { case SIOCGIFNAME: err = copy_to_user((void __user *) arg, ax->dev->name, strlen(ax->dev->name) + 1) ? -EFAULT : 0; break; case SIOCGIFENCAP: err = put_user(4, (int __user *) arg); break; case SIOCSIFENCAP: if (get_user(tmp, (int __user *) arg)) { err = -EFAULT; break; } ax->mode = tmp; 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: { 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; } default: err = -ENOIOCTLCMD; } mkiss_put(ax); return err;}/* * Handle the 'receiver data ready' interrupt. * This function is called by the 'tty_io' module in the kernel when * a block of data has been received, which can now be decapsulated * and sent on to the AX.25 layer for further processing. */static void mkiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count){ struct mkiss *ax = mkiss_get(tty); if (!ax) return; /* * Argh! mtu change time! - costs us the packet part received * at the change */ if (ax->mtu != ax->dev->mtu + 73) ax_changedmtu(ax); /* Read the characters out of the buffer */ while (count--) { if (fp != NULL && *fp++) { if (!test_and_set_bit(AXF_ERROR, &ax->flags)) ax->stats.rx_errors++; cp++; continue; } kiss_unesc(ax, *cp++); } mkiss_put(ax); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->driver->unthrottle) tty->driver->unthrottle(tty);}static int mkiss_receive_room(struct tty_struct *tty){ return 65536; /* We can handle an infinite amount of data. :-) */}/* * Called by the driver when there's room for more data. If we have * more packets to send, we send them here. */static void mkiss_write_wakeup(struct tty_struct *tty){ struct mkiss *ax = mkiss_get(tty); int actual; if (!ax) return; if (ax->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); netif_wake_queue(ax->dev); goto out; } actual = tty->driver->write(tty, ax->xhead, ax->xleft); ax->xleft -= actual; ax->xhead += actual;out: mkiss_put(ax);}static struct tty_ldisc ax_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "mkiss", .open = mkiss_open, .close = mkiss_close, .ioctl = mkiss_ioctl, .receive_buf = mkiss_receive_buf, .receive_room = mkiss_receive_room, .write_wakeup = mkiss_write_wakeup};static char banner[] __initdata = KERN_INFO \ "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n";static char msg_regfail[] __initdata = KERN_ERR \ "mkiss: can't register line discipline (err = %d)\n";static int __init mkiss_init_driver(void){ int status; printk(banner); if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0) printk(msg_regfail); return status;}static const char msg_unregfail[] __exitdata = KERN_ERR \ "mkiss: can't unregister line discipline (err = %d)\n";static void __exit mkiss_exit_driver(void){ int ret; if ((ret = tty_unregister_ldisc(N_AX25))) printk(msg_unregfail, ret);}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);module_init(mkiss_init_driver);module_exit(mkiss_exit_driver);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -