📄 vlsi_ir.c
字号:
/********************************************************************* * * 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 + -