From 442b7ce17d593cce2431dfef6cbddbd67383aa55 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Tue, 14 Nov 2006 15:33:55 +0000 Subject: Changes from Mick Durkin. --- www/writing-a-driver.xml | 137 ++++++++++++++++++++++++++++++----------------- 1 file changed, 87 insertions(+), 50 deletions(-) (limited to 'www') diff --git a/www/writing-a-driver.xml b/www/writing-a-driver.xml index fea20be9..afea4f3b 100644 --- a/www/writing-a-driver.xml +++ b/www/writing-a-driver.xml @@ -16,11 +16,11 @@ - 1.2 + 1.3 14 November 2006 md - Track split of wrapup method. + Updated to conform to SVN 3884. @@ -28,7 +28,7 @@ If you are thinking of writing a GPSD driver for some GPS-like device, these notes by a person who did it may help you decide -whether or not it's a good idea, and if it is help you get started. +whether or not it's a good idea, and if it is, help you get started. @@ -42,7 +42,7 @@ supports around 40 different GPS devices, so it may be that your device is already supported by an existing driver. It may be worth noting that these notes were written against -gpsd version 2.34 compiled from SVN revision 3783 of November 5th +gpsd version 2.34 compiled from SVN revision 3884 of November 14th 2006. The situation will likely have changed by the time you read this, but the broad principles should still apply. Check what version you are using. @@ -169,17 +169,16 @@ get some general ideas on how to handle this from drivers like I found from the manufacturer's documentation that the Jupiter-T produces output only when instructed, rather than spewing out a -set sequence at some pre-defined rate and so it would be possible -to generate almost all the wanted data for -gpsd by activating two main messages, - @@Ea and -@@Bb. All the commands and +set sequence at some pre-defined rate. All the commands and responses in Jupiter-T-speak start with a 4 character ASCII string @@Nn followed by a payload of 0 to approx 300 binary bytes, a single byte binary checksum -and an ASCII CR/LF pair. This particular structure was the cause of -some headaches in the interpretation, but it means that the important -data is impressively dense. The first command +and an ASCII CR/LF pair. Thus it would be possible to generate all +the wanted data for gpsd by activating +two main messages, @@Bb and +@@Ea. This particular structure +was the cause of some headaches in the interpretation, but it means +that the important data is impressively dense. The first command (@@Bb) gives the status of all visible satellites (up to 12) in 92 bytes and the second command (@@Ea) gives all the navigational data plus @@ -194,8 +193,8 @@ had a routine static gps_mask_t handle1000(struct gps_device_t *session) that did a very similar job. -This brought me neatly to the first chicken/egg problem; the -device, as I said earlier, is mute on power-up and unless you send it +This brought me neatly to a chicken/egg problem; the device, +as I said earlier, is mute on power-up and unless you send it some instructions to turn on one or more messages, you will have no indication if it is even alive. Actually, this is not strictly true, as it does output a 1PPS and a 10kHz square wave on power up, but @@ -468,7 +467,7 @@ match them to an existing driver. Here is where our driver will be called, so the changes are a little larger. The driver starts at the beginning of each packet and tries to match, character by character, until it has determined which (if any) driver owns -this package in routine +this packet in routine nextstate. As all Jupiter-T packets start with @@, this collides with the @@ -480,12 +479,12 @@ data. This checking is done in a new block of code lower down in nextstate that was modelled on the other drivers, but must needs be unique. The -package is scanned byte by byte until a fully formed packet +packet is scanned byte by byte until a fully formed packet has been detected and then it can be parsed in the main driver. If it fails any of the tests, the state engine is set back to GROUND_STATE and detection starts again. The code to trigger parsing and deletion of the -package after it has been parsed is included lower down in the +packet after it has been parsed is included lower down in the code packet_parse and is based on existing drivers. @@ -613,7 +612,7 @@ is not active.) The premise is that there may be a special mode you initialized the device into for gpsd operation which should be turned off otherwise. It allows for changing the device to a low power mode, for instance. Any changes you made in -the .configurator meethod should be undone +the .configurator method should be undone here. .wrapup points to a block of code @@ -716,7 +715,12 @@ point in the startup. The unfortunate thing is that to implement the function could mean getting down to low level programming of the tty port since you may find the normal operating mode capabilities may not match your device's -requirement, even if the baud rate is correct. +requirement, even if the baud rate is correct. This +proved to be the case for me and was the single most difficult +part of writing the driver. This, I am sure, is because it involves +working virtually directly with the system hardware. I have documented +this process in some detail in the hope that it may save some other +poor soul the trials I went through. I looked at the code in serial.c, @@ -747,45 +751,78 @@ read it carefully). I also found that even this was not the whole story, since even when I had allowed the device to catch up on -a settings change, I could not get it to respond cleanly to a -device identify command. I -found that I was missing some or all of the response message -when operating at 9600 bps. +a settings change, I could not get it to respond reliably to a +device identify command. I found that I was +missing some or all of the response message when operating +at 9600 bps. -The reason was that I check the port with a +The reason was that I originally checked the port with a single character sniff routine a maximum of 300 times (just bigger than the block of text being returned), which comes out at 300 * -(1 start bit + 8 data bits + 1 stop bit) = 3000 bits. This equates -to about 312 milliseconds. This method is necessary because the -probe is speculative and must handle cases like wrong port speed -or the type of device being probed for is not present. - -I found in the Oncore manual that the device's internal -scheduler uses a 1 second loop time. Within this loop, the -navigation tasks are handled first, followed by processing of -the input commands. Any resultant output will be generated as -soon as the input buffer is processed, assuming the buffer -holds one or more complete commands. +(1 start bit + 8 data bits + 1 stop bit) = 3000 bits. I expected +this would occupy about 312 milliseconds which I considered as an +acceptable delay during the probing phase, but my understanding of +how the serial port is accessed turned out to be faulty. This method +was originally chosen because the probe is speculative and must handle +cases like wrong port speed or the type of device being probed for +is not present and should not hold up progress for too long. Don't +forget that all installed drivers get a chance to probe, one after +another, so the delays for each are cumulative and if no driver +finds and claims the device, you can have many seconds of delay. + +When it failed to work as expected, I investigated the GPS device's +documentation (RTFM did I hear you say!) and I found in the +Oncore manual that the device's internal scheduler uses a 1 +second loop time. Within this loop, the navigation tasks are handled +first, followed by processing of the input commands. Any +resultant output will be generated as soon as the input +buffer is processed, assuming the buffer holds one or more +complete commands. If you are lucky and just finish your input as the buffer is ready to be scanned, you can get a result back in 70 milliseconds. If you are unlucky, the most extreme delay is 2 -seconds. On average, the turnaround is 1025 -milliseconds. Unfortunately, in the probing code, we have to allow for -the worst case, so once the code issues a command, it has then to wait -a full 2 seconds before scanning for output. - -The only serial port setting which was not immediately obvious to me -(although present in both serial.c -and sirfmon.c) was -session->ttyset.c_cflag |= CREAD | +seconds. On average, the turnaround is 1025 milliseconds. +Unfortunately, in the probing code, we have to allow for the +worst case, so once the code issues a command, it has then to +allow a full 2 seconds before scanning for output. + +When I found that my initial scanning method was not viable, +I experimented and eventually settled on a loop using a +while statement that checked +timestamp() values and was set +to time out at 2 seconds maximum duration, with an early exit on +successfully finding the wanted data. Within the loop, I tested +the serial port for an available character. If one was available, +I checked it against my expected string; if one was not available, +I looped again if the timer had not expired. If I encountered an +error when reading the port I exited. All exits returned a +success/fail value. + +This worked better, but still failed occasionally. I then used +the gpsd_report to check the +error returned and I saw that I was getting lots of EAGAIN +errors. This suggested that the port was not able to handle all my +read requests, so I suspected the rate of reading was too fast. Not knowing +for sure, I trapped this particular error and applied a +usleep() of a couple of milliseconds when it occurred. This was +enough to cure the problem and I could get the detection to track +the device's responses reliably. I saw a spread of detection timing +between 250 milliseconds and 1.7 seconds over a large number of tests, +so I concluded that the manufacturer's predictions were being +satisfied. + +The only other serial port setting which was not immediately +obvious to me (although present in both serial.c + and sirfmon.c +) was session->ttyset.c_cflag |= CREAD | CLOCAL;. This is needed to enable the port and -cause it to ignore any modem control lines. If you are using a binary -protocol, you will also need to issue a cfmakeraw -(struct termios *termios_p); to quickly set the -most important flags correctly. I was bitten by this and found that -<CR/LF> sequences were being modified to <CR/CR/LF> by -the kernel's tty port driver. +cause it to ignore any modem control lines. If you are using a +binary protocol , you will also need to issue a +cfmakeraw (struct termios *termios_p); +to quickly set the most important flags correctly. I was bitten by +this and found that transmitted <CR/LF> sequences were being modified +to <CR/CR/LF> by the kernel's tty port driver. Sign off -- cgit v1.2.1