📄 scc.c
字号:
static int scc_net_tx(struct sk_buff *skb, struct device *dev){ struct scc_channel *scc = (struct scc_channel *) dev->priv; unsigned long flags; char kisscmd; if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy) { dev_kfree_skb(skb); return 0; } 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 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; if (scc == NULL || scc->magic != SCC_MAGIC) return -EINVAL; arg = (void *) ifr->ifr_data; if (!Driver_Initialized) { if (cmd == SIOCSCCCFG) { int found = 1; if (!suser()) 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 (!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 = 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 (!suser()) return -EPERM; if (Nchips == 0) return -EINVAL; z8530_init(); return 0; } return -EINVAL; /* confuse the user */ } if (!scc->init) { if (cmd == SIOCSCCCHANINI) { if (!suser()) 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 (!suser()) 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 (!suser()) 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 (!suser()) 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 device *dev, void *addr){ struct sockaddr *sa = (struct sockaddr *) addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); return 0;}/* ----> "hard" header <---- */static int scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len){ return ax25_encapsulate(skb, dev, type, daddr, saddr, len);}/* ----> get statistics <---- */static struct net_device_stats *scc_net_get_stats(struct device *dev){ struct scc_channel *scc = (struct scc_channel *) dev->priv; if (scc == NULL || scc->magic != SCC_MAGIC) return NULL; 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, int dummy){ 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;}#ifdef CONFIG_PROC_FSstruct proc_dir_entry scc_proc_dir_entry = { PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, scc_net_get_info };#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry);#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);#else#define scc_net_procfs_init()#define scc_net_procfs_remove()#endif /* ******************************************************************** *//* * Init SCC driver * *//* ******************************************************************** */__initfunc(int scc_init (void)){ int chip, chan, k, result; char devname[10]; printk(KERN_INFO BANNER); memset(&SCC_ctrl, 0, sizeof(SCC_ctrl)); /* pre-init channel information */ for (chip = 0; chip < SCC_MAXCHIPS; chip++) { memset((char *) &SCC_Info[2*chip ], 0, sizeof(struct scc_channel)); memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel)); for (chan = 0; chan < 2; chan++) SCC_Info[2*chip+chan].magic = SCC_MAGIC; } for (k = 0; k < 16; k++) Ivec[k].used = 0; 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; } scc_net_procfs_init(); return 0;}/* ******************************************************************** *//* * Module support * *//* ******************************************************************** */#ifdef MODULEint init_module(void){ int result = 0; result = scc_init(); if (result == 0) printk(KERN_INFO "Copyright 1993,1998 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n"); return result;}void cleanup_module(void){ long flags; io_port ctrl; int k; struct scc_channel *scc; save_flags(flags); cli(); if (Nchips == 0) unregister_netdev(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) { release_region(scc->ctrl, 1); release_region(scc->data, 1); if (scc->dev) { unregister_netdev(scc->dev); kfree(scc->dev); } } } for (k=0; k < 16 ; k++) if (Ivec[k].used) free_irq(k, NULL); restore_flags(flags); scc_net_procfs_remove();}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -