📄 isicom.c
字号:
/* * 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 */#include <linux/module.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/tty.h>#include <linux/termios.h>#include <linux/fs.h>#include <linux/sched.h>#include <linux/serial.h>#include <linux/mm.h>#include <linux/miscdevice.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/ioport.h>#include <asm/segment.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/system.h>#include <linux/isicom.h>static int isicom_refcount = 0;static int prev_card = 3; /* start servicing isi_card[0] */static struct isi_board * irq_to_board[16] = { NULL, };static struct tty_driver isicom_normal, isicom_callout;static struct tty_struct * isicom_table[PORT_COUNT] = { NULL, };static struct termios * isicom_termios[PORT_COUNT] = { NULL, };static struct termios * isicom_termios_locked[PORT_COUNT] = { NULL, };static struct isi_board isi_card[BOARD_COUNT];static struct isi_port isi_ports[PORT_COUNT];DECLARE_TASK_QUEUE(tq_isicom);static struct timer_list tx;static char re_schedule = 1;#ifdef ISICOM_DEBUGstatic unsigned long tx_count = 0;#endifstatic int ISILoad_open(struct inode *inode, struct file *filp);static int ISILoad_release(struct inode *inode, struct file *filp);static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);static void isicom_tx(unsigned long _data);static void isicom_start(struct tty_struct * tty);static unsigned char * tmp_buf = 0;static struct semaphore tmp_buf_sem = MUTEX;/* baud index mappings from linux defns to isi */static char linuxb_to_isib[] = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19};/* * Firmware loader driver specific routines * */static struct file_operations ISILoad_fops = { NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* select */ ISILoad_ioctl, NULL, /* mmap */ ISILoad_open, NULL, /* flush */ ISILoad_release, NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL, /* revalidate */};struct miscdevice isiloader_device = { ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops}; extern inline int WaitTillCardIsFree(unsigned short base){ unsigned long count=0; while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000)); if (inw(base+0xe)&0x1) return 0; else return 1;}static int ISILoad_open(struct inode *inode, struct file *filp){#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISILoad:Firmware loader Opened!!!\n");#endif MOD_INC_USE_COUNT; return 0;}static int ISILoad_release(struct inode *inode, struct file *filp){#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISILoad:Firmware loader Close(Release)d\n",);#endif MOD_DEC_USE_COUNT; return 0;}static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ unsigned int card, i, j, signature, status; unsigned short word_count, base; bin_frame frame; /* exec_record exec_rec; */ if(get_user(card, (int *)arg)) return -EFAULT; if(card < 0 || card >= BOARD_COUNT) return -ENXIO; base=isi_card[card].base; if(base==0) return -ENXIO; /* disabled or not used */ switch(cmd) { case MIOCTL_RESET_CARD: if (!capable(CAP_SYS_ADMIN)) return -EPERM; printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%x ",card+1,base); inw(base+0x8); for(i=jiffies+HZ/100;time_before(jiffies, i);); outw(0,base+0x8); /* Reset */ for(j=1;j<=3;j++) { for(i=jiffies+HZ;time_before(jiffies, i);); printk("."); } signature=(inw(base+0x4)) & 0xff; if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) {#ifdef ISICOM_DEBUG printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));#endif printk("\nISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base); return -EIO; } switch(signature) { case 0xa5: case 0xbb: case 0xdd: isi_card[card].port_count = 8; isi_card[card].shift_count = 12; break; case 0xcc: isi_card[card].port_count = 16; isi_card[card].shift_count = 11; break; default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);#ifdef ISICOM_DEBUG printk("Sig=0x%x\n",signature);#endif return -EIO; } printk("-Done\n"); return put_user(signature,(unsigned int*)arg); case MIOCTL_LOAD_FIRMWARE: if (!capable(CAP_SYS_ADMIN)) return -EPERM; if(copy_from_user(&frame, (void *) arg, sizeof(bin_frame))) return -EFAULT; if (WaitTillCardIsFree(base)) return -EIO; outw(0xf0,base); /* start upload sequence */ outw(0x00,base); outw((frame.addr), base);/* lsb of adderess */ word_count=(frame.count >> 1) + frame.count % 2; outw(word_count, base); InterruptTheCard(base); for(i=0;i<=0x2f;i++); /* a wee bit of delay */ if (WaitTillCardIsFree(base)) return -EIO; if ((status=inw(base+0x4))!=0) { printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", card+1, frame.addr, frame.count, status); return -EIO; } outsw(base, (void *) frame.bin_data, word_count); InterruptTheCard(base); for(i=0;i<=0x0f;i++); /* another wee bit of delay */ if (WaitTillCardIsFree(base)) return -EIO; if ((status=inw(base+0x4))!=0) { printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status); return -EIO; } return 0; case MIOCTL_READ_FIRMWARE: if (!capable(CAP_SYS_ADMIN)) return -EPERM; if(copy_from_user(&frame, (void *) arg, sizeof(bin_header))) return -EFAULT; if (WaitTillCardIsFree(base)) return -EIO; outw(0xf1,base); /* start download sequence */ outw(0x00,base); outw((frame.addr), base);/* lsb of adderess */ word_count=(frame.count >> 1) + frame.count % 2; outw(word_count+1, base); InterruptTheCard(base); for(i=0;i<=0xf;i++); /* a wee bit of delay */ if (WaitTillCardIsFree(base)) return -EIO; if ((status=inw(base+0x4))!=0) { printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", card+1, frame.addr, frame.count, status); return -EIO; } inw(base); insw(base, frame.bin_data, word_count); InterruptTheCard(base); for(i=0;i<=0x0f;i++); /* another wee bit of delay */ if (WaitTillCardIsFree(base)) return -EIO; if ((status=inw(base+0x4))!=0) { printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status); return -EIO; } if(copy_to_user((void *) arg, &frame, sizeof(bin_frame))) return -EFAULT; return 0; case MIOCTL_XFER_CTRL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (WaitTillCardIsFree(base)) return -EIO; outw(0xf2, base); outw(0x800, base); outw(0x0, base); outw(0x0, base); InterruptTheCard(base); isi_card[card].status |= FIRMWARE_LOADED; return 0; default:#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd); #endif return -ENOIOCTLCMD; } } /* * ISICOM Driver specific routines ... * */ static inline int isicom_paranoia_check(struct isi_port const * port, kdev_t dev, const char * routine){#ifdef ISICOM_DEBUG static const char * badmagic = KERN_WARNING "ISICOM: Warning: bad isicom magic for dev %s in %s.\n"; static const char * badport = KERN_WARNING "ISICOM: Warning: NULL isicom port for dev %s in %s.\n"; if (!port) { printk(badport, kdevname(dev), routine); return 1; } if (port->magic != ISICOM_MAGIC) { printk(badmagic, kdevname(dev), routine); return 1; } #endif return 0;} extern inline void schedule_bh(struct isi_port * port){ queue_task(&port->bh_tqueue, &tq_isicom); mark_bh(ISICOM_BH);} /* Transmitter */static void isicom_tx(unsigned long _data){ short count = (BOARD_COUNT-1), card, base; short txcount, wait, wrd, residue, word_count, cnt; struct isi_port * port; struct tty_struct * tty; unsigned long flags; #ifdef ISICOM_DEBUG ++tx_count;#endif /* 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; for (;count > 0;count--, port++) { /* port not active or tx disabled to force flow control */ if (!(port->status & ISI_TXOK)) continue; tty = port->tty; save_flags(flags); cli(); txcount = MIN(TX_SIZE, port->xmit_cnt); if ((txcount <= 0) || tty->stopped || tty->hw_stopped) { restore_flags(flags); continue; } wait = 200; while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0)); if (wait <= 0) { restore_flags(flags);#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: isicom_tx:Card(0x%x) found busy.\n", card);#endif continue; } if (!(inw(base + 0x02) & (1 << port->channel))) { restore_flags(flags);#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: isicom_tx: cannot tx to 0x%x:%d.\n", base, port->channel + 1);#endif continue; }#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: txing %d bytes, port%d.\n", txcount, port->channel+1); #endif outw((port->channel << isi_card[card].shift_count) | txcount , base); residue = NO; wrd = 0; while (1) { cnt = MIN(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; outsw(base, port->xmit_buf+port->xmit_tail, word_count); port->xmit_tail = (port->xmit_tail + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); txcount -= (word_count << 1); port->xmit_cnt -= (word_count << 1); if (cnt & 0x0001) { residue = YES; wrd = port->xmit_buf[port->xmit_tail]; port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; txcount--; } }/* * Replaced the code below with hopefully a faster loop - sameer *//* while (1) { wrd = port->xmit_buf[port->xmit_tail++]; port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; if (--txcount > 0) { wrd |= (port->xmit_buf[port->xmit_tail++] << 8); port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; outw(wrd, base); if (--txcount <= 0) break; } else { outw(wrd, base); break; } }*/ InterruptTheCard(base); if (port->xmit_cnt <= 0) port->status &= ~ISI_TXOK; if (port->xmit_cnt <= WAKEUP_CHARS) schedule_bh(port); restore_flags(flags); } /* schedule another tx for hopefully in about 10ms */ sched_again: if (!re_schedule) return; init_timer(&tx); tx.expires = jiffies + HZ/100; tx.data = 0; tx.function = isicom_tx; add_timer(&tx); return; } /* Interrupt handlers */static void do_isicom_bh(void){ run_task_queue(&tq_isicom);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -