📄 ep93xx_irda.c
字号:
/********************************************************************* * * Filename: ep93xx_irda.c * Version: 0.2 * Description: Driver for the EP93xx SOC IrDA. * Status: Experimental. * * Copyright 2003 Cirrus Logic, Inc. * * Based loosely on the ali-ircc.c implementation: * * Author: Benjamin Kong <benjamin_kong@ali.com.tw> * Created at: 2000/10/16 03:46PM * Modified at: 2001/1/3 02:55PM * Modified by: Benjamin Kong <benjamin_kong@ali.com.tw> * * Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw> * 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 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: * 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 <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#include <linux/pm.h>#include <net/irda/wrapper.h>#include <net/irda/irda.h>#include <net/irda/irmod.h>#include <net/irda/irlap_frame.h>#include <net/irda/irda_device.h>#include <asm/arch/dma.h>#include <net/irda/ep93xx_irda.h>#include <asm/arch/irqs.h>static char *driver_name = "ep93xx-irda";static char *SIR_str = "SIR only";static char *SIRMIR_str = "SIR/MIR only";static char *SIRFIR_str = "SIR/MIR only";static char *SIRMFIR_str = "SIR/MIR/FIR";/* * Turnaround time: * Min. time the HW can respond. * * Interestingly, most drivers set this value to * 0x1. That would mean the index in bits_to_value * would end up with a value of 0 and the min_turn_times * array's [0] value is '10000'(us). * * 0-7 are the only valid index positions for the * min_turn_times array. With the bitshift discovery * method used (and the IrDA spec wanting contiguous bits), * only values of 0xFF, 0x7f, 0x3F, 0x1F, 0xF, 0x7, 0x3, * & 0x1 are valid assignments for this variable. * * (look at qos.c's min_turn_times array, bits_to_value and * msb_index functions to validate) */static int qos_mtt_bits = 0x7;/* * Modes the clock settings will support. */static int g_iClkSupport = 0;/* * Initialization prototypes. */static int ep93xx_irda_init_9312(ep93xx_chip_t *chip);/* * Currently known EP93xx SOC packages. */static ep93xx_chip_t chips[] ={ /* name|irqs|sir dmatx|sir dmarx|mfir dmatx|mfir dmarx|init func */ { "EP9312", IRQ_UART2, IRQ_IRDA, DMATx_UART2, DMATx_IRDA, DMARx_IRDA, ep93xx_irda_init_9312 }, { NULL }};/* * Max 4 instances (as is "standard" for an IrDA driver). */static struct ep93xx_irda_cb *dev_self[] = { NULL, NULL, NULL, NULL };/* * Prototypes. */ /* * General ep93xx family. */ static int ep93xx_irda_open(int i, ep93xx_chip_t *chip); #ifdef MODULE static int ep93xx_irda_close(struct ep93xx_irda_cb *self); #endif /* MODULE */ static int ep93xx_irda_setup(ep93xx_chip_t *chip); static int ep93xx_irda_is_receiving(struct ep93xx_irda_cb *self); static int ep93xx_irda_net_init(struct net_device *dev); static int ep93xx_irda_net_open(struct net_device *dev); static int ep93xx_irda_net_close(struct net_device *dev); static int ep93xx_irda_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void ep93xx_irda_change_speed(struct ep93xx_irda_cb *self, __u32 baud); static void ep93xx_irda_interrupt(int irq, void *dev_id, struct pt_regs *regs);#ifdef POWER_SAVING static int ep93xx_irda_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); static void ep93xx_irda_suspend(struct ep93xx_irda_cb *self); static void ep93xx _irda_wakeup(struct ep93xx_irda_cb *self);#endif static struct net_device_stats *ep93xx_irda_net_get_stats(struct net_device *dev);/* * Driver SIR functions */static void ep93xx_irda_sir_change_speed(struct ep93xx_irda_cb *priv, __u32 speed);static void ep93xx_irda_sir_interrupt(int irq, struct ep93xx_irda_cb *self, struct pt_regs *regs);static void ep93xx_irda_sir_receive(struct ep93xx_irda_cb *self);static void ep93xx_irda_sir_write_wakeup(struct ep93xx_irda_cb *self);static int ep93xx_irda_sir_write(int iFifo_size, __u8 *pBuf, int iLen);/* * Driver MIR/FIR functions */static void ep93xx_irda_mfir_change_speed(struct ep93xx_irda_cb *priv, __u32 speed);static void ep93xx_irda_mfir_interrupt(int irq, struct ep93xx_irda_cb *self, struct pt_regs *regs);/* * Driver DMA functions */static int ep93xx_irda_dma_receive(struct ep93xx_irda_cb *self); static int ep93xx_irda_dma_receive_complete(struct ep93xx_irda_cb *self, unsigned int frameSize);static int ep93xx_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev);static void ep93xx_irda_dma_xmit(struct ep93xx_irda_cb *self);static int ep93xx_irda_dma_xmit_complete(struct ep93xx_irda_cb *self, int irqID);/* * State change functions */static void SIR2MFIR(struct ep93xx_irda_cb *self, __u32 TargetSpeed);static void MFIR2SIR(struct ep93xx_irda_cb *self, __u32 TargetSpeed);static void SetInterrupts(struct ep93xx_irda_cb *self, unsigned char enable);static void SetIR_Transmit(unsigned char ucEnable);static void SetIR_Receive(unsigned char ucEnable);static void SetDMA(struct ep93xx_irda_cb *self, unsigned char direction, unsigned char enable);/* * Local DEFINES *//* * Define to signal usage of PIO mode only for transmits. */#define PIO_TX/* * Define to signal console printing of debug statements. *///#define DEBUG#ifdef DEBUG /* * DEBUG_LEVEL determines which DEBUG statements to print. */ #define DEBUG_LEVEL 1 #define EP93XX_DEBUG(x,y...) (x <= DEBUG_LEVEL) ? printk( ##y ) : 0#else #define EP93XX_DEBUG(x,y...)#endif/* * Function ep93xx_irda_init () * * Initialize block. Verify some basic knowns before proceding setup/load. * */int __init ep93xx_irda_init(void){ ep93xx_chip_t *chip; unsigned long ulRegValue; int ret = -ENODEV; int i = 0; EP93XX_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); /* * Check for all the EP93xx SOC packages we know about . */ for(chip = chips; chip->name; chip++, i++) { EP93XX_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__, chip->name); /* * Prepare to test some knowns for the SOC. */ ulRegValue = 0; /* * Read the CHIP_ID register to make sure the family is correct. */ ulRegValue = inl(SYSCON_CHIPID); EP93XX_DEBUG(1, "SYSCON_CHIPID = 0x%x\n", ulRegValue); if(chip->name) { EP93XX_DEBUG(2, "%s(), Found %s SOC IrDA block, at 0x%03x\n", __FUNCTION__, chip->name, IRDA_BASE); /* * Resource usage is fixed. Initialize and open. */ chip->init(chip); if(ep93xx_irda_open(i, chip) == 0) { ret = 0; } i++; } else { EP93XX_DEBUG(2, "%s(), No %s SOC IrDA block found.\n", __FUNCTION__, chip->name); } } EP93XX_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); return(ret);}/* * Function ep93xx_irda_cleanup () * * Close all configured chips * */#ifdef MODULEstatic void ep93xx_irda_cleanup(void){ int i; EP93XX_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); pm_unregister_all(ep93xx_irda_pmproc); for(i = 0; i < 4; i++) { if(dev_self[i]) { ep93xx_irda_close(dev_self[i]); } } EP93XX_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__);}#endif /* MODULE *//* * Function ep93xx_irda_open (int i, ep93xx_chip_t *info) * * Open driver instance. * */static int ep93xx_irda_open(int i, ep93xx_chip_t *info){ struct net_device *dev; struct ep93xx_irda_cb *self;#ifdef POWER_SAVING struct pm_dev *pmdev;#endif int err; dma_addr_t rxdmaphys, txdmaphys; EP93XX_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); /* * Get setup for the attack run... */ if((ep93xx_irda_setup(info)) == -1) { return(-1); } /* * Allocate a new instance of the driver */ self = kmalloc(sizeof(struct ep93xx_irda_cb), GFP_KERNEL); if(self == NULL) { ERROR("%s(), can't allocate memory for control block!\n", __FUNCTION__); return(-ENOMEM); } memset(self, 0, sizeof(struct ep93xx_irda_cb)); spin_lock_init(&self->lock); /* * Track 'self' pointer */ dev_self[i] = self; self->index = i; /* * Save important SOC data, starting with IRQs used */ self->iSIR_irq = info->iSIR_IRQ; self->iMFIR_irq = info->iMFIR_IRQ; /* * ep93xx DMA port ids */ self->ePorts[DMA_SIR_TX] = info->eSIR_DMATx; self->ePorts[DMA_MFIR_TX] = info->eMFIR_DMATx; self->ePorts[DMA_MFIR_RX] = info->eMFIR_DMARx; self->fifo_size = 16; /* * Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); /* * Override some values returned by max_qos */ self->qos.baud_rate.bits = 0; if(g_iClkSupport & CLK_SIR) { self->qos.baud_rate.bits |= IR_9600|IR_19200|IR_38400|IR_57600| IR_115200; } if(g_iClkSupport & CLK_MIR) { self->qos.baud_rate.bits |= IR_576000|IR_1152000; } if(g_iClkSupport & CLK_FIR) { self->qos.baud_rate.bits |= (IR_4000000 << 8); } /* * Turnaround time */ self->qos.min_turn_time.bits = qos_mtt_bits; irda_qos_bits_to_value(&self->qos); self->flags = IFF_SIR|IFF_MIR|IFF_FIR|IFF_DMA|IFF_PIO; /* * Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ self->rx_buff.truesize = 14384; self->tx_buff.truesize = 14384; /* * Allocate memory */ self->rx_buff.head = (__u8 *)consistent_alloc(GFP_KERNEL|GFP_DMA, self->rx_buff.truesize, &rxdmaphys); if(self->rx_buff.head == NULL) { kfree(self); return(-ENOMEM); } memcpy(&self->rx_dmaphysh, &rxdmaphys, sizeof(dma_addr_t)); memset(self->rx_buff.head, 0, self->rx_buff.truesize); self->tx_buff.head = (__u8 *) consistent_alloc(GFP_KERNEL|GFP_DMA, self->tx_buff.truesize, &txdmaphys); if(self->tx_buff.head == NULL) { consistent_free(self->rx_buff.head, self->rx_buff.truesize, rxdmaphys); kfree(self); return(-ENOMEM); } memcpy(&self->tx_dmaphysh, &txdmaphys, sizeof(dma_addr_t)); memset(self->tx_buff.head, 0, self->tx_buff.truesize); /* * Conversion buffer. Temp holding space for DMA buffers. * Needed for SIR RX. */ self->conv_buf = (__u8 *) kmalloc(self->rx_buff.truesize, GFP_KERNEL); memset(self->conv_buf, 0, self->rx_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; if(!(dev = dev_alloc("irda%d", &err))) { ERROR("%s(), dev_alloc() failed!\n", __FUNCTION__); return(-ENOMEM); } dev->priv = (void *) self; self->netdev = dev; /* * Override the network functions we need to use */ dev->init = ep93xx_irda_net_init; dev->hard_start_xmit = ep93xx_irda_hard_xmit; dev->open = ep93xx_irda_net_open; dev->stop = ep93xx_irda_net_close; dev->do_ioctl = ep93xx_irda_net_ioctl; dev->get_stats = ep93xx_irda_net_get_stats; rtnl_lock(); err = register_netdevice(dev); rtnl_unlock(); if(err) { ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); return(-1); } MESSAGE("IrDA: Registered device %s\n", dev->name);#ifdef POWER_SAVING pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ep93xx_irda_pmproc); if(pmdev) { pmdev->data = self; }#endif EP93XX_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); return(0);}#ifdef MODULE/* * Function ep93xx_irda_close (self) * * Close driver instance. * */static int ep93xx_irda_close(struct ep93xx_irda_cb *self){ EP93XX_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); ASSERT(self != NULL, return -1;); /* * Remove netdevice */ if(self->netdev) { rtnl_lock(); unregister_netdevice(self->netdev); rtnl_unlock(); } if(self->tx_buff.head) { consistent_free(self->tx_buff.head, self->tx_buff.truesize, self->tx_dmaphysh); } if(self->rx_buff.head) { consistent_free(self->rx_buff.head, self->rx_buff.truesize, self->rx_dmaphysh); } dev_self[self->index] = NULL; kfree(self); EP93XX_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); return(0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -