836f0ed09b55c38855cdc30e94ddb752c03db099
[profile/ivi/automotive-message-broker.git] / plugins / tpms / tpmsplugin.cpp
1 /*
2 Copyright (C) 2012 Intel Corporation
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19 #include "tpmsplugin.h"
20
21 #include <iostream>
22 #include <boost/assert.hpp>
23 #include <glib.h>
24 #include <libusb.h>
25
26 using namespace std;
27
28 #include "debugout.h"
29 #include "timestamp.h"
30
31 #define ENDPOINT_IN             0x81
32 #define ENDPOINT_OUT            0x01
33
34 #define DEVICE_VID 0x0000
35 #define DEVICE_PID 0x0001
36
37 #define MAX_SENSORS 4
38
39 //timeout for performing interrupt r/w operations in milliseconds
40 #define INTR_TIMEOUT 1000
41
42 #define PSI_MULTIPLIER 14.5038
43 #define KPA_MULTIPLIER 100
44 #define PRESSURE_SCALE 0.025
45  
46 #define FARENHEIT_MULTIPLIER 1.8
47
48
49 static gboolean timeoutCallback(gpointer data)
50 {
51         TpmsPlugin* src = (TpmsPlugin*)data;
52         
53         int r = src->readValues();
54
55         return true;
56 }
57
58 TpmsPlugin::TpmsPlugin(AbstractRoutingEngine* re, map<string, string> config)
59 :AbstractSource(re, config)
60 {
61     lfPressure = rfPressure = lrPressure = rrPressure = 0;
62     lfTemperature = rfTemperature = lrTemperature = rrTemperature = 0;
63
64     int r = 1;
65     
66     r = libusb_init(NULL);
67     if (r < 0) {
68       DebugOut() << "TPMS: Plugin load failure. Failed to initialize libusb" << endl;
69     }
70     else {
71       r = findDevice();
72       if (r < 0) {
73         DebugOut() << "TPMS: Plugin load failure. Could not find/open device - run as root?" << endl;
74       }
75       else {
76         // need to detach device from kernel driver before claiming the interface
77         r = detachDevice();
78         if (r < 0) {
79           DebugOut() << "TPMS: Plugin load failure. USB device detach failed with code " << r << endl;
80         }
81         else {
82           r = libusb_claim_interface(mDeviceHandle, 0);
83           if (r < 0) {
84             DebugOut() << "TPMS: Plugin load failure. usb_claim_interface error " << r << endl;
85           }
86           else {
87             DebugOut() << "TPMS: USB interface initialized" << endl;    
88
89             re->setSupported(supported(), this);
90             g_timeout_add(5000, timeoutCallback, this );
91             DebugOut() << "TPMS: set to read sensor every 5 seconds" << endl;
92           }
93         }
94       }
95     }
96 }
97
98
99 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
100 {
101         return new TpmsPlugin(routingengine, config);
102         
103 }
104
105 string TpmsPlugin::uuid()
106 {
107         return "CHANGE THIS 6dd4268a-c605-4a06-9034-59c1e8344c8e";
108 }
109
110
111 void TpmsPlugin::getPropertyAsync(AsyncPropertyReply *reply)
112 {
113         DebugOut() << "TPMS: getPropertyAsync called for property: " << reply->property << endl;
114
115     if(reply->property == VehicleProperty::TirePressureLeftFront) {
116       VehicleProperty::TirePressureType temp(lfPressure);
117       reply->value = &temp;
118       reply->completed(reply);
119     }
120     else if(reply->property == VehicleProperty::TirePressureRightFront) {
121       VehicleProperty::TirePressureType temp(rfPressure);
122       reply->value = &temp;
123       reply->completed(reply);
124     }
125     else if(reply->property == VehicleProperty::TirePressureLeftRear) {
126       VehicleProperty::TirePressureType temp(lrPressure);
127       reply->value = &temp;
128       reply->completed(reply);
129     }
130     else if(reply->property == VehicleProperty::TirePressureRightRear) {
131       VehicleProperty::TirePressureType temp(rrPressure);
132       reply->value = &temp;
133       reply->completed(reply);
134     }
135     else if(reply->property == VehicleProperty::TireTemperatureLeftFront) {
136       VehicleProperty::EngineSpeedType temp(lfTemperature);
137       reply->value = &temp;
138       reply->completed(reply);
139     }
140     else if(reply->property == VehicleProperty::TireTemperatureRightFront) {
141       VehicleProperty::EngineSpeedType temp(rfTemperature);
142       reply->value = &temp;
143       reply->completed(reply);
144     }
145     else if(reply->property == VehicleProperty::TireTemperatureLeftRear) {
146       VehicleProperty::EngineSpeedType temp(lrTemperature);
147       reply->value = &temp;
148       reply->completed(reply);
149     }
150     else if(reply->property == VehicleProperty::TireTemperatureRightRear) {
151       VehicleProperty::EngineSpeedType temp(rrTemperature);
152       reply->value = &temp;
153       reply->completed(reply);
154     }
155
156     else {
157       DebugOut() << "TPMS: no such getProperty type: " << reply->property << endl;
158       reply->value = nullptr;
159       reply->completed(reply);
160         }
161 }
162
163 void TpmsPlugin::getRangePropertyAsync(AsyncRangePropertyReply *reply)
164 {
165         ///not supported
166         reply->completed(reply);
167 }
168
169 AsyncPropertyReply *TpmsPlugin::setProperty(AsyncSetPropertyRequest request )
170 {
171         return NULL;
172 }
173
174 void TpmsPlugin::subscribeToPropertyChanges(VehicleProperty::Property property)
175 {
176         mRequests.push_back(property);
177 }
178
179 PropertyList TpmsPlugin::supported()
180 {
181         PropertyList props;
182     props.push_back(VehicleProperty::TirePressureLeftFront);
183         props.push_back(VehicleProperty::TirePressureRightFront);
184         props.push_back(VehicleProperty::TirePressureLeftRear);
185         props.push_back(VehicleProperty::TirePressureRightRear);
186         props.push_back(VehicleProperty::TireTemperatureLeftFront);
187         props.push_back(VehicleProperty::TireTemperatureRightFront);
188         props.push_back(VehicleProperty::TireTemperatureLeftRear);
189         props.push_back(VehicleProperty::TireTemperatureRightRear);
190         
191         return props;
192 }
193
194 void TpmsPlugin::unsubscribeToPropertyChanges(VehicleProperty::Property property)
195 {
196         mRequests.remove(property);
197 }
198
199 int TpmsPlugin::findDevice(void)
200 {
201   int deviceVid = DEVICE_VID;
202   int devicePid = DEVICE_PID;
203
204   DebugOut() << "TPMS: Trying to open USB device with VID: " << deviceVid << " PID: " << devicePid << endl;
205   mDeviceHandle = libusb_open_device_with_vid_pid(NULL, DEVICE_VID, DEVICE_PID);
206
207   return mDeviceHandle ? 0 : -1;
208 }
209
210
211 int TpmsPlugin::detachDevice(void)
212 {
213   int r;
214   r = libusb_kernel_driver_active(mDeviceHandle, 0);
215   if (r == 1) {
216     DebugOut() << "TPMS: USB device seems to be kernel driven, trying to detach" << endl;
217     r = libusb_detach_kernel_driver(mDeviceHandle, 0);
218   }
219   return r;
220 }
221
222
223 int TpmsPlugin::exitClean(int deinit)
224 {
225   if (deinit) {
226     libusb_release_interface(mDeviceHandle, 0);
227     libusb_attach_kernel_driver(mDeviceHandle, 0);
228     libusb_close(mDeviceHandle);
229   }
230   libusb_exit(NULL);
231 }
232
233
234 int TpmsPlugin::readValues()
235 {
236   int snum;
237   unsigned char buf[4];
238
239   // Sensor 1 = Left Front
240   // Sensor 2 = Right Front
241   // Sensor 3 = Left Rear
242   // Sensor 4 = Right Rear
243
244   for (snum = 1; snum <= MAX_SENSORS; snum++) {
245     readUsbSensor(snum, buf);
246
247     // only do this if sensor is available
248     if (buf[3] != 0xff) {
249       string mode_string;
250       char print_string[100];
251
252       switch (snum) {
253       case 1:
254         lfPressure = ((float)buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
255         lfTemperature = (float)buf[1]-40;
256         sprintf(print_string, "TPMS: Left front pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", lfPressure, lfTemperature);
257         DebugOut() << print_string << endl;
258         break;
259       case 2:
260         rfPressure = (buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
261         rfTemperature = buf[1]-40;
262         sprintf(print_string, "TPMS: Right front pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", rfPressure, rfTemperature);
263         DebugOut() << print_string << endl;
264         break;
265       case 3:
266         lrPressure = (buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
267         lrTemperature = buf[1]-40;
268         sprintf(print_string, "TPMS: Left rear pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", lrPressure, lrTemperature);
269         DebugOut() << print_string << endl;
270         break;
271       case 4:
272         rrPressure = (buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
273         rrTemperature = buf[1]-40;
274         sprintf(print_string, "TPMS: Right rear pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", rrPressure, rrTemperature);
275         DebugOut() << print_string << endl;
276         break;
277       }
278
279       // make sensor mode human-readable
280       // FIXME: for future reference, modes not being used
281       switch (buf[3]) {
282         case 0x01: mode_string = "normal"; break;
283         case 0x02: mode_string = "pressure_alert"; break;
284         // more to add here...
285         default: mode_string = "unknown"; break;
286       }
287     }
288     else {
289       DebugOut() << "TPMS: Unable to read sensor " << sensorNumberToString(snum) << " (" << snum << ")" << endl;
290     }
291   }
292
293   VehicleProperty::TirePressureType lfPres(lfPressure);
294   VehicleProperty::TirePressureType rfPres(rfPressure);
295   VehicleProperty::TirePressureType lrPres(lrPressure);
296   VehicleProperty::TirePressureType rrPres(rrPressure);
297   VehicleProperty::TireTemperatureType lfTemp(lfTemperature);
298   VehicleProperty::TireTemperatureType rfTemp(rfTemperature);
299   VehicleProperty::TireTemperatureType lrTemp(lrTemperature);
300   VehicleProperty::TireTemperatureType rrTemp(rrTemperature);
301
302   routingEngine->updateProperty(VehicleProperty::TirePressureLeftFront, &lfPres, uuid(), amb::currentTime(), 0);
303   routingEngine->updateProperty(VehicleProperty::TirePressureRightFront, &rfPres, uuid(), amb::currentTime(), 0);
304   routingEngine->updateProperty(VehicleProperty::TirePressureLeftRear, &lrPres, uuid(), amb::currentTime(), 0);
305   routingEngine->updateProperty(VehicleProperty::TirePressureRightRear, &rrPres, uuid(), amb::currentTime(), 0);
306   routingEngine->updateProperty(VehicleProperty::TireTemperatureLeftFront, &lfTemp, uuid(), amb::currentTime(), 0);
307   routingEngine->updateProperty(VehicleProperty::TireTemperatureRightFront, &rfTemp, uuid(), amb::currentTime(), 0);
308   routingEngine->updateProperty(VehicleProperty::TireTemperatureLeftRear, &lrTemp, uuid(), amb::currentTime(), 0);
309   routingEngine->updateProperty(VehicleProperty::TireTemperatureRightRear, &rrTemp, uuid(), amb::currentTime(), 0);
310
311   return 0;
312 }
313
314 int TpmsPlugin::readUsbSensor(int sid, unsigned char *buf)
315 {
316   int r, transferred;
317
318   buf[0] = 0x20 + sid;
319   r = libusb_interrupt_transfer(mDeviceHandle, ENDPOINT_OUT, buf, 1, &transferred, INTR_TIMEOUT);
320   if (r < 0) {
321     DebugOut() << "TPMS: USB write interrupt failed, code " << r << endl;
322   }
323
324   r = libusb_interrupt_transfer(mDeviceHandle, ENDPOINT_IN, buf, 4, &transferred, INTR_TIMEOUT);
325   if (r < 0) {
326     DebugOut() << "TPMS: USB read interrupt failed, code " << r << endl;
327   }
328
329   return r;
330 }
331
332
333 string TpmsPlugin::sensorNumberToString(int snid)
334 {
335   switch (snid) {
336   case 1: return "left front"; break;
337   case 2: return "right front"; break;
338   case 3: return "left rear"; break;
339   case 4: return "right rear"; break;
340   default: return "unknown";
341   }
342 }