📄 yam.c
字号:
.next = yam_seq_next, .stop = yam_seq_stop, .show = yam_seq_show,};static int yam_info_open(struct inode *inode, struct file *file){ return seq_open(file, &yam_seqops);}static struct file_operations yam_info_fops = { .owner = THIS_MODULE, .open = yam_info_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release,};#endif/* --------------------------------------------------------------------- */static struct net_device_stats *yam_get_stats(struct net_device *dev){ struct yam_port *yp; if (!dev) return NULL; yp = netdev_priv(dev); if (yp->magic != YAM_MAGIC) return NULL; /* * Get the current statistics. This may be called with the * card open or closed. */ return &yp->stats;}/* --------------------------------------------------------------------- */static int yam_open(struct net_device *dev){ struct yam_port *yp = netdev_priv(dev); enum uart u; int i; int ret=0; printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq); if (!dev || !yp->bitrate) return -ENXIO; if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT || dev->irq < 2 || dev->irq > 15) { return -ENXIO; } if (!request_region(dev->base_addr, YAM_EXTENT, dev->name)) { printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr); return -EACCES; } if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) { printk(KERN_ERR "%s: cannot find uart type\n", dev->name); ret = -EIO; goto out_release_base; } if (fpga_download(dev->base_addr, yp->bitrate)) { printk(KERN_ERR "%s: cannot init FPGA\n", dev->name); ret = -EIO; goto out_release_base; } outb(0, IER(dev->base_addr)); if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ, dev->name, dev)) { printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); ret = -EBUSY; goto out_release_base; } yam_set_uart(dev); netif_start_queue(dev); yp->slotcnt = yp->slot / 10; /* Reset overruns for all ports - FPGA programming makes overruns */ for (i = 0; i < NR_PORTS; i++) { struct net_device *dev = yam_devs[i]; struct yam_port *yp = netdev_priv(dev); inb(LSR(dev->base_addr)); yp->stats.rx_fifo_errors = 0; } printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq, uart_str[u]); return 0;out_release_base: release_region(dev->base_addr, YAM_EXTENT); return ret;}/* --------------------------------------------------------------------- */static int yam_close(struct net_device *dev){ struct sk_buff *skb; struct yam_port *yp = netdev_priv(dev); if (!dev) return -EINVAL; /* * disable interrupts */ outb(0, IER(dev->base_addr)); outb(1, MCR(dev->base_addr)); /* Remove IRQ handler if last */ free_irq(dev->irq,dev); release_region(dev->base_addr, YAM_EXTENT); netif_stop_queue(dev); while ((skb = skb_dequeue(&yp->send_queue))) dev_kfree_skb(skb); printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n", yam_drvname, dev->base_addr, dev->irq); return 0;}/* --------------------------------------------------------------------- */static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct yam_port *yp = netdev_priv(dev); struct yamdrv_ioctl_cfg yi; struct yamdrv_ioctl_mcs *ym; int ioctl_cmd; if (copy_from_user(&ioctl_cmd, ifr->ifr_data, sizeof(int))) return -EFAULT; if (yp->magic != YAM_MAGIC) return -EINVAL; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (cmd != SIOCDEVPRIVATE) return -EINVAL; switch (ioctl_cmd) { case SIOCYAMRESERVED: return -EINVAL; /* unused */ case SIOCYAMSMCS: if (netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ if ((ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_KERNEL)) == NULL) return -ENOBUFS; ym->bitrate = 9600; if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs))) { kfree(ym); return -EFAULT; } if (ym->bitrate > YAM_MAXBITRATE) { kfree(ym); return -EINVAL; } add_mcs(ym->bits, ym->bitrate); kfree(ym); break; case SIOCYAMSCFG: if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg))) return -EFAULT; if ((yi.cfg.mask & YAM_IOBASE) && netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ if ((yi.cfg.mask & YAM_IRQ) && netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ if (yi.cfg.mask & YAM_IOBASE) { yp->iobase = yi.cfg.iobase; dev->base_addr = yi.cfg.iobase; } if (yi.cfg.mask & YAM_IRQ) { if (yi.cfg.irq > 15) return -EINVAL; yp->irq = yi.cfg.irq; dev->irq = yi.cfg.irq; } if (yi.cfg.mask & YAM_BITRATE) { if (yi.cfg.bitrate > YAM_MAXBITRATE) return -EINVAL; yp->bitrate = yi.cfg.bitrate; } if (yi.cfg.mask & YAM_BAUDRATE) { if (yi.cfg.baudrate > YAM_MAXBAUDRATE) return -EINVAL; yp->baudrate = yi.cfg.baudrate; } if (yi.cfg.mask & YAM_MODE) { if (yi.cfg.mode > YAM_MAXMODE) return -EINVAL; yp->dupmode = yi.cfg.mode; } if (yi.cfg.mask & YAM_HOLDDLY) { if (yi.cfg.holddly > YAM_MAXHOLDDLY) return -EINVAL; yp->holdd = yi.cfg.holddly; } if (yi.cfg.mask & YAM_TXDELAY) { if (yi.cfg.txdelay > YAM_MAXTXDELAY) return -EINVAL; yp->txd = yi.cfg.txdelay; } if (yi.cfg.mask & YAM_TXTAIL) { if (yi.cfg.txtail > YAM_MAXTXTAIL) return -EINVAL; yp->txtail = yi.cfg.txtail; } if (yi.cfg.mask & YAM_PERSIST) { if (yi.cfg.persist > YAM_MAXPERSIST) return -EINVAL; yp->pers = yi.cfg.persist; } if (yi.cfg.mask & YAM_SLOTTIME) { if (yi.cfg.slottime > YAM_MAXSLOTTIME) return -EINVAL; yp->slot = yi.cfg.slottime; yp->slotcnt = yp->slot / 10; } break; case SIOCYAMGCFG: yi.cfg.mask = 0xffffffff; yi.cfg.iobase = yp->iobase; yi.cfg.irq = yp->irq; yi.cfg.bitrate = yp->bitrate; yi.cfg.baudrate = yp->baudrate; yi.cfg.mode = yp->dupmode; yi.cfg.txdelay = yp->txd; yi.cfg.holddly = yp->holdd; yi.cfg.txtail = yp->txtail; yi.cfg.persist = yp->pers; yi.cfg.slottime = yp->slot; if (copy_to_user(ifr->ifr_data, &yi, sizeof(struct yamdrv_ioctl_cfg))) return -EFAULT; break; default: return -EINVAL; } return 0;}/* --------------------------------------------------------------------- */static int yam_set_mac_address(struct net_device *dev, void *addr){ struct sockaddr *sa = (struct sockaddr *) addr; /* addr is an AX.25 shifted ASCII mac address */ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); return 0;}/* --------------------------------------------------------------------- */static void yam_setup(struct net_device *dev){ struct yam_port *yp = netdev_priv(dev); yp->magic = YAM_MAGIC; yp->bitrate = DEFAULT_BITRATE; yp->baudrate = DEFAULT_BITRATE * 2; yp->iobase = 0; yp->irq = 0; yp->dupmode = 0; yp->holdd = DEFAULT_HOLDD; yp->txd = DEFAULT_TXD; yp->txtail = DEFAULT_TXTAIL; yp->slot = DEFAULT_SLOT; yp->pers = DEFAULT_PERS; yp->dev = dev; dev->base_addr = yp->iobase; dev->irq = yp->irq; SET_MODULE_OWNER(dev); dev->open = yam_open; dev->stop = yam_close; dev->do_ioctl = yam_ioctl; dev->hard_start_xmit = yam_send_packet; dev->get_stats = yam_get_stats; skb_queue_head_init(&yp->send_queue); dev->hard_header = ax25_hard_header; dev->rebuild_header = ax25_rebuild_header; dev->set_mac_address = yam_set_mac_address; dev->type = ARPHRD_AX25; dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->mtu = AX25_MTU; dev->addr_len = AX25_ADDR_LEN; memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);}static int __init yam_init_driver(void){ struct net_device *dev; int i, err; char name[IFNAMSIZ]; printk(yam_drvinfo); for (i = 0; i < NR_PORTS; i++) { sprintf(name, "yam%d", i); dev = alloc_netdev(sizeof(struct yam_port), name, yam_setup); if (!dev) { printk(KERN_ERR "yam: cannot allocate net device %s\n", dev->name); err = -ENOMEM; goto error; } err = register_netdev(dev); if (err) { printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name); goto error; } yam_devs[i] = dev; } yam_timer.function = yam_dotimer; yam_timer.expires = jiffies + HZ / 100; add_timer(&yam_timer); proc_net_fops_create("yam", S_IRUGO, &yam_info_fops); return 0; error: while (--i >= 0) { unregister_netdev(yam_devs[i]); free_netdev(yam_devs[i]); } return err;}/* --------------------------------------------------------------------- */static void __exit yam_cleanup_driver(void){ struct yam_mcs *p; int i; del_timer(&yam_timer); for (i = 0; i < NR_PORTS; i++) { struct net_device *dev = yam_devs[i]; if (dev) { unregister_netdev(dev); free_netdev(dev); } } while (yam_data) { p = yam_data; yam_data = yam_data->next; kfree(p); } proc_net_remove("yam");}/* --------------------------------------------------------------------- */MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr");MODULE_DESCRIPTION("Yam amateur radio modem driver");MODULE_LICENSE("GPL");module_init(yam_init_driver);module_exit(yam_cleanup_driver);/* --------------------------------------------------------------------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -