📄 comx-hw-mixcom.c
字号:
/* * Hardware driver for the MixCom synchronous serial board * * Author: Gergely Madarasz <gorgo@itc.hu> * * based on skeleton driver code and a preliminary hscx driver by * Tivadar Szemethy <tiv@itc.hu> * * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu> * * Contributors: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.65) * * 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. * * Version 0.60 (99/06/11): * - ported to the kernel, now works as builtin code * * Version 0.61 (99/06/11): * - recognize the one-channel MixCOM card (id byte = 0x13) * - printk fixes * * Version 0.62 (99/07/15): * - fixes according to the new hw docs * - report line status when open * * Version 0.63 (99/09/21): * - line status report fixes * * Version 0.64 (99/12/01): * - some more cosmetical fixes * * Version 0.65 (00/08/15) * - resource release on failure at MIXCOM_init */#define VERSION "0.65"#include <linux/module.h>#include <linux/version.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/netdevice.h>#include <linux/proc_fs.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/init.h>#include "comx.h"#include "mixcom.h"#include "hscx.h"MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");MODULE_LICENSE("GPL");#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \ HW_privdata))#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \ (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \ (1 - channel) * MIXCOM_CHANNEL_OFFSET)/* Values used to set the IRQ line */static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};struct mixcom_privdata { u16 clock; char channel; long txbusy; struct sk_buff *sending; unsigned tx_ptr; struct sk_buff *recving; unsigned rx_ptr; unsigned char status; char card_has_status;};static inline void wr_hscx(struct net_device *dev, int reg, unsigned char val) { outb(val, dev->base_addr + reg);}static inline unsigned char rd_hscx(struct net_device *dev, int reg){ return inb(dev->base_addr + reg);}static inline void hscx_cmd(struct net_device *dev, int cmd){ unsigned long jiffs = jiffies; unsigned char cec; unsigned delay = 0; while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && (jiffs + HZ > jiffies)) { udelay(1); if (++delay > (100000 / HZ)) break; } if (cec) { printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name); } else { wr_hscx(dev, HSCX_CMDR, cmd); }}static inline void hscx_fill_fifo(struct net_device *dev){ struct comx_channel *ch = dev->priv; struct mixcom_privdata *hw = ch->HW_privdata; register word to_send = hw->sending->len - hw->tx_ptr; outsb(dev->base_addr + HSCX_FIFO, &(hw->sending->data[hw->tx_ptr]), min_t(unsigned int, to_send, 32)); if (to_send <= 32) { hscx_cmd(dev, HSCX_XTF | HSCX_XME); kfree_skb(hw->sending); hw->sending = NULL; hw->tx_ptr = 0; } else { hscx_cmd(dev, HSCX_XTF); hw->tx_ptr += 32; }}static inline void hscx_empty_fifo(struct net_device *dev, int cnt){ struct comx_channel *ch = dev->priv; struct mixcom_privdata *hw = ch->HW_privdata; if (hw->recving == NULL) { if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) { ch->stats.rx_dropped++; hscx_cmd(dev, HSCX_RHR); } else { skb_reserve(hw->recving, 16); skb_put(hw->recving, HSCX_MTU); } hw->rx_ptr = 0; } if (cnt > 32 || !cnt || hw->recving == NULL) { printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n", cnt, (void *)hw->recving); return; } insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt); hw->rx_ptr += cnt; hscx_cmd(dev, HSCX_RMC);}static int MIXCOM_txe(struct net_device *dev){ struct comx_channel *ch = dev->priv; struct mixcom_privdata *hw = ch->HW_privdata; return !test_bit(0, &hw->txbusy);}static int mixcom_probe(struct net_device *dev){ unsigned long flags; int id, vstr, ret=0; save_flags(flags); cli(); id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET) & 0x7f; if (id != MIXCOM_ID ) { ret=-ENODEV; printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr); goto out; } vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f; if(vstr>=sizeof(hscx_versions)/sizeof(char*) || hscx_versions[vstr]==NULL) { printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr); ret = -ENODEV; } else { printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]); ret = 0; }out: restore_flags(flags); return ret;}#if 0static void MIXCOM_set_clock(struct net_device *dev){ struct comx_channel *ch = dev->priv; struct mixcom_privdata *hw = ch->HW_privdata; if (hw->clock) { ; } else { ; }}#endifstatic void mixcom_board_on(struct net_device *dev){ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); udelay(1000); outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); udelay(1000);}static void mixcom_board_off(struct net_device *dev){ outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET); udelay(1000);}static void mixcom_off(struct net_device *dev){ wr_hscx(dev, HSCX_CCR1, 0x0);}static void mixcom_on(struct net_device *dev){ struct comx_channel *ch = dev->priv; wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ ); wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS ); wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN ); hscx_cmd(dev, HSCX_XRES | HSCX_RHR); if (ch->HW_set_clock) ch->HW_set_clock(dev);}static int MIXCOM_send_packet(struct net_device *dev, struct sk_buff *skb) { struct comx_channel *ch = dev->priv; struct mixcom_privdata *hw = ch->HW_privdata; unsigned long flags; if (ch->debug_flags & DEBUG_HW_TX) { comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet"); } if (!(ch->line_status & LINE_UP)) { return FRAME_DROPPED; } if (skb->len > HSCX_MTU) { ch->stats.tx_errors++; return FRAME_ERROR; } save_flags(flags); cli(); if (test_and_set_bit(0, &hw->txbusy)) { printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len); restore_flags(flags); return FRAME_DROPPED; } hw->sending = skb; hw->tx_ptr = 0; hw->txbusy = 1;// atomic_inc(&skb->users); // save it hscx_fill_fifo(dev); restore_flags(flags); ch->stats.tx_packets++; ch->stats.tx_bytes += skb->len; if (ch->debug_flags & DEBUG_HW_TX) { comx_debug(dev, "MIXCOM_send_packet was successful\n\n"); } return FRAME_ACCEPTED;}static inline void mixcom_receive_frame(struct net_device *dev) { struct comx_channel *ch=dev->priv; struct mixcom_privdata *hw=ch->HW_privdata; register byte rsta; register word length; rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | HSCX_CRC | HSCX_RAB); length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | rd_hscx(dev, HSCX_RBCL); if ( length > hw->rx_ptr ) { hscx_empty_fifo(dev, length - hw->rx_ptr); } if (!(rsta & HSCX_VFR)) { ch->stats.rx_length_errors++; } if (rsta & HSCX_RDO) { ch->stats.rx_over_errors++; } if (!(rsta & HSCX_CRC)) { ch->stats.rx_crc_errors++; } if (rsta & HSCX_RAB) { ch->stats.rx_frame_errors++; } ch->stats.rx_packets++; ch->stats.rx_bytes += length; if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) { skb_trim(hw->recving, hw->rx_ptr - 1); if (ch->debug_flags & DEBUG_HW_RX) { comx_debug_skb(dev, hw->recving, "MIXCOM_interrupt receiving"); } hw->recving->dev = dev; if (ch->LINE_rx) { ch->LINE_rx(dev, hw->recving); } } else if(hw->recving) { kfree_skb(hw->recving); } hw->recving = NULL; hw->rx_ptr = 0;}static inline void mixcom_extended_interrupt(struct net_device *dev) { struct comx_channel *ch=dev->priv; struct mixcom_privdata *hw=ch->HW_privdata; register byte exir; exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC ); if (exir & HSCX_RFO) { ch->stats.rx_over_errors++; if (hw->rx_ptr) { kfree_skb(hw->recving); hw->recving = NULL; hw->rx_ptr = 0; } printk(KERN_ERR "MIXCOM: rx overrun\n"); hscx_cmd(dev, HSCX_RHR); } if (exir & HSCX_XDU) { // xmit underrun ch->stats.tx_errors++; ch->stats.tx_aborted_errors++; if (hw->tx_ptr) { kfree_skb(hw->sending); hw->sending = NULL; hw->tx_ptr = 0; } hscx_cmd(dev, HSCX_XRES); clear_bit(0, &hw->txbusy); if (ch->LINE_tx) { ch->LINE_tx(dev); } printk(KERN_ERR "MIXCOM: tx underrun\n"); } if (exir & HSCX_CSC) { ch->stats.tx_carrier_errors++; if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le if (test_and_clear_bit(0, &ch->lineup_pending)) { del_timer(&ch->lineup_timer); } else if (ch->line_status & LINE_UP) { ch->line_status &= ~LINE_UP; if (ch->LINE_status) { ch->LINE_status(dev,ch->line_status); } } } if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & HSCX_CTS)) { // Vonal fol if (!test_and_set_bit(0,&ch->lineup_pending)) { ch->lineup_timer.function = comx_lineup_func; ch->lineup_timer.data = (unsigned long)dev; ch->lineup_timer.expires = jiffies + HZ * ch->lineup_delay; add_timer(&ch->lineup_timer); hscx_cmd(dev, HSCX_XRES); clear_bit(0, &hw->txbusy); if (hw->sending) { kfree_skb(hw->sending); } hw->sending=NULL; hw->tx_ptr = 0; } } }}static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; struct net_device *dev = (struct net_device *)dev_id; struct comx_channel *ch, *twin_ch; struct mixcom_privdata *hw, *twin_hw; register unsigned char ista; if (dev==NULL) { printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq); return; } ch = dev->priv; hw = ch->HW_privdata; save_flags(flags); cli(); while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) { register byte ista2 = 0; if (ista & HSCX_RME) { mixcom_receive_frame(dev); } if (ista & HSCX_RPF) { hscx_empty_fifo(dev, 32); } if (ista & HSCX_XPR) { if (hw->tx_ptr) { hscx_fill_fifo(dev); } else { clear_bit(0, &hw->txbusy); ch->LINE_tx(dev); } } if (ista & HSCX_EXB) { mixcom_extended_interrupt(dev); } if ((ista & HSCX_EXA) && ch->twin) { mixcom_extended_interrupt(ch->twin); } if ((ista & HSCX_ICA) && ch->twin && (ista2 = rd_hscx(ch->twin, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | HSCX_XPR ))) { if (ista2 & HSCX_RME) { mixcom_receive_frame(ch->twin); } if (ista2 & HSCX_RPF) { hscx_empty_fifo(ch->twin, 32); } if (ista2 & HSCX_XPR) { twin_ch=ch->twin->priv; twin_hw=twin_ch->HW_privdata; if (twin_hw->tx_ptr) { hscx_fill_fifo(ch->twin); } else { clear_bit(0, &twin_hw->txbusy); ch->LINE_tx(ch->twin); } } } } restore_flags(flags); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -