isicom.c
来自「linux 内核源代码」· C语言 代码 · 共 1,898 行 · 第 1/4 页
C
1,898 行
/* * 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. * * Original driver code supplied by Multi-Tech * * Changes * 1/9/98 alan@redhat.com Merge to 2.0.x kernel tree * Obtain and use official major/minors * Loader switched to a misc device * (fixed range check bug as a side effect) * Printk clean up * 9/12/98 alan@redhat.com Rough port to 2.1.x * * 10/6/99 sameer Merged the ISA and PCI drivers to * a new unified driver. * * 3/9/99 sameer Added support for ISI4616 cards. * * 16/9/99 sameer We do not force RTS low anymore. * This is to prevent the firmware * from getting confused. * * 26/10/99 sameer Cosmetic changes:The driver now * dumps the Port Count information * along with I/O address and IRQ. * * 13/12/99 sameer Fixed the problem with IRQ sharing. * * 10/5/00 sameer Fixed isicom_shutdown_board() * to not lower DTR on all the ports * when the last port on the card is * closed. * * 10/5/00 sameer Signal mask setup command added * to isicom_setup_port and * isicom_shutdown_port. * * 24/5/00 sameer The driver is now SMP aware. * * * 27/11/00 Vinayak P Risbud Fixed the Driver Crash Problem * * * 03/01/01 anil .s Added support for resetting the * internal modems on ISI cards. * * 08/02/01 anil .s Upgraded the driver for kernel * 2.4.x * * 11/04/01 Kevin Fixed firmware load problem with * ISIHP-4X card * * 30/04/01 anil .s Fixed the remote login through * ISI port problem. Now the link * does not go down before password * prompt. * * 03/05/01 anil .s Fixed the problem with IRQ sharing * among ISI-PCI cards. * * 03/05/01 anil .s Added support to display the version * info during insmod as well as module * listing by lsmod. * * 10/05/01 anil .s Done the modifications to the source * file and Install script so that the * same installation can be used for * 2.2.x and 2.4.x kernel. * * 06/06/01 anil .s Now we drop both dtr and rts during * shutdown_port as well as raise them * during isicom_config_port. * * 09/06/01 acme@conectiva.com.br use capable, not suser, do * restore_flags on failure in * isicom_send_break, verify put_user * result * * 11/02/03 ranjeeth Added support for 230 Kbps and 460 Kbps * Baud index extended to 21 * * 20/03/03 ranjeeth Made to work for Linux Advanced server. * Taken care of license warning. * * 10/12/03 Ravindra Made to work for Fedora Core 1 of * Red Hat Distribution * * 06/01/05 Alan Cox Merged the ISI and base kernel strands * into a single 2.6 driver * * *********************************************************** * * To use this driver you also need the support package. You * can find this in RPM format on * ftp://ftp.linux.org.uk/pub/linux/alan * * You can find the original tools for this direct from Multitech * ftp://ftp.multitech.com/ISI-Cards/ * * Having installed the cards the module options (/etc/modprobe.conf) * * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4 * * Omit those entries for boards you don't have installed. * * TODO * Merge testing * 64-bit verification */#include <linux/module.h>#include <linux/firmware.h>#include <linux/kernel.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/termios.h>#include <linux/fs.h>#include <linux/sched.h>#include <linux/serial.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/ioport.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/system.h>#include <linux/pci.h>#include <linux/isicom.h>#define InterruptTheCard(base) outw(0, (base) + 0xc)#define ClearInterrupt(base) inw((base) + 0x0a)#define pr_dbg(str...) pr_debug("ISICOM: " str)#ifdef DEBUG#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))#else#define isicom_paranoia_check(a, b, c) 0#endifstatic int isicom_probe(struct pci_dev *, const struct pci_device_id *);static void __devexit isicom_remove(struct pci_dev *);static struct pci_device_id isicom_pci_tbl[] = { { PCI_DEVICE(VENDOR_ID, 0x2028) }, { PCI_DEVICE(VENDOR_ID, 0x2051) }, { PCI_DEVICE(VENDOR_ID, 0x2052) }, { PCI_DEVICE(VENDOR_ID, 0x2053) }, { PCI_DEVICE(VENDOR_ID, 0x2054) }, { PCI_DEVICE(VENDOR_ID, 0x2055) }, { PCI_DEVICE(VENDOR_ID, 0x2056) }, { PCI_DEVICE(VENDOR_ID, 0x2057) }, { PCI_DEVICE(VENDOR_ID, 0x2058) }, { 0 }};MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);static struct pci_driver isicom_driver = { .name = "isicom", .id_table = isicom_pci_tbl, .probe = isicom_probe, .remove = __devexit_p(isicom_remove)};static int prev_card = 3; /* start servicing isi_card[0] */static struct tty_driver *isicom_normal;static void isicom_tx(unsigned long _data);static void isicom_start(struct tty_struct *tty);static DEFINE_TIMER(tx, isicom_tx, 0, 0);/* baud index mappings from linux defns to isi */static signed char linuxb_to_isib[] = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21};struct isi_board { unsigned long base; int irq; unsigned char port_count; unsigned short status; unsigned short port_status; /* each bit for each port */ unsigned short shift_count; struct isi_port * ports; signed char count; spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */ unsigned long flags; unsigned int index;};struct isi_port { unsigned short magic; unsigned int flags; int count; int blocked_open; int close_delay; u16 channel; u16 status; u16 closing_wait; struct isi_board * card; struct tty_struct * tty; wait_queue_head_t close_wait; wait_queue_head_t open_wait; unsigned char * xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt;};static struct isi_board isi_card[BOARD_COUNT];static struct isi_port isi_ports[PORT_COUNT];/* * Locking functions for card level locking. We need to own both * the kernel lock for the card and have the card in a position that * it wants to talk. */static inline int WaitTillCardIsFree(unsigned long base){ unsigned int count = 0; unsigned int a = in_atomic(); /* do we run under spinlock? */ while (!(inw(base + 0xe) & 0x1) && count++ < 100) if (a) mdelay(1); else msleep(1); return !(inw(base + 0xe) & 0x1);}static int lock_card(struct isi_board *card){ unsigned long base = card->base; unsigned int retries, a; for (retries = 0; retries < 10; retries++) { spin_lock_irqsave(&card->card_lock, card->flags); for (a = 0; a < 10; a++) { if (inw(base + 0xe) & 0x1) return 1; udelay(10); } spin_unlock_irqrestore(&card->card_lock, card->flags); msleep(10); } printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%lx)\n", card->base); return 0; /* Failed to acquire the card! */}static void unlock_card(struct isi_board *card){ spin_unlock_irqrestore(&card->card_lock, card->flags);}/* * ISI Card specific ops ... *//* card->lock HAS to be held */static void raise_dtr(struct isi_port *port){ struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0504, base); InterruptTheCard(base); port->status |= ISI_DTR;}/* card->lock HAS to be held */static inline void drop_dtr(struct isi_port *port){ struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0404, base); InterruptTheCard(base); port->status &= ~ISI_DTR;}/* card->lock HAS to be held */static inline void raise_rts(struct isi_port *port){ struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0a04, base); InterruptTheCard(base); port->status |= ISI_RTS;}/* card->lock HAS to be held */static inline void drop_rts(struct isi_port *port){ struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0804, base); InterruptTheCard(base); port->status &= ~ISI_RTS;}/* card->lock MUST NOT be held */static inline void raise_dtr_rts(struct isi_port *port){ struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (!lock_card(card)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0f04, base); InterruptTheCard(base); port->status |= (ISI_DTR | ISI_RTS); unlock_card(card);}/* card->lock HAS to be held */static void drop_dtr_rts(struct isi_port *port){ struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0c04, base); InterruptTheCard(base); port->status &= ~(ISI_RTS | ISI_DTR);}/* * ISICOM Driver specific routines ... * */static inline int __isicom_paranoia_check(struct isi_port const *port, char *name, const char *routine){ if (!port) { printk(KERN_WARNING "ISICOM: Warning: bad isicom magic for " "dev %s in %s.\n", name, routine); return 1; } if (port->magic != ISICOM_MAGIC) { printk(KERN_WARNING "ISICOM: Warning: NULL isicom port for " "dev %s in %s.\n", name, routine); return 1; } return 0;}/* * Transmitter. * * We shovel data into the card buffers on a regular basis. The card * will do the rest of the work for us. */static void isicom_tx(unsigned long _data){ unsigned long flags, base; unsigned int retries; short count = (BOARD_COUNT-1), card; short txcount, wrd, residue, word_count, cnt; struct isi_port *port; struct tty_struct *tty; /* find next active board */ card = (prev_card + 1) & 0x0003; while(count-- > 0) { if (isi_card[card].status & BOARD_ACTIVE) break; card = (card + 1) & 0x0003; } if (!(isi_card[card].status & BOARD_ACTIVE)) goto sched_again; prev_card = card; count = isi_card[card].port_count; port = isi_card[card].ports; base = isi_card[card].base; spin_lock_irqsave(&isi_card[card].card_lock, flags); for (retries = 0; retries < 100; retries++) { if (inw(base + 0xe) & 0x1) break; udelay(2); } if (retries >= 100) goto unlock; for (;count > 0;count--, port++) { /* port not active or tx disabled to force flow control */ if (!(port->flags & ASYNC_INITIALIZED) || !(port->status & ISI_TXOK)) continue; tty = port->tty; if (tty == NULL) continue; txcount = min_t(short, TX_SIZE, port->xmit_cnt); if (txcount <= 0 || tty->stopped || tty->hw_stopped) continue; if (!(inw(base + 0x02) & (1 << port->channel))) continue; pr_dbg("txing %d bytes, port%d.\n", txcount, port->channel + 1); outw((port->channel << isi_card[card].shift_count) | txcount, base); residue = NO; wrd = 0; while (1) { cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - port->xmit_tail)); if (residue == YES) { residue = NO; if (cnt > 0) { wrd |= (port->xmit_buf[port->xmit_tail] << 8); port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; txcount--; cnt--; outw(wrd, base); } else { outw(wrd, base); break; } } if (cnt <= 0) break; word_count = cnt >> 1;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?