OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/geolocation/libgps_wrapper_linux.h" | 5 #include "content/browser/geolocation/libgps_wrapper_linux.h" |
6 | 6 |
| 7 #include <math.h> |
7 #include <dlfcn.h> | 8 #include <dlfcn.h> |
8 #include <errno.h> | 9 #include <errno.h> |
9 | 10 |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
12 #include "content/common/geoposition.h" | 13 #include "content/common/geoposition.h" |
| 14 #include "third_party/gpsd/release-3.1/gps.h" |
| 15 |
| 16 COMPILE_ASSERT(GPSD_API_MAJOR_VERSION == 5, GPSD_API_version_is_not_5); |
13 | 17 |
14 namespace { | 18 namespace { |
15 // Attempts to load dynamic library named |lib| and initialize the required | 19 const char kLibGpsName[] = "libgps.so.20"; |
16 // function pointers according to |mode|. Returns ownership a new instance | 20 } // namespace |
17 // of the library loader class, or NULL on failure. | 21 |
18 // TODO(joth): This is a hang-over from when we dynamically two different | 22 LibGps::LibGps(void* dl_handle, |
19 // versions of libgps and chose between them. Now we could remove at least one | 23 gps_open_fn gps_open, |
20 // layer of wrapper class and directly #include gps.h in this cc file. | 24 gps_close_fn gps_close, |
21 // See http://crbug.com/98132 and http://crbug.com/99177 | 25 gps_read_fn gps_read) |
22 LibGpsLibraryWrapper* TryToOpen(const char* lib) { | 26 : dl_handle_(dl_handle), |
23 void* dl_handle = dlopen(lib, RTLD_LAZY); | 27 gps_open_(gps_open), |
| 28 gps_close_(gps_close), |
| 29 gps_read_(gps_read), |
| 30 gps_data_(new gps_data_t), |
| 31 is_open_(false) { |
| 32 DCHECK(gps_open_); |
| 33 DCHECK(gps_close_); |
| 34 DCHECK(gps_read_); |
| 35 } |
| 36 |
| 37 LibGps::~LibGps() { |
| 38 Stop(); |
| 39 if (dl_handle_) { |
| 40 const int err = dlclose(dl_handle_); |
| 41 DCHECK_EQ(0, err) << "Error closing dl handle: " << err; |
| 42 } |
| 43 } |
| 44 |
| 45 LibGps* LibGps::New() { |
| 46 void* dl_handle = dlopen(kLibGpsName, RTLD_LAZY); |
24 if (!dl_handle) { | 47 if (!dl_handle) { |
25 VLOG(1) << "Could not open " << lib << ": " << dlerror(); | 48 DLOG(WARNING) << "Could not open " << kLibGpsName << ": " << dlerror(); |
26 return NULL; | 49 return NULL; |
27 } | 50 } |
28 VLOG(1) << "Loaded " << lib; | 51 |
| 52 DLOG(INFO) << "Loaded " << kLibGpsName; |
29 | 53 |
30 #define DECLARE_FN_POINTER(function) \ | 54 #define DECLARE_FN_POINTER(function) \ |
31 LibGpsLibraryWrapper::function##_fn function; \ | 55 function##_fn function = reinterpret_cast<function##_fn>( \ |
32 function = reinterpret_cast<LibGpsLibraryWrapper::function##_fn>( \ | |
33 dlsym(dl_handle, #function)); \ | 56 dlsym(dl_handle, #function)); \ |
34 if (!function) { \ | 57 if (!function) { \ |
35 LOG(WARNING) << "libgps " << #function << " error: " << dlerror(); \ | 58 DLOG(WARNING) << "libgps " << #function << " error: " << dlerror(); \ |
36 dlclose(dl_handle); \ | 59 dlclose(dl_handle); \ |
37 return NULL; \ | 60 return NULL; \ |
38 } | 61 } |
39 DECLARE_FN_POINTER(gps_open); | 62 DECLARE_FN_POINTER(gps_open); |
40 DECLARE_FN_POINTER(gps_close); | 63 DECLARE_FN_POINTER(gps_close); |
41 DECLARE_FN_POINTER(gps_poll); | 64 DECLARE_FN_POINTER(gps_read); |
42 DECLARE_FN_POINTER(gps_stream); | 65 // We don't use gps_shm_read() directly, just to make sure that libgps has |
43 DECLARE_FN_POINTER(gps_waiting); | 66 // the shared memory support. |
| 67 typedef int (*gps_shm_read_fn)(struct gps_data_t*); |
| 68 DECLARE_FN_POINTER(gps_shm_read); |
44 #undef DECLARE_FN_POINTER | 69 #undef DECLARE_FN_POINTER |
45 | 70 |
46 return new LibGpsLibraryWrapper(dl_handle, | 71 return new LibGps(dl_handle, gps_open, gps_close, gps_read); |
47 gps_open, | |
48 gps_close, | |
49 gps_poll, | |
50 gps_stream, | |
51 gps_waiting); | |
52 } | |
53 } // namespace | |
54 | |
55 LibGps::LibGps(LibGpsLibraryWrapper* dl_wrapper) | |
56 : library_(dl_wrapper) { | |
57 DCHECK(dl_wrapper != NULL); | |
58 } | |
59 | |
60 LibGps::~LibGps() { | |
61 } | |
62 | |
63 LibGps* LibGps::New() { | |
64 LibGpsLibraryWrapper* wrapper; | |
65 wrapper = TryToOpen("libgps.so.19"); | |
66 if (wrapper) | |
67 return NewV294(wrapper); | |
68 wrapper = TryToOpen("libgps.so"); | |
69 if (wrapper) | |
70 return NewV294(wrapper); | |
71 return NULL; | |
72 } | 72 } |
73 | 73 |
74 bool LibGps::Start() { | 74 bool LibGps::Start() { |
75 if (library().is_open()) | 75 if (is_open_) |
76 return true; | 76 return true; |
| 77 |
| 78 #if defined(OS_CHROMEOS) |
77 errno = 0; | 79 errno = 0; |
78 static int fail_count = 0; | 80 if (gps_open_(GPSD_SHARED_MEMORY, 0, gps_data_.get()) != 0) { |
79 if (!library().open(NULL, NULL)) { | |
80 // See gps.h NL_NOxxx for definition of gps_open() error numbers. | 81 // See gps.h NL_NOxxx for definition of gps_open() error numbers. |
81 LOG_IF(WARNING, 0 == fail_count++) << "gps_open() failed: " << errno; | 82 DLOG(WARNING) << "gps_open() failed " << errno; |
82 return false; | 83 return false; |
83 } | 84 } |
84 fail_count = 0; | 85 #else // drop the support for desktop linux for now |
85 if (!StartStreaming()) { | 86 DLOG(WARNING) << "LibGps is only supported on ChromeOS"; |
86 VLOG(1) << "StartStreaming failed"; | 87 return false; |
87 library().close(); | 88 #endif |
88 return false; | 89 |
89 } | 90 is_open_ = true; |
90 return true; | 91 return true; |
91 } | 92 } |
92 | |
93 void LibGps::Stop() { | 93 void LibGps::Stop() { |
94 library().close(); | 94 if (is_open_) |
| 95 gps_close_(gps_data_.get()); |
| 96 is_open_ = false; |
95 } | 97 } |
96 | 98 |
97 bool LibGps::Poll() { | 99 bool LibGps::Read(Geoposition* position) { |
98 last_error_ = "no data received from gpsd"; | |
99 while (DataWaiting()) { | |
100 int error = library().poll(); | |
101 if (error) { | |
102 last_error_ = base::StringPrintf("poll() returned %d", error); | |
103 Stop(); | |
104 return false; | |
105 } | |
106 last_error_.clear(); | |
107 } | |
108 return last_error_.empty(); | |
109 } | |
110 | |
111 bool LibGps::GetPosition(Geoposition* position) { | |
112 DCHECK(position); | 100 DCHECK(position); |
113 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; | 101 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; |
114 if (!library().is_open()) { | 102 if (!is_open_) { |
115 position->error_message = "No gpsd connection"; | 103 DLOG(WARNING) << "No gpsd connection"; |
116 return false; | 104 position->error_message = "No gpsd connection"; |
| 105 return false; |
117 } | 106 } |
| 107 |
| 108 if (gps_read_(gps_data_.get()) < 0) { |
| 109 DLOG(WARNING) << "gps_read() fails"; |
| 110 position->error_message = "gps_read() fails"; |
| 111 return false; |
| 112 } |
| 113 |
118 if (!GetPositionIfFixed(position)) { | 114 if (!GetPositionIfFixed(position)) { |
119 position->error_message = last_error_; | 115 DLOG(WARNING) << "No fixed position"; |
120 return false; | 116 position->error_message = "No fixed position"; |
| 117 return false; |
121 } | 118 } |
| 119 |
122 position->error_code = Geoposition::ERROR_CODE_NONE; | 120 position->error_code = Geoposition::ERROR_CODE_NONE; |
123 position->timestamp = base::Time::Now(); | 121 position->timestamp = base::Time::Now(); |
124 if (!position->IsValidFix()) { | 122 if (!position->IsValidFix()) { |
125 // GetPositionIfFixed returned true, yet we've not got a valid fix. | 123 // GetPositionIfFixed returned true, yet we've not got a valid fix. |
126 // This shouldn't happen; something went wrong in the conversion. | 124 // This shouldn't happen; something went wrong in the conversion. |
127 NOTREACHED() << "Invalid position from GetPositionIfFixed: lat,long " | 125 NOTREACHED() << "Invalid position from GetPositionIfFixed: lat,long " |
128 << position->latitude << "," << position->longitude | 126 << position->latitude << "," << position->longitude |
129 << " accuracy " << position->accuracy << " time " | 127 << " accuracy " << position->accuracy << " time " |
130 << position->timestamp.ToDoubleT(); | 128 << position->timestamp.ToDoubleT(); |
131 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; | 129 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; |
132 position->error_message = "Bad fix from gps"; | 130 position->error_message = "Bad fix from gps"; |
133 return false; | 131 return false; |
134 } | 132 } |
135 return true; | 133 return true; |
136 } | 134 } |
137 | 135 |
138 LibGpsLibraryWrapper::LibGpsLibraryWrapper(void* dl_handle, | 136 bool LibGps::GetPositionIfFixed(Geoposition* position) { |
139 gps_open_fn gps_open, | 137 DCHECK(position); |
140 gps_close_fn gps_close, | 138 if (gps_data_->status == STATUS_NO_FIX) { |
141 gps_poll_fn gps_poll, | 139 DVLOG(2) << "Status_NO_FIX"; |
142 gps_stream_fn gps_stream, | 140 return false; |
143 gps_waiting_fn gps_waiting) | 141 } |
144 : dl_handle_(dl_handle), | 142 |
145 gps_open_(gps_open), | 143 if (isnan(gps_data_->fix.latitude) || isnan(gps_data_->fix.longitude)) { |
146 gps_close_(gps_close), | 144 DVLOG(2) << "No valid lat/lon value"; |
147 gps_poll_(gps_poll), | 145 return false; |
148 gps_stream_(gps_stream), | 146 } |
149 gps_waiting_(gps_waiting), | 147 |
150 gps_data_(NULL) { | 148 position->latitude = gps_data_->fix.latitude; |
| 149 position->longitude = gps_data_->fix.longitude; |
| 150 |
| 151 if (!isnan(gps_data_->fix.epx) && !isnan(gps_data_->fix.epy)) { |
| 152 position->accuracy = std::max(gps_data_->fix.epx, gps_data_->fix.epy); |
| 153 } else if (isnan(gps_data_->fix.epx) && !isnan(gps_data_->fix.epy)) { |
| 154 position->accuracy = gps_data_->fix.epy; |
| 155 } else if (!isnan(gps_data_->fix.epx) && isnan(gps_data_->fix.epy)) { |
| 156 position->accuracy = gps_data_->fix.epx; |
| 157 } else { |
| 158 // TODO(joth): Fixme. This is a workaround for http://crbug.com/99326 |
| 159 DVLOG(2) << "libgps reported accuracy NaN, forcing to zero"; |
| 160 position->accuracy = 0; |
| 161 } |
| 162 |
| 163 if (gps_data_->fix.mode == MODE_3D && !isnan(gps_data_->fix.altitude)) { |
| 164 position->altitude = gps_data_->fix.altitude; |
| 165 if (!isnan(gps_data_->fix.epv)) |
| 166 position->altitude_accuracy = gps_data_->fix.epv; |
| 167 } |
| 168 |
| 169 if (!isnan(gps_data_->fix.track)) |
| 170 position->heading = gps_data_->fix.track; |
| 171 if (!isnan(gps_data_->fix.speed)) |
| 172 position->speed = gps_data_->fix.speed; |
| 173 return true; |
151 } | 174 } |
152 | |
153 LibGpsLibraryWrapper::~LibGpsLibraryWrapper() { | |
154 close(); | |
155 if (dl_handle_) { | |
156 const int err = dlclose(dl_handle_); | |
157 CHECK_EQ(0, err) << "Error closing dl handle: " << err; | |
158 } | |
159 } | |
160 | |
161 bool LibGpsLibraryWrapper::open(const char* host, const char* port) { | |
162 DCHECK(!gps_data_) << "libgps already opened"; | |
163 DCHECK(gps_open_); | |
164 gps_data_ = gps_open_(host, port); | |
165 return is_open(); | |
166 } | |
167 | |
168 void LibGpsLibraryWrapper::close() { | |
169 if (is_open()) { | |
170 DCHECK(gps_close_); | |
171 gps_close_(gps_data_); | |
172 gps_data_ = NULL; | |
173 } | |
174 } | |
175 | |
176 int LibGpsLibraryWrapper::poll() { | |
177 DCHECK(is_open()); | |
178 DCHECK(gps_poll_); | |
179 return gps_poll_(gps_data_); | |
180 } | |
181 | |
182 int LibGpsLibraryWrapper::stream(int flags) { | |
183 DCHECK(is_open()); | |
184 DCHECK(gps_stream_); | |
185 return gps_stream_(gps_data_, flags, NULL); | |
186 } | |
187 | |
188 bool LibGpsLibraryWrapper::waiting() { | |
189 DCHECK(is_open()); | |
190 DCHECK(gps_waiting_); | |
191 return gps_waiting_(gps_data_); | |
192 } | |
193 | |
194 const gps_data_t& LibGpsLibraryWrapper::data() const { | |
195 DCHECK(is_open()); | |
196 return *gps_data_; | |
197 } | |
198 | |
199 bool LibGpsLibraryWrapper::is_open() const { | |
200 return gps_data_ != NULL; | |
201 } | |
OLD | NEW |