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

📄 vlsi_ir.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 4 页
字号:
/********************************************************************* * *	vlsi_ir.c:	VLSI82C147 PCI IrDA controller driver for Linux * *	Copyright (c) 2001-2003 Martin Diehl * *	This program is free software; you can redistribute it and/or  *	modify it under the terms of the GNU General Public License as  *	published by the Free Software Foundation; either version 2 of  *	the License, or (at your option) any later version. * *	This program is distributed in the hope that it will be useful, *	but WITHOUT ANY WARRANTY; without even the implied warranty of *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *	GNU General Public License for more details. * *	You should have received a copy of the GNU General Public License  *	along with this program; if not, write to the Free Software  *	Foundation, Inc., 59 Temple Place, Suite 330, Boston,  *	MA 02111-1307 USA * ********************************************************************/#include <linux/module.h> #define DRIVER_NAME 		"vlsi_ir"#define DRIVER_VERSION		"v0.5"#define DRIVER_DESCRIPTION	"IrDA SIR/MIR/FIR driver for VLSI 82C147"#define DRIVER_AUTHOR		"Martin Diehl <info@mdiehl.de>"MODULE_DESCRIPTION(DRIVER_DESCRIPTION);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_LICENSE("GPL");/********************************************************/#include <linux/kernel.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/netdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#include <asm/byteorder.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include <net/irda/wrapper.h>#include <net/irda/crc.h>#include "vlsi_ir.h"/********************************************************/static /* const */ char drivername[] = DRIVER_NAME;static struct pci_device_id vlsi_irda_table [] = {	{		.class =        PCI_CLASS_WIRELESS_IRDA << 8,		.class_mask =	PCI_CLASS_SUBCLASS_MASK << 8, 		.vendor =       PCI_VENDOR_ID_VLSI,		.device =       PCI_DEVICE_ID_VLSI_82C147,		.subvendor = 	PCI_ANY_ID,		.subdevice =	PCI_ANY_ID,	},	{ /* all zeroes */ }};MODULE_DEVICE_TABLE(pci, vlsi_irda_table);/********************************************************//*	clksrc: which clock source to be used *		0: auto - try PLL, fallback to 40MHz XCLK *		1: on-chip 48MHz PLL *		2: external 48MHz XCLK *		3: external 40MHz XCLK (HP OB-800) */static int clksrc = 0;			/* default is 0(auto) */module_param(clksrc, int, 0);MODULE_PARM_DESC(clksrc, "clock input source selection");/*	ringsize: size of the tx and rx descriptor rings *		independent for tx and rx *		specify as ringsize=tx[,rx] *		allowed values: 4, 8, 16, 32, 64 *		Due to the IrDA 1.x max. allowed window size=7, *		there should be no gain when using rings larger than 8 */static int ringsize[] = {8,8};		/* default is tx=8 / rx=8 */module_param_array(ringsize, int, NULL, 0);MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size");/*	sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits *		0: very short, 1.5us (exception: 6us at 2.4 kbaud) *		1: nominal 3/16 bittime width *	note: IrDA compliant peer devices should be happy regardless *		which one is used. Primary goal is to save some power *		on the sender's side - at 9.6kbaud for example the short *		pulse width saves more than 90% of the transmitted IR power. */static int sirpulse = 1;		/* default is 3/16 bittime */module_param(sirpulse, int, 0);MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning");/*	qos_mtt_bits: encoded min-turn-time value we require the peer device *		 to use before transmitting to us. "Type 1" (per-station) *		 bitfield according to IrLAP definition (section 6.6.8) *		 Don't know which transceiver is used by my OB800 - the *		 pretty common HP HDLS-1100 requires 1 msec - so lets use this. */static int qos_mtt_bits = 0x07;		/* default is 1 ms or more */module_param(qos_mtt_bits, int, 0);MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time");/********************************************************/static void vlsi_reg_debug(unsigned iobase, const char *s){	int	i;	printk(KERN_DEBUG "%s: ", s);	for (i = 0; i < 0x20; i++)		printk("%02x", (unsigned)inb((iobase+i)));	printk("\n");}static void vlsi_ring_debug(struct vlsi_ring *r){	struct ring_descr *rd;	unsigned i;	printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n",		__FUNCTION__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw);	printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __FUNCTION__,		atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask);	for (i = 0; i < r->size; i++) {		rd = &r->rd[i];		printk(KERN_DEBUG "%s - ring descr %u: ", __FUNCTION__, i);		printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw);		printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n",			__FUNCTION__, (unsigned) rd_get_status(rd),			(unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd));	}}/********************************************************//* needed regardless of CONFIG_PROC_FS */static struct proc_dir_entry *vlsi_proc_root = NULL;#ifdef CONFIG_PROC_FSstatic void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev){	unsigned iobase = pci_resource_start(pdev, 0);	unsigned i;	seq_printf(seq, "\n%s (vid/did: %04x/%04x)\n",		   pci_name(pdev), (int)pdev->vendor, (int)pdev->device);	seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state);	seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n",		   pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask);	seq_printf(seq, "hw registers: ");	for (i = 0; i < 0x20; i++)		seq_printf(seq, "%02x", (unsigned)inb((iobase+i)));	seq_printf(seq, "\n");}		static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev){	vlsi_irda_dev_t *idev = ndev->priv;	u8 byte;	u16 word;	unsigned delta1, delta2;	struct timeval now;	unsigned iobase = ndev->base_addr;	seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name,		netif_device_present(ndev) ? "attached" : "detached", 		netif_running(ndev) ? "running" : "not running",		netif_carrier_ok(ndev) ? "carrier ok" : "no carrier",		netif_queue_stopped(ndev) ? "queue stopped" : "queue running");	if (!netif_running(ndev))		return;	seq_printf(seq, "\nhw-state:\n");	pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte);	seq_printf(seq, "IRMISC:%s%s%s uart%s",		(byte&IRMISC_IRRAIL) ? " irrail" : "",		(byte&IRMISC_IRPD) ? " irpd" : "",		(byte&IRMISC_UARTTST) ? " uarttest" : "",		(byte&IRMISC_UARTEN) ? "@" : " disabled\n");	if (byte&IRMISC_UARTEN) {		seq_printf(seq, "0x%s\n",			(byte&2) ? ((byte&1) ? "3e8" : "2e8")				 : ((byte&1) ? "3f8" : "2f8"));	}	pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte);	seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n",		(byte&CLKCTL_PD_INV) ? "powered" : "down",		(byte&CLKCTL_LOCK) ? " locked" : "",		(byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "",		(byte&CLKCTL_CLKSTP) ? "stopped" : "running",		(byte&CLKCTL_WAKE) ? "enabled" : "disabled");	pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte);	seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte);	byte = inb(iobase+VLSI_PIO_IRINTR);	seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n",		(byte&IRINTR_ACTEN) ? " ACTEN" : "",		(byte&IRINTR_RPKTEN) ? " RPKTEN" : "",		(byte&IRINTR_TPKTEN) ? " TPKTEN" : "",		(byte&IRINTR_OE_EN) ? " OE_EN" : "",		(byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "",		(byte&IRINTR_RPKTINT) ? " RPKTINT" : "",		(byte&IRINTR_TPKTINT) ? " TPKTINT" : "",		(byte&IRINTR_OE_INT) ? " OE_INT" : "");	word = inw(iobase+VLSI_PIO_RINGPTR);	seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word));	word = inw(iobase+VLSI_PIO_RINGBASE);	seq_printf(seq, "RINGBASE: busmap=0x%08x\n",		((unsigned)word << 10)|(MSTRPAGE_VALUE<<24));	word = inw(iobase+VLSI_PIO_RINGSIZE);	seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word),		RINGSIZE_TO_TXSIZE(word));	word = inw(iobase+VLSI_PIO_IRCFG);	seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n",		(word&IRCFG_LOOP) ? " LOOP" : "",		(word&IRCFG_ENTX) ? " ENTX" : "",		(word&IRCFG_ENRX) ? " ENRX" : "",		(word&IRCFG_MSTR) ? " MSTR" : "",		(word&IRCFG_RXANY) ? " RXANY" : "",		(word&IRCFG_CRC16) ? " CRC16" : "",		(word&IRCFG_FIR) ? " FIR" : "",		(word&IRCFG_MIR) ? " MIR" : "",		(word&IRCFG_SIR) ? " SIR" : "",		(word&IRCFG_SIRFILT) ? " SIRFILT" : "",		(word&IRCFG_SIRTEST) ? " SIRTEST" : "",		(word&IRCFG_TXPOL) ? " TXPOL" : "",		(word&IRCFG_RXPOL) ? " RXPOL" : "");	word = inw(iobase+VLSI_PIO_IRENABLE);	seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n",		(word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "",		(word&IRENABLE_CFGER) ? " CFGERR" : "",		(word&IRENABLE_FIR_ON) ? " FIR_ON" : "",		(word&IRENABLE_MIR_ON) ? " MIR_ON" : "",		(word&IRENABLE_SIR_ON) ? " SIR_ON" : "",		(word&IRENABLE_ENTXST) ? " ENTXST" : "",		(word&IRENABLE_ENRXST) ? " ENRXST" : "",		(word&IRENABLE_CRC16_ON) ? " CRC16_ON" : "");	word = inw(iobase+VLSI_PIO_PHYCTL);	seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n",		(unsigned)PHYCTL_TO_BAUD(word),		(unsigned)PHYCTL_TO_PLSWID(word),		(unsigned)PHYCTL_TO_PREAMB(word));	word = inw(iobase+VLSI_PIO_NPHYCTL);	seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n",		(unsigned)PHYCTL_TO_BAUD(word),		(unsigned)PHYCTL_TO_PLSWID(word),		(unsigned)PHYCTL_TO_PREAMB(word));	word = inw(iobase+VLSI_PIO_MAXPKT);	seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word);	word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK;	seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word);	seq_printf(seq, "\nsw-state:\n");	seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, 		(idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR"));	do_gettimeofday(&now);	if (now.tv_usec >= idev->last_rx.tv_usec) {		delta2 = now.tv_usec - idev->last_rx.tv_usec;		delta1 = 0;	}	else {		delta2 = 1000000 + now.tv_usec - idev->last_rx.tv_usec;		delta1 = 1;	}	seq_printf(seq, "last rx: %lu.%06u sec\n",		now.tv_sec - idev->last_rx.tv_sec - delta1, delta2);		seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu",		idev->stats.rx_packets, idev->stats.rx_bytes, idev->stats.rx_errors,		idev->stats.rx_dropped);	seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n",		idev->stats.rx_over_errors, idev->stats.rx_length_errors,		idev->stats.rx_frame_errors, idev->stats.rx_crc_errors);	seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n",		idev->stats.tx_packets, idev->stats.tx_bytes, idev->stats.tx_errors,		idev->stats.tx_dropped, idev->stats.tx_fifo_errors);}		static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r){	struct ring_descr *rd;	unsigned i, j;	int h, t;	seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n",		r->size, r->mask, r->len, r->dir, r->rd[0].hw);	h = atomic_read(&r->head) & r->mask;	t = atomic_read(&r->tail) & r->mask;	seq_printf(seq, "head = %d / tail = %d ", h, t);	if (h == t)		seq_printf(seq, "(empty)\n");	else {		if (((t+1)&r->mask) == h)			seq_printf(seq, "(full)\n");		else			seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask)); 		rd = &r->rd[h];		j = (unsigned) rd_get_count(rd);		seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n",				h, (unsigned)rd_get_status(rd), j);		if (j > 0) {			seq_printf(seq, "   data:");			if (j > 20)				j = 20;			for (i = 0; i < j; i++)				seq_printf(seq, " %02x", (unsigned)((unsigned char *)rd->buf)[i]);			seq_printf(seq, "\n");		}	}	for (i = 0; i < r->size; i++) {		rd = &r->rd[i];		seq_printf(seq, "> ring descr %u: ", i);		seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw);		seq_printf(seq, "  hw: status=%02x count=%u busaddr=0x%08x\n",			(unsigned) rd_get_status(rd),			(unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd));	}}static int vlsi_seq_show(struct seq_file *seq, void *v){	struct net_device *ndev = seq->private;	vlsi_irda_dev_t *idev = ndev->priv;	unsigned long flags;	seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION);	seq_printf(seq, "clksrc: %s\n", 		(clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK")			    : ((clksrc==1)?"48MHz PLL":"autodetect"));	seq_printf(seq, "ringsize: tx=%d / rx=%d\n",		ringsize[0], ringsize[1]);	seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short");	seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits);	spin_lock_irqsave(&idev->lock, flags);	if (idev->pdev != NULL) {		vlsi_proc_pdev(seq, idev->pdev);		if (idev->pdev->current_state == 0)			vlsi_proc_ndev(seq, ndev);		else			seq_printf(seq, "\nPCI controller down - resume_ok = %d\n",				idev->resume_ok);		if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) {			seq_printf(seq, "\n--------- RX ring -----------\n\n");			vlsi_proc_ring(seq, idev->rx_ring);			seq_printf(seq, "\n--------- TX ring -----------\n\n");			vlsi_proc_ring(seq, idev->tx_ring);		}	}	seq_printf(seq, "\n");	spin_unlock_irqrestore(&idev->lock, flags);	return 0;}static int vlsi_seq_open(struct inode *inode, struct file *file){	return single_open(file, vlsi_seq_show, PDE(inode)->data);}static struct file_operations vlsi_proc_fops = {	.owner	 = THIS_MODULE,	.open    = vlsi_seq_open,	.read    = seq_read,	.llseek  = seq_lseek,	.release = single_release,};#define VLSI_PROC_FOPS		(&vlsi_proc_fops)#else	/* CONFIG_PROC_FS */#define VLSI_PROC_FOPS		NULL#endif/********************************************************/static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap,						unsigned size, unsigned len, int dir){	struct vlsi_ring *r;	struct ring_descr *rd;	unsigned	i, j;	dma_addr_t	busaddr;	if (!size  ||  ((size-1)&size)!=0)	/* must be >0 and power of 2 */		return NULL;	r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL);	if (!r)		return NULL;	memset(r, 0, sizeof(*r));	r->pdev = pdev;	r->dir = dir;	r->len = len;	r->rd = (struct ring_descr *)(r+1);	r->mask = size - 1;	r->size = size;	atomic_set(&r->head, 0);	atomic_set(&r->tail, 0);	for (i = 0; i < size; i++) {		rd = r->rd + i;		memset(rd, 0, sizeof(*rd));		rd->hw = hwmap + i;		rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA);		if (rd->buf == NULL		    ||  !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) {			if (rd->buf) {				IRDA_ERROR("%s: failed to create PCI-MAP for %p",					   __FUNCTION__, rd->buf);				kfree(rd->buf);				rd->buf = NULL;			}			for (j = 0; j < i; j++) {				rd = r->rd + j;				busaddr = rd_get_addr(rd);				rd_set_addr_status(rd, 0, 0);				if (busaddr)					pci_unmap_single(pdev, busaddr, len, dir);				kfree(rd->buf);				rd->buf = NULL;			}			kfree(r);			return NULL;		}		rd_set_addr_status(rd, busaddr, 0);		/* initially, the dma buffer is owned by the CPU */		rd->skb = NULL;	}	return r;}static int vlsi_free_ring(struct vlsi_ring *r){	struct ring_descr *rd;	unsigned	i;	dma_addr_t	busaddr;	for (i = 0; i < r->size; i++) {		rd = r->rd + i;		if (rd->skb)			dev_kfree_skb_any(rd->skb);		busaddr = rd_get_addr(rd);		rd_set_addr_status(rd, 0, 0);		if (busaddr)			pci_unmap_single(r->pdev, busaddr, r->len, r->dir);		kfree(rd->buf);	}	kfree(r);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -