?? rt_com.c
字號:
} } return( done );}/** Get first byte from the write buffer. * * @param p rt_com_struct of the line we are writing to. * @param c Address to put the char in. * @return Number of characters we actually got. * * @author Jens Michaelsen, Jochen K黳per * @version 1999/10/01 */static inline int rt_com_irq_get( struct rt_com_struct *p, unsigned char *c ){ struct rt_buf_struct *b = &( p->obuf ); if( b->head != b->tail ) { *c = b->buf[ b->tail++ ]; b->tail &= ( RT_COM_BUF_SIZ - 1 ); return( 1 ); } return( 0 );}/** Concatenate a byte to the read buffer. * * @param p rt_com_struct of the line we are writing to. * @param ch Byte to put into buffer. * * @author Jens Michaelsen, Jochen K黳per * @version 1999/07/20 */static inline void rt_com_irq_put( struct rt_com_struct *p, unsigned char ch ){ struct rt_buf_struct *b = &( p->ibuf ); b->buf[ b->head++ ] = ch; b->head &= ( RT_COM_BUF_SIZ - 1 );}/** Registered interrupt handlers. * * These simply call the general interrupt handler for the current * line to do the work. * * @author Jens Michaelsen, Jochen K黳per, Hua Mao * @version 1999/11/11 */#if defined RTLINUX_V1 || defined RTAIstatic void rt_com0_isr( void ){ rt_com_isr( 0 );}static void rt_com1_isr( void ){ rt_com_isr( 1 );}#elsestatic unsigned int rt_com0_isr( unsigned int num, struct pt_regs *r ){ return rt_com_isr( 0, NULL );}static unsigned int rt_com1_isr( unsigned int num, struct pt_regs *r ){ return rt_com_isr( 1, NULL );}#endif/** Real interrupt handler. * * Called by the registered ISRs to do the actual work. * * @param com Port to use corresponding to internal numbering scheme. * * @author Jens Michaelsen, Jochen K黳per, Hua Mao, Renoldi Giuseppe * @version 2000/10/02 */#if defined RTLINUX_V1 || defined RTAIstatic inline void rt_com_isr( unsigned int com )#elseunsigned int rt_com_isr( unsigned int com, struct pt_regs *r )#endif{ struct rt_com_struct *p = &(rt_com_table[com]); struct rt_buf_struct *b = &(p->ibuf); //!! unsigned int B = p->port; unsigned char data, sta; int buff,t; char loop = 4; char toFifo = 16; int rxd_bytes = 0; do { /* get available data from port */ sta = inb( B + RT_COM_LSR ); if( 0x1e & sta) p->error = sta & 0x1e; while( RT_COM_DATA_READY & sta ) { data = inb( B + RT_COM_RXB ); rt_com_irq_put( p, data ); rxd_bytes = 1; sta = inb( B + RT_COM_LSR ); if( 0x1e & sta ) p->error = sta & 0x1e; } /* controls on buffer full and RTS clear on hardware flow control */ buff = rt_com_buff_free( b->head, b->tail ); if (buff < RT_COM_BUF_FULL) p->error = RT_COM_BUFFER_FULL; if ( ( p->mode & 0x02 ) && ( buff < RT_COM_BUF_LOW ) ) { p->mcr &= ~0x02; outb( p->mcr, p->port+RT_COM_MCR ); } /* if possible, put data to port */ sta = inb( B + RT_COM_MSR ); if ( ( ( 0x20 & sta ) && ( ( 0x10 & sta ) || ( 0 == ( p->mode & 0x02 ) ) ) ) || ( p->mode & 0x01 ) ) { /* (DSR && (CTS || Mode==no hw flow)) or Mode==no hand shake */ if ( ( sta = inb(B + RT_COM_LSR) ) & 0x20 ) { // if (THRE==1) i.e. transmitter empty if( 0 != ( t = rt_com_irq_get( p, &data ) ) ) { /* if data in output buffer */ do { outb( data, B + RT_COM_TXB ); } while( ( --toFifo > 0 ) && ( 0 != ( t = rt_com_irq_get( p, &data ) ) ) ); } if( ! t ) { /* no more data in output buffer, disable Transmitter Holding Register Empty Interrupt */ p->ier &= ~0x02; outb( p->ier, B + RT_COM_IER ); } } } /* check the low nibble of IIR to check if there is another pending interrupt */ /* Note: why is it done at most 4 times ? */ } while( 1 != ( ( inb( B + RT_COM_IIR ) & 0x0f ) ) && ( --loop > 0 ) ); if (rxd_bytes) { /* We received 1+ bytes. If user requested a callback, invoke it. */ if (p->callback) (*(p->callback)) (com); }#if defined RTLINUX_V1 || defined RTAI return;#else rtl_hard_enable_irq( p->irq ); return 0;#endif}/** Setup one port * * Calls from init_module + cleanup_module have baud == 0; in these * cases we only do some cleanup. * * To allocate a port, give usefull setup parameter, to deallocate * give negative baud. * * @param com Number corresponding to internal port numbering scheme. * This is esp. the index of the rt_com_table to use. * @param baud Data transmission rate to use [Byte/s]. * @param parity Parity for transmission protocol. * (RT_COM_PARITY_EVEN, RT_COM_PARITY_ODD or RT_COM_PARITY_NONE) * @param stopbits Number of stopbits to use. 1 gives you one stopbit, 2 * actually gives really two stopbits for wordlengths of * 6 - 8 bit, but 1.5 stopbits for a wordlength of 5 bits. * @param wordlength Number of bits per word (5 - 8 bits). * @return -1 if error, 0 if all right * * @author Jens Michaelsen, Jochen K黳per * @version 1999/07/20 */int rt_com_setup( unsigned int com, int baud, unsigned int parity, unsigned int stopbits, unsigned int wordlength ){ if ( com < RT_COM_CNT ) { //!! struct rt_com_struct *p = &( rt_com_table[ com ] ); unsigned int base = p->port, divider, par = parity; if( 0 == p->used ) return( -EBUSY ); /* Stop everything, set DLAB */ outb( 0x00, base + RT_COM_IER ); outb( 0x80, base + RT_COM_LCR ); /* clear irq */ inb( base + RT_COM_IIR ); inb( base + RT_COM_LSR ); inb( base + RT_COM_RXB ); inb( base + RT_COM_MSR ); p->error = 0; //!! init. last error code p->callback = 0; if( 0 == baud ) { /* return */ } else if( 0 > baud ) { MOD_DEC_USE_COUNT; } else { MOD_INC_USE_COUNT; divider = p->baud_base / baud; outb( divider % 256, base + RT_COM_DLL ); outb( divider / 256, base + RT_COM_DLM ); /* bits 3,4 + 5 determine parity - clear all other bits */ par &= 0x38; /* set transmission parameters and clear DLAB */ outb( ( wordlength - 5 ) + ( ( stopbits - 1 ) << 2 ) + par, base + RT_COM_LCR ); p->mcr = RT_COM_DTR + RT_COM_RTS + RT_COM_Out1 + RT_COM_Out2; outb( p->mcr , base + RT_COM_MCR ); if (p->mode & 0x01) p->ier = 0x05; /* if no hs signals enable only receiver interrupts */ else p->ier = 0x0D; /* else enable receiver and modem interrupts */ outb( p->ier, base + RT_COM_IER ); rt_com_enable_fifo( base, RT_COM_FIFO_TRIGGER, 0 ); p->used |= 0x02; /* mark setup done */ } return( 0 ); } return( -ENODEV );}/** Optional callback function. * * If the user supplies a callback function, it is called whenever * characters are received. * * *** NOTE *** The callback function is called in the context of the * interrupt handler! * * @param com Port to use; corresponding to internal numbering scheme. * @param fn Callback function (0 = de-register callback function). * @return Pointer to previously-installed callback function. * * @author James Puttick * @version 2000/02/08 */IRQ_CALLBACK_FN rt_com_set_callback (unsigned int com, IRQ_CALLBACK_FN fn){ struct rt_com_struct *p; IRQ_CALLBACK_FN old_fn; if( com >= RT_COM_CNT ) return( 0 ); p = &( rt_com_table[ com ] ); old_fn = p->callback; p->callback = fn; return( old_fn );}/** Port and irq setting for a specified COM. * * This must run in standard Linux context ! * * @param com Port to use; corresponding to internal numbering scheme. * @param port port address, if zero, use standard value from rt_com_table, * if negative, deinitialize. * @param irq irq address, if zero, use standard value from rt_com_table * @return 0 if all right, -1 if port is used yet, -2 if error on port region request * * @author Roberto Finazzi * @version 1999/10/31 */int rt_com_set_param( unsigned int com, int port, int irq ){ if( com >= RT_COM_CNT ) { return( -ENODEV ); } else { struct rt_com_struct *p = &( rt_com_table[ com ] ); if( 0 == p->used ) { if( 0 != port ) p->port = port; if( 0 != irq ) p->irq = irq; if( -EBUSY == check_region( p->port, 8 ) ) { return( -EBUSY ); } request_region( p->port, 8, "rt_com" ); rt_com_request_irq( p->irq, p->isr ); rt_com_setup( com, 0, 0, 0, 0 ); p->used = 1; } else { if( 0 > port ) { rt_com_setup( com, 0, 0, 0, 0 ); rt_com_free_irq( p->irq ); release_region( p->port, 8 ); p->used = 0; } else { return( -EBUSY ); } } } return( 0 );}/** Initialization * * Request port memory and register ISRs, if we cannot get the memory * of all ports, release all already requested ports and return an * error. * * @return Success status, zero on success. * * @author Jochen K黳per, Hua Mao * @version 2000/05/05 */int init_module( void ){ struct rt_com_struct *p; int errorcode = 0, i, j; for( i=0; i<RT_COM_CNT; i++ ) { p = &( rt_com_table[ i ] ); if( p->used > 0 ) { if(-EBUSY == check_region( p->port, 8 ) ) { errorcode = 1; break; } request_region( p->port, 8, "rt_com" ); rt_com_request_irq( p->irq, p->isr ); rt_com_setup( i, 0, 0, 0, 0 ); } } if( 0 == errorcode ) { printk( KERN_INFO "rt_com: RT-Linux serial port driver (version " VERSION ") sucessfully loaded.\n" KERN_INFO "rt_com: Copyright (C) 1997-2000 Jochen K黳per et al.\n" ); } else { printk( KERN_WARNING "rt_com: cannot request all port regions,\nrt_com: giving up.\n" ); for( j=0; j<i; j++ ) { p = &( rt_com_table[ j ] ); if( 0 < p->used ) { rt_com_free_irq( p->irq ); release_region( p->port, 8 ); } } } return( errorcode );}/** Cleanup * * Unregister ISR and releases memory for all ports * * @author Jochen K黳per, Hua Mao * @version 1999/10/01 */void cleanup_module( void ){ struct rt_com_struct *p; int i; for( i=0; i<RT_COM_CNT; i++ ) { p = &( rt_com_table[ i ] ); if( p->used > 0 ) { rt_com_free_irq( p->irq ); rt_com_setup( i, 0, 0, 0, 0 ); release_region( p->port, 8 ); } } printk( KERN_INFO "rt_com unloaded.\n" ); return;}/** * Local Variables: * mode: C * c-file-style: "Stroustrup" * End: */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -