vxb@mhuxh.UUCP (Vern Bradner) (12/22/87)
Can anyone share their knowledge/experiences in writing C routines to communicate with remote systems via the PC serial port? I tried inp and outp without much success. Are there any C libraries that might make this job easier? I have seen requests for this information recently, but I have not seen the answers posted (I will post a summary if anyone is interested). Thanks in advance for any help. Vern Bradner ihnp4!mhuxh!vxb
osm@metavax.UUCP (Owen Scott Medd) (12/23/87)
In article <991@mhuxh.UUCP> vxb@mhuxh.UUCP (Vern Bradner) writes: >Can anyone share their knowledge/experiences in writing C routines to >communicate with remote systems via the PC serial port? > >I tried inp and outp without much success. Are there any C libraries >that might make this job easier? I've just glanced at the documentation on writing interrupt handlers in Microsoft C 5.0. This looks like a very nice facility to avoid coding serial drivers in assembler. Has anyone made an attempt to rewrite any of the public domain serial drivers in MSC5.0? [ It's one item on my list of things to do in my copious spare time :-> ] Owen -- USMail: Meta Systems, Ltd. 315 East Eisenhower Parkway, Ann Arbor, MI 48108 Phone: +1 313 663 6027 UUCP: uunet!umix!metavax!osm Internet: osm%metavax.uucp@umix.cc.umich.edu
manes@dasys1.UUCP (Steve Manes) (12/24/87)
In article <991@mhuxh.UUCP> vxb@mhuxh.UUCP (Vern Bradner) writes: >Can anyone share their knowledge/experiences in writing C routines to >communicate with remote systems via the PC serial port? > >I tried inp and outp without much success. Are there any C libraries >that might make this job easier? Writing code to control the PC's serial port can range from fairly simple to gruesomely elaborate depending on what you want to do. However, in most applications you want to avoid the IBM's built-in ROM BIOS serial support because it doesn't support interrupt-driven character receipt, i.e. it doesn't buffer. If you're not all that experienced writing code for hardware and particularly the PC's 8250/16450 UART and 8259 interrupt controller the first thing you'll need to do is study how they work. <PC Tech> did an excellent series on the IBM hardware interrupt structure several years ago. Another good source of reference is <The IBM Personal Computer for the Inside Out> by Sargent & Shoemaker (Addison-Wesley). The 8250 can be tricky to work with if you're starting out with a blank page. I probably spent 6 months tweaking and refining (and fixing many subtle bugs) in my comm library. You can save yourself a lot of aggravation by purchasing the Greenleaf comm library, or equivalent. The downside is that you'll lose a valuable education in hardware programming. The upside: you'll sleep better knowing that you've got a predebugged library to work with. You can't just pop bits in and out of the comm port if you want an effective serial device controller. The UART has to be prepared and initialized beforehand. The Sargent/Shoemaker book explains this well. -- +----------------------------------------------------------------------- + Steve Manes Roxy Recorders, Inc. NYC + decvax!philabs!cmcl2!hombre!magpie!manes Magpie BBS: 212-420-0527 + uunet!iuvax!bsu-cs!zoo-hq!magpie!manes 300/1200/2400
dipto@umbc3.UMD.EDU (Dipto Chakravarty) (12/25/87)
In article <2574@metavax.UUCP> osm@metavax.UUCP (Owen Scott Medd) writes: > >In article <991@mhuxh.UUCP> vxb@mhuxh.UUCP (Vern Bradner) writes: >>Can anyone share their knowledge/experiences in writing C routines to >>communicate with remote systems via the PC serial port? >> >I've just glanced at the documentation on writing interrupt handlers in >Microsoft C 5.0. This looks like a very nice facility to avoid coding >serial drivers in assembler. Has anyone made an attempt to rewrite any >of the public domain serial drivers in MSC 5.0? > I am currently working on an interrupt driven multi-station LAN, using the MSC compiler. So far the project has been exceedingly interesting. Since we havenot yet received the 5.0 we were compelled to mess around with assembly. I am eagerly awaiting for the arrival of 5.0 which is SUPPOSED to be real good for developing serial communications related softwares. I would really like to hear from our readers about their experiences with serial communications and PCs. My favorite questions to our readers will be the following :- * Can a RAM resident program be written completely in C (MSC) ? If yes, then pplleeaassee email me a code segment of it! * Does anyone know of a serial driver which has been developed in C without having to resort to assembly at all. Happy holidays to all of our readers ! Dipto -- BITNET : dipto@umbc2 ------\ ARPANET: dipto@umbc3.UMD.EDU -------> In-real-life: Dipto Chakravarty USMAIL : CMSC, UMBC,Md 21228 ------/
pjh@mccc.UUCP (Peter J. Holsberg) (12/27/87)
There is an excellent book by Joe Campbell -- "C Programmer's Guide to Serial Communications." It covers IBM PC serial port (as well s the Kaypro CP/M machine serial port). -- Peter Holsberg UUCP: {rutgers!}princeton!mccc!pjh Technology Division CompuServe: 70240,334 Mercer College GEnie: PJHOLSBERG Trenton, NJ 08690 Voice: 1-609-586-4800
tommie@psivax.UUCP (Tom Levin) (01/07/88)
In article <991@mhuxh.UUCP> vxb@mhuxh.UUCP (Vern Bradner) writes: >Can anyone share their knowledge/experiences in writing C routines to >communicate with remote systems via the PC serial port? > I have used the Greenleaf Communications package with complete success on two different projects. The package contains a set of linkable libraries for a specific C compiler (I have the Turbo C version), along with a very complete manual. This package has routines for just about anything you would ever want to do through a serial port including full Hayes modem support. It can handle something like 16 serial ports at 9600 baud. I bought my copy from the Programmers Connection for about $100.00 - $125.00. (can't exactly remember...drain bramage!) All in all, this is one of the finest tools I own! Buy it! -- Tom Levin (The Robo-Programmer) {ihnp4|sdcrdcf|ttidca|scgvaxd|nrcvax| part man, part machine... jplpro|hoptoad |csun|quad1|bellcore| all hacker! logico|rdlvax}!psivax!tommie
Usenet_area_"Cs.I.Pc"@watmath.waterloo.edu (01/07/88)
From Usenet: seismo!uunet!psivax!tommie From: tommie@psivax.UUCP (Tom Levin) Newsgroups: comp.sys.ibm.pc Subject: Re: Serial Communications in C Keywords: Greenleaf Communications Package Message-ID: <1975@psivax.UUCP> Date: 6 Jan 88 22:32:06 GMT References: <991@mhuxh.UUCP> Reply-To: tommie@psivax.UUCP (Tom Levin) Organization: Pacesetter Systems Inc., Sylmar, CA Lines: 22 In article <991@mhuxh.UUCP> vxb@mhuxh.UUCP (Vern Bradner) writes: >Can anyone share their knowledge/experiences in writing C routines to >communicate with remote systems via the PC serial port? > I have used the Greenleaf Communications package with complete success on two different projects. The package contains a set of linkable libraries for a specific C compiler (I have the Turbo C version), along with a very complete manual. This package has routines for just about anything you would ever want to do through a serial port including full Hayes modem support. It can handle something like 16 serial ports at 9600 baud. I bought my copy from the Programmers Connection for about $100.00 - $125.00. (can't exactly remember...drain bramage!) All in all, this is one of the finest tools I own! Buy it! -- Tom Levin (The Robo-Programmer) {ihnp4|sdcrdcf|ttidca|scgvaxd| nrcvax| part man, part machine... jplpro|hoptoad |csun|quad1| bellcore| all hacker! logico|rdlvax}!psivax!tommie --- via UGate v1.6 * Origin: watmath (221/163)
nev@edison.GE.COM (Niles VanDenburg) (01/11/88)
In article <991@mhuxh.UUCP>, vxb@mhuxh.UUCP (Vern Bradner) writes: > Can anyone share their knowledge/experiences in writing C routines to > communicate with remote systems via the PC serial port? since this discussion has sort of degenerated of late I would like to fire it back up a little by actually supplying a C code segment that I wrote a while ago to handle XON/XOFF serial communications in C. WARNING: this code segment has only been used in one program and is being presented here only for its educational value and to promote discussion about serial interfaces in C for an IBM-PC. Any other use of the code hereby presented is not authorized. This code is presented ASIS. There is no representation that this is good code. In fact it is recommended that every one who uses this code for unauthorized uses realizes that he/she is taking total responsibility for any liability thereby resulting. Now with that out of the way here is the code (~350 lines follow): /* * compiler: Turbo C version 1.0 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <dos.h> #include <bios.h> #define TRUE 1 #define FALSE 0 #define XOFF 0x13 /* XOFF/DC3 character */ #define XON 0x11 /* XON/DC1 character */ #define MODEM_CONTROL 0x0b /* value of the ACE's modem control */ /* register: RTS, DTR, and OUT2 true */ /* OUT2 required to get interrupts */ #define TX_SIZE 64 /* transmit circular buffer size */ #define RX_SIZE 1024 /* receive circular buffer size */ #define INT_MASK_REG 0x21 /* I/O port 8259 interrupt controller */ #define COMM_PORT_INIT 0xe3 /* default initialization for ACE : */ /* 9600 BPS, no parity, 1 stop, */ /* 8 bits/char See BIOS for details */ char volatile tx_buf[TX_SIZE]; /* circular buffer sending chars */ char volatile *tx_buf_head; /* ptr to next char to send */ char volatile *tx_buf_tail; /* ptr to next avail loc for insert */ char volatile *tx_buf_end; /* ptr to last possible loc in buffer */ char volatile rx_buf[RX_SIZE]; /* circular buf for input chars */ char volatile *rx_buf_head; /* ptr to next char to remove */ char volatile *rx_buf_tail; /* ptr to next avail loc for insert */ char volatile *rx_buf_end; /* ptr to last possible loc in buffer */ char volatile xoff_sent = FALSE; /* flag set to indicate an Xon should */ /* be sent when data buffer has more */ /* space (can be set by ISR) */ char xoff_enable = TRUE; /* flag used to indicate Xon/Xoff */ /* s/w flow control is permitted */ /* default set TRUE only set FALSE */ /* when in file transfer mode */ int volatile num_in_tx_buf = 0; /* count of characters being held in */ /* in the transmit FIFO */ /* used to determine if possible to */ /* put another character into FIFO */ int volatile num_in_rx_buf = 0; /* count of characters being held in */ /* in the receive FIFO */ /* used to determine when S/W flow */ /* control characters should be sent */ char com1 = TRUE; /* use com port 1 flag */ char com_init = COMM_PORT_INIT; /* value used for BIOS com init int */ char com_div = FALSE; /* flag to ind. BRG divisor supplied */ int com_div_value; /* value of supplied BRG divisor */ int data_reg = 0x3f8; /* I/O port for ACE data register */ int int_en_reg = 0x3f9; /* ACE interrupt enable register port */ int int_id_reg = 0x3fa; /* ACE interrupt ID register port */ int line_con_reg = 0x3fb; /* ACE line control register port */ int modem_con_reg = 0x3fc; /* ACE modem control register port */ int line_sts_reg = 0x3fd; /* ACE line status register port */ int modem_sts_reg = 0x3fe; /* ACE modem status register port */ int int_mask = 0x10; /* 8259 interrupt controller mask for */ /* com port */ int dos_int = 0x0c; /* int number for com ints */ /* NOTE above values are default for */ /* COM1: */ union REGS rg; /* structure used for int86 calls */ /* to pass register values */ /* * ISR for serial interrupts from Asynch. Comm. Element (ACE) - gets interrupt * ID and if no interrupts are active sends EOI code to 8259 to permit further * interrupts and then returns to interrupted routine. Otherwise if * interrupt ID indicates transmitter holding register empty send Xoff if * appropriate or if no more data to be sent turn off transmitter holding * register empty interrupt enable or if data to be sent send next byte and * adjust tail pointer (including possible wrap around of circular queue) * and decrement number in transmit queue. Otherwise it is assumed that * the interrupt ID register indicates that received data character is * available and the character is read into the received data circular queue * and that queue's head pointer is adjusted (including wraparound) and * the number in received data queue is incremented. If less than 16 * spaces are left in the received data queue then the sent Xoff flag is * set and depending on whether the transmitter holding register is empty * or not either an Xoff is sent to the ACE or the send Xoff flag is set * so that the next transmitter holding register empty interrupt shall cause * the Xoff to be sent. For efficiency the ISR stays active until all * sources of interrupts are processed. */ void interrupt proc_intr() { static char send_xoff = FALSE; register unsigned char i; top: disable(); if ((i = inportb(int_id_reg)) == 1) { outportb(0x20, 0x20); /* send EOI to interrupt controller */ return; } enable(); /* permit higher priority ints. */ if (i == 2) { /* Tx interrupt ? */ if (inportb(line_sts_reg) & 0x20) { /* THRE ? needed on XT only */ if (xoff_enable && send_xoff) { /* need to send Xon ? */ outportb(data_reg, XOFF); send_xoff = FALSE; } else if (tx_buf_head == tx_buf_tail) { /* more to send ? */ outportb(int_en_reg, 1); /* no - reset interrupt enable */ } else { outportb(data_reg, *tx_buf_tail++); /* yes - send it */ if (tx_buf_tail == tx_buf_end) { /* adjust tail ptr ? */ tx_buf_tail = tx_buf; } --num_in_tx_buf; } } goto top; } else if (i == 4) { /* Rx interrupt ? */ *rx_buf_head++ = inportb(data_reg); /* get data from ACE */ if (rx_buf_head == rx_buf_end) { /* adjust head pointer ? */ rx_buf_head = rx_buf; } ++num_in_rx_buf; /* need send Xoff ? */ if (xoff_enable && (num_in_rx_buf > RX_SIZE - 16)) { if (inportb(line_sts_reg) & 0x20) { /* THRE ? ok send immed. ? */ outportb(data_reg, XOFF); /* yes send it */ } else { send_xoff = TRUE; /* no flg send upon THRE int. */ } xoff_sent = TRUE; /* tell BG to send Xon when ok */ } goto top; } else { /* other sources of interrupt (should never occur) */ inportb(modem_sts_reg); /* clears modem status interrupt */ inportb(line_sts_reg); /* clears rec. line status interrupt */ goto top; } } /* * send a character to serial interface (COM1/2), if transmit buffer full * delay until space is available in the buffer, put character in circular * queue at the head pointer then increment the head pointer adjusting * for possible wraparound of the pointer, increment the number of * characters in the output queue, make sure that transmitter interrupts are * enabled as well as receive interrupts (which are never turned off) */ void send_char(ch) char ch; { while (disable(), num_in_tx_buf >= TX_SIZE - 2) { enable(); disable(); } *tx_buf_head++ = ch; if (tx_buf_head == tx_buf_end) { tx_buf_head = tx_buf; } ++num_in_tx_buf; outportb(int_en_reg, 3); /* insure tx interrupts are enabled */ enable(); } /* * get a character from the receive queue if any, return TRUE if character * found, FALSE if no character available. If Xoff previously sent see if * adequate space is now available in the receive queue so that character * reception can now resume and if so then an Xon is sent. */ int receive_char(chp) char *chp; { register char c; if (num_in_rx_buf) { disable(); c = *rx_buf_tail++; if (rx_buf_tail == rx_buf_end) { rx_buf_tail = rx_buf; } --num_in_rx_buf; enable(); if (xoff_sent && (num_in_rx_buf < (RX_SIZE - 64))) { send_char(XON); xoff_sent = FALSE; } *chp = (char) (c & char_mask); return (TRUE); } else { return (FALSE); } } /* * Initialize the tx/rx buffers, UART, and interrupt vectors for UART * Note that the COM1/2 interrupt vector is assumed to be disposable. * Note in the UART cleanup the setting of OUT2 is required or else no * interrupts are ever generated by the async. card. Initialization * finishes by sending an Xon to the serial interface to re-enable data * transfers from the remote unit. */ void init() { register int i; /* setup FIFO buffers */ tx_buf_head = tx_buf_tail = tx_buf; tx_buf_end = tx_buf + TX_SIZE - 1; rx_buf_head = rx_buf_tail = rx_buf; rx_buf_end = rx_buf + RX_SIZE - 1; /* initialize UART register pointers */ if (!com1) { /* only change if not COM1 */ data_reg = 0x2f8; int_en_reg = 0x2f9; int_id_reg = 0x2fa; line_con_reg = 0x2fb; modem_con_reg = 0x2fc; line_sts_reg = 0x2fd; modem_sts_reg = 0x2fe; int_mask = 0x08; dos_int = 0x0b; } /* initialize UART */ rg.h.ah = 0; rg.h.al = com_init; rg.x.dx = 1 - com1; int86(0x14, &rg, &rg); /* cleanup UART setup */ disable(); if (com_div) { i = inportb(line_con_reg) | 0x80; /* insure div latch on */ outportb(line_con_reg, i); outportb(data_reg, com_div_value); /* send divisor value to ACE */ outportb(int_en_reg, com_div_value >> 8); } i = inportb(line_con_reg) & 0x7f; /* insure div latch off */ outportb(line_con_reg, i); outportb(modem_con_reg, MODEM_CONTROL); /* set RTS, DTR, OUT2 */ inportb(line_sts_reg); /* clear status reg */ inportb(data_reg); /* discard any char rcvd */ i = inportb(INT_MASK_REG) & ~int_mask; /* enable intr on 8259 */ outportb(INT_MASK_REG, i); outportb(int_en_reg, 1); /* enable rx data ints */ enable(); /* setup linkage to COM1/2 interrupt routine */ setvect(dos_int, proc_intr); /* send Xon in case link is waiting on software flow control */ send_char(XON); } /* * Restore machine state upon exit, send Xoff to tell remote unit * to stop sending data and kill interrupt processing on the async * interface */ void restore() { register int temp; send_char(XOFF); /* tell other end to stop sending */ /* disable interupt processing on COM port */ disable(); /* disable intr on 8259 */ temp = inportb(INT_MASK_REG) | int_mask; outportb(INT_MASK_REG, temp); outportb(int_en_reg, 0); /* disable recv data int */ outportb(modem_con_reg, 0); /* reset RTS, DTR, OUT2 */ enable(); } /* * find and read initialization parameters from a user specified file * given on the command line. The file must have the following format: * binary file, * byte 1 - normal initialization data used by BIOS to initialize UART * byte 2 - flag indicating presence of user supplied BRG divisor value * byte 3 - com1 flag TRUE (1) if use COM1 else FALSE (0) for COM2 * byte 4 - LSBs of user supplied BRG divisor value (only present if * byte 2 is TRUE) * byte 5 - MSBs of user supplied BRG divisor value (only present if * byte 2 is TRUE) */ static void init_file(incoming) char *incoming; { FILE *stream; int found; found = FALSE; if (incoming != NULL) { if ((stream = fopen(incoming, "rb")) == NULL) { puts("could not open requested init file\n"); } else { found = TRUE; } } if (found) { com_init = fgetc(stream); com_div = fgetc(stream); com1 = fgetc(stream); if (com_div) { i = fgetc(stream); com_div_value = (i & 255) | (fgetc(stream) << 8); } fclose(stream); } } /* * Main routine, can accept one optional command line parameter - the * name of an initialization data file. The user specified data file * is read in and using the parameters from the file or the default * parameter values the software and hardware is initialized. */ main(argc, argv) int argc; char *argv[]; { char ch, *chp; if (argc < 2) { chp = NULL; } else { chp = argv[1]; } init_file(chp); /* read in initialization data if present */ init(); /* initialize software and hardware */ /* * The main body of the code goes here $$$$$$$$$$$$$$$$$$$$$$$ */ restore(); }