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

📄 serial167.c

📁 powerpc内核mpc8241linux系统下char驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
		    *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!!! */	}    }    queue_task(&tty->flip.tqueue, &tq_timer);    /* end of service */    base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;} /* cy_rxerr_interrupt */static voidcd2401_modem_interrupt(int irq, void *dev_id, struct pt_regs *fp){    struct cyclades_port *info;    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;    int channel;    int mdm_change;    int mdm_status;    /* determine the channel and change to that context */    channel = (u_short ) (base_addr[CyLICR] >> 2);    info = &cy_port[channel];    info->last_active = jiffies;    mdm_change = base_addr[CyMISR];    mdm_status = base_addr[CyMSVR1];    if(info->tty == 0){ /* nowhere to put the data, ignore it */	;    }else{	if((mdm_change & CyDCD)	&& (info->flags & ASYNC_CHECK_CD)){	    if(mdm_status & CyDCD){/* CP('!'); */		cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);	    }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)		     &&(info->flags & ASYNC_CALLOUT_NOHUP))){/* CP('@'); */		cy_sched_event(info, Cy_EVENT_HANGUP);	    }	}	if((mdm_change & CyCTS)	&& (info->flags & ASYNC_CTS_FLOW)){	    if(info->tty->stopped){		if(mdm_status & CyCTS){		    /* !!! cy_start isn't used because... */		    info->tty->stopped = 0;        	    base_addr[CyIER] |= CyTxMpty;		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);		}	    }else{		if(!(mdm_status & CyCTS)){		    /* !!! cy_stop isn't used because... */		    info->tty->stopped = 1;        	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);		}	    }	}	if(mdm_status & CyDSR){	}    }    base_addr[CyMEOIR] = 0;} /* cy_modem_interrupt */static voidcd2401_tx_interrupt(int irq, void *dev_id, struct pt_regs *fp){    struct cyclades_port *info;    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;    int channel;    int char_count, saved_cnt;    int outch;    /* determine the channel and change to that context */    channel = (u_short ) (base_addr[CyLICR] >> 2);#ifdef CONFIG_REMOTE_DEBUG    if (channel == DEBUG_PORT) {	panic ("TxInt on debug port!!!");    }#endif    info = &cy_port[channel];    /* validate the port number (as configured and open) */    if( (channel < 0) || (NR_PORTS <= channel) ){	base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);	base_addr[CyTEOIR] = CyNOTRANS;	return;    }    info->last_active = jiffies;    if(info->tty == 0){	base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);        if (info->xmit_cnt < WAKEUP_CHARS) {	    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);        }	base_addr[CyTEOIR] = CyNOTRANS;	return;    }    /* load the on-chip space available for outbound data */    saved_cnt = char_count = base_addr[CyTFTC];    if(info->x_char) { /* send special char */	outch = info->x_char;	base_addr[CyTDR] = 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.	    Need to check these values - RGH 141095.	 */	base_addr[CyTDR] = 0; /* start break */	base_addr[CyTDR] = 0x81;	base_addr[CyTDR] = 0; /* delay a bit */	base_addr[CyTDR] = 0x82;	base_addr[CyTDR] = info->x_break*200/HZ;	base_addr[CyTDR] = 0; /* terminate break */	base_addr[CyTDR] = 0x83;	char_count -= 7;	info->x_break = 0;    }    while (char_count > 0){	if (!info->xmit_cnt){	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);	    break;	}	if (info->xmit_buf == 0){	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);	    break;	}	if (info->tty->stopped || info->tty->hw_stopped){	    base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy);	    break;	}	/* 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.	 */	outch = info->xmit_buf[info->xmit_tail];	if( outch ){	    info->xmit_cnt--;	    info->xmit_tail = (info->xmit_tail + 1)				      & (PAGE_SIZE - 1);	    base_addr[CyTDR] = outch;	    char_count--;	}else{	    if(char_count > 1){		info->xmit_cnt--;		info->xmit_tail = (info->xmit_tail + 1)					  & (PAGE_SIZE - 1);		base_addr[CyTDR] = outch;		base_addr[CyTDR] = 0;		char_count--;		char_count--;	    }else{		break;	    }	}    }    if (info->xmit_cnt < WAKEUP_CHARS) {	cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);    }    base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS;} /* cy_tx_interrupt */static voidcd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp){    struct tty_struct *tty;    struct cyclades_port *info;    volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;    int channel;    char data;    int char_count;    int save_cnt;    /* determine the channel and change to that context */    channel = (u_short ) (base_addr[CyLICR] >> 2);    info = &cy_port[channel];    info->last_active = jiffies;    save_cnt = char_count = base_addr[CyRFOC];#ifdef CONFIG_REMOTE_DEBUG    if (channel == DEBUG_PORT) {	while (char_count--) {            data = base_addr[CyRDR];	    queueDebugChar(data);	}    }    else#endif    /* if there is nowhere to put the data, discard it */    if(info->tty == 0){	while(char_count--){	    data = base_addr[CyRDR];	}    }else{ /* there is an open port for this data */	tty = info->tty;	/* load # characters available from the chip */#ifdef CYCLOM_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	while(char_count--){	    data = base_addr[CyRDR];	    if (tty->flip.count >= TTY_FLIPBUF_SIZE){		continue;	    }	    tty->flip.count++;	    *tty->flip.flag_buf_ptr++ = TTY_NORMAL;	    *tty->flip.char_buf_ptr++ = data;#ifdef CYCLOM_16Y_HACK	    udelay(10L);#endif        }	queue_task(&tty->flip.tqueue, &tq_timer);    }    /* end of service */    base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;} /* cy_rx_interrupt *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using cy_sched_event(), and they get done here. * * This is done through one level of indirection--the task queue. * When a hardware interrupt service routine wants service by the * driver's bottom half, it enqueues the appropriate tq_struct (one * per port) to the tq_cyclades work queue and sets a request flag * via mark_bh for processing that queue.  When the time is right, * do_cyclades_bh is called (because of the mark_bh) and it requests * that the work queue be processed. * * Although this may seem unwieldy, it gives the system a way to * pass an argument (in this case the pointer to the cyclades_port * structure) to the bottom half of the driver.  Previous kernels * had to poll every port to see if that port needed servicing. */static voiddo_cyclades_bh(void){    run_task_queue(&tq_cyclades);} /* do_cyclades_bh */static voiddo_softint(void *private_){  struct cyclades_port *info = (struct cyclades_port *) private_;  struct tty_struct    *tty;    tty = info->tty;    if (!tty)	return;    if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) {	tty_hangup(info->tty);	wake_up_interruptible(&info->open_wait);	info->flags &= ~(ASYNC_NORMAL_ACTIVE|			     ASYNC_CALLOUT_ACTIVE);    }    if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {	wake_up_interruptible(&info->open_wait);    }    if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {	if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))	&& tty->ldisc.write_wakeup){	    (tty->ldisc.write_wakeup)(tty);	}	wake_up_interruptible(&tty->write_wait);    }} /* do_softint *//* This is called whenever a port becomes active;   interrupts are enabled and DTR & RTS are turned on. */static intstartup(struct cyclades_port * info){  unsigned long flags;  volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR;  int channel;    if (info->flags & ASYNC_INITIALIZED){	return 0;    }    if (!info->type){	if (info->tty){	    set_bit(TTY_IO_ERROR, &info->tty->flags);	}	return 0;    }    if (!info->xmit_buf){	info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);	if (!info->xmit_buf){	    return -ENOMEM;	}    }    config_setup(info);    channel = info->line;#ifdef SERIAL_DEBUG_OPEN    printk("startup channel %d\n", channel);#endif    save_flags(flags); cli();	base_addr[CyCAR] = (u_char)channel;	write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR);	base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */	base_addr[CyMSVR1] = CyRTS;/* CP('S');CP('1'); */	base_addr[CyMSVR2] = CyDTR;#ifdef SERIAL_DEBUG_DTR        printk("cyc: %d: raising DTR\n", __LINE__);        printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);#endif	base_addr[CyIER] |= CyRxData;	info->flags |= ASYNC_INITIALIZED;	if (info->tty){	    clear_bit(TTY_IO_ERROR, &info->tty->flags);	}	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;    restore_flags(flags);#ifdef SERIAL_DEBUG_OPEN    printk(" done\n");#endif    return 0;} /* startup */voidstart_xmit( struct cyclades_port *info ){  unsigned long flags;  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;  int channel;    channel = info->line;    save_flags(flags); cli();	base_addr[CyCAR] = channel;	base_addr[CyIER] |= CyTxMpty;    restore_flags(flags);} /* start_xmit *//* * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. */static voidshutdown(struct cyclades_port * info){  unsigned long flags;  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;  int channel;    if (!(info->flags & ASYNC_INITIALIZED)){/* CP('$'); */	return;    }    channel = info->line;#ifdef SERIAL_DEBUG_OPEN    printk("shutdown channel %d\n", channel);#endif    /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE       SENT BEFORE DROPPING THE LINE !!!  (Perhaps       set some flag that is read when XMTY happens.)       Other choices are to delay some fixed interval       or schedule some later processing.     */    save_flags(flags); cli();	if (info->xmit_buf){	    free_page((unsigned long) info->xmit_buf);	    info->xmit_buf = 0;	}	base_addr[CyCAR] = (u_char)channel;	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {	    base_addr[CyMSVR1] = 0;/* CP('C');CP('1'); */	    base_addr[CyMSVR2] = 0;#ifdef SERIAL_DEBUG_DTR            printk("cyc: %d: dropping DTR\n", __LINE__);            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);#endif        }	write_cy_cmd(base_addr,CyDIS_RCVR);         /* it may be appropriate to clear _XMIT at           some later date (after testing)!!! */	if (info->tty){	    set_bit(TTY_IO_ERROR, &info->tty->flags);	}	info->flags &= ~ASYNC_INITIALIZED;    restore_flags(flags);#ifdef SERIAL_DEBUG_OPEN    printk(" done\n");#endif    return;} /* shutdown *//* * This routine finds or computes the various line characteristics. */static voidconfig_setup(struct cyclades_port * info){  unsigned long flags;  volatile unsigned char *base_addr = (u_char *)BASE_ADDR;  int channel;  unsigned cflag;  int   i;  unsigned char ti, need_init_chan = 0;    if (!info->tty || !info->tty->termios){        return;    }    if (info->line == -1){        return;    }    cflag = info->tty->termios->c_cflag;    /* baud rate */    i = cflag & CBAUD;#ifdef CBAUDEX/* Starting with kernel 1.1.65, there is direct support for   higher baud rates.  The following code supports those   changes.  The conditional aspect allows this driver to be   used for earlier as well as later kernel versions.  (The

⌨️ 快捷键说明

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