/* * This file contains the MVME162LX extended console IO package. * * This file was created originally by * On-Line Applications Research Corporation (OAR) * and modified by: * * Katsutoshi Shibuya - BU-Denken Co.,Ltd. - Sapporo, JAPAN * * featuring support of: * * - Multi-SCC chip handling * - Non-blocking I/O (O_NDELAY flag in libc) * - Raw mode device (no CR/LF detection) * - RTS/CTS flow control * * REMARKS: This routine requires multiple interrupt vectors * from SCC_VECTOR (normaly 0x40) * to SCC_VECTOR+(number of SCC chips) * * The original copyright follows; * * COPYRIGHT (c) 1989-1997. * On-Line Applications Research Corporation (OAR). * Copyright assigned to U.S. Government, 1994. * * The license and distribution terms for this file may in * the file LICENSE in this distribution or at * http://www.OARcorp.com/rtems/license.html. * * Modifications of respective RTEMS file: COPYRIGHT (c) 1994. * EISCAT Scientific Association. M.Savitski * * This material is a part of the MVME162 Board Support Package * for the RTEMS executive. Its licensing policies are those of the * RTEMS above. * */ #define M162_INIT #include "consolex.h" #include #include #include #include #include #define NPORTS 4 /* Number of ports */ #define DEVICEPREFIX "tty" #define RAWDEVICEPREFIX "rtty" #define CONSOLEPORT 0 /* port# of console: undef this if you do not want /dev/console */ #define READRETRY 1 /* Maximum retry count for read one char */ #define WRITERETRY 8 /* Maximum retry count for write one char */ #define PORTFROM 0 /* for debug */ static unsigned char opencount[NPORTS]; /*********************************************************************** Ring buffer for device ***********************************************************************/ #define QUEUE_LENGTH 128 /* Must be 2^n number */ typedef struct { char buffer[QUEUE_LENGTH]; volatile int head; volatile int tail; } ReceiverBuffer; #define ReceiverBufferInitialize( _buffer ) \ do { \ (_buffer)->head = (_buffer)->tail = 0; \ } while ( 0 ) #define ReceiverBufferIsEmpty( _buffer ) \ ( (_buffer)->tail == (_buffer)->head ) #define ReceiverBufferIsNearEmpty( _buffer ) \ ( (_buffer)->tail == (((_buffer)->head + 3) & (QUEUE_LENGTH-1)) ) #define ReceiverBufferIsFull( _buffer ) \ ( (_buffer)->head == (((_buffer)->tail + 1) & (QUEUE_LENGTH-1)) ) #define ReceiverBufferIsNearFull( _buffer ) \ ( (_buffer)->head == (((_buffer)->tail + 3) & (QUEUE_LENGTH-1)) ) #define ReceiverBufferAdd( _buffer, _ch ) \ do { \ rtems_unsigned32 isrlevel; \ \ rtems_interrupt_disable( isrlevel ); \ (_buffer)->tail = ((_buffer)->tail+1) & (QUEUE_LENGTH-1); \ (_buffer)->buffer[ (_buffer)->tail ] = (_ch); \ rtems_interrupt_enable( isrlevel ); \ } while ( 0 ) #define ReceiverBufferRemove( _buffer, _ch ) \ do { \ rtems_unsigned32 isrlevel; \ \ rtems_interrupt_disable( isrlevel ); \ (_buffer)->head = ((_buffer)->head+1) & (QUEUE_LENGTH-1); \ (_ch) = (_buffer)->buffer[ (_buffer)->head ]; \ rtems_interrupt_enable( isrlevel ); \ } while ( 0 ) /*********************************************************************** DEVICE DEPENDED PART ***********************************************************************/ /* Time constant parameters CAUTION: These parameters are for MVME162LX-213 board. */ #define TC38400 0x0006 #define TC19200 0x000e #define TC9600 0x001e /* Re-defining SCC register control macros to support Multi SCC chips */ #undef scc #if defined(mvme162lx) static scc_regs *scc[NPORTS] = { ((scc_regs * const) 0xFFF45004), ((scc_regs * const) 0xFFF45000), ((scc_regs * const) 0xFFF45804), ((scc_regs * const) 0xFFF45800) }; #else /* XXX fix me */ #warning "MVME162 BSP -- unknown address for SCC's" #endif #undef ZWRITE0 #define ZWRITE0(port, v) (scc[port]->csr = (unsigned char)(v)) #undef ZREAD0 #define ZREAD0(port) (scc[port]->csr) #undef ZREAD #define ZREAD(port, n) (ZWRITE0(port, n), (scc[port]->csr)) #undef ZREADD #define ZREADD(port) (scc[port]->csr=0x08, scc[port]->csr ) #undef ZWRITE #define ZWRITE(port, n, v) (ZWRITE0(port, n), ZWRITE0(port, v)) #undef ZWRITED #define ZWRITED(port, v) (scc[port]->csr = 0x08, \ scc[port]->csr = (unsigned char)(v)) static ReceiverBuffer receiverBuffer[NPORTS]; /* * Control flags (DTR/DCD/RTS/CTS) */ static unsigned char wr4[NPORTS]; static unsigned char wr5[NPORTS]; #define SCCSetDTR(port) ZWRITE(port, 5, (wr5[port] |= 0x80)) #define SCCResetDTR(port) ZWRITE(port, 5, (wr5[port] &= ~0x80)) #define SCCSetRTS(port) ZWRITE(port, 5, (wr5[port] |= 0x02)) #define SCCResetRTS(port) ZWRITE(port,5, (wr5[port] &= ~0x02)) #define SCCGetDCD(port) (ZREAD0(port)&0x08) #define SCCGetCTS(port) (ZREAD0(port)&0x20) /* * Interrupt handler for receiver interrupts */ static rtems_isr SCCReceiverISR(rtems_vector_number vector) { register int ipend, port; port = (vector-SCC_VECTOR)*2; ZWRITE0(port, 0x38); /* reset highest IUS */ ipend = ZREAD(port, 3); /* read int pending from A side */ if(ipend == 0x04) port++; /* channel B intr pending */ else if(ipend == 0x20) ; /* channel A intr pending */ else return; ReceiverBufferAdd(&receiverBuffer[port], ZREADD(port)); if(ZREAD(port,1) & 0x70){ /* check error stat */ ZWRITE0(port, 0x30); /* reset error */ } if(ReceiverBufferIsNearFull(&receiverBuffer[port])) SCCResetRTS(port); } /* * Initialize */ void SCCInitialize() { int i; for(i = PORTFROM; i < NPORTS; i+=2) ZWRITE(i, 9,0xc0); /* Reset SCC Chip */ for(i = PORTFROM; i < NPORTS; i++){ ReceiverBufferInitialize(&(receiverBuffer[i])); wr4[i] = 0x44; ZWRITE(i, 4, wr4[i]); /* x16 clock, 1 stop, parity none */ ZWRITE(i, 1, 0); /* disable interrupts */ ZWRITE(i, 2, SCC_VECTOR+(i/2)); ZWRITE(i, 3, 0xc1); /* receiver enable, 8bits */ wr5[i] = 0x68; ZWRITE(i, 5, wr5[i]); /* transmitter enable, 8bits, DTR&RTS off */ ZWRITE(i,14, 0); /* stop baudrate gen. */ ZWRITE(i,11,0x50); /* use baurate gen. */ ZWRITE(i,15, 1); /* Select WR7' */ ZWRITE(i, 7, 0); /* Disable all special interrupts */ ZWRITE(i,10, 0); ZWRITE(i, 1, 0x10); /* int on all Rx chars or special condition */ set_vector(SCCReceiverISR, SCC_VECTOR+(i/2), 1); /* install ISR */ ZWRITE(i, 9, 8); /* master interrupt enable */ ZWRITE(i,12,TC38400&0xff); /* set 38400 baud */ ZWRITE(i,13,TC38400>>8); ZWRITE(i,14, 3); /* start baudrate gen. */ /* CAUTION: If your SCC use XTAL on RTxC, write 1 */ } mcchip->vector_base = 0; mcchip->gen_control = 2; /* MIEN */ mcchip->SCC_int_ctl = 0x13; /* SCC IEN, IPL3 */ } /* * Non-blocking char input */ rtems_boolean SCCGetOne(int port, char *ch) { int retry = READRETRY; while(ReceiverBufferIsEmpty(&receiverBuffer[port])) if(--retry <= 0) return FALSE; ReceiverBufferRemove(&receiverBuffer[port],*ch); if(ReceiverBufferIsNearEmpty(&receiverBuffer[port])) SCCSetRTS(port); return TRUE; } /* * Blocking char input */ char SCCGetOneBlocked(int port) { unsigned char tmp_char; while (!SCCGetOne(port, &tmp_char)) rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); return tmp_char; } /* * Non-blocking char input * No longer supports XON/XOFF flow control. */ rtems_boolean SCCSendOne(int port, char ch) { int retry = WRITERETRY; if(!SCCGetCTS(port)) return FALSE; while(!(ZREAD0(port) & TX_BUFFER_EMPTY)) if(--retry <= 0) return FALSE; ZWRITED(port, ch); return TRUE; } /* * Blocking char output * No longer supports XON/XOFF flow control. */ void SCCSendOneBlocked(int port, char ch) { while(!SCCSendOne(port, ch)) rtems_task_wake_after(RTEMS_YIELD_PROCESSOR); } /* * Set parameters for transmission */ unsigned32 SCCSetAttributes(int port, struct termios *t) { unsigned char wr3; ZWRITE(port,1,0); /* Disable interrupt */ ZWRITE(port,3,0); /* Disable receiver */ /* Baud */ switch(t->c_cflag & CBAUD){ case B38400: ZWRITE(port,12,TC38400&0xff); ZWRITE(port,13,TC38400>>8); break; case B19200: ZWRITE(port,12,TC19200&0xff); ZWRITE(port,13,TC19200>>8); break; case B9600: ZWRITE(port,12,TC9600&0xff); ZWRITE(port,13,TC9600>>8); break; } /* Code size */ wr5[port] &= 0x8f; wr3 = 0; /* receiver control */ switch(t->c_cflag & CSIZE){ case CS8: wr5[port] |= 0x60; wr3 |= 0xc0; break; case CS7: wr5[port] |= 0x20; wr3 |= 0x40; break; } /* Parity */ wr4[port] &= 0xf0; if(t->c_cflag & PARENB) wr4[port] |= 0x01; if(!(t->c_cflag & PARODD)) wr4[port] |= 0x02; /* ZWRITE(port,4,wr4[port]);*/ /* Stop bits */ /* wr4[port] = ZREAD(port,4) & 0xfc;*/ if(t->c_cflag & CSTOPB) wr4[port] |= 0x0c; else wr4[port] |= 0x04; ZWRITE(port,4,wr4[port]); /* TxRx parameters */ ZWRITE(port,5,wr5[port]); /* Transmission parameters */ ZWRITE(port,3,wr3|0x01); /* Enable receiver */ ZWRITE(port,1,0x10); /* Enable interrupt */ return 0; } /* * Get parameters for transmission */ unsigned32 SCCGetAttributes(int port, struct termios *t) { unsigned32 b; t->c_cflag = 0; /* Baud */ b = ZREAD(port,13); b <<= 8; b |= ZREAD(port,12); switch(b){ case TC38400: t->c_cflag |= B38400; break; case TC19200: t->c_cflag |= B19200; break; case TC9600: t->c_cflag |= B9600; break; } /* Code size */ /* wr = ZREAD(port,5);*/ t->c_cflag &= ~CSIZE; switch(wr5[port]&0x60){ case 0x60: t->c_cflag |= CS8; break; case 0x20: t->c_cflag |= CS7; break; } /* Parity */ /* wr = ZREAD(port,4);*/ if(wr4[port] & 0x01) t->c_cflag |= PARENB; else t->c_cflag &= ~PARENB; if(wr4[port] & 0x02) t->c_cflag &= ~PARODD; else t->c_cflag |= PARODD; /* Stop bits */ /* wr = ZREAD(port,4);*/ if((wr4[port]&0xc0) == 0xc0) t->c_cflag |= CSTOPB; else t->c_cflag &= ~CSTOPB; return 0; } /*********************************************************************** DEVICE INDEPENDED PART ***********************************************************************/ #define LOCAL_ISTRIP 0x0001 #define LOCAL_INLCR 0x0002 #define LOCAL_IGNCR 0x0004 #define LOCAL_ICRNL 0x0008 #define LOCAL_IUCLC 0x0010 #define LOCAL_OLCUC 0x0020 #define LOCAL_ONLCR 0x0040 #define LOCAL_OCRNL 0x0080 #define LOCAL_ICANON 0x0100 #define LOCAL_ECHO 0x0200 /* * Device initialize entry point */ rtems_device_driver consolex_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_status_code status; char devname[16]; int i; SCCInitialize(); #ifdef CONSOLEPORT status = rtems_io_register_name("/dev/console",major, (rtems_device_minor_number) CONSOLEPORT); if (status != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred(status); #endif for(i = PORTFROM; i < NPORTS; i++){ /* Register cooked ttys */ sprintf(devname,"/dev/%s%02d",DEVICEPREFIX,i); status = rtems_io_register_name(strdup(devname),major, (rtems_device_minor_number) i); if (status != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred(status); /* Register raw ttys */ sprintf(devname,"/dev/%s%02d",RAWDEVICEPREFIX,i); status = rtems_io_register_name(strdup(devname),major, (rtems_device_minor_number) i+NPORTS); if (status != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred(status); } for(i = 0; i < NPORTS; i++){ opencount[i] = 0; } return RTEMS_SUCCESSFUL; } /* * Open entry point */ rtems_device_driver consolex_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_libio_open_close_args_t *openargs = (rtems_libio_open_close_args_t *) arg; if(minor >= NPORTS) minor -= NPORTS; if(minor >= NPORTS) return RTEMS_INVALID_NUMBER; if(opencount[minor]++ == 0){ /* first open */ SCCSetDTR(minor); SCCSetRTS(minor); } openargs->iop->data0 = LOCAL_ICRNL|LOCAL_ONLCR|LOCAL_ICANON|LOCAL_ECHO; return RTEMS_SUCCESSFUL; } /* * Close entry point */ rtems_device_driver consolex_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { if(minor >= NPORTS) minor -= NPORTS; if(minor >= NPORTS) return RTEMS_INVALID_NUMBER; if(--(opencount[minor]) == 0){ /* closed all */ SCCResetRTS(minor); SCCResetDTR(minor); } return RTEMS_SUCCESSFUL; } /* * read bytes from the serial port. */ rtems_device_driver consolex_read_raw(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg; char *buffer; int count; if(minor >= NPORTS) return RTEMS_INVALID_NUMBER; buffer = rw_args->buffer; count = rw_args->count; if(rw_args->flags & LIBIO_FLAGS_NO_DELAY){ /* Non blocking read */ while(count){ if(!SCCGetOne(minor,buffer)) break; buffer++; count--; } }else{ /* Blocking read */ while(count){ *buffer = SCCGetOneBlocked(minor); buffer++; count--; } } rw_args->bytes_moved = rw_args->count-count; return count ? RTEMS_UNSATISFIED : RTEMS_SUCCESSFUL; } rtems_device_driver consolex_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg; char *buffer; int count; unsigned32 mode; if(minor >= NPORTS) return consolex_read_raw(major,minor-NPORTS,arg); buffer = rw_args->buffer; count = rw_args->count; mode = rw_args->iop->data0; /* Cooked read */ while(count){ if(rw_args->flags & LIBIO_FLAGS_NO_DELAY){ /* Non blocking read */ if(!SCCGetOne(minor,buffer)) break; }else{ /* Blocking read */ *buffer = SCCGetOneBlocked(minor); } if((mode&LOCAL_ICANON) && (*buffer == '\b')){ if(buffer > (char *)(rw_args->buffer)){ buffer--; count++; if(mode&LOCAL_ECHO){ SCCSendOneBlocked(minor,'\b'); SCCSendOneBlocked(minor,' '); SCCSendOneBlocked(minor,'\b'); } } continue; } if((mode&LOCAL_IGNCR) && (*buffer == '\r')) continue; if((mode&LOCAL_INLCR) && (*buffer == '\n')) *buffer = '\r'; if((mode&LOCAL_ICRNL) && (*buffer == '\r')) *buffer = '\n'; if((mode&LOCAL_IUCLC) && isupper((int)*buffer)) *buffer = tolower(*buffer); if(mode&LOCAL_ISTRIP) *buffer &= 0x7f; if(mode&LOCAL_ECHO){ /* Caution: Echo back is blocking output */ SCCSendOneBlocked(minor,*buffer); } if((mode&LOCAL_ICANON) && (*buffer == '\n')){ buffer++; count--; if(count) *buffer = 0; if((mode&LOCAL_ECHO)&&(mode&LOCAL_ONLCR)) SCCSendOneBlocked(minor,'\r'); break; /* finish reading */ } buffer++; count--; } rw_args->bytes_moved = rw_args->count-count; return count ? RTEMS_UNSATISFIED : RTEMS_SUCCESSFUL; } /* * write bytes to the serial port. */ rtems_device_driver consolex_write_raw(rtems_device_major_number major, rtems_device_minor_number minor, void * arg) { rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg; char *buffer; int count; if(minor >= NPORTS) return RTEMS_INVALID_NUMBER; buffer = rw_args->buffer; count = rw_args->count; if(rw_args->flags & LIBIO_FLAGS_NO_DELAY){ /* Non blocking write */ while(count){ if(!SCCSendOne(minor,*buffer)) break; buffer++; count--; } }else{ /* Blocking write */ while(count){ SCCSendOneBlocked(minor,*buffer); buffer++; count--; } } rw_args->bytes_moved = rw_args->count-count; return count ? RTEMS_UNSATISFIED : RTEMS_SUCCESSFUL; } rtems_device_driver consolex_write(rtems_device_major_number major, rtems_device_minor_number minor, void * arg) { rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg; char *buffer; int count; char ch; unsigned32 mode; if(minor >= NPORTS) return consolex_write_raw(major,minor-NPORTS,arg); buffer = rw_args->buffer; count = rw_args->count; mode = rw_args->iop->data0; /* Cooked write */ while(count){ ch = *buffer; if((mode&LOCAL_ONLCR) && (ch == '\n')){ /* Output CRLF */ if(rw_args->flags & LIBIO_FLAGS_NO_DELAY){ /* Non blocking write */ if(!SCCSendOne(minor,'\r')) break; }else{ SCCSendOneBlocked(minor,'\r'); } } if((mode&LOCAL_OCRNL) && (ch == '\r')) ch = '\n'; if((mode&OLCUC) && (islower((int)ch))) ch = toupper(ch); if(rw_args->flags & LIBIO_FLAGS_NO_DELAY){ /* Non blocking write */ if(!SCCSendOne(minor,ch)) break; }else{ SCCSendOneBlocked(minor,ch); } buffer++; count--; } rw_args->bytes_moved = rw_args->count-count; return count ? RTEMS_UNSATISFIED : RTEMS_SUCCESSFUL; } /* * IO Control entry point */ rtems_device_driver consolex_control(rtems_device_major_number major, rtems_device_minor_number minor, void * arg) { rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; struct termios *tm = ioarg->buffer; unsigned32 *mode = &(ioarg->iop->data0); if(minor >= NPORTS) minor -= NPORTS; if(minor >= NPORTS) return RTEMS_INVALID_NUMBER; switch(ioarg->command){ case RTEMS_IO_GET_ATTRIBUTES: tm->c_iflag = tm->c_oflag = tm->c_cflag = tm->c_lflag = 0; if(*mode & LOCAL_ISTRIP) tm->c_iflag |= ISTRIP; if(*mode & LOCAL_INLCR) tm->c_iflag |= INLCR; if(*mode & LOCAL_IGNCR) tm->c_iflag |= IGNCR; if(*mode & LOCAL_ICRNL) tm->c_iflag |= ICRNL; if(*mode & LOCAL_IUCLC) tm->c_iflag |= IUCLC; if(*mode & LOCAL_OLCUC) tm->c_oflag |= OLCUC; if(*mode & LOCAL_ONLCR) tm->c_oflag |= ONLCR; if(*mode & LOCAL_OCRNL) tm->c_oflag |= OCRNL; if(*mode & LOCAL_ICANON) tm->c_lflag |= ICANON; if(*mode & LOCAL_ECHO) tm->c_lflag |= ECHO; ioarg->ioctl_return = SCCGetAttributes(minor,tm); break; case RTEMS_IO_SET_ATTRIBUTES: *mode = 0; if(tm->c_iflag & ISTRIP) *mode |= LOCAL_ISTRIP; if(tm->c_iflag & INLCR) *mode |= LOCAL_INLCR; if(tm->c_iflag & IGNCR) *mode |= LOCAL_IGNCR; if(tm->c_iflag & ICRNL) *mode |= LOCAL_ICRNL; if(tm->c_iflag & IUCLC) *mode |= LOCAL_IUCLC; if(tm->c_oflag & OLCUC) *mode |= LOCAL_OLCUC; if(tm->c_oflag & ONLCR) *mode |= LOCAL_ONLCR; if(tm->c_oflag & OCRNL) *mode |= LOCAL_OCRNL; if(tm->c_lflag & ICANON) *mode |= LOCAL_ICANON; if(tm->c_lflag & ECHO) *mode |= LOCAL_ECHO; ioarg->ioctl_return = SCCSetAttributes(minor,tm); break; default: return RTEMS_NOT_DEFINED; } return RTEMS_SUCCESSFUL; }