epca.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,180 行 · 第 1/5 页
C
2,180 行
case PC64XE: bd->memory_seg = 0xf000; break; case PCXI: board_id = inb((int)bd->port); if ((board_id & 0x1) == 0x1) { /* Begin it's an XI card */ /* Is it a 64K board */ if ((board_id & 0x30) == 0) bd->memory_seg = 0xf000; /* Is it a 128K board */ if ((board_id & 0x30) == 0x10) bd->memory_seg = 0xe000; /* Is is a 256K board */ if ((board_id & 0x30) == 0x20) bd->memory_seg = 0xc000; /* Is it a 512K board */ if ((board_id & 0x30) == 0x30) bd->memory_seg = 0x8000; } /* End it is an XI card */ else { printk(KERN_ERR "<Error> - Board at 0x%x doesn't appear to be an XI\n",(int)bd->port); } break; } /* End switch on bd->type */ } /* End for each card */ if (tty_register_driver(pc_driver)) panic("Couldn't register Digi PC/ driver"); if (tty_register_driver(pc_info)) panic("Couldn't register Digi PC/ info "); /* ------------------------------------------------------------------- Start up the poller to check for events on all enabled boards ---------------------------------------------------------------------- */ init_timer(&epca_timer); epca_timer.function = epcapoll; mod_timer(&epca_timer, jiffies + HZ/25); restore_flags(flags); return 0;} /* End pc_init *//* ------------------ Begin post_fep_init ---------------------- */static void post_fep_init(unsigned int crd){ /* Begin post_fep_init */ int i; unchar *memaddr; volatile struct global_data *gd; struct board_info *bd; volatile struct board_chan *bc; struct channel *ch; int shrinkmem = 0, lowwater ; /* ------------------------------------------------------------- This call is made by the user via. the ioctl call DIGI_INIT. It is responsible for setting up all the card specific stuff. ---------------------------------------------------------------- */ bd = &boards[crd]; /* ----------------------------------------------------------------- If this is a PCI board, get the port info. Remember PCI cards do not have entries into the epcaconfig.h file, so we can't get the number of ports from it. Unfortunetly, this means that anyone doing a DIGI_GETINFO before the board has booted will get an invalid number of ports returned (It should return 0). Calls to DIGI_GETINFO after DIGI_INIT has been called will return the proper values. ------------------------------------------------------------------- */ if (bd->type >= PCIXEM) /* If the board in question is PCI */ { /* Begin get PCI number of ports */ /* -------------------------------------------------------------------- Below we use XEMPORTS as a memory offset regardless of which PCI card it is. This is because all of the supported PCI cards have the same memory offset for the channel data. This will have to be changed if we ever develop a PCI/XE card. NOTE : The FEP manual states that the port offset is 0xC22 as opposed to 0xC02. This is only true for PC/XE, and PC/XI cards; not for the XEM, or CX series. On the PCI cards the number of ports is determined by reading a ID PROM located in the box attached to the card. The card can then determine the index the id to determine the number of ports available. (FYI - The id should be located at 0x1ac (And may use up to 4 bytes if the box in question is a XEM or CX)). ------------------------------------------------------------------------ */ bd->numports = (unsigned short)*(unsigned char *)bus_to_virt((unsigned long) (bd->re_map_membase + XEMPORTS)); epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); nbdevs += (bd->numports); } /* End get PCI number of ports */ if (crd != 0) card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; else card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ ch = card_ptr[crd]; epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); memaddr = (unchar *)bd->re_map_membase; /* The below command is necessary because newer kernels (2.1.x and up) do not have a 1:1 virtual to physical mapping. The below call adjust for that. */ memaddr = (unsigned char *)bus_to_virt((unsigned long)memaddr); /* ----------------------------------------------------------------- The below assignment will set bc to point at the BEGINING of the cards channel structures. For 1 card there will be between 8 and 64 of these structures. -------------------------------------------------------------------- */ bc = (volatile struct board_chan *)((ulong)memaddr + CHANSTRUCT); /* ------------------------------------------------------------------- The below assignment will set gd to point at the BEGINING of global memory address 0xc00. The first data in that global memory actually starts at address 0xc1a. The command in pointer begins at 0xd10. ---------------------------------------------------------------------- */ gd = (volatile struct global_data *)((ulong)memaddr + GLOBAL); /* -------------------------------------------------------------------- XEPORTS (address 0xc22) points at the number of channels the card supports. (For 64XE, XI, XEM, and XR use 0xc02) ----------------------------------------------------------------------- */ if (((bd->type == PCXEVE) | (bd->type == PCXE)) && (*(ushort *)((ulong)memaddr + XEPORTS) < 3)) shrinkmem = 1; if (bd->type < PCIXEM) if (!request_region((int)bd->port, 4, board_desc[bd->type])) return; memwinon(bd, 0); /* -------------------------------------------------------------------- Remember ch is the main drivers channels structure, while bc is the cards channel structure. ------------------------------------------------------------------------ */ /* For every port on the card do ..... */ for (i = 0; i < bd->numports; i++, ch++, bc++) { /* Begin for each port */ ch->brdchan = bc; ch->mailbox = gd; INIT_WORK(&ch->tqueue, do_softint, ch); ch->board = &boards[crd]; switch (bd->type) { /* Begin switch bd->type */ /* ---------------------------------------------------------------- Since some of the boards use different bitmaps for their control signals we cannot hard code these values and retain portability. We virtualize this data here. ------------------------------------------------------------------- */ case EISAXEM: case PCXEM: case PCIXEM: case PCIXRJ: case PCIXR: ch->m_rts = 0x02 ; ch->m_dcd = 0x80 ; ch->m_dsr = 0x20 ; ch->m_cts = 0x10 ; ch->m_ri = 0x40 ; ch->m_dtr = 0x01 ; break; case PCXE: case PCXEVE: case PCXI: case PC64XE: ch->m_rts = 0x02 ; ch->m_dcd = 0x08 ; ch->m_dsr = 0x10 ; ch->m_cts = 0x20 ; ch->m_ri = 0x40 ; ch->m_dtr = 0x80 ; break; } /* End switch bd->type */ if (boards[crd].altpin) { ch->dsr = ch->m_dcd; ch->dcd = ch->m_dsr; ch->digiext.digi_flags |= DIGI_ALTPIN; } else { ch->dcd = ch->m_dcd; ch->dsr = ch->m_dsr; } ch->boardnum = crd; ch->channelnum = i; ch->magic = EPCA_MAGIC; ch->tty = NULL; if (shrinkmem) { fepcmd(ch, SETBUFFER, 32, 0, 0, 0); shrinkmem = 0; } switch (bd->type) { /* Begin switch bd->type */ case PCIXEM: case PCIXRJ: case PCIXR: /* Cover all the 2MEG cards */ ch->txptr = memaddr + (((bc->tseg) << 4) & 0x1fffff); ch->rxptr = memaddr + (((bc->rseg) << 4) & 0x1fffff); ch->txwin = FEPWIN | ((bc->tseg) >> 11); ch->rxwin = FEPWIN | ((bc->rseg) >> 11); break; case PCXEM: case EISAXEM: /* Cover all the 32K windowed cards */ /* Mask equal to window size - 1 */ ch->txptr = memaddr + (((bc->tseg) << 4) & 0x7fff); ch->rxptr = memaddr + (((bc->rseg) << 4) & 0x7fff); ch->txwin = FEPWIN | ((bc->tseg) >> 11); ch->rxwin = FEPWIN | ((bc->rseg) >> 11); break; case PCXEVE: case PCXE: ch->txptr = memaddr + (((bc->tseg - bd->memory_seg) << 4) & 0x1fff); ch->txwin = FEPWIN | ((bc->tseg - bd->memory_seg) >> 9); ch->rxptr = memaddr + (((bc->rseg - bd->memory_seg) << 4) & 0x1fff); ch->rxwin = FEPWIN | ((bc->rseg - bd->memory_seg) >>9 ); break; case PCXI: case PC64XE: ch->txptr = memaddr + ((bc->tseg - bd->memory_seg) << 4); ch->rxptr = memaddr + ((bc->rseg - bd->memory_seg) << 4); ch->txwin = ch->rxwin = 0; break; } /* End switch bd->type */ ch->txbufhead = 0; ch->txbufsize = bc->tmax + 1; ch->rxbufhead = 0; ch->rxbufsize = bc->rmax + 1; lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); /* Set transmitter low water mark */ fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); /* Set receiver low water mark */ fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); /* Set receiver high water mark */ fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); bc->edelay = 100; bc->idata = 1; ch->startc = bc->startc; ch->stopc = bc->stopc; ch->startca = bc->startca; ch->stopca = bc->stopca; ch->fepcflag = 0; ch->fepiflag = 0; ch->fepoflag = 0; ch->fepstartc = 0; ch->fepstopc = 0; ch->fepstartca = 0; ch->fepstopca = 0; ch->close_delay = 50; ch->count = 0; ch->blocked_open = 0; init_waitqueue_head(&ch->open_wait); init_waitqueue_head(&ch->close_wait); ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); if (!(ch->tmp_buf)) { printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); release_region((int)bd->port, 4); while(i-- > 0) kfree((ch--)->tmp_buf); return; } else memset((void *)ch->tmp_buf,0,ch->txbufsize); } /* End for each port */ printk(KERN_INFO "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); sprintf(mesg, "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); console_print(mesg); memwinoff(bd, 0);} /* End post_fep_init *//* --------------------- Begin epcapoll ------------------------ */static void epcapoll(unsigned long ignored){ /* Begin epcapoll */ unsigned long flags; int crd; volatile unsigned int head, tail; struct channel *ch; struct board_info *bd; /* ------------------------------------------------------------------- This routine is called upon every timer interrupt. Even though the Digi series cards are capable of generating interrupts this method of non-looping polling is more efficient. This routine checks for card generated events (Such as receive data, are transmit buffer empty) and acts on those events. ----------------------------------------------------------------------- */ save_flags(flags); cli(); for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ bd = &boards[crd]; ch = card_ptr[crd]; if ((bd->status == DISABLED) || digi_poller_inhibited) continue; /* Begin loop next interation */ /* ----------------------------------------------------------- assertmemoff is not needed here; indeed it is an empty subroutine. It is being kept because future boards may need this as well as some legacy boards. ---------------------------------------------------------------- */ assertmemoff(ch); globalwinon(ch); /* --------------------------------------------------------------- In this case head and tail actually refer to the event queue not the transmit or receive queue. ------------------------------------------------------------------- */ head = ch->mailbox->ein; tail = ch->mailbox->eout; /* If head isn't equal to tail we have an event */ if (head != tail) doevent(crd); memoff(ch); } /* End for each card */ mod_timer(&epca_timer, jiffies + (HZ / 25)); restore_flags(flags);} /* End epcapoll *//* --------------------- Begin doevent ------------------------ */static void doevent(int crd){ /* Begin doevent */ volatile unchar *eventbuf; struct channel *ch, *chan0; static struct tty_struct *tty; volatile struct board_info *bd; volatile struct board_chan *bc; register volatile unsigned int tail, head; register int event, channel; register int mstat, lstat; /* ------------------------------------------------------------------- This subroutine is called by epcapoll when an event is detected in the event queue. This routine responds to those events. --------------------------------------------------------------------- */ bd = &boards[crd]; chan0 = card_ptr[crd]; epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); assertgwinon(chan0); while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) { /* Begin while something in event queue */ assertgwinon(chan0); eventbuf = (volatile unchar *)bus_to_virt((ulong)(bd->re_map_membase + tail + ISTART)); /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?