📄 cosa.c
字号:
/* $Id: cosa.c,v 1.31 2000/03/08 17:47:16 kas Exp $ *//* * Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz> * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. *//* * The driver for the SRP and COSA synchronous serial cards. * * HARDWARE INFO * * Both cards are developed at the Institute of Computer Science, * Masaryk University (http://www.ics.muni.cz/). The hardware is * developed by Jiri Novotny <novotny@ics.muni.cz>. More information * and the photo of both cards is available at * http://www.pavoucek.cz/cosa.html. The card documentation, firmwares * and other goods can be downloaded from ftp://ftp.ics.muni.cz/pub/cosa/. * For Linux-specific utilities, see below in the "Software info" section. * If you want to order the card, contact Jiri Novotny. * * The SRP (serial port?, the Czech word "srp" means "sickle") card * is a 2-port intelligent (with its own 8-bit CPU) synchronous serial card * with V.24 interfaces up to 80kb/s each. * * The COSA (communication serial adapter?, the Czech word "kosa" means * "scythe") is a next-generation sync/async board with two interfaces * - currently any of V.24, X.21, V.35 and V.36 can be selected. * It has a 16-bit SAB80166 CPU and can do up to 10 Mb/s per channel. * The 8-channels version is in development. * * Both types have downloadable firmware and communicate via ISA DMA. * COSA can be also a bus-mastering device. * * SOFTWARE INFO * * The homepage of the Linux driver is at http://www.fi.muni.cz/~kas/cosa/. * The CVS tree of Linux driver can be viewed there, as well as the * firmware binaries and user-space utilities for downloading the firmware * into the card and setting up the card. * * The Linux driver (unlike the present *BSD drivers :-) can work even * for the COSA and SRP in one computer and allows each channel to work * in one of the three modes (character device, Cisco HDLC, Sync PPP). * * AUTHOR * * The Linux driver was written by Jan "Yenya" Kasprzak <kas@fi.muni.cz>. * * You can mail me bugfixes and even success reports. I am especially * interested in the SMP and/or muliti-channel success/failure reports * (I wonder if I did the locking properly :-). * * THE AUTHOR USED THE FOLLOWING SOURCES WHEN PROGRAMMING THE DRIVER * * The COSA/SRP NetBSD driver by Zdenek Salvet and Ivos Cernohlavek * The skeleton.c by Donald Becker * The SDL Riscom/N2 driver by Mike Natale * The Comtrol Hostess SV11 driver by Alan Cox * The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox *//* * 5/25/1999 : Marcelo Tosatti <marcelo@conectiva.com.br> * fixed a deadlock in cosa_sppp_open *//* ---------- Headers, macros, data structures ---------- */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/fs.h>#include <linux/devfs_fs_kernel.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/netdevice.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/device.h>#undef COSA_SLOW_IO /* for testing purposes only */#undef REALLY_SLOW_IO#include <asm/io.h>#include <asm/dma.h>#include <asm/byteorder.h>#include <net/syncppp.h>#include "cosa.h"/* Maximum length of the identification string. */#define COSA_MAX_ID_STRING 128/* Maximum length of the channel name */#define COSA_MAX_NAME (sizeof("cosaXXXcXXX")+1)/* Per-channel data structure */struct channel_data { void *if_ptr; /* General purpose pointer (used by SPPP) */ int usage; /* Usage count; >0 for chrdev, -1 for netdev */ int num; /* Number of the channel */ struct cosa_data *cosa; /* Pointer to the per-card structure */ int txsize; /* Size of transmitted data */ char *txbuf; /* Transmit buffer */ char name[COSA_MAX_NAME]; /* channel name */ /* The HW layer interface */ /* routine called from the RX interrupt */ char *(*setup_rx)(struct channel_data *channel, int size); /* routine called when the RX is done (from the EOT interrupt) */ int (*rx_done)(struct channel_data *channel); /* routine called when the TX is done (from the EOT interrupt) */ int (*tx_done)(struct channel_data *channel, int size); /* Character device parts */ struct semaphore rsem, wsem; char *rxdata; int rxsize; wait_queue_head_t txwaitq, rxwaitq; int tx_status, rx_status; /* SPPP/HDLC device parts */ struct ppp_device pppdev; struct sk_buff *rx_skb, *tx_skb; struct net_device_stats stats;};/* cosa->firmware_status bits */#define COSA_FW_RESET (1<<0) /* Is the ROM monitor active? */#define COSA_FW_DOWNLOAD (1<<1) /* Is the microcode downloaded? */#define COSA_FW_START (1<<2) /* Is the microcode running? */struct cosa_data { int num; /* Card number */ char name[COSA_MAX_NAME]; /* Card name - e.g "cosa0" */ unsigned int datareg, statusreg; /* I/O ports */ unsigned short irq, dma; /* IRQ and DMA number */ unsigned short startaddr; /* Firmware start address */ unsigned short busmaster; /* Use busmastering? */ int nchannels; /* # of channels on this card */ int driver_status; /* For communicating with firware */ int firmware_status; /* Downloaded, reseted, etc. */ long int rxbitmap, txbitmap; /* Bitmap of channels who are willing to send/receive data */ long int rxtx; /* RX or TX in progress? */ int enabled; int usage; /* usage count */ int txchan, txsize, rxsize; struct channel_data *rxchan; char *bouncebuf; char *txbuf, *rxbuf; struct channel_data *chan; spinlock_t lock; /* For exclusive operations on this structure */ char id_string[COSA_MAX_ID_STRING]; /* ROM monitor ID string */ char *type; /* card type */};/* * Define this if you want all the possible ports to be autoprobed. * It is here but it probably is not a good idea to use this. *//* #define COSA_ISA_AUTOPROBE 1 *//* * Character device major number. 117 was allocated for us. * The value of 0 means to allocate a first free one. */static int cosa_major = 117;/* * Encoding of the minor numbers: * The lowest CARD_MINOR_BITS bits means the channel on the single card, * the highest bits means the card number. */#define CARD_MINOR_BITS 4 /* How many bits in minor number are reserved * for the single card *//* * The following depends on CARD_MINOR_BITS. Unfortunately, the "MODULE_STRING" * macro doesn't like anything other than the raw number as an argument :-( */#define MAX_CARDS 16/* #define MAX_CARDS (1 << (8-CARD_MINOR_BITS)) */#define DRIVER_RX_READY 0x0001#define DRIVER_TX_READY 0x0002#define DRIVER_TXMAP_SHIFT 2#define DRIVER_TXMAP_MASK 0x0c /* FIXME: 0xfc for 8-channel version *//* * for cosa->rxtx - indicates whether either transmit or receive is * in progress. These values are mean number of the bit. */#define TXBIT 0#define RXBIT 1#define IRQBIT 2#define COSA_MTU 2000 /* FIXME: I don't know this exactly */#undef DEBUG_DATA //1 /* Dump the data read or written to the channel */#undef DEBUG_IRQS //1 /* Print the message when the IRQ is received */#undef DEBUG_IO //1 /* Dump the I/O traffic */#define TX_TIMEOUT (5*HZ)/* Maybe the following should be allocated dynamically */static struct cosa_data cosa_cards[MAX_CARDS];static int nr_cards;#ifdef COSA_ISA_AUTOPROBEstatic int io[MAX_CARDS+1] = { 0x220, 0x228, 0x210, 0x218, 0, };/* NOTE: DMA is not autoprobed!!! */static int dma[MAX_CARDS+1] = { 1, 7, 1, 7, 1, 7, 1, 7, 0, };#elsestatic int io[MAX_CARDS+1];static int dma[MAX_CARDS+1];#endif/* IRQ can be safely autoprobed */static int irq[MAX_CARDS+1] = { -1, -1, -1, -1, -1, -1, 0, };/* for class stuff*/static struct class_simple *cosa_class;#ifdef MODULEMODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i");MODULE_PARM_DESC(io, "The I/O bases of the COSA or SRP cards");MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i");MODULE_PARM_DESC(irq, "The IRQ lines of the COSA or SRP cards");MODULE_PARM(dma, "1-" __MODULE_STRING(MAX_CARDS) "i");MODULE_PARM_DESC(dma, "The DMA channels of the COSA or SRP cards");MODULE_AUTHOR("Jan \"Yenya\" Kasprzak, <kas@fi.muni.cz>");MODULE_DESCRIPTION("Modular driver for the COSA or SRP synchronous card");MODULE_LICENSE("GPL");#endif/* I use this mainly for testing purposes */#ifdef COSA_SLOW_IO#define cosa_outb outb_p#define cosa_outw outw_p#define cosa_inb inb_p#define cosa_inw inw_p#else#define cosa_outb outb#define cosa_outw outw#define cosa_inb inb#define cosa_inw inw#endif#define is_8bit(cosa) (!(cosa->datareg & 0x08))#define cosa_getstatus(cosa) (cosa_inb(cosa->statusreg))#define cosa_putstatus(cosa, stat) (cosa_outb(stat, cosa->statusreg))#define cosa_getdata16(cosa) (cosa_inw(cosa->datareg))#define cosa_getdata8(cosa) (cosa_inb(cosa->datareg))#define cosa_putdata16(cosa, dt) (cosa_outw(dt, cosa->datareg))#define cosa_putdata8(cosa, dt) (cosa_outb(dt, cosa->datareg))/* Initialization stuff */static int cosa_probe(int ioaddr, int irq, int dma);/* HW interface */static void cosa_enable_rx(struct channel_data *chan);static void cosa_disable_rx(struct channel_data *chan);static int cosa_start_tx(struct channel_data *channel, char *buf, int size);static void cosa_kick(struct cosa_data *cosa);static int cosa_dma_able(struct channel_data *chan, char *buf, int data);/* SPPP/HDLC stuff */static void sppp_channel_init(struct channel_data *chan);static void sppp_channel_delete(struct channel_data *chan);static int cosa_sppp_open(struct net_device *d);static int cosa_sppp_close(struct net_device *d);static void cosa_sppp_timeout(struct net_device *d);static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *d);static char *sppp_setup_rx(struct channel_data *channel, int size);static int sppp_rx_done(struct channel_data *channel);static int sppp_tx_done(struct channel_data *channel, int size);static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);static struct net_device_stats *cosa_net_stats(struct net_device *dev);/* Character device */static void chardev_channel_init(struct channel_data *chan);static char *chrdev_setup_rx(struct channel_data *channel, int size);static int chrdev_rx_done(struct channel_data *channel);static int chrdev_tx_done(struct channel_data *channel, int size);static ssize_t cosa_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);static ssize_t cosa_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos);static unsigned int cosa_poll(struct file *file, poll_table *poll);static int cosa_open(struct inode *inode, struct file *file);static int cosa_release(struct inode *inode, struct file *file);static int cosa_chardev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);#ifdef COSA_FASYNC_WORKINGstatic int cosa_fasync(struct inode *inode, struct file *file, int on);#endifstatic struct file_operations cosa_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = cosa_read, .write = cosa_write, .poll = cosa_poll, .ioctl = cosa_chardev_ioctl, .open = cosa_open, .release = cosa_release,#ifdef COSA_FASYNC_WORKING .fasync = cosa_fasync,#endif};/* Ioctls */static int cosa_start(struct cosa_data *cosa, int address);static int cosa_reset(struct cosa_data *cosa);static int cosa_download(struct cosa_data *cosa, void __user *a);static int cosa_readmem(struct cosa_data *cosa, void __user *a);/* COSA/SRP ROM monitor */static int download(struct cosa_data *cosa, const char __user *data, int addr, int len);static int startmicrocode(struct cosa_data *cosa, int address);static int readmem(struct cosa_data *cosa, char __user *data, int addr, int len);static int cosa_reset_and_read_id(struct cosa_data *cosa, char *id);/* Auxilliary functions */static int get_wait_data(struct cosa_data *cosa);static int put_wait_data(struct cosa_data *cosa, int data);static int puthexnumber(struct cosa_data *cosa, int number);static void put_driver_status(struct cosa_data *cosa);static void put_driver_status_nolock(struct cosa_data *cosa);/* Interrupt handling */static irqreturn_t cosa_interrupt(int irq, void *cosa, struct pt_regs *regs);/* I/O ops debugging */#ifdef DEBUG_IOstatic void debug_data_in(struct cosa_data *cosa, int data);static void debug_data_out(struct cosa_data *cosa, int data);static void debug_data_cmd(struct cosa_data *cosa, int data);static void debug_status_in(struct cosa_data *cosa, int status);static void debug_status_out(struct cosa_data *cosa, int status);#endif/* ---------- Initialization stuff ---------- */static int __init cosa_init(void){ int i, err = 0; printk(KERN_INFO "cosa v1.08 (c) 1997-2000 Jan Kasprzak <kas@fi.muni.cz>\n");#ifdef CONFIG_SMP printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n");#endif if (cosa_major > 0) { if (register_chrdev(cosa_major, "cosa", &cosa_fops)) { printk(KERN_WARNING "cosa: unable to get major %d\n", cosa_major); err = -EIO; goto out; } } else { if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) { printk(KERN_WARNING "cosa: unable to register chardev\n"); err = -EIO; goto out; } } for (i=0; i<MAX_CARDS; i++) cosa_cards[i].num = -1; for (i=0; io[i] != 0 && i < MAX_CARDS; i++) cosa_probe(io[i], irq[i], dma[i]); if (!nr_cards) { printk(KERN_WARNING "cosa: no devices found.\n"); unregister_chrdev(cosa_major, "cosa"); err = -ENODEV; goto out; } devfs_mk_dir("cosa"); cosa_class = class_simple_create(THIS_MODULE, "cosa"); if (IS_ERR(cosa_class)) { err = PTR_ERR(cosa_class); goto out_chrdev; } for (i=0; i<nr_cards; i++) { class_simple_device_add(cosa_class, MKDEV(cosa_major, i), NULL, "cosa%d", i); err = devfs_mk_cdev(MKDEV(cosa_major, i), S_IFCHR|S_IRUSR|S_IWUSR, "cosa/%d", i); if (err) { class_simple_device_remove(MKDEV(cosa_major, i)); goto out_chrdev; } } err = 0; goto out; out_chrdev: unregister_chrdev(cosa_major, "cosa");out: return err;}module_init(cosa_init);static void __exit cosa_exit(void){ struct cosa_data *cosa; int i; printk(KERN_INFO "Unloading the cosa module\n"); for (i=0; i<nr_cards; i++) { class_simple_device_remove(MKDEV(cosa_major, i)); devfs_remove("cosa/%d", i); } class_simple_destroy(cosa_class); devfs_remove("cosa"); for (cosa=cosa_cards; nr_cards--; cosa++) { /* Clean up the per-channel data */ for (i=0; i<cosa->nchannels; i++) { /* Chardev driver has no alloc'd per-channel data */ sppp_channel_delete(cosa->chan+i); } /* Clean up the per-card data */ kfree(cosa->chan); kfree(cosa->bouncebuf); free_irq(cosa->irq, cosa); free_dma(cosa->dma); release_region(cosa->datareg,is_8bit(cosa)?2:4); } unregister_chrdev(cosa_major, "cosa");}module_exit(cosa_exit);/* * This function should register all the net devices needed for the * single channel. */static __inline__ void channel_init(struct channel_data *chan){ sprintf(chan->name, "cosa%dc%d", chan->cosa->num, chan->num); /* Initialize the chardev data structures */ chardev_channel_init(chan); /* Register the sppp interface */ sppp_channel_init(chan);} static int cosa_probe(int base, int irq, int dma){ struct cosa_data *cosa = cosa_cards+nr_cards; int i, err = 0; memset(cosa, 0, sizeof(struct cosa_data)); /* Checking validity of parameters: */ /* IRQ should be 2-7 or 10-15; negative IRQ means autoprobe */ if ((irq >= 0 && irq < 2) || irq > 15 || (irq < 10 && irq > 7)) { printk (KERN_INFO "cosa_probe: invalid IRQ %d\n", irq); return -1; } /* I/O address should be between 0x100 and 0x3ff and should be * multiple of 8. */ if (base < 0x100 || base > 0x3ff || base & 0x7) { printk (KERN_INFO "cosa_probe: invalid I/O address 0x%x\n", base); return -1; } /* DMA should be 0,1 or 3-7 */ if (dma < 0 || dma == 4 || dma > 7) { printk (KERN_INFO "cosa_probe: invalid DMA %d\n", dma); return -1; } /* and finally, on 16-bit COSA DMA should be 4-7 and * I/O base should not be multiple of 0x10 */ if (((base & 0x8) && dma < 4) || (!(base & 0x8) && dma > 3)) { printk (KERN_INFO "cosa_probe: 8/16 bit base and DMA mismatch" " (base=0x%x, dma=%d)\n", base, dma); return -1; } cosa->dma = dma; cosa->datareg = base; cosa->statusreg = is_8bit(cosa)?base+1:base+2; spin_lock_init(&cosa->lock); if (!request_region(base, is_8bit(cosa)?2:4,"cosa")) return -1; if (cosa_reset_and_read_id(cosa, cosa->id_string) < 0) { printk(KERN_DEBUG "cosa: probe at 0x%x failed.\n", base); err = -1; goto err_out; } /* Test the validity of identification string */ if (!strncmp(cosa->id_string, "SRP", 3)) cosa->type = "srp"; else if (!strncmp(cosa->id_string, "COSA", 4)) cosa->type = is_8bit(cosa)? "cosa8": "cosa16"; else {/* Print a warning only if we are not autoprobing */#ifndef COSA_ISA_AUTOPROBE printk(KERN_INFO "cosa: valid signature not found at 0x%x.\n", base);#endif err = -1; goto err_out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -