2dea1fdb88f94a3ad2d6a68ac7f0f864626f39af
[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 const string TpmsPlugin::uuid()
106 {
107         return "5e896a00-15b3-11e3-8ffd-0800200c9a66";
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::TirePressureLeftFrontType temp(lfPressure);
117           reply->success = true;
118       reply->value = &temp;
119       reply->completed(reply);
120     }
121     else if(reply->property == VehicleProperty::TirePressureRightFront) {
122           VehicleProperty::TirePressureRightFrontType temp(rfPressure);
123           reply->success = true;
124       reply->value = &temp;
125       reply->completed(reply);
126     }
127     else if(reply->property == VehicleProperty::TirePressureLeftRear) {
128           VehicleProperty::TirePressureLeftRearType temp(lrPressure);
129           reply->success = true;
130       reply->value = &temp;
131       reply->completed(reply);
132     }
133     else if(reply->property == VehicleProperty::TirePressureRightRear) {
134           VehicleProperty::TirePressureRightRearType temp(rrPressure);
135           reply->success = true;
136       reply->value = &temp;
137       reply->completed(reply);
138     }
139     else if(reply->property == VehicleProperty::TireTemperatureLeftFront) {
140           VehicleProperty::TireTemperatureLeftFrontType temp(lfTemperature);
141           reply->success = true;
142       reply->value = &temp;
143       reply->completed(reply);
144     }
145     else if(reply->property == VehicleProperty::TireTemperatureRightFront) {
146           VehicleProperty::TireTemperatureRightFrontType temp(rfTemperature);
147           reply->success = true;
148       reply->value = &temp;
149       reply->completed(reply);
150     }
151     else if(reply->property == VehicleProperty::TireTemperatureLeftRear) {
152           VehicleProperty::TireTemperatureLeftRearType temp(lrTemperature);
153           reply->success = true;
154       reply->value = &temp;
155       reply->completed(reply);
156     }
157     else if(reply->property == VehicleProperty::TireTemperatureRightRear) {
158           VehicleProperty::TireTemperatureRightRearType temp(rrTemperature);
159           reply->success = true;
160       reply->value = &temp;
161       reply->completed(reply);
162     }
163
164     else {
165       DebugOut() << "TPMS: no such getProperty type: " << reply->property << endl;
166           reply->success = false;
167           reply->error = AsyncPropertyReply::InvalidOperation;
168       reply->value = nullptr;
169       reply->completed(reply);
170         }
171 }
172
173 void TpmsPlugin::getRangePropertyAsync(AsyncRangePropertyReply *reply)
174 {
175         ///not supported
176         reply->completed(reply);
177 }
178
179 AsyncPropertyReply *TpmsPlugin::setProperty(AsyncSetPropertyRequest request )
180 {
181         return NULL;
182 }
183
184 void TpmsPlugin::subscribeToPropertyChanges(VehicleProperty::Property property)
185 {
186         mRequests.push_back(property);
187 }
188
189 PropertyList TpmsPlugin::supported()
190 {
191         PropertyList props;
192     props.push_back(VehicleProperty::TirePressureLeftFront);
193         props.push_back(VehicleProperty::TirePressureRightFront);
194         props.push_back(VehicleProperty::TirePressureLeftRear);
195         props.push_back(VehicleProperty::TirePressureRightRear);
196         props.push_back(VehicleProperty::TireTemperatureLeftFront);
197         props.push_back(VehicleProperty::TireTemperatureRightFront);
198         props.push_back(VehicleProperty::TireTemperatureLeftRear);
199         props.push_back(VehicleProperty::TireTemperatureRightRear);
200         
201         return props;
202 }
203
204 void TpmsPlugin::unsubscribeToPropertyChanges(VehicleProperty::Property property)
205 {
206         mRequests.remove(property);
207 }
208
209 int TpmsPlugin::findDevice(void)
210 {
211   int deviceVid = DEVICE_VID;
212   int devicePid = DEVICE_PID;
213
214   DebugOut() << "TPMS: Trying to open USB device with VID: " << deviceVid << " PID: " << devicePid << endl;
215   mDeviceHandle = libusb_open_device_with_vid_pid(NULL, DEVICE_VID, DEVICE_PID);
216
217   return mDeviceHandle ? 0 : -1;
218 }
219
220
221 int TpmsPlugin::detachDevice(void)
222 {
223   int r;
224   r = libusb_kernel_driver_active(mDeviceHandle, 0);
225   if (r == 1) {
226     DebugOut() << "TPMS: USB device seems to be kernel driven, trying to detach" << endl;
227     r = libusb_detach_kernel_driver(mDeviceHandle, 0);
228   }
229   return r;
230 }
231
232
233 int TpmsPlugin::exitClean(int deinit)
234 {
235   if (deinit) {
236     libusb_release_interface(mDeviceHandle, 0);
237     libusb_attach_kernel_driver(mDeviceHandle, 0);
238     libusb_close(mDeviceHandle);
239   }
240   libusb_exit(NULL);
241 }
242
243
244 int TpmsPlugin::readValues()
245 {
246   int snum;
247   unsigned char buf[4];
248
249   // Sensor 1 = Left Front
250   // Sensor 2 = Right Front
251   // Sensor 3 = Left Rear
252   // Sensor 4 = Right Rear
253
254   for (snum = 1; snum <= MAX_SENSORS; snum++) {
255     readUsbSensor(snum, buf);
256
257     // only do this if sensor is available
258     if (buf[3] != 0xff) {
259       string mode_string;
260       char print_string[100];
261
262       switch (snum) {
263       case 1:
264         lfPressure = ((float)buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
265         lfTemperature = (float)buf[1]-40;
266         sprintf(print_string, "TPMS: Left front pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", lfPressure, lfTemperature);
267         DebugOut() << print_string << endl;
268         break;
269       case 2:
270         rfPressure = (buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
271         rfTemperature = buf[1]-40;
272         sprintf(print_string, "TPMS: Right front pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", rfPressure, rfTemperature);
273         DebugOut() << print_string << endl;
274         break;
275       case 3:
276         lrPressure = (buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
277         lrTemperature = buf[1]-40;
278         sprintf(print_string, "TPMS: Left rear pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", lrPressure, lrTemperature);
279         DebugOut() << print_string << endl;
280         break;
281       case 4:
282         rrPressure = (buf[0]-40) * PRESSURE_SCALE * KPA_MULTIPLIER;
283         rrTemperature = buf[1]-40;
284         sprintf(print_string, "TPMS: Right rear pressure = %5.1f kPa, temperature = %5.1f degrees Celsius", rrPressure, rrTemperature);
285         DebugOut() << print_string << endl;
286         break;
287       }
288
289       // make sensor mode human-readable
290       // FIXME: for future reference, modes not being used
291       switch (buf[3]) {
292         case 0x01: mode_string = "normal"; break;
293         case 0x02: mode_string = "pressure_alert"; break;
294         // more to add here...
295         default: mode_string = "unknown"; break;
296       }
297     }
298     else {
299       DebugOut() << "TPMS: Unable to read sensor " << sensorNumberToString(snum) << " (" << snum << ")" << endl;
300     }
301   }
302
303   VehicleProperty::TirePressureLeftFrontType lfPres(lfPressure);
304   VehicleProperty::TirePressureRightFrontType rfPres(rfPressure);
305   VehicleProperty::TirePressureLeftRearType lrPres(lrPressure);
306   VehicleProperty::TirePressureRightRearType rrPres(rrPressure);
307   VehicleProperty::TireTemperatureLeftFrontType lfTemp(lfTemperature);
308   VehicleProperty::TireTemperatureRightFrontType rfTemp(rfTemperature);
309   VehicleProperty::TireTemperatureLeftRearType lrTemp(lrTemperature);
310   VehicleProperty::TireTemperatureRightRearType rrTemp(rrTemperature);
311
312   routingEngine->updateProperty(&lfPres, uuid());
313   routingEngine->updateProperty(&rfPres, uuid());
314   routingEngine->updateProperty(&lrPres, uuid());
315   routingEngine->updateProperty(&rrPres, uuid());
316   routingEngine->updateProperty(&lfTemp, uuid());
317   routingEngine->updateProperty(&rfTemp, uuid());
318   routingEngine->updateProperty(&lrTemp, uuid());
319   routingEngine->updateProperty(&rrTemp, uuid());
320
321   return 0;
322 }
323
324 int TpmsPlugin::readUsbSensor(int sid, unsigned char *buf)
325 {
326   int r, transferred;
327
328   buf[0] = 0x20 + sid;
329   r = libusb_interrupt_transfer(mDeviceHandle, ENDPOINT_OUT, buf, 1, &transferred, INTR_TIMEOUT);
330   if (r < 0) {
331     DebugOut() << "TPMS: USB write interrupt failed, code " << r << endl;
332   }
333
334   r = libusb_interrupt_transfer(mDeviceHandle, ENDPOINT_IN, buf, 4, &transferred, INTR_TIMEOUT);
335   if (r < 0) {
336     DebugOut() << "TPMS: USB read interrupt failed, code " << r << endl;
337   }
338
339   return r;
340 }
341
342
343 string TpmsPlugin::sensorNumberToString(int snid)
344 {
345   switch (snid) {
346   case 1: return "left front"; break;
347   case 2: return "right front"; break;
348   case 3: return "left rear"; break;
349   case 4: return "right rear"; break;
350   default: return "unknown";
351   }
352 }