fp@0: /** rt_com fp@0: * ====== fp@0: * fp@0: * RT-Linux kernel module for communication across serial lines. fp@0: * fp@0: * Copyright (C) 1997 Jens Michaelsen fp@0: * Copyright (C) 1997-2000 Jochen Kupper fp@0: * Copyright (C) 1999 Hua Mao fp@0: * Copyright (C) 1999 Roberto Finazzi fp@0: * Copyright (C) 2000-2002 Giuseppe Renoldi fp@0: * fp@0: * This program is free software; you can redistribute it and/or fp@0: * modify it under the terms of the GNU General Public License as fp@0: * published by the Free Software Foundation; either version 2 of the fp@0: * License, or (at your option) any later version. fp@0: * fp@0: * This program is distributed in the hope that it will be useful, but fp@0: * WITHOUT ANY WARRANTY; without even the implied warranty of fp@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU fp@0: * General Public License for more details. fp@0: * fp@0: * You should have received a copy of the GNU General Public License fp@0: * along with this program; see the file License. if not, write to the fp@0: * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, fp@0: * MA 02111-1307, USA. fp@0: * fp@0: * $Id: aip_com.c,v 1.1 2005/09/16 14:16:31 hm Exp hm $ */ fp@0: #define VERSION "0.6.pre2-rtaicvs (modified by Hm, IgH for aip)" //Hm, IgH fp@0: fp@0: #include fp@0: #include fp@0: #include fp@0: #include fp@0: #include fp@0: #include fp@0: fp@0: #include fp@0: #include fp@0: fp@0: #ifdef _RTAI fp@0: #include fp@0: #endif fp@0: fp@0: #include "aip_com.h" fp@0: #include "aip_comP.h" fp@0: fp@0: #include fp@0: fp@0: RCS_ID("$Header: /home/hm/projekte/msr_messen_steuern_regeln/ethercat/src-hm/rs232rt/RCS/aip_com.c,v 1.1 2005/09/16 14:16:31 hm Exp hm $"); fp@0: fp@0: /* Hm, IgH fp@0: MODULE_AUTHOR("Jochen Kuepper"); fp@0: MODULE_DESCRIPTION("real-time serial port driver"); fp@0: MODULE_LICENSE("GPL"); fp@0: */ fp@0: fp@0: fp@0: /* size of internal queues fp@0: * This is the default value. */ fp@0: unsigned int rt_com_buffersize = RT_COM_BUF_SIZ; fp@0: fp@0: /** Default: mode=0 - no hw flow control fp@0: * used=0 - port and irq setting by rt_com_hwparam. fp@0: * If you want to work like fp@0: * a standard rt_com you can set used=1. */ fp@0: struct rt_com_struct rt_com_table[] = { fp@0: { // ttyS0 - COM1 fp@0: RT_COM_BASE_BAUD, // int baud_base; fp@0: 0x3f8, // int port; fp@0: 4, // int irq; fp@0: rt_com0_isr, // void (*isr)(void); fp@0: 115200, // int baud; fp@0: 8, // unsigned int wordlength; fp@0: RT_COM_PARITY_NONE, // unsigned int parity; fp@0: 1, // stopbits; fp@0: RT_COM_NO_HAND_SHAKE, // int mode; fp@0: RT_COM_FIFO_SIZE_8, // int fifotrig; fp@0: 1 //Hm, IgH // int used; fp@0: }, { // ttyS1 - COM2 fp@0: RT_COM_BASE_BAUD, // int baud_base; fp@0: 0x2f8, // int port; fp@0: 3, // int irq; fp@0: rt_com1_isr, // void (*isr)(void); fp@0: 0, // int baud; fp@0: 8, // unsigned int wordlength; fp@0: RT_COM_PARITY_NONE, // unsigned int parity; fp@0: 1, // stopbits; fp@0: RT_COM_NO_HAND_SHAKE, // int mode; fp@0: RT_COM_FIFO_SIZE_8, // int fifotrig; fp@0: 0 // int used; fp@0: } fp@0: }; fp@0: fp@0: /** Number and descriptions of serial ports to manage. You also need fp@0: * to create an ISR ( rt_comN_isr() ) for each port. */ fp@0: #define RT_COM_CNT (sizeof(rt_com_table) / sizeof(struct rt_com_struct)) fp@0: fp@0: /** Internal: Remaining free space of buffer fp@0: * fp@0: * @return amount of free space remaining in a buffer (input or output) fp@0: * fp@0: * @author Jochen Kupper fp@0: * @version 2000/03/10 */ fp@0: static inline unsigned int rt_com_buffer_free(unsigned int head, unsigned int tail) fp@0: { fp@0: return(head < tail) ? (tail - head) : (rt_com_buffersize - (head - tail)); fp@0: } fp@0: fp@0: /** Clear input buffer. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @return 0 if all right, -ENODEV or -EPERM on error. fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/12 */ fp@0: int rt_com_clear_input(unsigned int ttyS) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else if (0 >= (rt_com_table[ttyS]).used) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: struct rt_buf_struct *b = &(p->ibuf); fp@0: long state; fp@0: rt_com_irq_off(state); fp@0: b->tail = b->head; fp@0: if (p->fifotrig) fp@0: outb(inb(p->port + RT_COM_FCR) | FCR_INPUT_FIFO_RESET, p->port + RT_COM_FCR); fp@0: rt_com_irq_on(state); fp@0: if (p->mode & RT_COM_HW_FLOW) { fp@0: /* with hardware flow set RTS */ fp@0: p->mcr |= MCR_RTS; fp@0: outb(p->mcr, p->port + RT_COM_MCR); fp@0: } fp@0: fp@0: return(0); fp@0: } fp@0: } fp@0: fp@0: /** Clear output buffer. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @return 0 if all right, negative error conditions otherwise fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/12 fp@0: */ fp@0: int rt_com_clear_output(unsigned int ttyS) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (0 >= (rt_com_table[ttyS]).used) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: struct rt_buf_struct *b = &(p->obuf); fp@0: long state; fp@0: rt_com_irq_off(state); fp@0: p->ier &= ~IER_ETBEI; fp@0: outb(p->ier, p->port | RT_COM_IER); fp@0: b->tail = b->head; fp@0: if (p->fifotrig) fp@0: outb(inb(p->port + RT_COM_FCR) | FCR_OUTPUT_FIFO_RESET, p->port + RT_COM_FCR); fp@0: rt_com_irq_on(state); fp@0: return(0); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /** Set functioning mode. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @param mode functioning mode fp@0: * @return 0 if all right, negative on error fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/12 fp@0: */ fp@0: int rt_com_set_mode(unsigned int ttyS, int mode) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (0 >= p->used) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: p->mode = mode; fp@0: if (p->used & RT_COM_PORT_SETUP) { fp@0: /* setup done */ fp@0: if (mode == RT_COM_NO_HAND_SHAKE) { fp@0: /* if no hw signals disable modem interrupts */ fp@0: p->ier &= ~IER_EDSSI; fp@0: } fp@0: else { fp@0: /* else enable it */ fp@0: p->ier |= IER_EDSSI; fp@0: } fp@0: fp@0: outb(p->ier, p->port + RT_COM_IER); fp@0: } fp@0: fp@0: return(0); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /** Set receiver fifo trigger level. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @param fifotrig receiver fifo trigger level fp@0: * @return 0 if all right, negative on error fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/12 fp@0: */ fp@0: int rt_com_set_fifotrig(unsigned int ttyS, int fifotrig) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: p->fifotrig = fifotrig; fp@0: if (p->used & RT_COM_PORT_SETUP) { fp@0: /* setup done */ fp@0: if (p->fifotrig) fp@0: outb(FCR_FIFO_ENABLE | p->fifotrig, p->port + RT_COM_FCR); // enable fifo fp@0: else fp@0: outb(0, p->port + RT_COM_FCR); // disable fifo fp@0: } fp@0: } fp@0: fp@0: return(0); fp@0: } fp@0: fp@0: /** Set output signal for modem control (DTR, RTS). fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @param signal Output signals: RT_COM_DTR or RT_COM_RTS. fp@0: * @param value Status: 0 or 1. fp@0: * @return 0 if all right, negative error code otherwise fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/12 */ fp@0: int rt_com_write_modem(unsigned int ttyS, int signal, int value) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else if (value &~0x01) { fp@0: return(-EINVAL); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (0 >= p->used) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: if (value) fp@0: p->mcr |= signal; fp@0: else fp@0: p->mcr &= ~signal; fp@0: outb(p->mcr, p->port + RT_COM_MCR); fp@0: return(0); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /** Read input signal from modem (CTS, DSR, RI, DCD). fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @param signal Input signals: RT_COM_CTS, RT_COM_DSR, RT_COM_RI, RT_COM_DCD fp@0: * or any combination. fp@0: * @return input signal status; that is the bitwise-OR of the signal fp@0: * argument and the MSR register. fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/12 */ fp@0: int rt_com_read_modem(unsigned int ttyS, int signal) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else if (signal & 0xf) { fp@0: return(-EINVAL); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (0 >= p->used) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: return(inb(p->port + RT_COM_MSR) & signal); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /** Return last error detected. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @return bit 0 :1 = Input buffer overflow fp@0: * bit 1 :1 = Receive data overrun fp@0: * bit 2 :1 = Parity error fp@0: * bit 3 :1 = Framing error fp@0: * bit 4 :1 = Break detected fp@0: * fp@0: * @author Roberto Finazzi fp@0: * @version 2000/03/12 fp@0: */ fp@0: int rt_com_error(unsigned int ttyS) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: int tmp = p->error; fp@0: p->error = 0; fp@0: return(tmp); fp@0: } fp@0: } fp@0: fp@0: /** Write data to a line. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @param buffer Address of data. fp@0: * @param count Number of bytes to write. If negative, send byte only if fp@0: * possible to send them all together. fp@0: * @return Number of bytes not written. Negative values are error fp@0: * conditions. fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper, Giuseppe Renoldi fp@0: * @version 2000/03/12 */ fp@0: int rt_com_write(unsigned int ttyS, char *buffer, int count) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (!(p->used & RT_COM_PORT_SETUP)) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: struct rt_buf_struct *b = &(p->obuf); fp@0: long state; fp@0: int bytestosend; fp@0: if (count == 0) fp@0: return(0); fp@0: bytestosend = rt_com_buffer_free(b->head, b->tail); fp@0: if (count < 0) { fp@0: count = -count; fp@0: if (count > bytestosend) fp@0: return(count); fp@0: bytestosend = count; fp@0: } fp@0: else { fp@0: if (count <= bytestosend) fp@0: bytestosend = count; fp@0: } fp@0: fp@0: rt_com_irq_off(state); fp@0: while (bytestosend-- > 0) { fp@0: /* put byte into buffer, move pointers to next elements */ fp@0: b->buf[b->head++] = *buffer++; fp@0: if (b->head >= rt_com_buffersize) fp@0: b->head = 0; fp@0: --count; fp@0: } fp@0: fp@0: p->ier |= IER_ETBEI; fp@0: outb(p->ier, p->port + RT_COM_IER); fp@0: rt_com_irq_on(state); fp@0: return(count); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /** Read data we got from a line. fp@0: * fp@0: * @param ttyS Port to use corresponding to internal numbering scheme. fp@0: * @param buffer Address of data buffer. Needs to be of size > cnt ! fp@0: * @param count Number of bytes to read. fp@0: * @return Number of bytes actually read. fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper fp@0: * @version 2000/03/17 */ fp@0: int rt_com_read(unsigned int ttyS, char *buffer, int count) fp@0: { fp@0: if (0 > count) { fp@0: return(-EINVAL); fp@0: } fp@0: else if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (!(p->used & RT_COM_PORT_SETUP)) { fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: struct rt_buf_struct *b = &(p->ibuf); fp@0: int done = 0; fp@0: long state; fp@0: rt_com_irq_off(state); fp@0: while ((b->head != b->tail) && (--count >= 0)) { fp@0: done++; fp@0: *buffer++ = b->buf[b->tail++]; fp@0: b->tail &= (RT_COM_BUF_SIZ - 1); fp@0: } fp@0: fp@0: rt_com_irq_on(state); fp@0: if ((p->mode & RT_COM_HW_FLOW) && (rt_com_buffer_free(b->head, b->tail) > RT_COM_BUF_HI)) { fp@0: /* if hardware flow and enough free space on input buffer fp@0: then set RTS */ fp@0: p->mcr |= MCR_RTS; fp@0: outb(p->mcr, p->port + RT_COM_MCR); fp@0: } fp@0: fp@0: return(done); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /** Get first byte from the write buffer. fp@0: * fp@0: * @param p rt_com_struct of the line we are writing to. fp@0: * @param c Address to put the char in. fp@0: * @return Number of characters we actually got. fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper fp@0: * @version 1999/10/01 fp@0: */ fp@0: static inline int rt_com_irq_get(struct rt_com_struct *p, unsigned char *c) fp@0: { fp@0: struct rt_buf_struct *b = &(p->obuf); fp@0: if (b->head != b->tail) { fp@0: *c = b->buf[b->tail++]; fp@0: b->tail &= (RT_COM_BUF_SIZ - 1); fp@0: return(1); fp@0: } fp@0: fp@0: return(0); fp@0: } fp@0: fp@0: /** Concatenate a byte to the read buffer. fp@0: * fp@0: * @param p rt_com_struct of the line we are writing to. fp@0: * @param ch Byte to put into buffer. fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper fp@0: * @version 1999/07/20 */ fp@0: static inline void rt_com_irq_put(struct rt_com_struct *p, unsigned char ch) fp@0: { fp@0: struct rt_buf_struct *b = &(p->ibuf); fp@0: b->buf[b->head++] = ch; fp@0: b->head &= (RT_COM_BUF_SIZ - 1); fp@0: } fp@0: fp@0: /** Real interrupt handler. fp@0: * fp@0: * This one is called by the registered ISRs and does the actual work. fp@0: * fp@0: * @param ttyS Port to use corresponding to internal numbering scheme. fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper, Hua Mao, Roberto Finazzi fp@0: * @version 2000/03/17 */ fp@0: static inline int rt_com_isr(unsigned int ttyS) fp@0: { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: struct rt_buf_struct *b = &(p->ibuf); fp@0: unsigned int base = p->port; fp@0: int buff, data_to_tx; fp@0: int loop = 4; fp@0: int toFifo = 16; fp@0: unsigned char data, msr, lsr, iir; fp@0: fp@0: do { fp@0: //iir=inb(base + RT_COM_IIR); fp@0: //rt_printk("iir=0x%02x\n",iir); fp@0: /* get available data from port */ fp@0: lsr = inb(base + RT_COM_LSR); fp@0: if (0x1e & lsr) fp@0: p->error = lsr & 0x1e; fp@0: while (LSR_DATA_READY & lsr) { fp@0: data = inb(base + RT_COM_RXB); fp@0: fp@0: //rt_printk("[%02x<- ",data); fp@0: rt_com_irq_put(p, data); fp@0: lsr = inb(base + RT_COM_LSR); fp@0: if (0x1e & lsr) fp@0: p->error = 0x1e & lsr; fp@0: } fp@0: fp@0: /* controls on buffer full and RTS clear on hardware flow fp@0: control */ fp@0: buff = rt_com_buffer_free(b->head, b->tail); fp@0: if (buff < RT_COM_BUF_FULL) fp@0: p->error = RT_COM_BUFFER_FULL; fp@0: fp@0: if ((p->mode & RT_COM_HW_FLOW) && (buff < RT_COM_BUF_LOW)) { fp@0: p->mcr &= ~MCR_RTS; fp@0: outb(p->mcr, p->port + RT_COM_MCR); fp@0: } fp@0: fp@0: /* if possible, put data to port */ fp@0: msr = inb(base + RT_COM_MSR); fp@0: if fp@0: ( fp@0: (p->mode == RT_COM_NO_HAND_SHAKE) || fp@0: ((p->mode & RT_COM_DSR_ON_TX) && (MSR_DSR & msr) && (p->mode & RT_COM_HW_FLOW) && (MSR_CTS & msr)) fp@0: ) { fp@0: /* (DSR && (CTS || Mode==no hw flow)) or Mode==no handshake */ fp@0: // if (THRE==1) i.e. transmitter empty fp@0: if ((lsr = inb(base + RT_COM_LSR)) & LSR_THRE) { fp@0: // if there are data to transmit fp@0: if ((data_to_tx = rt_com_irq_get(p, &data)) != 0) { fp@0: do { fp@0: //rt_printk("->%02x] ",data); fp@0: outb(data, base + RT_COM_TXB); fp@0: } while ((--toFifo > 0) && (data_to_tx = rt_com_irq_get(p, &data) != 0)); fp@0: } fp@0: fp@0: if (!data_to_tx) { fp@0: /* no data in output buffer, disable Transmitter fp@0: Holding Register Empty Interrupt */ fp@0: p->ier &= ~IER_ETBEI; fp@0: outb(p->ier, base + RT_COM_IER); fp@0: } fp@0: } fp@0: } fp@0: fp@0: /* check the low nibble of IIR wheather there is another pending fp@0: interrupt. bit 0 = 0 if interrupt pending, bits 1,2,3 fp@0: are the interrupt ID */ fp@0: iir = inb(base + RT_COM_IIR); fp@0: } while (((iir & 0x0F) != 1) && (--loop > 0)); fp@0: fp@0: #if defined RTLINUX_V2 fp@0: rtl_hard_enable_irq(p->irq); fp@0: #endif fp@0: return 0; fp@0: } fp@0: fp@0: /** Interrupt Service Routines fp@0: * fp@0: * These are the Interrupt Service Routines to be registered with the fp@0: * OS. They simply call the genereric interrupt handler for the fp@0: * current port to do the work. fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper, Hua Mao fp@0: * @version 1999/11/11 */ fp@0: static void rt_com0_isr(void) fp@0: { fp@0: //rt_printk("rt_com0_isr\n"); fp@0: rt_com_isr(0); fp@0: } fp@0: fp@0: static void rt_com1_isr(void) fp@0: { fp@0: //rt_printk("rt_com1_isr\n"); fp@0: rt_com_isr(1); fp@0: } fp@0: fp@0: /** Setup one port fp@0: * fp@0: * Calls from init_module + cleanup_module have baud == 0; in these fp@0: * cases we only do some cleanup. fp@0: * fp@0: * To allocate a port, give usefull setup parameter, to deallocate fp@0: * give negative baud. fp@0: * fp@0: * @param ttyS Number corresponding to internal port numbering scheme. fp@0: * This is esp. the index of the rt_com_table to use. fp@0: * @param baud Data transmission rate to use [Byte/s]. If negative, fp@0: * deallocate port instead. Called with baud == 0 from fp@0: * init_module for basic initialization. Needs to be called fp@0: * by user-task again before use ! fp@0: * @param mode see rt_com_set_mode docs for now fp@0: * @param parity Parity for transmission protocol. fp@0: * (RT_COM_PARITY_EVEN, RT_COM_PARITY_ODD, RT_COM_PARITY_NONE) fp@0: * @param stopbits Number of stopbits to use. 1 gives you one stopbit, 2 fp@0: * actually gives really two stopbits for wordlengths of fp@0: * 6 - 8 bit, but 1.5 stopbits for a wordlength of 5 bits. fp@0: * @param wordlength Number of bits per word (5 - 8 bits). fp@0: * @param fifotrig if <0 set trigger fifo using default value set fp@0: * in rt_com_table[], otherwise set trigger fifo accordingly fp@0: * to the parameter fp@0: * @return 0 - all right fp@0: * -ENODEV - no entry for that ttyS in rt_com_table fp@0: * -EPERM - get hardware resources first (the port needs to fp@0: * be setup hardware-wise first, that means you have fp@0: * to specify a positive used flag at compile time fp@0: * or call rt_com_set_hwparm first.) fp@0: * fp@0: * @author Jens Michaelsen, Jochen Kupper, Roberto Finazzi fp@0: * @version 2000/03/12 */ fp@0: int rt_com_setup fp@0: ( fp@0: unsigned int ttyS, fp@0: int baud, fp@0: int mode, fp@0: unsigned int parity, fp@0: unsigned int stopbits, fp@0: unsigned int wordlength, fp@0: int fifotrig fp@0: ) fp@0: { fp@0: if (ttyS >= RT_COM_CNT) { fp@0: return(-ENODEV); fp@0: } fp@0: else { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (0 == p->used) { fp@0: /* */ fp@0: return(-EPERM); fp@0: } fp@0: else { fp@0: unsigned int base = p->port; fp@0: fp@0: /* Stop everything, set DLAB */ fp@0: outb(0x00, base + RT_COM_IER); fp@0: outb(0x80, base + RT_COM_LCR); fp@0: fp@0: /* clear irqs */ fp@0: inb(base + RT_COM_IIR); fp@0: inb(base + RT_COM_LSR); fp@0: inb(base + RT_COM_RXB); fp@0: inb(base + RT_COM_MSR); fp@0: fp@0: /* initialize error code */ fp@0: p->error = 0; fp@0: fp@0: /* if 0 == baud, nothing else to do ! */ fp@0: if (baud == 0) fp@0: return(0); fp@0: fp@0: if (0 > baud) { fp@0: /* free the port */ fp@0: /* disable interrupts */ fp@0: outb(0, base + RT_COM_IER); fp@0: //MOD_DEC_USE_COUNT; Hm, IgH fp@0: return(0); fp@0: } fp@0: else { fp@0: /* allocate and set-up the port */ fp@0: unsigned int divider = p->baud_base / baud; fp@0: fp@0: //MOD_INC_USE_COUNT; Hm, IgH fp@0: fp@0: /* set transfer rate */ fp@0: outb(divider % 256, base + RT_COM_DLL); fp@0: outb(divider / 256, base + RT_COM_DLM); fp@0: fp@0: /* bits 3,4 + 5 determine parity, mask away anything else */ fp@0: parity &= 0x38; fp@0: fp@0: /* set transmission parameters and clear DLAB */ fp@0: outb((wordlength - 5) | ((stopbits - 1) << 2) | parity, base + RT_COM_LCR); fp@0: fp@0: /* set-up MCR value and write it */ fp@0: p->mcr = MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2; fp@0: outb(p->mcr, base + RT_COM_MCR); fp@0: fp@0: /* set-up IER value and write it */ fp@0: p->mode = mode; fp@0: if (p->mode == RT_COM_NO_HAND_SHAKE) { fp@0: /* if no handshaking signals enable only receiver interrupts */ fp@0: p->ier = IER_ERBI | IER_ELSI; fp@0: } fp@0: else { fp@0: /* enable receiver and modem interrupts */ fp@0: p->ier = IER_ERBI | IER_ELSI | IER_EDSSI; fp@0: } fp@0: fp@0: outb(p->ier, base + RT_COM_IER); fp@0: if (fifotrig>=0) fp@0: p->fifotrig = fifotrig; fp@0: outb(FCR_FIFO_ENABLE | p->fifotrig, base + RT_COM_FCR); // enable fifo fp@0: /* mark setup done */ fp@0: p->used |= RT_COM_PORT_SETUP; fp@0: return(0); fp@0: } fp@0: } fp@0: fp@0: return(0); fp@0: } fp@0: } fp@0: fp@0: /** Set hardware parameter for a specific port. fp@0: * fp@0: * Change port address and irq setting for a specified port. The port fp@0: * must have an entry in rt_com_table beforehand. fp@0: * fp@0: * To allow the specification of additional ports we would need to fp@0: * dynamically allocate memory, that's not really feasible within a fp@0: * real-time context, although we could preallocate a few entries in fp@0: * init_module. However, it doesn't make too much sense, as you can fp@0: * specify all ports that really exist (in hardware) at compile time fp@0: * and enable only these you want to use. fp@0: * fp@0: * @param ttyS Port to use; corresponding to internal numbering scheme. fp@0: * @param port port address, if zero, use standard value from rt_com_table fp@0: * @param irq irq address, if zero, use standard value from rt_com_table fp@0: * @return 0 everything all right, fp@0: * -ENODEV no entry in rt_com_table for that device, fp@0: * -EBUSY port-region is used already. fp@0: * fp@0: * @author Roberto Finazzi, Jochen Kupper fp@0: * @version 2000/03/10 */ fp@0: int rt_com_hwsetup(unsigned int ttyS, int port, int irq) fp@0: { fp@0: if (ttyS < RT_COM_CNT) { fp@0: struct rt_com_struct *p = &(rt_com_table[ttyS]); fp@0: if (0 == p->used) { fp@0: if (0 != port) fp@0: p->port = port; fp@0: if (0 != irq) fp@0: p->irq = irq; fp@0: if (-EBUSY == check_region(p->port, 8)) { fp@0: return(-EBUSY); fp@0: } fp@0: fp@0: request_region(p->port, 8, RT_COM_NAME); fp@0: rt_com_request_irq(p->irq, p->isr); fp@0: p->used = 1; fp@0: rt_com_setup(ttyS, p->baud, p->mode, p->parity, p->stopbits, p->wordlength, p->fifotrig); fp@0: return(0); fp@0: } fp@0: else { fp@0: if (port >= 0) fp@0: return(-EBUSY); fp@0: rt_com_setup(ttyS, 0, 0, 0, 0, 0, 0); fp@0: rt_com_free_irq(p->irq); fp@0: release_region(p->port, 8); fp@0: p->used = 0; fp@0: return(0); fp@0: } fp@0: } fp@0: fp@0: return(-ENODEV); fp@0: } fp@0: fp@0: /** Initialization fp@0: * fp@0: * For all ports that have a used flag greater than 0, request port fp@0: * memory and register ISRs. If we cannot get the memory of all these fp@0: * ports, release all already requested ports and return an error. fp@0: * fp@0: * @return Success status, zero on success. With a non-zero return fp@0: * value of this routine, insmod will fail to load the module ! fp@0: * fp@0: * @author Jochen Kupper, Hua Mao, Roberto Finazzi fp@0: * @version 2000/03/10 */ fp@0: //int init_module(void) //Hm, IgH fp@0: int init_aip_com(void) fp@0: { fp@0: int errorcode = 0; fp@0: unsigned int i, j; fp@0: fp@0: printk(KERN_INFO "rt_com: Loading real-time serial port driver (version "VERSION ").\n"); fp@0: fp@0: for (i = 0; i < RT_COM_CNT; i++) { fp@0: struct rt_com_struct *p = &(rt_com_table[i]); fp@0: fp@0: // if used set default values fp@0: if (p->used > 0) { fp@0: printk(KERN_WARNING "RS232 testing %d\n",i); fp@0: if (-EBUSY == check_region(p->port, 8)) { fp@0: errorcode = -EBUSY; fp@0: printk(KERN_WARNING "rt_com: error %d: cannot request port region %x\n", errorcode, p->port); fp@0: break; fp@0: } fp@0: fp@0: request_region(p->port, 8, "rt_com"); fp@0: rt_com_request_irq(p->irq, p->isr); fp@0: printk(KERN_WARNING "RS232 Request IRQ: %d\n",p->irq); fp@0: rt_com_setup(i, p->baud, p->mode, p->parity, p->stopbits, p->wordlength, p->fifotrig); fp@0: } fp@0: } fp@0: fp@0: if (0 != errorcode) { fp@0: printk(KERN_WARNING "rt_com: giving up.\n"); fp@0: for (j = 0; j < i; j++) { fp@0: struct rt_com_struct *p = &(rt_com_table[j]); fp@0: if (0 < p->used) { fp@0: rt_com_free_irq(p->irq); fp@0: release_region(p->port, 8); fp@0: } fp@0: } fp@0: } fp@0: else { fp@0: printk(KERN_INFO "rt_com: sucessfully loaded.\n"); fp@0: } fp@0: fp@0: return(errorcode); fp@0: } fp@0: fp@0: /** Cleanup fp@0: * fp@0: * Unregister ISR and releases memory for all ports fp@0: * fp@0: * @author Jochen Kupper fp@0: * @version 1999/10/01 */ fp@0: //void cleanup_module(void) Hm, IgH fp@0: void cleanup_aip_com(void) fp@0: { fp@0: int i; fp@0: for (i = 0; i < RT_COM_CNT; i++) { fp@0: struct rt_com_struct *p = &(rt_com_table[i]); fp@0: if (0 < p->used) { fp@0: rt_com_free_irq(p->irq); fp@0: rt_com_setup(i, 0, 0, 0, 0, 0, 0); fp@0: release_region(p->port, 8); fp@0: } fp@0: } fp@0: fp@0: printk(KERN_INFO "rt_com: unloaded.\n"); fp@0: } fp@0: fp@0: /* fp@0: EXPORT_SYMBOL(rt_com_buffersize); fp@0: EXPORT_SYMBOL(rt_com_clear_input); fp@0: EXPORT_SYMBOL(rt_com_clear_output); fp@0: EXPORT_SYMBOL(rt_com_error); fp@0: EXPORT_SYMBOL(rt_com_hwsetup); fp@0: EXPORT_SYMBOL(rt_com_read); fp@0: EXPORT_SYMBOL(rt_com_read_modem); fp@0: EXPORT_SYMBOL(rt_com_setup); fp@0: EXPORT_SYMBOL(rt_com_table); fp@0: EXPORT_SYMBOL(rt_com_write); fp@0: EXPORT_SYMBOL(rt_com_write_modem); fp@0: EXPORT_SYMBOL(rt_com_set_mode); fp@0: EXPORT_SYMBOL(rt_com_set_fifotrig); fp@0: */ fp@0: fp@0: /** fp@0: * Local Variables: fp@0: * mode: C fp@0: * c-file-style: "Stroustrup" fp@0: * End: fp@0: */