nsc-ircc.c
来自「linux 内核源代码」· C语言 代码 · 共 2,406 行 · 第 1/4 页
C
2,406 行
/********************************************************************* * * Filename: nsc-ircc.c * Version: 1.0 * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets * Status: Stable. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sat Nov 7 21:43:15 1998 * Modified at: Wed Mar 1 11:29:34 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> * Copyright (c) 1998 Lichen Wang, <lwang@actisys.com> * Copyright (c) 1998 Actisys Corp., www.actisys.com * Copyright (c) 2000-2004 Jean Tourrilhes <jt@hpl.hp.com> * All Rights Reserved * * 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. * * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * * Notice that all functions that needs to access the chip in _any_ * way, must save BSR register on entry, and restore it on exit. * It is _very_ important to follow this policy! * * __u8 bank; * * bank = inb(iobase+BSR); * * do_your_stuff_here(); * * outb(bank, iobase+BSR); * * If you find bugs in this file, its very likely that the same bug * will also be in w83977af_ir.c since the implementations are quite * similar. * ********************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/rtnetlink.h>#include <linux/dma-mapping.h>#include <linux/pnp.h>#include <linux/platform_device.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#include <net/irda/wrapper.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include "nsc-ircc.h"#define CHIP_IO_EXTENT 8#define BROKEN_DONGLE_IDstatic char *driver_name = "nsc-ircc";/* Power Management */#define NSC_IRCC_DRIVER_NAME "nsc-ircc"static int nsc_ircc_suspend(struct platform_device *dev, pm_message_t state);static int nsc_ircc_resume(struct platform_device *dev);static struct platform_driver nsc_ircc_driver = { .suspend = nsc_ircc_suspend, .resume = nsc_ircc_resume, .driver = { .name = NSC_IRCC_DRIVER_NAME, },};/* Module parameters */static int qos_mtt_bits = 0x07; /* 1 ms or more */static int dongle_id;/* Use BIOS settions by default, but user may supply module parameters */static unsigned int io[] = { ~0, ~0, ~0, ~0, ~0 };static unsigned int irq[] = { 0, 0, 0, 0, 0 };static unsigned int dma[] = { 0, 0, 0, 0, 0 };static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info);static int nsc_ircc_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *id);/* These are the known NSC chips */static nsc_chip_t chips[] = {/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */ { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, nsc_ircc_probe_108, nsc_ircc_init_108 }, { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, nsc_ircc_probe_338, nsc_ircc_init_338 }, /* Contributed by Steffen Pingel - IBM X40 */ { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff, nsc_ircc_probe_39x, nsc_ircc_init_39x }, /* Contributed by Jan Frey - IBM A30/A31 */ { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, nsc_ircc_probe_39x, nsc_ircc_init_39x }, /* IBM ThinkPads using PC8738x (T60/X60/Z60) */ { "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff, nsc_ircc_probe_39x, nsc_ircc_init_39x }, /* IBM ThinkPads using PC8394T (T43/R52/?) */ { "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff, nsc_ircc_probe_39x, nsc_ircc_init_39x }, { NULL }};static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL };static char *dongle_types[] = { "Differential serial interface", "Differential serial interface", "Reserved", "Reserved", "Sharp RY5HD01", "Reserved", "Single-ended serial interface", "Consumer-IR only", "HP HSDL-2300, HP HSDL-3600/HSDL-3610", "IBM31T1100 or Temic TFDS6000/TFDS6500", "Reserved", "Reserved", "HP HSDL-1100/HSDL-2100", "HP HSDL-1100/HSDL-2100", "Supports SIR Mode only", "No dongle connected",};/* PNP probing */static chipio_t pnp_info;static const struct pnp_device_id nsc_ircc_pnp_table[] = { { .id = "NSC6001", .driver_data = 0 }, { .id = "IBM0071", .driver_data = 0 }, { }};MODULE_DEVICE_TABLE(pnp, nsc_ircc_pnp_table);static struct pnp_driver nsc_ircc_pnp_driver = { .name = "nsc-ircc", .id_table = nsc_ircc_pnp_table, .probe = nsc_ircc_pnp_probe,};/* Some prototypes */static int nsc_ircc_open(chipio_t *info);static int nsc_ircc_close(struct nsc_ircc_cb *self);static int nsc_ircc_setup(chipio_t *info);static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self);static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase);static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size);static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase);static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud);static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self);static int nsc_ircc_read_dongle_id (int iobase);static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id);static int nsc_ircc_net_open(struct net_device *dev);static int nsc_ircc_net_close(struct net_device *dev);static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev);/* Globals */static int pnp_registered;static int pnp_succeeded;/* * Function nsc_ircc_init () * * Initialize chip. Just try to find out how many chips we are dealing with * and where they are */static int __init nsc_ircc_init(void){ chipio_t info; nsc_chip_t *chip; int ret; int cfg_base; int cfg, id; int reg; int i = 0; ret = platform_driver_register(&nsc_ircc_driver); if (ret) { IRDA_ERROR("%s, Can't register driver!\n", driver_name); return ret; } /* Register with PnP subsystem to detect disable ports */ ret = pnp_register_driver(&nsc_ircc_pnp_driver); if (!ret) pnp_registered = 1; ret = -ENODEV; /* Probe for all the NSC chipsets we know about */ for (chip = chips; chip->name ; chip++) { IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__, chip->name); /* Try all config registers for this chip */ for (cfg = 0; cfg < ARRAY_SIZE(chip->cfg); cfg++) { cfg_base = chip->cfg[cfg]; if (!cfg_base) continue; /* Read index register */ reg = inb(cfg_base); if (reg == 0xff) { IRDA_DEBUG(2, "%s() no chip at 0x%03x\n", __FUNCTION__, cfg_base); continue; } /* Read chip identification register */ outb(chip->cid_index, cfg_base); id = inb(cfg_base+1); if ((id & chip->cid_mask) == chip->cid_value) { IRDA_DEBUG(2, "%s() Found %s chip, revision=%d\n", __FUNCTION__, chip->name, id & ~chip->cid_mask); /* * If we found a correct PnP setting, * we first try it. */ if (pnp_succeeded) { memset(&info, 0, sizeof(chipio_t)); info.cfg_base = cfg_base; info.fir_base = pnp_info.fir_base; info.dma = pnp_info.dma; info.irq = pnp_info.irq; if (info.fir_base < 0x2000) { IRDA_MESSAGE("%s, chip->init\n", driver_name); chip->init(chip, &info); } else chip->probe(chip, &info); if (nsc_ircc_open(&info) >= 0) ret = 0; } /* * Opening based on PnP values failed. * Let's fallback to user values, or probe * the chip. */ if (ret) { IRDA_DEBUG(2, "%s, PnP init failed\n", driver_name); memset(&info, 0, sizeof(chipio_t)); info.cfg_base = cfg_base; info.fir_base = io[i]; info.dma = dma[i]; info.irq = irq[i]; /* * If the user supplies the base address, then * we init the chip, if not we probe the values * set by the BIOS */ if (io[i] < 0x2000) { chip->init(chip, &info); } else chip->probe(chip, &info); if (nsc_ircc_open(&info) >= 0) ret = 0; } i++; } else { IRDA_DEBUG(2, "%s(), Wrong chip id=0x%02x\n", __FUNCTION__, id); } } } if (ret) { platform_driver_unregister(&nsc_ircc_driver); pnp_unregister_driver(&nsc_ircc_pnp_driver); pnp_registered = 0; } return ret;}/* * Function nsc_ircc_cleanup () * * Close all configured chips * */static void __exit nsc_ircc_cleanup(void){ int i; for (i = 0; i < ARRAY_SIZE(dev_self); i++) { if (dev_self[i]) nsc_ircc_close(dev_self[i]); } platform_driver_unregister(&nsc_ircc_driver); if (pnp_registered) pnp_unregister_driver(&nsc_ircc_pnp_driver); pnp_registered = 0;}/* * Function nsc_ircc_open (iobase, irq) * * Open driver instance * */static int __init nsc_ircc_open(chipio_t *info){ struct net_device *dev; struct nsc_ircc_cb *self; void *ret; int err, chip_index; IRDA_DEBUG(2, "%s()\n", __FUNCTION__); for (chip_index = 0; chip_index < ARRAY_SIZE(dev_self); chip_index++) { if (!dev_self[chip_index]) break; } if (chip_index == ARRAY_SIZE(dev_self)) { IRDA_ERROR("%s(), maximum number of supported chips reached!\n", __FUNCTION__); return -ENOMEM; } IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, info->cfg_base); if ((nsc_ircc_setup(info)) == -1) return -1; IRDA_MESSAGE("%s, driver loaded (Dag Brattli)\n", driver_name); dev = alloc_irdadev(sizeof(struct nsc_ircc_cb)); if (dev == NULL) { IRDA_ERROR("%s(), can't allocate memory for " "control block!\n", __FUNCTION__); return -ENOMEM; } self = dev->priv; self->netdev = dev; spin_lock_init(&self->lock); /* Need to store self somewhere */ dev_self[chip_index] = self; self->index = chip_index; /* Initialize IO */ self->io.cfg_base = info->cfg_base; self->io.fir_base = info->fir_base; self->io.irq = info->irq; self->io.fir_ext = CHIP_IO_EXTENT; self->io.dma = info->dma; self->io.fifo_size = 32; /* Reserve the ioports that we need */ ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name); if (!ret) { IRDA_WARNING("%s(), can't get iobase of 0x%03x\n", __FUNCTION__, self->io.fir_base); err = -ENODEV; goto out1; } /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); /* The only value we must override it the baudrate */ self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| IR_115200|IR_576000|IR_1152000 |(IR_4000000 << 8); self->qos.min_turn_time.bits = qos_mtt_bits; irda_qos_bits_to_value(&self->qos); /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ self->rx_buff.truesize = 14384; self->tx_buff.truesize = 14384; /* Allocate memory if needed */ self->rx_buff.head = dma_alloc_coherent(NULL, self->rx_buff.truesize, &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) { err = -ENOMEM; goto out2; } memset(self->rx_buff.head, 0, self->rx_buff.truesize); self->tx_buff.head = dma_alloc_coherent(NULL, self->tx_buff.truesize, &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) { err = -ENOMEM; goto out3; } memset(self->tx_buff.head, 0, self->tx_buff.truesize); self->rx_buff.in_frame = FALSE; self->rx_buff.state = OUTSIDE_FRAME; self->tx_buff.data = self->tx_buff.head; self->rx_buff.data = self->rx_buff.head; /* Reset Tx queue info */ self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; self->tx_fifo.tail = self->tx_buff.head; /* Override the network functions we need to use */ dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; dev->open = nsc_ircc_net_open; dev->stop = nsc_ircc_net_close; dev->do_ioctl = nsc_ircc_net_ioctl; dev->get_stats = nsc_ircc_net_get_stats; err = register_netdev(dev); if (err) { IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); goto out4; } IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); /* Check if user has supplied a valid dongle id or not */ if ((dongle_id <= 0) || (dongle_id >= ARRAY_SIZE(dongle_types))) { dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); IRDA_MESSAGE("%s, Found dongle: %s\n", driver_name, dongle_types[dongle_id]); } else { IRDA_MESSAGE("%s, Using dongle: %s\n", driver_name, dongle_types[dongle_id]); } self->io.dongle_id = dongle_id; nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); self->pldev = platform_device_register_simple(NSC_IRCC_DRIVER_NAME, self->index, NULL, 0); if (IS_ERR(self->pldev)) { err = PTR_ERR(self->pldev); goto out5; } platform_set_drvdata(self->pldev, self); return chip_index; out5: unregister_netdev(dev); out4: dma_free_coherent(NULL, self->tx_buff.truesize, self->tx_buff.head, self->tx_buff_dma); out3: dma_free_coherent(NULL, self->rx_buff.truesize, self->rx_buff.head, self->rx_buff_dma); out2: release_region(self->io.fir_base, self->io.fir_ext); out1: free_netdev(dev); dev_self[chip_index] = NULL; return err;}/* * Function nsc_ircc_close (self) * * Close driver instance * */static int __exit nsc_ircc_close(struct nsc_ircc_cb *self){ int iobase; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); IRDA_ASSERT(self != NULL, return -1;); iobase = self->io.fir_base; platform_device_unregister(self->pldev); /* Remove netdevice */ unregister_netdev(self->netdev); /* Release the PORT that this driver is using */ IRDA_DEBUG(4, "%s(), Releasing Region %03x\n", __FUNCTION__, self->io.fir_base); release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) dma_free_coherent(NULL, self->tx_buff.truesize, self->tx_buff.head, self->tx_buff_dma); if (self->rx_buff.head) dma_free_coherent(NULL, self->rx_buff.truesize, self->rx_buff.head, self->rx_buff_dma); dev_self[self->index] = NULL; free_netdev(self->netdev); return 0;}/* * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) * * Initialize the NSC '108 chip * */static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info){ int cfg_base = info->cfg_base; __u8 temp=0; outb(2, cfg_base); /* Mode Control Register (MCTL) */ outb(0x00, cfg_base+1); /* Disable device */ /* Base Address and Interrupt Control Register (BAIC) */ outb(CFG_108_BAIC, cfg_base); switch (info->fir_base) { case 0x3e8: outb(0x14, cfg_base+1); break; case 0x2e8: outb(0x15, cfg_base+1); break; case 0x3f8: outb(0x16, cfg_base+1); break; case 0x2f8: outb(0x17, cfg_base+1); break; default: IRDA_ERROR("%s(), invalid base_address", __FUNCTION__); } /* Control Signal Routing Register (CSRT) */ switch (info->irq) { case 3: temp = 0x01; break; case 4: temp = 0x02; break; case 5: temp = 0x03; break; case 7: temp = 0x04; break; case 9: temp = 0x05; break; case 11: temp = 0x06; break; case 15: temp = 0x07; break; default: IRDA_ERROR("%s(), invalid irq", __FUNCTION__); } outb(CFG_108_CSRT, cfg_base); switch (info->dma) { case 0: outb(0x08+temp, cfg_base+1); break; case 1: outb(0x10+temp, cfg_base+1); break; case 3: outb(0x18+temp, cfg_base+1); break; default: IRDA_ERROR("%s(), invalid dma", __FUNCTION__); } outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */ outb(0x03, cfg_base+1); /* Enable device */ return 0;}/* * Function nsc_ircc_probe_108 (chip, info) * * * */static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) { int cfg_base = info->cfg_base; int reg; /* Read address and interrupt control register (BAIC) */ outb(CFG_108_BAIC, cfg_base); reg = inb(cfg_base+1); switch (reg & 0x03) { case 0: info->fir_base = 0x3e8; break;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?