📄 scc.c
字号:
{ struct scc_channel *scc = (struct scc_channel *) dev->priv; unsigned long flags; char kisscmd; if (skb->len > scc->stat.bufsize || skb->len < 2) { scc->dev_stat.tx_dropped++; /* bogus frame */ dev_kfree_skb(skb); return 0; } scc->dev_stat.tx_packets++; scc->dev_stat.tx_bytes += skb->len; scc->stat.txframes++; kisscmd = *skb->data & 0x1f; skb_pull(skb, 1); if (kisscmd) { scc_set_param(scc, kisscmd, *skb->data); dev_kfree_skb(skb); return 0; } spin_lock_irqsave(&scc->lock, flags); if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { struct sk_buff *skb_del; skb_del = skb_dequeue(&scc->tx_queue); dev_kfree_skb(skb_del); } skb_queue_tail(&scc->tx_queue, skb); dev->trans_start = jiffies; /* * Start transmission if the trx state is idle or * t_idle hasn't expired yet. Use dwait/persistence/slottime * algorithm for normal halfduplex operation. */ if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) { scc->stat.tx_state = TXS_BUSY; if (scc->kiss.fulldup == KISS_DUPLEX_HALF) __scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); else __scc_start_tx_timer(scc, t_dwait, 0); } spin_unlock_irqrestore(&scc->lock, flags); return 0;}/* ----> ioctl functions <---- *//* * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg * SIOCSCCINI - initialize driver arg: --- * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg */static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ struct scc_kiss_cmd kiss_cmd; struct scc_mem_config memcfg; struct scc_hw_config hwcfg; struct scc_calibrate cal; struct scc_channel *scc = (struct scc_channel *) dev->priv; int chan; unsigned char device_name[IFNAMSIZ]; void __user *arg = ifr->ifr_data; if (!Driver_Initialized) { if (cmd == SIOCSCCCFG) { int found = 1; if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (!arg) return -EFAULT; if (Nchips >= SCC_MAXCHIPS) return -EINVAL; if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) return -EFAULT; if (hwcfg.irq == 2) hwcfg.irq = 9; if (hwcfg.irq < 0 || hwcfg.irq >= NR_IRQS) return -EINVAL; if (!Ivec[hwcfg.irq].used && hwcfg.irq) { if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); else Ivec[hwcfg.irq].used = 1; } if (hwcfg.vector_latch && !Vector_Latch) { if (!request_region(hwcfg.vector_latch, 1, "scc vector latch")) printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch); else Vector_Latch = hwcfg.vector_latch; } if (hwcfg.clock == 0) hwcfg.clock = SCC_DEFAULT_CLOCK;#ifndef SCC_DONT_CHECK if(request_region(hwcfg.ctrl_a, 1, "scc-probe")) { disable_irq(hwcfg.irq); Outb(hwcfg.ctrl_a, 0); OutReg(hwcfg.ctrl_a, R9, FHWRES); udelay(100); OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ udelay(5); if (InReg(hwcfg.ctrl_a,R13) != 0x55) found = 0; enable_irq(hwcfg.irq); release_region(hwcfg.ctrl_a, 1); } else found = 0;#endif if (found) { SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; SCC_Info[2*Nchips ].data = hwcfg.data_a; SCC_Info[2*Nchips ].irq = hwcfg.irq; SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; SCC_Info[2*Nchips+1].data = hwcfg.data_b; SCC_Info[2*Nchips+1].irq = hwcfg.irq; SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; SCC_ctrl[Nchips].irq = hwcfg.irq; } for (chan = 0; chan < 2; chan++) { sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); SCC_Info[2*Nchips+chan].special = hwcfg.special; SCC_Info[2*Nchips+chan].clock = hwcfg.clock; SCC_Info[2*Nchips+chan].brand = hwcfg.brand; SCC_Info[2*Nchips+chan].option = hwcfg.option; SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;#ifdef SCC_DONT_CHECK printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", device_name, SCC_Info[2*Nchips+chan].data, SCC_Info[2*Nchips+chan].ctrl);#else printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", device_name, chan? hwcfg.data_b : hwcfg.data_a, chan? hwcfg.ctrl_b : hwcfg.ctrl_a, found? "found" : "missing");#endif if (found) { request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); if (Nchips+chan != 0 && scc_net_alloc(device_name, &SCC_Info[2*Nchips+chan])) return -EINVAL; } } if (found) Nchips++; return 0; } if (cmd == SIOCSCCINI) { if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (Nchips == 0) return -EINVAL; z8530_init(); return 0; } return -EINVAL; /* confuse the user */ } if (!scc->init) { if (cmd == SIOCSCCCHANINI) { if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!arg) return -EINVAL; scc->stat.bufsize = SCC_BUFSIZE; if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) return -EINVAL; /* default KISS Params */ if (scc->modem.speed < 4800) { scc->kiss.txdelay = 36; /* 360 ms */ scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ scc->kiss.slottime = 16; /* 160 ms */ scc->kiss.tailtime = 4; /* minimal reasonable value */ scc->kiss.fulldup = 0; /* CSMA */ scc->kiss.waittime = 50; /* 500 ms */ scc->kiss.maxkeyup = 10; /* 10 s */ scc->kiss.mintime = 3; /* 3 s */ scc->kiss.idletime = 30; /* 30 s */ scc->kiss.maxdefer = 120; /* 2 min */ scc->kiss.softdcd = 0; /* hardware dcd */ } else { scc->kiss.txdelay = 10; /* 100 ms */ scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ scc->kiss.slottime = 8; /* 160 ms */ scc->kiss.tailtime = 1; /* minimal reasonable value */ scc->kiss.fulldup = 0; /* CSMA */ scc->kiss.waittime = 50; /* 500 ms */ scc->kiss.maxkeyup = 7; /* 7 s */ scc->kiss.mintime = 3; /* 3 s */ scc->kiss.idletime = 30; /* 30 s */ scc->kiss.maxdefer = 120; /* 2 min */ scc->kiss.softdcd = 0; /* hardware dcd */ } scc->tx_buff = NULL; skb_queue_head_init(&scc->tx_queue); scc->init = 1; return 0; } return -EINVAL; } switch(cmd) { case SIOCSCCRESERVED: return -ENOIOCTLCMD; case SIOCSCCSMEM: if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) return -EINVAL; scc->stat.bufsize = memcfg.bufsize; return 0; case SIOCSCCGSTAT: if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) return -EINVAL; return 0; case SIOCSCCGKISS: if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) return -EINVAL; kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) return -EINVAL; return 0; case SIOCSCCSKISS: if (!capable(CAP_NET_ADMIN)) return -EPERM; if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) return -EINVAL; return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); case SIOCSCCCAL: if (!capable(CAP_SYS_RAWIO)) return -EPERM; if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) return -EINVAL; scc_start_calibrate(scc, cal.time, cal.pattern); return 0; default: return -ENOIOCTLCMD; } return -EINVAL;}/* ----> set interface callsign <---- */static int scc_net_set_mac_address(struct net_device *dev, void *addr){ struct sockaddr *sa = (struct sockaddr *) addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); return 0;}/* ----> get statistics <---- */static struct net_device_stats *scc_net_get_stats(struct net_device *dev){ struct scc_channel *scc = (struct scc_channel *) dev->priv; scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; return &scc->dev_stat;}/* ******************************************************************** *//* * dump statistics to /proc/net/z8530drv * *//* ******************************************************************** */#ifdef CONFIG_PROC_FSstatic inline struct scc_channel *scc_net_seq_idx(loff_t pos){ int k; for (k = 0; k < Nchips*2; ++k) { if (!SCC_Info[k].init) continue; if (pos-- == 0) return &SCC_Info[k]; } return NULL;}static void *scc_net_seq_start(struct seq_file *seq, loff_t *pos){ return *pos ? scc_net_seq_idx(*pos - 1) : SEQ_START_TOKEN; }static void *scc_net_seq_next(struct seq_file *seq, void *v, loff_t *pos){ unsigned k; struct scc_channel *scc = v; ++*pos; for (k = (v == SEQ_START_TOKEN) ? 0 : (scc - SCC_Info)+1; k < Nchips*2; ++k) { if (SCC_Info[k].init) return &SCC_Info[k]; } return NULL;}static void scc_net_seq_stop(struct seq_file *seq, void *v){}static int scc_net_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) { seq_puts(seq, "z8530drv-"VERSION"\n"); } else if (!Driver_Initialized) { seq_puts(seq, "not initialized\n"); } else if (!Nchips) { seq_puts(seq, "chips missing\n"); } else { const struct scc_channel *scc = v; const struct scc_stat *stat = &scc->stat; const struct scc_kiss *kiss = &scc->kiss; /* dev data ctrl irq clock brand enh vector special option * baud nrz clocksrc softdcd bufsize * rxints txints exints spints * rcvd rxerrs over / xmit txerrs under / nospace bufsize * txd pers slot tail ful wait min maxk idl defr txof grp * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## */ seq_printf(seq, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", scc->dev->name, scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, scc->enhanced, Vector_Latch, scc->special, scc->option); seq_printf(seq, "\t%lu %d %d %d %d\n", scc->modem.speed, scc->modem.nrz, scc->modem.clocksrc, kiss->softdcd, stat->bufsize); seq_printf(seq, "\t%lu %lu %lu %lu\n", stat->rxints, stat->txints, stat->exints, stat->spints); seq_printf(seq, "\t%lu %lu %d / %lu %lu %d / %d %d\n", stat->rxframes, stat->rxerrs, stat->rx_over, stat->txframes, stat->txerrs, stat->tx_under, stat->nospace, stat->tx_state);#define K(x) kiss->x seq_printf(seq, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", K(txdelay), K(persist), K(slottime), K(tailtime), K(fulldup), K(waittime), K(mintime), K(maxkeyup), K(idletime), K(maxdefer), K(tx_inhibit), K(group));#undef K#ifdef SCC_DEBUG { int reg; seq_printf(seq, "\tW "); for (reg = 0; reg < 16; reg++) seq_printf(seq, "%2.2x ", scc->wreg[reg]); seq_printf(seq, "\n"); seq_printf(seq, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); for (reg = 3; reg < 8; reg++) seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); seq_printf(seq, "XX "); for (reg = 9; reg < 16; reg++) seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); seq_printf(seq, "\n"); }#endif seq_putc(seq, '\n'); } return 0;}static struct seq_operations scc_net_seq_ops = { .start = scc_net_seq_start, .next = scc_net_seq_next, .stop = scc_net_seq_stop, .show = scc_net_seq_show,};static int scc_net_seq_open(struct inode *inode, struct file *file){ return seq_open(file, &scc_net_seq_ops);}static struct file_operations scc_net_seq_fops = { .owner = THIS_MODULE, .open = scc_net_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private,};#endif /* CONFIG_PROC_FS */ /* ******************************************************************** *//* * Init SCC driver * *//* ******************************************************************** */static int __init scc_init_driver (void){ char devname[IFNAMSIZ]; printk(banner); sprintf(devname,"%s0", SCC_DriverName); rtnl_lock(); if (scc_net_alloc(devname, SCC_Info)) { rtnl_unlock(); printk(KERN_ERR "z8530drv: cannot initialize module\n"); return -EIO; } rtnl_unlock(); proc_net_fops_create("z8530drv", 0, &scc_net_seq_fops); return 0;}static void __exit scc_cleanup_driver(void){ io_port ctrl; int k; struct scc_channel *scc; struct net_device *dev; if (Nchips == 0 && (dev = SCC_Info[0].dev)) { unregister_netdev(dev); free_netdev(dev); } /* Guard against chip prattle */ local_irq_disable(); for (k = 0; k < Nchips; k++) if ( (ctrl = SCC_ctrl[k].chan_A) ) { Outb(ctrl, 0); OutReg(ctrl,R9,FHWRES); /* force hardware reset */ udelay(50); } /* To unload the port must be closed so no real IRQ pending */ for (k=0; k < NR_IRQS ; k++) if (Ivec[k].used) free_irq(k, NULL); local_irq_enable(); /* Now clean up */ for (k = 0; k < Nchips*2; k++) { scc = &SCC_Info[k]; if (scc->ctrl) { release_region(scc->ctrl, 1); release_region(scc->data, 1); } if (scc->dev) { unregister_netdev(scc->dev); free_netdev(scc->dev); } } if (Vector_Latch) release_region(Vector_Latch, 1); proc_net_remove("z8530drv");}MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards");MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio");MODULE_LICENSE("GPL");module_init(scc_init_driver);module_exit(scc_cleanup_driver);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -