📄 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 * * * 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 <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#include <linux/spinlock.h>#include <linux/pm.h>#include <net/irda/wrapper.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include "smsc-ircc2.h"#include "smsc-sio.h"/* Types */struct smsc_transceiver { char *name; void (*set_for_speed)(int fir_base, u32 speed); int (*probe)(int fir_base);};typedef struct smsc_transceiver smsc_transceiver_t;#if 0struct smc_chip { char *name; u16 flags; u8 devid; u8 rev;};typedef struct smc_chip smc_chip_t;#endifstruct smsc_chip { char *name; #if 0 u8 type; #endif u16 flags; u8 devid; u8 rev;};typedef struct smsc_chip smsc_chip_t;struct smsc_chip_address { unsigned int cfg_base; unsigned int type;};typedef struct smsc_chip_address smsc_chip_address_t;/* 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 pm_dev *pmdev;};/* Constants */static const char *driver_name = "smsc-ircc2";#define DIM(x) (sizeof(x)/(sizeof(*(x))))#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 0/* 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, int iobase); static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase);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 iobase, int bofs);static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase);static void smsc_ircc_change_speed(void *priv, u32 speed);static void smsc_ircc_set_sir_speed(void *priv, u32 speed);static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);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_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);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 smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type);static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfg_base, char *type);static int __init smsc_superio_paged(const smsc_chip_t *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);/* 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 void smsc_ircc_suspend(struct smsc_ircc_cb *self);static void smsc_ircc_wakeup(struct smsc_ircc_cb *self);static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);/* Transceivers for SMSC-ircc */smsc_transceiver_t 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 (DIM(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 smsc_chip_t __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 smsc_chip_t __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 smsc_chip_t __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 smsc_chip_t __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 smsc_chip_address_t __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 int ircc_irq=255;static int ircc_dma=255;static int ircc_fir=0;static int ircc_sir=0;static int ircc_cfg=0;static int ircc_transceiver=0;static unsigned short dev_count=0;static inline void register_bank(int iobase, int bank){ outb(((inb(iobase+IRCC_MASTER) & 0xf0) | (bank & 0x07)), iobase+IRCC_MASTER);}/******************************************************************************* * * * 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=-ENODEV; IRDA_DEBUG(1, "%s\n", __FUNCTION__); dev_count=0; if ((ircc_fir>0)&&(ircc_sir>0)) { MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir); MESSAGE(" Overriding SIR address 0x%04x\n", ircc_sir); if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq) == 0) return 0; return -ENODEV; } /* try user provided configuration register base address */ if (ircc_cfg>0) { 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; 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 > DIM(dev_self)) { 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) { 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 = dev->priv; 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, &self->rx_buff_dma, GFP_KERNEL); if (self->rx_buff.head == NULL) { ERROR("%s, Can't allocate memory for receive buffer!\n", driver_name); goto err_out2; } self->tx_buff.head = dma_alloc_coherent(NULL, self->tx_buff.truesize, &self->tx_buff_dma, GFP_KERNEL); if (self->tx_buff.head == NULL) { ERROR("%s, Can't allocate memory for transmit buffer!\n", driver_name); goto err_out3; } memset(self->rx_buff.head, 0, self->rx_buff.truesize); 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; smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); smsc_ircc_setup_qos(self); smsc_ircc_init_chip(self); if(ircc_transceiver > 0 && ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS) self->transceiver = ircc_transceiver; else smsc_ircc_probe_transceiver(self); err = register_netdev(self->netdev); if(err) { ERROR("%s, Network device registration failed!\n", driver_name); goto err_out4; } self->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, smsc_ircc_pmproc); if (self->pmdev) self->pmdev->data = self; MESSAGE("IrDA: Registered device %s\n", dev->name); return 0; err_out4: dma_free_coherent(NULL, self->tx_buff.truesize, self->tx_buff.head, self->tx_buff_dma); err_out3: dma_free_coherent(NULL, self->rx_buff.truesize, self->rx_buff.head, self->rx_buff_dma); err_out2: free_netdev(self->netdev); dev_self[--dev_count] = NULL; err_out1: release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); err_out: return err;}/* * Function smsc_ircc_present(fir_base, sir_base) * * Check the smsc-ircc chip presence * */static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base){ unsigned char low, high, chip, config, dma, irq, version; if (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT, driver_name)) { WARNING("%s: can't get fir_base of 0x%03x\n", __FUNCTION__, fir_base); goto out1; } if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT, driver_name)) { WARNING("%s: can't get sir_base of 0x%03x\n", __FUNCTION__, sir_base); goto out2; } register_bank(fir_base, 3); high = inb(fir_base+IRCC_ID_HIGH); low = inb(fir_base+IRCC_ID_LOW); chip = inb(fir_base+IRCC_CHIP_ID); version = inb(fir_base+IRCC_VERSION); config = inb(fir_base+IRCC_INTERFACE); dma = config & IRCC_INTERFACE_DMA_MASK; irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) { WARNING("%s(), addr 0x%04x - no device found!\n", __FUNCTION__, fir_base); goto out3; } MESSAGE("SMsC IrDA Controller found\n IrCC version %d.%d, " "firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n", chip & 0x0f, version, fir_base, sir_base, dma, irq); return 0; out3: release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); out2: release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); out1: return -ENODEV;}/* * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) * * Setup I/O * */static void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq){ unsigned char config, chip_dma, chip_irq; register_bank(fir_base, 3); config = inb(fir_base+IRCC_INTERFACE); chip_dma = config & IRCC_INTERFACE_DMA_MASK; chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4; self->io.fir_base = fir_base; self->io.sir_base = sir_base; self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT; self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT; self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE; self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED; if (irq < 255) { if (irq != chip_irq) MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n", driver_name, chip_irq, irq); self->io.irq = irq; } else self->io.irq = chip_irq; if (dma < 255) { if (dma != chip_dma) MESSAGE("%s, Overriding DMA - chip says %d, using %d\n", driver_name, chip_dma, dma); self->io.dma = dma; } else self->io.dma = chip_dma;}/* * Function smsc_ircc_setup_qos(self) * * Setup qos * */static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self){ /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); 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 = SMSC_IRCC2_MIN_TURN_TIME; self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE; irda_qos_bits_to_value(&self->qos);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -