Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

serial.c

Go to the documentation of this file.
00001 /*!     \file drivers/rs232/serial.c
00002  *      \brief Serial/RS232 port driver.
00003  *      \author Andrea Righi <drizzt@inwind.it>
00004  *      \date Last update: 2003-11-08
00005  *      \note Copyright (&copy;) 2003 Andrea Righi
00006  */
00007 
00008 #include <const.h>
00009 #include <string.h>
00010 
00011 #include <arch/i386.h>
00012 #include <arch/interrupt.h>
00013 
00014 #include <kernel/console.h>
00015 #include <kernel/keyboard.h>
00016 #include <kernel/task.h>
00017 
00018 #include <kernel/serial.h>
00019 
00020 /** \ingroup Drivers
00021  *  \defgroup RS232Driver Serial Port (RS232)
00022  *  The RS232 COM-port driver.
00023  *  @{
00024  */
00025 
00026 //! RS232 buffer for read operations :: head pointer.
00027 static int rs232_read_head=0;
00028 //! RS232 buffer for read operations :: tail pointer.
00029 static int rs232_read_tail=0;
00030 
00031 //! RS232 buffer for read operations.
00032 byte rs232_read_buf[RS232_BUF_DIM];
00033 
00034 //! RS232 buffer for write operations :: head pointer.
00035 static int rs232_write_head=0;
00036 //! RS232 buffer for write operations :: tail pointer.
00037 static int rs232_write_tail=0;
00038 
00039 //! RS232 buffer for write operations.
00040 byte rs232_write_buf[RS232_BUF_DIM];
00041 
00042 //! RS232 current port.
00043 static word curr_port = COM1;
00044 
00045 //! \brief RS232 common handler routine.
00046 //! \param port The serial port.
00047 void rs232_handler(word port)
00048 {
00049         byte flag, ch;
00050 
00051         while(TRUE)
00052         {
00053                 // Get a char from the serial port //
00054                 flag = inportb(port+2);
00055 
00056                 if (flag & 0x01) break;
00057 
00058                 switch(flag & 0x06)
00059                 {
00060                         // Modem status - clear intr by reading modem status reg //
00061                         case 0x00:
00062                         (void)inportb(port+6);
00063                         break;
00064 
00065                         // Write char - Send the char to the serial port //
00066                         case 0x02:
00067                         if (rs232_write_head != rs232_write_tail)
00068                         {
00069                                 ch = rs232_write_buf[rs232_write_head];
00070                                 rs232_write_head = (rs232_write_head+1)%RS232_BUF_DIM;
00071                                 outportb(port, ch);
00072                         }
00073                         // Inhibit TX interrupts if write buffer is empty (leave RX enabled) //
00074                         if (rs232_write_head == rs232_write_tail)
00075                                 outportb(port+1, 0x01);
00076                         break;
00077 
00078                         // Read char - Get the char from the serial port //
00079                         case 0x04:
00080                         while(TRUE)
00081                         {
00082                                 // Check for data ready //
00083                                 flag = inportb(port+5);
00084                                 if (!(flag & 0x01)) break;
00085 
00086                                 // Get the character //
00087                                 ch = inportb(port);
00088 
00089                                 // Store it into the buffer if it is not full //
00090                                 if (((rs232_read_tail+1)%RS232_BUF_DIM) != rs232_read_head)
00091                                 {
00092                                         rs232_read_buf[rs232_read_tail] = ch;
00093                                         rs232_read_tail = (rs232_read_tail+1)%RS232_BUF_DIM;
00094                                 }
00095                         }
00096                         break;
00097 
00098                         // Line status - clear intr by reading line status reg //
00099                         case 0x06:
00100                         (void)inportb(port+5);
00101                         break;
00102                 }
00103         }
00104 }
00105 
00106 /** \ingroup Handlers */
00107 //! \brief The COM1 interrupt handler.
00108 void rs232_handler_port1()
00109 {
00110         rs232_handler(PORT1);
00111 }
00112 
00113 /** \ingroup Handlers */
00114 //! \brief The COM2 interrupt handler.
00115 void rs232_handler_port2()
00116 {
00117         rs232_handler(PORT2);
00118 }
00119 
00120 //! \brief Close a specific RS232 port.
00121 //! \param port The port to close.
00122 void close_rs232(word port)
00123 {
00124         // Turn off rs232 interrupt //
00125         outportb(port+1, 0);
00126 
00127         // Disable the FIFO //
00128         outportb(port+2, 0);
00129 
00130         // Disconnect UART from the ICU //
00131         outportb(port+4, 0);
00132 }
00133 
00134 //! \brief Initialize the RS232 interface with a bps rate.
00135 //! \param port The serial port to initialize.
00136 //! \param divisor The divisor to set the bps rate (see serial.h).
00137 //! \param
00138 void init_rs232(word port, byte divisor)
00139 {
00140         // Install the IRQ handler routine                              //
00141         if ( (port == COM1) || (port == COM3) )
00142                 install_irq_handler( RS232_1_IRQ, (void *)rs232_handler_port1 );
00143         else if ( (port == COM2) || (port == COM4) )
00144                 install_irq_handler( RS232_2_IRQ, (void *)rs232_handler_port2 );
00145         else
00146                 return;
00147 
00148         // Turn off rs232 interrupt //
00149         outportb(port+1, 0);
00150 
00151         // Set DLAB on //
00152         outportb(port+3, 0x80);
00153 
00154         // Set baud rate - Divisor Latch Low Byte //
00155         outportb(port, divisor);
00156         // Set baud rate - Divisor Latch High Byte //
00157         outportb(port+1, 0x00);
00158 
00159         // 8 bits, no parity, 1 stop-bit //
00160         outportb(port+3, 0x03);
00161 
00162         // FIFO Control Register //
00163         outportb(port+2, 0xC7);
00164 
00165         // Turn on DTR, RTS and OUT2 //
00166         outportb(port+4, 0x0B);
00167 
00168         // Enable interrupts when data is received //
00169         outportb(port+1, 0x01);
00170 
00171         // Clear receiver //
00172         (void)inportb(port);
00173         // Clear line status //
00174         (void)inportb(port+5);
00175         // Clear modem status //
00176         (void)inportb(port+6);
00177 }
00178 
00179 //! \brief
00180 //!     Get a character from the current serial port.
00181 //! \return
00182 //!     The ASCII code of the character read.
00183 byte rs232_getchar()
00184 {
00185         byte temp;
00186         dword IF = GET_IF();
00187 
00188         // If the buffer is empty enable interrupts and wait... //
00189         while( rs232_read_head==rs232_read_tail )
00190         {
00191                 enable();
00192                 idle();
00193         }
00194 
00195         disable();
00196 
00197         // Update the RS232 buffer in mutual exclusion & get the char //
00198         temp = rs232_read_buf[rs232_read_head];
00199         rs232_read_head = (rs232_read_head+1)%RS232_BUF_DIM;
00200 
00201         SET_IF(IF);
00202 
00203         return(temp);
00204 }
00205 
00206 //! \brief
00207 //!     Write a character to the current serial port.
00208 //! \param c
00209 //!     The ASCII code of the character to write.
00210 void rs232_putchar(byte c)
00211 {
00212         dword IF = GET_IF();
00213 
00214         // Check if the write buffer is full //
00215         if (((rs232_write_tail+1)%RS232_BUF_DIM) == rs232_write_head) return;
00216 
00217         disable();
00218 
00219         // Write the char to the buffer in mutual exclusion //
00220         rs232_write_buf[rs232_write_tail] = c;
00221         rs232_write_tail = (rs232_write_tail+1)%RS232_BUF_DIM;
00222 
00223         SET_IF(IF);
00224 
00225         // Enable RX & TX interrupts //
00226         outportb(curr_port+1, 0x03);
00227 }
00228 
00229 //! \brief
00230 //!     Get permanently characters from the serial port and print it
00231 //!     to the screen.
00232 //! \note
00233 //!     This is no-exit function, you have to use it not as a simple
00234 //!     procedure, but as a kernel-thread (see task.c).
00235 void rs232_task()
00236 {
00237         while(TRUE)
00238         {
00239                 kputchar(rs232_getchar());
00240         }
00241 }
00242 
00243 //! \brief Chat with a modem or a serial device.
00244 //! \param argv
00245 //!     A string that contains the bps rate to initialize the
00246 //!     serial port connection.
00247 void rs232_chat(char *argv)
00248 {
00249         static char *default_bps = "38400";
00250         task_t *child;
00251         word key;
00252 
00253         kprintf(        "\n\rOpening RS232-1 interface (COM1)... "
00254                 "\n\rPress CTRL-X to quit."     );
00255 
00256         if (strcmp(argv, "2400")==0)            init_rs232(COM1, BPS_2400);
00257         else if (strcmp(argv, "4800")==0)       init_rs232(COM1, BPS_4800);
00258         else if (strcmp(argv, "9600")==0)       init_rs232(COM1, BPS_9600);
00259         else if (strcmp(argv, "19200")==0)      init_rs232(COM1, BPS_19200);
00260         else if (strcmp(argv, "38400")==0)      init_rs232(COM1, BPS_38400);
00261         else if (strcmp(argv, "56700")==0)      init_rs232(COM1, BPS_56700);
00262         else if (strcmp(argv, "115200")==0)     init_rs232(COM1, BPS_115200);
00263         else
00264         {
00265                 // Default bps //
00266                 argv = default_bps;
00267                 init_rs232(COM1, BPS_38400);
00268         }
00269 
00270         kprintf("\n\rBitrate: %s bps", argv);
00271         child = create_process(&rs232_task, &rs232_task, 0, "rs232_task", KERNEL_PRIVILEGE);
00272         set_color(GREEN);
00273         kprintf("\r                                             [ OK ]\n\r");
00274         set_color(DEFAULT_COLOR);
00275 
00276         while(TRUE)
00277         {
00278                 // Get a char from the keyboard //
00279                 key = kgetchar();
00280                 if (key==CTRL_X) break;
00281                 if ((key&0xFF)==13) rs232_putchar(10);
00282 
00283                 // Write the char to the serial port //
00284                 rs232_putchar(key);
00285         }
00286 
00287         // Local echo of the ESCape char //
00288         kputchar((byte)CTRL_X);
00289 
00290         // Kill the rs232 task //
00291         kill(child->pid);
00292 
00293         // Close RS232 connection //
00294         close_rs232(curr_port);
00295         kprintf("\n\rBye.\n\r");
00296 }
00297 
00298 /** @} */ // end of RS232Driver

Generated on Fri Feb 20 15:32:16 2004 for Minirighi by doxygen1.2.18