📄 scc.c
字号:
scc_discard_buffers(scc); return 0;}/* ----> receive frame, called from scc_rxint() <---- */static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb){ if (skb->len == 0) { dev_kfree_skb_irq(skb); return; } scc->dev_stat.rx_packets++; skb->dev = scc->dev; skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; skb->pkt_type = PACKET_HOST; netif_rx(skb); return;}/* ----> transmit frame <---- */static int scc_net_tx(struct sk_buff *skb, struct net_device *dev){ 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->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; } save_flags(flags); cli(); 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/persistance/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); } restore_flags(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; int chan; unsigned char device_name[10]; void *arg; struct scc_channel *scc; scc = (struct scc_channel *) dev->priv; arg = (void *) 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 disable_irq(hwcfg.irq); check_region(scc->ctrl, 1); 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);#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_setup(&SCC_Info[2*Nchips+chan], device_name, 1); } } 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 * *//* ******************************************************************** */static int scc_net_get_info(char *buffer, char **start, off_t offset, int length){ struct scc_channel *scc; struct scc_kiss *kiss; struct scc_stat *stat; int len = 0; off_t pos = 0; off_t begin = 0; int k; len += sprintf(buffer, "z8530drv-"VERSION"\n"); if (!Driver_Initialized) { len += sprintf(buffer+len, "not initialized\n"); goto done; } if (!Nchips) { len += sprintf(buffer+len, "chips missing\n"); goto done; } for (k = 0; k < Nchips*2; k++) { scc = &SCC_Info[k]; stat = &scc->stat; kiss = &scc->kiss; if (!scc->init) continue; /* 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 ## ## ## ## ## ## ## */ len += sprintf(buffer+len, "%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); len += sprintf(buffer+len, "\t%lu %d %d %d %d\n", scc->modem.speed, scc->modem.nrz, scc->modem.clocksrc, kiss->softdcd, stat->bufsize); len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n", stat->rxints, stat->txints, stat->exints, stat->spints); len += sprintf(buffer+len, "\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 len += sprintf(buffer+len, "\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; len += sprintf(buffer+len, "\tW "); for (reg = 0; reg < 16; reg++) len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); len += sprintf(buffer+len, "\n"); len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); for (reg = 3; reg < 8; reg++) len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); len += sprintf(buffer+len, "XX "); for (reg = 9; reg < 16; reg++) len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); len += sprintf(buffer+len, "\n"); }#endif len += sprintf(buffer+len, "\n"); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) break; }done: *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len;} /* ******************************************************************** *//* * Init SCC driver * *//* ******************************************************************** */static int __init scc_init_driver (void){ int result; char devname[10]; printk(banner); sprintf(devname,"%s0", SCC_DriverName); result = scc_net_setup(SCC_Info, devname, 0); if (result) { printk(KERN_ERR "z8530drv: cannot initialize module\n"); return result; } proc_net_create("z8530drv", 0, scc_net_get_info); return 0;}static void __exit scc_cleanup_driver(void){ unsigned long flags; io_port ctrl; int k; struct scc_channel *scc; save_flags(flags); cli(); if (Nchips == 0) { unregister_netdev(SCC_Info[0].dev); kfree(SCC_Info[0].dev); } 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); } 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); kfree(scc->dev); } } for (k=0; k < NR_IRQS ; k++) if (Ivec[k].used) free_irq(k, NULL); if (Vector_Latch) release_region(Vector_Latch, 1); restore_flags(flags); 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 + -