📄 synclink.c
字号:
static struct termios **serial_termios = NULL;static struct termios **serial_termios_locked = NULL;#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* * 1st function defined in .text section. Calling this function in * init_module() followed by a breakpoint allows a remote debugger * (gdb) to get the .text address for the add-symbol-file command. * This allows remote debugging of dynamically loadable modules. */void* mgsl_get_text_ptr(void);void* mgsl_get_text_ptr() {return mgsl_get_text_ptr;}/* * tmp_buf is used as a temporary buffer by mgsl_write. We need to * lock it in case the COPY_FROM_USER blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ioports, since it significantly saves * memory if large numbers of serial ports are open. */static unsigned char *tmp_buf;static struct semaphore tmp_buf_sem = MUTEX;static inline int mgsl_paranoia_check(struct mgsl_struct *info, kdev_t device, const char *routine){#ifdef MGSL_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for mgsl struct (%s) in %s\n"; static const char *badinfo = "Warning: null mgsl_struct for (%s) in %s\n"; if (!info) { printk(badinfo, kdevname(device), routine); return 1; } if (info->magic != MGSL_MAGIC) { printk(badmagic, kdevname(device), routine); return 1; }#endif return 0;}/* mgsl_stop() throttle (stop) transmitter * * Arguments: tty pointer to tty info structure * Return Value: None */static void mgsl_stop(struct tty_struct *tty){ struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; unsigned long flags; if (mgsl_paranoia_check(info, tty->device, "mgsl_stop")) return; if ( debug_level >= DEBUG_LEVEL_INFO ) printk("mgsl_stop(%s)\n",info->device_name); spin_lock_irqsave(&info->irq_spinlock,flags); if (info->tx_enabled) usc_stop_transmitter(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); } /* end of mgsl_stop() *//* mgsl_start() release (start) transmitter * * Arguments: tty pointer to tty info structure * Return Value: None */static void mgsl_start(struct tty_struct *tty){ struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; unsigned long flags; if (mgsl_paranoia_check(info, tty->device, "mgsl_start")) return; if ( debug_level >= DEBUG_LEVEL_INFO ) printk("mgsl_start(%s)\n",info->device_name); spin_lock_irqsave(&info->irq_spinlock,flags); if (!info->tx_enabled) usc_start_transmitter(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); } /* end of mgsl_start() *//* * Bottom half work queue access functions *//* mgsl_format_bh_queue() * * Initialize the bottom half processing queue * * Arguments: info pointer to device instance data * Return Value: None */void mgsl_format_bh_queue( struct mgsl_struct *info ){ BH_QUEUE bh_queue = info->bh_queue; int i; /* go through sequentially tacking the little bits together */ for ( i=0; i < MAX_BH_QUEUE_ENTRIES; i++ ) { if ( info->free_bh_queue_tail == NULL ) info->free_bh_queue_head = bh_queue; else info->free_bh_queue_tail->link = bh_queue; info->free_bh_queue_tail = bh_queue++; } /* As a safety measure, mark the end of the chain with a NULL */ info->free_bh_queue_tail->link = NULL;} /* end of mgsl_format_bh_queue() *//* mgsl_bh_queue_put() * * Add a BH event to the BH queue * * Arguments: info pointer to device instance data * type BH event type * status BH event status * * Return Value: None */void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status ){ BH_EVENT *event = info->free_bh_queue_head; if ( event != NULL ) { /* remove free element from head of free list */ info->free_bh_queue_head = event->link; event->link = NULL; /* file out new BH event */ event->type = type; event->status = status; /* add element to tail of pending list */ if ( info->bh_queue_head != NULL ){ /* BH queue is not empty, add current element to tail */ info->bh_queue_tail->link = event; } else { /* the BH queue is empty so this element becomes the head of queue */ info->bh_queue_head = event; } /* the new element becomes tail of queue */ info->bh_queue_tail = event; } else { /* No more free BH action elements in queue. */ /* This happens when too many interrupts are occuring */ /* for the mgsl_bh_handler to process so set a flag. */ info->isr_overflow = 1; }} /* end of mgsl_bh_queue_put() *//* mgsl_bh_queue_get() * * Free the current work item (if any) and get the * next work item from the head of the pending work item queue. * * Effects: * * If a BH action element is available on the BH action queue * then the head of the queue is removed and bh_action * is set to point to the removed element. * * Arguments: info pointer to device instance data * Return Value: 1 if BH action removed from queue */int mgsl_bh_queue_get( struct mgsl_struct *info ){ unsigned long flags; spin_lock_irqsave(&info->irq_spinlock,flags); if ( info->bh_action ) { /* free the current work item */ if ( info->free_bh_queue_head != NULL ){ /* free queue is not empty, add current element to tail */ info->free_bh_queue_tail->link = info->bh_action; } else { /* free queue is empty so this element becomes the head of queue */ info->free_bh_queue_head = info->bh_action; } /* add element to tail of free queue */ info->free_bh_queue_tail = info->bh_action; info->free_bh_queue_tail->link = NULL; } /* attempt to remove element from head of queue */ info->bh_action = info->bh_queue_head; if ( info->bh_action != NULL ){ /* BH queue is not empty, remove element from queue head */ info->bh_queue_head = info->bh_action->link; spin_unlock_irqrestore(&info->irq_spinlock,flags); return 1; } /* Mark BH routine as complete */ info->bh_running = 0; info->bh_requested = 0; spin_unlock_irqrestore(&info->irq_spinlock,flags); return 0;} /* end of mgsl_bh_queue_get() *//* mgsl_bh_handler() * * Perform bottom half processing of work items queued by ISR. * * Arguments: Context pointer to device instance data * Return Value: None */void mgsl_bh_handler(void* Context){ struct mgsl_struct *info = (struct mgsl_struct*)Context; if (!info) return; if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) entry\n", __FILE__,__LINE__,info->device_name); info->bh_running = 1; /* Attempt to clear out the BH queue */ while( mgsl_bh_queue_get(info) ) { /* Process work item */ if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler() work item action=%d\n", __FILE__,__LINE__,info->bh_action->type); switch ( info->bh_action->type ) { case BH_TYPE_RECEIVE_DMA: mgsl_bh_receive_dma( info, info->bh_action->status ); break; case BH_TYPE_TRANSMIT_STATUS: case BH_TYPE_TRANSMIT_DATA: mgsl_bh_transmit_data( info, info->bh_action->status ); break; case BH_TYPE_STATUS: mgsl_bh_status_handler( info, info->bh_action->status ); break; default: /* unknown work item ID */ printk("Unknown work item ID=%08X!\n", info->bh_action->type ); break; } } if ( info->isr_overflow ) { printk("ISR overflow detected.\n"); } if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_handler(%s) exit\n", __FILE__,__LINE__,info->device_name); } /* end of mgsl_bh_handler() *//* mgsl_bh_receive_dma() * * Perform bottom half processing for a receive DMA interrupt * This occurs in HDLC mode after a DMA buffer has terminated * or the DMA buffers have been exhausted. * * Arguments: * * info pointer to device instance data * status status word * * Return Value: None */void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status ){ if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_receive_dma(%s)\n", __FILE__,__LINE__,info->device_name); while( mgsl_get_rx_frame(info) );} /* end of mgsl_bh_receive_dma() *//* mgsl_bh_transmit_data() * * Process a transmit data interrupt event * This occurs in asynchronous communications mode. * * Arguments: info pointer to device instance data * Return Value: None */void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ){ struct tty_struct *tty = info->tty; if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n", __FILE__,__LINE__,info->device_name); /* wakeup any waiting write requests */ if (tty) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):calling ldisc.write_wakeup on %s\n", __FILE__,__LINE__,info->device_name); (tty->ldisc.write_wakeup)(tty); } wake_up_interruptible(&tty->write_wait); } } /* End Of mgsl_bh_transmit_data() *//* mgsl_bh_status_handler() * * Peform bottom half processing for a status interrupt * * This event is generated when a I/O pin (serial signal) * has a transition. If there is a pending WaitEvent call * and the status transition is identified in the EventMast * of the pending call then complete the pending call. * * Arguments: * * info pointer to device instance data * status status word * * Return Value: None */void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status ){ if ( debug_level >= DEBUG_LEVEL_BH ) printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n", __FILE__,__LINE__,info->device_name);} /* End Of mgsl_bh_status_handler() *//* mgsl_isr_receive_status() * * Service a receive status interrupt. The type of status * interrupt is indicated by the state of the RCSR. * This is only used for HDLC mode. * * Arguments: info pointer to device instance data * Return Value: None */void mgsl_isr_receive_status( struct mgsl_struct *info ){ u16 status = usc_InReg( info, RCSR ); if ( debug_level >= DEBUG_LEVEL_ISR ) printk("%s(%d):mgsl_isr_receive_status status=%04X\n", __FILE__,__LINE__,status); usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); usc_UnlatchRxstatusBits( info, status ); if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { if (status & RXSTATUS_EXITED_HUNT) info->icount.exithunt++; if (status & RXSTATUS_IDLE_RECEIVED) info->icount.rxidle++; wake_up_interruptible(&info->event_wait_q); } if (status & RXSTATUS_OVERRUN){ /* Purge receive FIFO to allow DMA buffer completion * with overrun status stored in the receive status block. */ usc_RCmd( info, RCmd_EnterHuntmode ); usc_RTCmd( info, RTCmd_PurgeRxFifo ); }} /* end of mgsl_isr_receive_status() *//* mgsl_isr_transmit_status() * * Service a transmit status interrupt * HDLC mode :end of transmit frame * Async mode:all data is sent * transmit status is indicated by bits in the TCSR. * * Arguments: info pointer to device instance data * Return Value: None */void mgsl_isr_transmit_status( struct mgsl_struct *info ){ u16 status = usc_InReg( info, TCSR ); if ( debug_level >= DEBUG_LEVEL_ISR ) printk("%s(%d):mgsl_isr_transmit_status status=%04X\n", __FILE__,__LINE__,status); usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); usc_UnlatchTxstatusBits( info, status ); if ( status & TXSTATUS_EOF_SENT ) info->icount.txok++; else if ( status & TXSTATUS_UNDERRUN ) info->icount.txunder++; else if ( status & TXSTATUS_ABORT_SENT ) info->icount.txabort++; else info->icount.txunder++; info->tx_active = 0; info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; del_timer(&info->tx_timer); if ( info->drop_rts_on_tx_done ) { usc_get_serial_signals( info ); if ( info->serial_signals & SerialSignal_RTS ) { info->serial_signals &= ~SerialSignal_RTS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -