⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cyclades.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
        if (cy_irq_triggered)            break;    }    probe_ready = 0;    return(cy_irq_triggered);} /* get_auto_irq *//* * Calls get_auto_irq() multiple times, to make sure we don't get * faked out by random interrupts */static intdo_auto_irq(volatile ucchar *address){  int                   irq_lines = 0;  int                   irq_try_1 = 0, irq_try_2 = 0;  int                   retries;  unsigned long		flags;    /* Turn on interrupts (they may be off) */    save_flags(flags); sti();        probe_ready = 0;        cy_wild_int_mask = check_wild_interrupts();        irq_lines = grab_all_interrupts(cy_wild_int_mask);                for (retries = 0; retries < 5; retries++) {            if (!irq_try_1)                irq_try_1 = get_auto_irq(address);            if (!irq_try_2)                irq_try_2 = get_auto_irq(address);            if (irq_try_1 && irq_try_2) {                if (irq_try_1 == irq_try_2)                    break;                irq_try_1 = irq_try_2 = 0;            }        }    restore_flags(flags);    free_all_interrupts(irq_lines);    return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;} /* do_auto_irq *//* * This interrupt routine is used * while we are probing for submarines. */static voidcy_probe(int irq, void *dev_id, struct pt_regs *regs){  int save_xir, save_car;  int index = 0;        /* probing interrupts is only for ISA */    if (!probe_ready) {        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);        return;    }    cy_irq_triggered = irq;    cy_triggered |= 1 << irq;        if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {            save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));            save_car = cy_readb(intr_base_addr+(CyCAR<<index));            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));            cy_writeb((u_long)intr_base_addr+(CySRER<<index),                cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);            cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));        }        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);	                          /* Cy_ClrIntr is 0x1800 */    return;} /* cy_probe *//* The real interrupt service routine is called   whenever the card wants its hand held--chars   received, out buffer empty, modem change, etc. */static voidcyy_interrupt(int irq, void *dev_id, struct pt_regs *regs){  struct tty_struct *tty;  int status;  struct cyclades_card *cinfo;  struct cyclades_port *info;  volatile unsigned char *base_addr, *card_base_addr;  int chip;  int save_xir, channel, save_car;  char data;  volatile int char_count;  int outch;  int i,j,index;  int too_many;  int had_work;  int mdm_change;  int mdm_status;    if((cinfo = IRQ_cards[irq]) == 0){#ifdef CY_DEBUG_INTERRUPTSprintk("cy_interrupt: spurious interrupt %d\n\r", irq);#endif        return; /* spurious interrupt */    }    card_base_addr = (unsigned char *)cinfo->base_addr;    index = cinfo->bus_index;    /* This loop checks all chips in the card.  Make a note whenever       _any_ chip had some work to do, as this is considered an       indication that there will be more to do.  Only when no chip       has any work does this outermost loop exit.     */    do{        had_work = 0;        for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {            base_addr = (unsigned char *)		       (cinfo->base_addr + (cy_chip_offset[chip]<<index));            too_many = 0;            while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) {                had_work++;                /* The purpose of the following test is to ensure that                   no chip can monopolize the driver.  This forces the                   chips to be checked in a round-robin fashion (after                   draining each of a bunch (1000) of characters).                 */                if(1000<too_many++){                    break;                }                if (status & CySRReceive) { /* reception interrupt */#ifdef CY_DEBUG_INTERRUPTSprintk("cy_interrupt: rcvd intr, chip %d\n\r", chip);#endif                    /* determine the channel & change to that context */                    save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));                    channel = (u_short ) (save_xir & CyIRChannel);                    i = channel + chip * 4 + cinfo->first_line;                    info = &cy_port[i];                    info->last_active = jiffies;                    save_car = cy_readb(base_addr+(CyCAR<<index));                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);                    /* if there is nowhere to put the data, discard it */                    if(info->tty == 0){                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);                        if ( j == CyIVRRxEx ) { /* exception */                            data = cy_readb(base_addr+(CyRDSR<<index));                        } else { /* normal character reception */                            char_count = cy_readb(base_addr+(CyRDCR<<index));                            while(char_count--){                                data = cy_readb(base_addr+(CyRDSR<<index));                            }                        }                    }else{ /* there is an open port for this data */                        tty = info->tty;                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);                        if ( j == CyIVRRxEx ) { /* exception */                            data = cy_readb(base_addr+(CyRDSR<<index));                            if(data & info->ignore_status_mask){                                continue;                            }                            if (tty->flip.count < TTY_FLIPBUF_SIZE){                                tty->flip.count++;                                if (data & info->read_status_mask){                                    if(data & CyBREAK){                                        *tty->flip.flag_buf_ptr++ =							    TTY_BREAK;                                        *tty->flip.char_buf_ptr++ =					  cy_readb(base_addr+(CyRDSR<<index));                                        if (info->flags & ASYNC_SAK){                                            do_SAK(tty);                                        }                                    }else if(data & CyFRAME){                                        *tty->flip.flag_buf_ptr++ =							    TTY_FRAME;                                        *tty->flip.char_buf_ptr++ =					  cy_readb(base_addr+(CyRDSR<<index));					info->idle_stats.frame_errs++;                                    }else if(data & CyPARITY){                                        *tty->flip.flag_buf_ptr++ =							    TTY_PARITY;                                        *tty->flip.char_buf_ptr++ =					  cy_readb(base_addr+(CyRDSR<<index));					info->idle_stats.parity_errs++;                                    }else if(data & CyOVERRUN){                                        *tty->flip.flag_buf_ptr++ =							    TTY_OVERRUN;                                        *tty->flip.char_buf_ptr++ = 0;                                        /* If the flip buffer itself is                                           overflowing, we still loose                                           the next incoming character.                                         */                                        if(tty->flip.count					           < TTY_FLIPBUF_SIZE){                                            tty->flip.count++;                                            *tty->flip.flag_buf_ptr++ =							     TTY_NORMAL;                                           *tty->flip.char_buf_ptr++ =					    cy_readb(base_addr+(CyRDSR<<index));                                        }					info->idle_stats.overruns++;                                    /* These two conditions may imply */                                    /* a normal read should be done. */                                    /* }else if(data & CyTIMEOUT){ */                                    /* }else if(data & CySPECHAR){ */                                    }else{                                        *tty->flip.flag_buf_ptr++ = 0;                                        *tty->flip.char_buf_ptr++ = 0;                                    }                                }else{                                    *tty->flip.flag_buf_ptr++ = 0;                                    *tty->flip.char_buf_ptr++ = 0;                                }                            }else{                                /* there was a software buffer				   overrun and nothing could be				   done about it!!! */				info->idle_stats.overruns++;                            }                        } else { /* normal character reception */                            /* load # chars available from the chip */                            char_count = cy_readb(base_addr+(CyRDCR<<index));#ifdef CY_ENABLE_MONITORING                            ++info->mon.int_count;                            info->mon.char_count += char_count;                            if (char_count > info->mon.char_max)                               info->mon.char_max = char_count;                            info->mon.char_last = char_count;#endif			    info->idle_stats.recv_bytes += char_count;			    info->idle_stats.recv_idle   = jiffies;                            while(char_count--){                                if (tty->flip.count >= TTY_FLIPBUF_SIZE){                                        break;                                }                                tty->flip.count++;                                data = cy_readb(base_addr+(CyRDSR<<index));                                *tty->flip.flag_buf_ptr++ = TTY_NORMAL;                                *tty->flip.char_buf_ptr++ = data;#ifdef CY_16Y_HACK                                udelay(10L);#endif                            }                        }                        queue_task(&tty->flip.tqueue, &tq_timer);                    }                    /* end of service */                    cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));                }                if (status & CySRTransmit) { /* transmission interrupt */                    /* Since we only get here when the transmit buffer                       is empty, we know we can always stuff a dozen                       characters. */#ifdef CY_DEBUG_INTERRUPTSprintk("cy_interrupt: xmit intr, chip %d\n\r", chip);#endif                    /* determine the channel & change to that context */                    save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));                    channel = (u_short ) (save_xir & CyIRChannel);                    i = channel + chip * 4 + cinfo->first_line;                    save_car = cy_readb(base_addr+(CyCAR<<index));                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);                    /* validate the port# (as configured and open) */                    if( (i < 0) || (NR_PORTS <= i) ){                        cy_writeb((u_long)base_addr+(CySRER<<index),                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);                        goto txend;                    }                    info = &cy_port[i];                    info->last_active = jiffies;                    if(info->tty == 0){                        cy_writeb((u_long)base_addr+(CySRER<<index),                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);                        goto txdone;                    }                    /* load the on-chip space for outbound data */                    char_count = info->xmit_fifo_size;                    if(info->x_char) { /* send special char */                        outch = info->x_char;                        cy_writeb((u_long)base_addr+(CyTDR<<index), outch);                        char_count--;                        info->x_char = 0;                    }                    if (info->x_break){                        /*  The Cirrus chip requires the "Embedded			    Transmit Commands" of start break, delay,			    and end break sequences to be sent.  The			    duration of the break is given in TICs,			    which runs at HZ (typically 100) and the			    PPR runs at 200 Hz, so the delay is			    duration * 200/HZ, and thus a break can			    run from 1/100 sec to about 5/4 sec.			    For CD1400 J or later, replace the 200 Hz			    by 500 Hz.                         */			/* start break */                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0);                         cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);			/* delay a bit */                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0);                         cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);                        if (info->chip_rev >= CD1400_REV_J ) {			    /* It is a CD1400 rev. J or later */                            cy_writeb((u_long)base_addr + (CyTDR<<index), 				      info->x_break*500/HZ);			} else {                            cy_writeb((u_long)base_addr + (CyTDR<<index), 				      info->x_break*200/HZ);			}			/* finish break */                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0);                         cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);                        char_count -= 7;                        info->x_break = 0;                    }                    while (char_count-- > 0){			if (!info->xmit_cnt){                            cy_writeb((u_long)base_addr+(CySRER<<index),				cy_readb(base_addr+(CySRER<<index)) & 					~CyTxMpty);			    goto txdone;			}			if (info->xmit_buf == 0){                            cy_writeb((u_long)base_addr+(CySRER<<index),				cy_readb(base_addr+(CySRER<<index)) & 					~CyTxMpty);                            goto txdone;			}			if (info->tty->stopped || info->tty->hw_stopped){                            cy_writeb((u_long)base_addr+(CySRER<<index),				cy_readb(base_addr+(CySRER<<index)) & 					~CyTxMpty);                            goto txdone;			}                        /* Because the Embedded Transmit Commands have                           been enabled, we must check to see if the			   escape character, NULL, is being sent.  If it			   is, we must ensure that there is room for it			   to be doubled in the output stream.  Therefore			   we no longer advance the pointer when the			   character is fetched, but rather wait until			   after the check for a NULL output character.			   This is necessary because there may not be			   room for the two chars needed to send a NULL.)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -