?? mxpcdrv.c
字號(hào):
MX_MOD_INC; if ( (line < 0) || (line > MXPCDRV_PORTS) ) return(-ENODEV); info = mxvar_table + line; if ( !info->base ) return(-ENODEV); tty->driver_data = info; info->tty = tty; if ( !mxvar_tmp_buf ) { page = GET_FPAGE(GFP_KERNEL); if ( !page ) return(-ENOMEM); if ( mxvar_tmp_buf ) free_page(page); else mxvar_tmp_buf = (unsigned char *)page; } /* * Start up serial port */ info->count++; retval = mxpcdrv_startup(info); if ( retval ) return(retval); retval = mxpcdrv_block_til_ready(tty, filp, info); if ( retval ) return(retval); if ( (info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS) ) { if ( MX_TTY_DRV(subtype) == SERIAL_TYPE_NORMAL ) *tty->termios = info->normal_termios; else *tty->termios = info->callout_termios; mxpcdrv_change_speed(info, 0); } info->session = MX_SESSION(); info->pgrp = MX_CGRP();#ifdef TTY_DONT_FLIP clear_bit(TTY_DONT_FLIP, &tty->flags);#endif/* unmark here for very high baud rate (ex. 921600 bps) used*/#if (LINUX_VERSION_CODE >= VERSION_CODE(2,1,0)) tty->low_latency = 1;#endif return(0);}/* * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. */static void mxpcdrv_close(struct tty_struct * tty, struct file * filp){ struct mxpcdrv_struct * info = (struct mxpcdrv_struct *)tty->driver_data;#if (LINUX_VERSION_CODE > VERSION_CODE(2,6,9)) struct tty_ldisc *ld;#endif unsigned long timeout; MX_LOCK_INIT(); if ( PORTNO(tty) == MXPCDRV_PORTS ) return; if ( !info ){ MX_MOD_DEC; return; } MX_LOCK(&info->slock); if ( tty_hung_up_p(filp) ) { MX_UNLOCK(&info->slock); MX_MOD_DEC; return; }#ifndef SP1 if ( (tty->count == 1) && (info->count != 1) ) {#else#if (LINUX_VERSION_CODE < VERSION_CODE(2,4,21)) if ( (tty->count == 1) && (info->count != 1) ) {#else if ((atomic_read(&tty->count) == 1) && (info->count != 1)) {#endif#endif /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk("mxpcdrv_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if ( --info->count < 0 ) { printk("mxpcdrv_close: bad serial port count for ttys%d: %d\n", info->port, info->count); info->count = 0; } if ( info->count ) { MX_UNLOCK(&info->slock); MX_MOD_DEC; return; } info->flags |= ASYNC_CLOSING; MX_UNLOCK(&info->slock); // add by Victor Yu. 09-26-2002 /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if ( info->flags & ASYNC_NORMAL_ACTIVE ) info->normal_termios = *tty->termios; if ( info->flags & ASYNC_CALLOUT_ACTIVE ) info->callout_termios = *tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if ( info->closing_wait != ASYNC_CLOSING_WAIT_NONE ) tty_wait_until_sent(tty, info->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ info->IER &= ~UART_IER_RLSI; if ( info->IsMoxaMustChipFlag ) info->IER &= ~MOXA_MUST_RECV_ISR;/* by William info->read_status_mask &= ~UART_LSR_DR;*/ if ( info->flags & ASYNC_INITIALIZED ) { outb(info->IER, info->base + UART_IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; while ( !(inb(info->base + UART_LSR) & UART_LSR_TEMT) ) {#if (LINUX_VERSION_CODE >= VERSION_CODE(2,3,0)) set_current_state(TASK_INTERRUPTIBLE);#else current->state = TASK_INTERRUPTIBLE;#endif schedule_timeout(5); if ( time_after(jiffies, timeout) ) break; } } mxpcdrv_shutdown(info); // following add by Victor Yu. 09-23-2002 /* if ( info->IsMoxaMustChipFlag ) SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->base); */ // above add by Victor Yu. 09-23-2002 if ( MX_TTY_DRV(flush_buffer) ) MX_TTY_DRV(flush_buffer)(tty); #if (LINUX_VERSION_CODE < VERSION_CODE(2,6,10)) if ( tty->ldisc.flush_buffer ) tty->ldisc.flush_buffer(tty);#else ld = tty_ldisc_ref(tty); if (ld) { if(ld->flush_buffer) ld->flush_buffer(tty); tty_ldisc_deref(ld); }#endif tty->closing = 0; info->event = 0; info->tty = 0; if ( info->blocked_open ) { if ( info->close_delay ) {#if (LINUX_VERSION_CODE >= VERSION_CODE(2,3,0)) set_current_state(TASK_INTERRUPTIBLE);#else current->state = TASK_INTERRUPTIBLE;#endif schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&info->close_wait);// MX_UNLOCK(&info->slock); // mask by Vicror Yu. 09-26-2002 MX_MOD_DEC;}#if (LINUX_VERSION_CODE < VERSION_CODE(2,6,10))static int mxpcdrv_write(struct tty_struct * tty, int from_user, const unsigned char * buf, int count)#elsestatic int mxpcdrv_write(struct tty_struct * tty, const unsigned char * buf, int count)#endif{ int c, total = 0; struct mxpcdrv_struct *info = (struct mxpcdrv_struct *)tty->driver_data; MX_LOCK_INIT(); if ( !tty || !info->xmit_buf || !mxvar_tmp_buf ) return(0);#if (LINUX_VERSION_CODE < VERSION_CODE(2,6,10)) if ( from_user ) down(&mxvar_tmp_buf_sem);#endif while ( 1 ) { c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if ( c <= 0 ) break;#if (LINUX_VERSION_CODE < VERSION_CODE(2,6,10)) if ( from_user ) { /* copy_from_user(mxvar_tmp_buf, buf, c); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c); */ if(copy_from_user(info->xmit_buf+info->xmit_head, buf, c)==c){ total = -EFAULT; break; } } else memcpy(info->xmit_buf + info->xmit_head, buf, c);#else memcpy(info->xmit_buf + info->xmit_head, buf, c);#endif MX_LOCK(&info->slock); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; MX_UNLOCK(&info->slock); buf += c; count -= c; total += c; }#if (LINUX_VERSION_CODE < VERSION_CODE(2,6,10)) if ( from_user ) up(&mxvar_tmp_buf_sem);#endif if ( info->xmit_cnt && !tty->stopped ){ MX_LOCK(&info->slock); info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); MX_UNLOCK(&info->slock); } return(total);}static void mxpcdrv_put_char(struct tty_struct * tty, unsigned char ch){ struct mxpcdrv_struct *info = (struct mxpcdrv_struct *)tty->driver_data; MX_LOCK_INIT(); if ( !tty || !info->xmit_buf ) return; if ( info->xmit_cnt >= SERIAL_XMIT_SIZE - 1 ) { return; } MX_LOCK(&info->slock); info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; MX_UNLOCK(&info->slock); if ( !tty->stopped/* && !(info->IER & UART_IER_THRI) */) { MX_LOCK(&info->slock); info->IER &= ~UART_IER_THRI ; outb(info->IER, info->base + UART_IER); info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); MX_UNLOCK(&info->slock); }}static void mxpcdrv_flush_chars(struct tty_struct * tty){ struct mxpcdrv_struct *info = (struct mxpcdrv_struct *)tty->driver_data; MX_LOCK_INIT(); if ( info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf || (tty->hw_stopped && (info->type!=PORT_16550A) && (!info->IsMoxaMustChipFlag))) return; MX_LOCK(&info->slock); info->IER &= ~UART_IER_THRI ; outb(info->IER, info->base + UART_IER); info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); MX_UNLOCK(&info->slock);}static int mxpcdrv_write_room(struct tty_struct * tty){ struct mxpcdrv_struct *info = (struct mxpcdrv_struct *)tty->driver_data; int ret; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if ( ret < 0 ) ret = 0; return(ret);}static int mxpcdrv_chars_in_buffer(struct tty_struct * tty){ int len; struct mxpcdrv_struct *info = (struct mxpcdrv_struct *)tty->driver_data; len = info->xmit_cnt; if(!(inb(info->base + UART_LSR) & UART_LSR_THRE)) len++; return len;}static void mxpcdrv_flush_buffer(struct tty_struct * tty){ struct mxpcdrv_struct *info = (struct mxpcdrv_struct *)tty->driver_data; char fcr; MX_LOCK_INIT(); MX_LOCK(&info->slock); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; MX_UNLOCK(&info->slock); fcr = inb(info->base + UART_FCR); outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), info->base + UART_FCR); outb(fcr, info->base+UART_FCR); wake_up_interruptible(&tty->write_wait); if ( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty);}static int mxpcdrv_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg){ int error; struct mxpcdrv_struct * info = (struct mxpcdrv_struct *)tty->driver_data; int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ unsigned long templ; MX_LOCK_INIT(); if ( PORTNO(tty) == MXPCDRV_PORTS ) return(mxpcdrv_ioctl_special(cmd, arg)); if ( (cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT) ) { if ( tty->flags & (1 << TTY_IO_ERROR) ) return(-EIO); } switch ( cmd ) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if ( retval ) return(retval); tty_wait_until_sent(tty, 0); if ( !arg ) mxpcdrv_send_break(info, HZ/4); /* 1/4 second */ return(0); case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if ( retval ) return(retval); tty_wait_until_sent(tty, 0); mxpcdrv_send_break(info, arg ? arg*(HZ/10) : HZ/4); return(0); case TIOCGSOFTCAR: error = MX_ACCESS_CHK(VERIFY_WRITE, (void *)arg, sizeof(long)); if ( MX_ERR(error) ) return(error); put_to_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); return 0; case TIOCSSOFTCAR: error = MX_ACCESS_CHK(VERIFY_READ, (void *)arg, sizeof(long)); if ( MX_ERR(error) ) return(error); get_from_user(templ,(unsigned long *)arg); arg = templ; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return(0);#if (LINUX_VERSION_CODE < VERSION_CODE(2,6,0)) case TIOCMGET: error = MX_ACCESS_CHK(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)); if ( MX_ERR(error) ) return(error); return(mxpcdrv_get_modem_info(info, (unsigned int *)arg)); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return(mxpcdrv_set_modem_info(info, cmd, (unsigned int *)arg));#endif case TIOCGSERIAL: error = MX_ACCESS_CHK(VERIFY_WRITE, (void *)arg, sizeof(struct serial_struct)); if ( MX_ERR(error) ) return(error); return(mxpcdrv_get_serial_info(info, (struct serial_struct *)arg)); case TIOCSSERIAL: error = MX_ACCESS_CHK(VERIFY_READ, (void *)arg, sizeof(struct serial_struct)); if ( MX_ERR(error) ) return(error); return(mxpcdrv_set_serial_info(info, (struct serial_struct *)arg)); case TIOCSERGETLSR: /* Get line status register */ error = MX_ACCESS_CHK(VERIFY_WRITE, (void *)arg, sizeof(unsigned int)); if ( MX_ERR(error) ) return(error); else return(mxpcdrv_get_lsr_info(info, (unsigned int *)arg)); /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: MX_LOCK(&info->slock); cprev = info->icount; /* note the counters on entry */ MX_UNLOCK(&info->slock); while ( 1 ) { //interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if ( signal_pending(current) ) return(-ERESTARTSYS); MX_LOCK(&info->slock);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -