📄 serial_cnxt.c
字号:
/* * Virtual serial port driver for Conexant modems * * Written by Marc Boucher <marc@linuxant.com> *//* * Copyright (c) 2001-2002 Conexant Systems, Inc. * Copyright (c) 2003-2004 Linuxant inc. * * 1. General Public License. This program is free software, and may * be redistributed or modified subject to the terms of the GNU General * Public License (version 2) or the GNU Lesser General Public License, * or (at your option) any later versions ("Open Source" code). You may * obtain a copy of the GNU General Public License at * http://www.fsf.org/copyleft/gpl.html and a copy of the GNU Lesser * General Public License at http://www.fsf.org/copyleft/less.html, * or you may alternatively write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * 2. Disclaimer of Warranties. CONEXANT AND OTHER CONTRIBUTORS MAKE NO * REPRESENTATION ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. * IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTIES OF ANY KIND. * CONEXANT AND OTHER CONTRIBUTORS DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, GOOD TITLE AND AGAINST INFRINGEMENT. * * This software has not been formally tested, and there is no guarantee that * it is free of errors including, but not limited to, bugs, defects, * interrupted operation, or unexpected results. Any use of this software is * at user's own risk. * * 3. No Liability. * * (a) Conexant or contributors shall not be responsible for any loss or * damage to Company, its customers, or any third parties for any reason * whatsoever, and CONEXANT OR CONTRIBUTORS SHALL NOT BE LIABLE FOR ANY * ACTUAL, DIRECT, INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL, OR CONSEQUENTIAL * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED, WHETHER IN CONTRACT, STRICT OR OTHER LEGAL THEORY OF * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * (b) User agrees to hold Conexant and contributors harmless from any * liability, loss, cost, damage or expense, including attorney's fees, * as a result of any claims which may be made by any person, including * but not limited to User, its agents and employees, its customers, or * any third parties that arise out of or result from the manufacture, * delivery, actual or alleged ownership, performance, use, operation * or possession of the software furnished hereunder, whether such claims * are based on negligence, breach of contract, absolute liability or any * other legal theory. * * 4. Notices. User hereby agrees not to remove, alter or destroy any * copyright, trademark, credits, other proprietary notices or confidential * legends placed upon, contained within or associated with the Software, * and shall include all such unaltered copyright, trademark, credits, * other proprietary notices or confidential legends on or in every copy of * the Software. * */#include <linux/version.h>#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/major.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <asm/serial.h>#include <linux/serial.h>#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))#if (!defined(CONFIG_SERIAL_CORE) && !defined(CONFIG_SERIAL_CORE_MODULE))#error CONFIG_SERIAL_CORE needed; enable 8250/16550 and compatible serial support in kernel config#endif#include <linux/serial_core.h>#endif#include "oscompat.h"#include "osservices.h"#include "comtypes.h"#include "comctrl_ex.h"#include "oslinux.h"#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)#include "serial_core.h"#endif#include "serial_cnxt.h"#define NR_PORTS CNXTMAXMDM#define CNXT_ISR_PASS_LIMIT 256#define CNXT_READBUF_SIZE 256#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)static struct tty_driver cnxt_tty_driver_normal;static struct tty_driver cnxt_tty_driver_callout;static struct tty_struct *cnxt_tty_table[NR_PORTS];static struct termios *cnxt_termios[NR_PORTS], *cnxt_termios_locked[NR_PORTS];#endifstruct cnxt_serial_inst { spinlock_t lock; struct module *owner; POS_DEVNODE devnode; HANDLE hcomctrl; char *typestr; struct uart_port *port; int rxenabled; int txenabled; int evt_rxchar; int evt_rxbreak; int evt_rxovrn; int evt_txempty; unsigned char readbuf[CNXT_READBUF_SIZE]; int readcount, readoffset; u_int mctrl_flags; OSSCHED intr_tqueue; struct uart_port *uart_port; struct uart_info *uart_info;#ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_unit_dir; struct proc_dir_entry *proc_hwinst; struct proc_dir_entry *proc_hwprofile; struct proc_dir_entry *proc_hwrevision;#ifdef COMCTRL_MONITOR_POUND_UG_SUPPORT struct proc_dir_entry *proc_lastcallstatus;#endif#endif};#ifdef CONFIG_PROC_FSstatic struct proc_dir_entry *cnxt_serial_proc_dir;#endifstatic struct cnxt_serial_inst cnxt_serial_inst[NR_PORTS];static struct uart_port cnxt_ports[NR_PORTS];#ifdef COMCTRL_MONITOR_POUND_UG_SUPPORTstatic int loglastcallstatus;#endifstatic voidcnxt_sched_intr(struct cnxt_serial_inst *inst){ if(inst->uart_info) { OsModuleUseCountInc(); if (OsThreadSchedule(OsMdmThread, &inst->intr_tqueue) <= 0) { OsModuleUseCountDec(); } }}static void#ifdef FOUND_TTY_START_STOPcnxt_stop_tx(struct uart_port *port, u_int tty_stop)#elsecnxt_stop_tx(struct uart_port *port)#endif{ struct cnxt_serial_inst *inst = &cnxt_serial_inst[port - cnxt_ports]; //printk(KERN_DEBUG "%s\n", __FUNCTION__); inst->txenabled = 0;}static void#ifdef FOUND_TTY_START_STOPcnxt_start_tx(struct uart_port *port, u_int tty_start)#elsecnxt_start_tx(struct uart_port *port)#endif{ struct cnxt_serial_inst *inst = &cnxt_serial_inst[port - cnxt_ports]; //printk(KERN_DEBUG "%s\n", __FUNCTION__); inst->txenabled = 1; cnxt_sched_intr(inst);}static voidcnxt_stop_rx(struct uart_port *port){ struct cnxt_serial_inst *inst = &cnxt_serial_inst[port - cnxt_ports]; //printk(KERN_DEBUG "%s\n", __FUNCTION__); inst->rxenabled = 0;}static inline intcnxt_rx_ready(struct cnxt_serial_inst *inst){ int r; if(!inst->rxenabled) return FALSE; if(inst->readoffset == inst->readcount) { r = ComCtrl_Read(inst->hcomctrl, inst->readbuf, sizeof(inst->readbuf)); if(r < 0) { printk(KERN_ERR"%s: ComCtrlRead returned %d\n", __FUNCTION__, r); } else { inst->readcount = r; inst->readoffset = 0; } } return (inst->readcount > inst->readoffset) || inst->evt_rxbreak || inst->evt_rxovrn;}static inline voidcnxt_rx_chars(struct cnxt_serial_inst *inst){ struct tty_struct *tty = inst->uart_info->tty; int max_count = sizeof(inst->readbuf); unsigned char flag; inst->evt_rxchar = 0; while(max_count-- > 0 && cnxt_rx_ready(inst)) {#ifndef FOUND_TTY_NEW_API if(unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) tty->flip.tqueue.routine((void *) tty);#else tty->flip.work.func((void *)tty);#endif if(tty->flip.count >= TTY_FLIPBUF_SIZE) { return; // if TTY_DONT_FLIP is set } }#endif if (inst->evt_rxovrn) { inst->evt_rxovrn = 0; inst->uart_port->icount.overrun++;#ifdef FOUND_TTY_NEW_API tty_insert_flip_char(tty, 0, TTY_OVERRUN);#else *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; *tty->flip.char_buf_ptr++ = 0; tty->flip.count++;#endif continue; } inst->uart_port->icount.rx++; if (inst->evt_rxbreak) { inst->evt_rxbreak = 0; inst->uart_port->icount.brk++; flag = TTY_BREAK; } else flag = TTY_NORMAL;#ifdef FOUND_TTY_NEW_API tty_insert_flip_char(tty, inst->readbuf[inst->readoffset++], flag);#else *tty->flip.flag_buf_ptr++ = flag; *tty->flip.char_buf_ptr++ = inst->readbuf[inst->readoffset++]; tty->flip.count++;#endif } tty_flip_buffer_push(tty); return;}#if 0static inline intcnxt_tx_free(struct cnxt_serial_inst *inst){ int r; UINT32 val = 0; if ((r=ComCtrl_Monitor(inst->hcomctrl, COMCTRL_MONITOR_TXFREE, &val)) != COM_STATUS_SUCCESS) { printk(KERN_ERR "%s: ComCtrlMonitor COMCTRL_MONITOR_TXFREE failed, status=%d\n", __FUNCTION__, r); return 0; } //printk(KERN_DEBUG "%s: val=%lu\n", __FUNCTION__, val); return val;}#endifstatic inline intcnxt_put_char(struct cnxt_serial_inst *inst, unsigned char ch){ int r; //printk(KERN_DEBUG "%s: ch=%x\n", __FUNCTION__, (int)ch); inst->evt_txempty = 0; r = ComCtrl_Write(inst->hcomctrl, &ch, 1); if(r < 0) { printk(KERN_ERR "%s: ComCtrlWrite returned %d\n", __FUNCTION__, r); } return r == 1;}static inline voidcnxt_tx_chars(struct cnxt_serial_inst *inst){ struct circ_buf *xmit; struct uart_info *info = inst->uart_info; struct uart_port *port; spinlock_t *lock; unsigned long flags; port = inst->uart_port; xmit = &info->xmit; if (port->x_char) { cnxt_put_char(inst, port->x_char); port->icount.tx++; port->x_char = 0; return; }#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) lock = &info->lock;#else lock = &port->lock;#endif spin_lock_irqsave(lock, flags); if (uart_circ_empty(xmit) || info->tty->stopped || info->tty->hw_stopped) {#ifdef FOUND_TTY_START_STOP cnxt_stop_tx(port, 0);#else cnxt_stop_tx(port);#endif spin_unlock_irqrestore(lock, flags); return; } while (xmit->buf/*&& cnxt_tx_free(inst) > 0*/) { if(!cnxt_put_char(inst, xmit->buf[xmit->tail])) break; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) uart_event(info, EVT_WRITE_WAKEUP);#else uart_write_wakeup(port);#endif if (uart_circ_empty(xmit))#ifdef FOUND_TTY_START_STOP cnxt_stop_tx(port, 0);#else cnxt_stop_tx(port);#endif spin_unlock_irqrestore(lock, flags);}static inline intcnxt_tx_ready(struct cnxt_serial_inst *inst) { return (inst->txenabled /*&& inst->evt_txempty*/);}static voidcnxt_intr(void *dev_id){ unsigned int pass_counter = 0; struct cnxt_serial_inst *inst = (struct cnxt_serial_inst *)dev_id; while (inst->uart_info && (cnxt_rx_ready(inst) || cnxt_tx_ready(inst))) { cnxt_rx_chars(inst); if(cnxt_tx_ready(inst)) cnxt_tx_chars(inst); if (pass_counter++ > CNXT_ISR_PASS_LIMIT) break; } OsModuleUseCountDec(); OsThreadScheduleDone();}/* * Return TIOCSER_TEMT when transmitter is not busy. */static u_intcnxt_tx_empty(struct uart_port *port){ struct cnxt_serial_inst *inst = &cnxt_serial_inst[port - cnxt_ports]; return inst->evt_txempty ? TIOCSER_TEMT : 0;}static u_intcnxt_get_mctrl(struct uart_port *port){ struct cnxt_serial_inst *inst = &cnxt_serial_inst[port - cnxt_ports]; return inst->mctrl_flags;}#ifdef COMCTRL_MONITOR_POUND_UG_SUPPORTstatic COM_STATUScnxt_monitor(struct cnxt_serial_inst *inst, COMCTRL_MONITOR_CODE eCode, PVOID pMonitor){ int r; if(!inst->hcomctrl) return COM_STATUS_INST_NOT_INIT; r = ComCtrl_Monitor(inst->hcomctrl, eCode, pMonitor); if (r != COM_STATUS_SUCCESS) { printk(KERN_ERR "%s: ComCtrlMonitor %d failed, status=%d\n", __FUNCTION__, eCode, r); } return r;}#endifstatic COM_STATUScnxt_control(struct cnxt_serial_inst *inst, COMCTRL_CONTROL_CODE eCode, PVOID pControl){ int r; if(!inst->hcomctrl) return COM_STATUS_INST_NOT_INIT; r = ComCtrl_Control(inst->hcomctrl, eCode, pControl); if (r != COM_STATUS_SUCCESS) { printk(KERN_ERR "%s: ComCtrlControl %d failed, status=%d\n", __FUNCTION__, eCode, r); } return r;}static voidcnxt_set_mctrl(struct uart_port *port, u_int mctrl){ struct cnxt_serial_inst *inst = &cnxt_serial_inst[port - cnxt_ports]; //printk(KERN_DEBUG "%s: mctrl=%x\n", __FUNCTION__, mctrl); if ((mctrl & TIOCM_RTS) && !(inst->mctrl_flags & TIOCM_RTS)) { inst->mctrl_flags |= TIOCM_RTS; cnxt_control(inst, COMCTRL_CONTROL_SETRTS, 0); } if (!(mctrl & TIOCM_RTS) && (inst->mctrl_flags & TIOCM_RTS)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -