📄 smsc-ircc2.c
字号:
/********************************************************************* * $Id: smsc-ircc2.c,v 1.19.2.5 2002/10/27 11:34:26 dip Exp $ * * Description: Driver for the SMC Infrared Communications Controller * Status: Experimental. * Author: Daniele Peri (peri@csai.unipa.it) * Created at: * Modified at: * Modified by: * * Copyright (c) 2002 Daniele Peri * All Rights Reserved. * Copyright (c) 2002 Jean Tourrilhes * Copyright (c) 2006 Linus Walleij * * * Based on smc-ircc.c: * * Copyright (c) 2001 Stefani Seibold * Copyright (c) 1999-2001 Dag Brattli * Copyright (c) 1998-1999 Thomas Davis, * * and irport.c: * * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, 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. * * 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>#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/serial_reg.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 <linux/spinlock.h>#include <linux/pm.h>#ifdef CONFIG_PCI#include <linux/pci.h>#endif#include <net/irda/wrapper.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include "smsc-ircc2.h"#include "smsc-sio.h"MODULE_AUTHOR("Daniele Peri <peri@csai.unipa.it>");MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver");MODULE_LICENSE("GPL");static int ircc_dma = 255;module_param(ircc_dma, int, 0);MODULE_PARM_DESC(ircc_dma, "DMA channel");static int ircc_irq = 255;module_param(ircc_irq, int, 0);MODULE_PARM_DESC(ircc_irq, "IRQ line");static int ircc_fir;module_param(ircc_fir, int, 0);MODULE_PARM_DESC(ircc_fir, "FIR Base Address");static int ircc_sir;module_param(ircc_sir, int, 0);MODULE_PARM_DESC(ircc_sir, "SIR Base Address");static int ircc_cfg;module_param(ircc_cfg, int, 0);MODULE_PARM_DESC(ircc_cfg, "Configuration register base address");static int ircc_transceiver;module_param(ircc_transceiver, int, 0);MODULE_PARM_DESC(ircc_transceiver, "Transceiver type");/* Types */#ifdef CONFIG_PCIstruct smsc_ircc_subsystem_configuration { unsigned short vendor; /* PCI vendor ID */ unsigned short device; /* PCI vendor ID */ unsigned short subvendor; /* PCI subsystem vendor ID */ unsigned short subdevice; /* PCI sybsystem device ID */ unsigned short sir_io; /* I/O port for SIR */ unsigned short fir_io; /* I/O port for FIR */ unsigned char fir_irq; /* FIR IRQ */ unsigned char fir_dma; /* FIR DMA */ unsigned short cfg_base; /* I/O port for chip configuration */ int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */ const char *name; /* name shown as info */};#endifstruct smsc_transceiver { char *name; void (*set_for_speed)(int fir_base, u32 speed); int (*probe)(int fir_base);};struct smsc_chip { char *name; #if 0 u8 type; #endif u16 flags; u8 devid; u8 rev;};struct smsc_chip_address { unsigned int cfg_base; unsigned int type;};/* Private data for each instance */struct smsc_ircc_cb { struct net_device *netdev; /* Yes! we are some kind of netdevice */ struct net_device_stats stats; struct irlap_cb *irlap; /* The link layer we are binded to */ chipio_t io; /* IrDA controller information */ iobuff_t tx_buff; /* Transmit buffer */ iobuff_t rx_buff; /* Receive buffer */ dma_addr_t tx_buff_dma; dma_addr_t rx_buff_dma; struct qos_info qos; /* QoS capabilities for this device */ spinlock_t lock; /* For serializing operations */ __u32 new_speed; __u32 flags; /* Interface flags */ int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ int tx_len; /* Number of frames in tx_buff */ int transceiver; struct platform_device *pldev;};/* Constants */#define SMSC_IRCC2_DRIVER_NAME "smsc-ircc2"#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1#define SMSC_IRCC2_C_NET_TIMEOUT 0#define SMSC_IRCC2_C_SIR_STOP 0static const char *driver_name = SMSC_IRCC2_DRIVER_NAME;/* Prototypes */static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq);static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base);static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq);static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self);static void smsc_ircc_init_chip(struct smsc_ircc_cb *self);static int __exit smsc_ircc_close(struct smsc_ircc_cb *self);static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self);static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self);static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self);static int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int bofs);static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self);static void smsc_ircc_change_speed(struct smsc_ircc_cb *self, u32 speed);static void smsc_ircc_set_sir_speed(struct smsc_ircc_cb *self, u32 speed);static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id);static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev);static void smsc_ircc_sir_start(struct smsc_ircc_cb *self);#if SMSC_IRCC2_C_SIR_STOPstatic void smsc_ircc_sir_stop(struct smsc_ircc_cb *self);#endifstatic void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self);static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len);static int smsc_ircc_net_open(struct net_device *dev);static int smsc_ircc_net_close(struct net_device *dev);static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);#if SMSC_IRCC2_C_NET_TIMEOUTstatic void smsc_ircc_timeout(struct net_device *dev);#endifstatic struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev);static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self);static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self);static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed);static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self);/* Probing */static int __init smsc_ircc_look_for_chips(void);static const struct smsc_chip * __init smsc_ircc_probe(unsigned short cfg_base, u8 reg, const struct smsc_chip *chip, char *type);static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned short cfg_base, char *type);static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type);static int __init smsc_superio_fdc(unsigned short cfg_base);static int __init smsc_superio_lpc(unsigned short cfg_base);#ifdef CONFIG_PCIstatic int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf);static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf);static void __init preconfigure_ali_port(struct pci_dev *dev, unsigned short port);static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf);static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, unsigned short ircc_fir, unsigned short ircc_sir, unsigned char ircc_dma, unsigned char ircc_irq);#endif/* Transceivers specific functions */static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed);static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base);static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed);static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base);static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed);static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base);/* Power Management */static int smsc_ircc_suspend(struct platform_device *dev, pm_message_t state);static int smsc_ircc_resume(struct platform_device *dev);static struct platform_driver smsc_ircc_driver = { .suspend = smsc_ircc_suspend, .resume = smsc_ircc_resume, .driver = { .name = SMSC_IRCC2_DRIVER_NAME, },};/* Transceivers for SMSC-ircc */static struct smsc_transceiver smsc_transceivers[] ={ { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800 }, { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select }, { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc }, { NULL, NULL }};#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (ARRAY_SIZE(smsc_transceivers) - 1)/* SMC SuperIO chipsets definitions */#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */#define SIR 0 /* SuperIO Chip has only slow IRDA */#define FIR 4 /* SuperIO Chip has fast IRDA */#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */static struct smsc_chip __initdata fdc_chips_flat[] ={ /* Base address 0x3f0 or 0x370 */ { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */ { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 }, { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 }, { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 }, { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */ { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 }, { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 }, { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 }, { NULL }};static struct smsc_chip __initdata fdc_chips_paged[] ={ /* Base address 0x3f0 or 0x370 */ { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 }, { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 }, { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 }, { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 }, { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 }, { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 }, { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 }, { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 }, { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 }, { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 }, { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 }, { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 }, { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 }, { NULL }};static struct smsc_chip __initdata lpc_chips_flat[] ={ /* Base address 0x2E or 0x4E */ { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, { NULL }};static struct smsc_chip __initdata lpc_chips_paged[] ={ /* Base address 0x2E or 0x4E */ { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 }, { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 }, { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 }, { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 }, { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 }, { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 }, { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 }, { NULL }};#define SMSCSIO_TYPE_FDC 1#define SMSCSIO_TYPE_LPC 2#define SMSCSIO_TYPE_FLAT 4#define SMSCSIO_TYPE_PAGED 8static struct smsc_chip_address __initdata possible_addresses[] ={ { 0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, { 0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, { 0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, { 0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, { 0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED }, { 0, 0 }};/* Globals */static struct smsc_ircc_cb *dev_self[] = { NULL, NULL };static unsigned short dev_count;static inline void register_bank(int iobase, int bank){ outb(((inb(iobase + IRCC_MASTER) & 0xf0) | (bank & 0x07)), iobase + IRCC_MASTER);}#ifdef CONFIG_PNP/* PNP hotplug support */static const struct pnp_device_id smsc_ircc_pnp_table[] = { { .id = "SMCf010", .driver_data = 0 }, /* and presumably others */ { }};MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table);#endif/******************************************************************************* * * * SMSC-ircc stuff * * *******************************************************************************//* * Function smsc_ircc_init () * * Initialize chip. Just try to find out how many chips we are dealing with * and where they are */static int __init smsc_ircc_init(void){ int ret; IRDA_DEBUG(1, "%s\n", __FUNCTION__); ret = platform_driver_register(&smsc_ircc_driver); if (ret) { IRDA_ERROR("%s, Can't register driver!\n", driver_name); return ret; }#ifdef CONFIG_PCI if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) { /* Ignore errors from preconfiguration */ IRDA_ERROR("%s, Preconfiguration failed !\n", driver_name); }#endif dev_count = 0; if (ircc_fir > 0 && ircc_sir > 0) { IRDA_MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir); IRDA_MESSAGE(" Overriding SIR address 0x%04x\n", ircc_sir); if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq)) ret = -ENODEV; } else { ret = -ENODEV; /* try user provided configuration register base address */ if (ircc_cfg > 0) { IRDA_MESSAGE(" Overriding configuration address " "0x%04x\n", ircc_cfg); if (!smsc_superio_fdc(ircc_cfg)) ret = 0; if (!smsc_superio_lpc(ircc_cfg)) ret = 0; } if (smsc_ircc_look_for_chips() > 0) ret = 0; } if (ret) platform_driver_unregister(&smsc_ircc_driver); return ret;}/* * Function smsc_ircc_open (firbase, sirbase, dma, irq) * * Try to open driver instance * */static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq){ struct smsc_ircc_cb *self; struct net_device *dev; int err; IRDA_DEBUG(1, "%s\n", __FUNCTION__); err = smsc_ircc_present(fir_base, sir_base); if (err) goto err_out; err = -ENOMEM; if (dev_count >= ARRAY_SIZE(dev_self)) { IRDA_WARNING("%s(), too many devices!\n", __FUNCTION__); goto err_out1; } /* * Allocate new instance of the driver */ dev = alloc_irdadev(sizeof(struct smsc_ircc_cb)); if (!dev) { IRDA_WARNING("%s() can't allocate net device\n", __FUNCTION__); goto err_out1; } SET_MODULE_OWNER(dev); dev->hard_start_xmit = smsc_ircc_hard_xmit_sir;#if SMSC_IRCC2_C_NET_TIMEOUT dev->tx_timeout = smsc_ircc_timeout; dev->watchdog_timeo = HZ * 2; /* Allow enough time for speed change */#endif dev->open = smsc_ircc_net_open; dev->stop = smsc_ircc_net_close; dev->do_ioctl = smsc_ircc_net_ioctl; dev->get_stats = smsc_ircc_net_get_stats; self = netdev_priv(dev); self->netdev = dev; /* Make ifconfig display some details */ dev->base_addr = self->io.fir_base = fir_base; dev->irq = self->io.irq = irq; /* Need to store self somewhere */ dev_self[dev_count] = self; spin_lock_init(&self->lock); self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; self->rx_buff.head = dma_alloc_coherent(NULL, self->rx_buff.truesize,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -