gil@ginger.sri.com (Gil Porat) (01/10/90)
I have a problem with reads hanging on the serial driver. Here are the facts. 1) My serial device, connected to the modem port of the mac, will transmit 80ish bytes when asked to do so. 2) I instruct my serial device to transmit. 3) I call FSRead successively with the serial port reference number and a request for 64 bytes. The first time through I get 64 bytes returned in my buffer, but the second time the FSRead hangs indefinitely. My questions are... 1) Why does the second FSRead hang? 2) Doesn't FSRead return as much as it can? 3) Is there a way to get the serial driver and/or FSRead to time out? 4) Does FSRead need an EOT to return? Any help will be greatly appreciated. Gil Porat gil@ginger.sri.com (415)323-2053
cc100aa@prism.gatech.EDU (Ray Spalding) (01/10/90)
In article <7770@unix.SRI.COM> gil@ginger.sri.com (Gil Porat) writes: [regarding FSRead with the serial driver] >1) Why does the second FSRead hang? Because it will not return until it gets the exact number of characters you asked it to read. >2) Doesn't FSRead return as much as it can? No, see above. >3) Is there a way to get the serial driver and/or FSRead to time out? The way to accomplish this is to use asynchronous I/O (in the sense of IM vol IV, not in the sense of async data communications). You start an asynchronous read request, then go about your business processing events and so on. Periodically, you check the request block for completion: 0 == normal completion; 1 == still in progress; anything else == error. If you want to time it out, you have to watch the time yourself (using TickCount). Caveat: Be sure to call SystemTask (or WaitNextEvent) periodically so that the serial driver will get a slice of CPU. Some fragments of the code I use (MPW C): ParamBlockRec commInPB; static unsigned char commInChar; static short commInPort = -6; CommStartIn() /* Queue serial port input request and return */ { commInPB.ioParam.ioCompletion = NULL; commInPB.ioParam.ioVRefNum = 0; commInPB.ioParam.ioRefNum = commInPort; commInPB.ioParam.ioBuffer = &commInChar; commInPB.ioParam.ioReqCount = 1; /* ask for one character only */ commInPB.ioParam.ioPosMode = 0; PBRead(&commInPB,true); } CommDisp() /* Check for serial port input completion */ { int rc; SystemTask(); rc = commInPB.ioParam.ioResult; if (rc == 1) return; /* or check for timeout here */ if (rc != noErr) { /* process errors */ return;} /* ... process one input character ... */ CommStartIn(); /* queue request for next character */ return; } >4) Does FSRead need an EOT to return? No, EOT is passed through to you like any other character. -- Ray Spalding, Office of Computing Services Georgia Institute of Technology, Atlanta Georgia, 30332-0275 uucp: ...!{allegra,amd,hplabs,ut-ngp}!gatech!prism!cc100aa Internet: cc100aa@prism.gatech.edu
chesley@goofy.apple.com (Harry Chesley) (01/11/90)
In article <4713@hydra.gatech.EDU> cc100aa@prism.gatech.EDU (Ray Spalding) writes: > >1) Why does the second FSRead hang? > > Because it will not return until it gets the exact number of characters > you asked it to read. More exactly: it will read until it gets at least as many characters as you ask for (more is OK). It sounds like you're experiencing either buffer overflow or baud rate mismatch. The default input buffer for the serial port is only 64 bytes. So 80 characters can easily overflow the buffer if you don't read it out fast. You can increase the buffer size by calling SerSetBuf (but be sure and unset the buffer by calling SerSetBuf with a length of zero before exiting the application or you'll get horrible crashes). Alternatively, the baud rates may not be set right. Therefore, you send enough characters at one speed, but they come through as fewer gibberish characters at the other speed. In article <4713@hydra.gatech.EDU> cc100aa@prism.gatech.EDU (Ray Spalding) writes: > >3) Is there a way to get the serial driver and/or FSRead to time out? > > The way to accomplish this is to use asynchronous I/O (in the > sense of IM vol IV, not in the sense of async data communications). > You start an asynchronous read request, then go about your business processing > events and so on. Periodically, you check the request block for completion: > 0 == normal completion; 1 == still in progress; anything else == error. > If you want to time it out, you have to watch the time yourself (using > TickCount). Even simpler, use SerGetBuf to find out how many input characters are available, and do a synchronous call but only when there are enough characters available.
ech@cbnewsk.ATT.COM (ned.horvath) (01/11/90)
In article <7770@unix.SRI.COM> gil@ginger.sri.com (Gil Porat) writes: >1) Why does the second FSRead hang? From article <4713@hydra.gatech.EDU>, by cc100aa@prism.gatech.EDU (Ray Spalding): > Because it will not return until it gets the exact number of characters > you asked it to read. A direct means of accomplishing what you want is to use SerGetBuf() to request the number of bytes available, then read the maximum of your buffer size and the available bytes. Wrapping that up in a little routine that behaves like the Unix read() on a serial port is left as an exercise... =Ned Horvath=
alain@atr-la.atr.co.jp (Alain de Cheveigne) (01/11/90)
In article <6113@internal.Apple.COM>, chesley@goofy.apple.com (Harry Chesley)writes: >The default input buffer for the serial port is only 64 bytes. >So 80 characters can easily overflow the buffer if you don't read it out >fast. You can increase the buffer size by calling SerSetBuf (but be sure >and unset the buffer by calling SerSetBuf with a length of zero before >exiting the application or you'll get horrible crashes). I currently use a 8172 byte buffer, and I don't unset the buffer on exit. I have never witnessed such a crash. While the subject is up: even with a 8172 byte buffer I sometimes lose characters. It seems that the ethernet pad I'm connected to takes too long to react to an XOFF. The question is: at what point does the serial driver send the XOFF? When a given *proportion* of the buffer is full? Or when the buffer is a given *number of bytes* from being full? If it is the latter, making the buffer bigger only reduces (slightly) the probability of losing characters. Alain de Cheveigne, alain@atr-la.atr.co.jp
urlichs@smurf.ira.uka.de (01/12/90)
In article <7770@unix.SRI.COM> gil@ginger.sri.com (Gil Porat) writes: [regarding FSRead with the serial driver] >1) Why does the second FSRead hang? It wants as many characters as you tell it to read. The way around this is to ask the driver for status, and then read exactly as many characters as are available. General hint: Try not to read one character at a time. You can do that with async PBControl calls but you'll run into speed problems with faster serial devices. It won't hurt to make the receive buffer (standard: 64 byte...) somewhat bigger, either. -- Matthias Urlichs
alain@atr-la.atr.co.jp (Alain de Cheveigne) (01/12/90)
In article <3915@atr-la.atr.co.jp>, alain@atr-la.atr.co.jp (Alain de Cheveigne) writes: >In article <6113@internal.Apple.COM>, chesley@goofy.apple.com (Harry >Chesley)writes: [...] >>You can increase the buffer size by calling SerSetBuf (but be sure >>and unset the buffer by calling SerSetBuf with a length of zero before >>exiting the application or you'll get horrible crashes). > >I currently use a 8172 byte buffer, and I don't unset the buffer on >exit. I have never witnessed such a crash. Second thoughts on that. It is definitely a good idea *not* to leave the buffer set to a piece of program memory on exit! Thanks to Harry for finding a bug lurking in my program from the start. As Dijkstra says, it may be easy to prove that a program is buggy, but no amount of testing can prove that it ain't :-(. I suppose that, if the driver is closed on exit, the buffer gets reset to default the next time the driver is opened. But this wouldn't be the case if the driver was left open. By the way, no one answered a previous question of mine: is it ok to leave the driver open on exit? Alain de Cheveigne, alain@atr-la.atr.co.jp
chesley@goofy.apple.com (Harry Chesley) (01/13/90)
In article <3915@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes: > I currently use a 8172 byte buffer, and I don't unset the buffer on > exit. I have never witnessed such a crash. It probably doesn't happen if you close the driver before exiting. I always deallocate the buffer, just to be paranoid. What happens is the driver keeps on putting received characters into the place it thinks the buffer is. In the meantime, however, the system has cleared out the application heap (where the buffer was allocated) and given it to the next application to allocate from. So the input characters go into an essentially random memory location in the middle of a running program. This will cause problems if you get more characters on the port, and if they happen to get stuck into a critical location for the next program. Therefore, this can cause random and unpredictable crashes some arbitrary time after the program you wrote was run. In short: a programmer's nightmare. The same situation arises in other drivers, such as MacTCP.
chesley@goofy.apple.com (Harry Chesley) (01/13/90)
In article <3916@atr-la.atr.co.jp> alain@atr-la.atr.co.jp (Alain de Cheveigne) writes: > I suppose that, if the driver is closed on exit, the buffer gets reset > to default the next time the driver is opened. But this wouldn't be > the case if the driver was left open. By the way, no one answered a > previous question of mine: is it ok to leave the driver open on exit? I'm not sure what the definitive answer is these days. I started working with the serial driver in the 512K days, when there were different ROM and RAM-based drivers. I always used the ROM drivers, just to keep things as simple as possible. Quoting from Inside Mac II-248: "You shouldn't ever close the ROM Serial Driver with a Device Manager Close call." It goes on to say that you SHOULD close the RAM drivers. A much later tech note points out that if you're running on a 128K ROM or later machine, you should just call OpenDriver, rather than RAMSDOpen, as from that point on the "RAM" driver is in ROM, and that'll be more compatible with future releases. So, these days, and assuming you don't expect your program to run on a 512K unenhanced or a 128K Mac, you should use OpenDriver and CloseDriver. The close will deallocate the buffer properly. (I haven't actually tried that, but that's what the docs say...) Or, forget the serial port driver and use the Comm Toolbox. That's what I'm about to do.