⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
{	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 + -