First Extras-Testing Candidate Release
[pierogi] / protocols / mceprotocol.cpp
1 #include "mceprotocol.h"
2
3 #include "pirrx51hardware.h"
4
5 #include "pirexception.h"
6
7 #include <QMutex>
8 extern bool commandInFlight;
9 extern QMutex commandIFMutex;
10
11 // These defines might need to be turned into variables, for odd devices.
12 #define HEADER_PULSE 2666
13 #define HEADER_SPACE 888
14 #define TRAILER_BIPHASE 888
15
16 // Ok, what I've got on MCE protocol is this:
17 // It is based on RC6 mode 6A, with a few odd tweaks.
18 // The biphase unit of time is 444 usec.
19 // The RC6 header block starts with the normal 2666 usec pulse, 888 usec space.
20 // The next bit is fixed as a "1", as usual.
21 // The next three bits are 110, marking this as a mode 6 protocol.
22 // The trailer bit has an 888 usec biphase, is set to 0, and is _not_ toggled!
23 // The next 16 bits I'm not sure about.  Hopefully they will be a fixed value
24 // for any given device...
25 // Then, the next bit is the new toggle bit.
26 // Following this, there are seven "device address" bits, and then eight
27 // "command" bits.
28 // A space of (at least) 2666 usec must follow any command.
29 // The carrier frequency is 36 kHZ, duty cycle between 25 and 50 %.
30
31 MCEProtocol::MCEProtocol(
32   QObject *guiObject,
33   unsigned int index,
34   unsigned int oemData)
35   : PIRProtocol(guiObject, index, 2666, false),
36     biphaseUnit(444),
37     buffer(0),
38     keypressCount(0)
39 {
40   setCarrierFrequency(36000);
41   appendToBitSeq(oemBits, oemData, 16);
42 }
43
44
45 void MCEProtocol::startSendingCommand(
46   unsigned int threadableID,
47   PIRKeyName command)
48 {
49   try
50   {
51     // Is this command meant for us?
52     if (threadableID != id) return;
53
54     clearRepeatFlag();
55
56     KeycodeCollection::const_iterator i = keycodes.find(command);
57
58     // Sanity check:
59     if (i == keycodes.end())
60     {
61       std::string s = "Tried to send a non-existent command.\n";
62       throw PIRException(s);
63     }
64
65     PIRRX51Hardware rx51device(carrierFrequency, dutyCycle);
66
67     int repeatCount = 0;
68     int duration = 0;
69     while (repeatCount < MAX_REPEAT_COUNT)
70     {
71       bufferContainsSpace = false;
72       bufferContainsPulse = false;
73       // First, construct the "Header" segment of the pulse train.
74       //
75       // The header involves:
76       // a) a "lead" of 2666 us pulse, 888 us space;
77       // b) a "start bit", value 1 (so 444 us pulse, 444 us space)
78       // c) three control bits, set to "110" (i.e., mode "6")
79       // d) the double-sized "trailer" bit, set to 0.
80
81       rx51device.addSingle(HEADER_PULSE); // lead pulse
82       duration += HEADER_PULSE;
83       rx51device.addSingle(HEADER_SPACE); // lead space
84       duration += HEADER_SPACE;
85       rx51device.addSingle(biphaseUnit); // start bit pulse
86       duration += biphaseUnit;
87       rx51device.addSingle(biphaseUnit); // start bit space
88       duration += biphaseUnit;
89       rx51device.addSingle(biphaseUnit); // bit 1 pulse;
90       duration += biphaseUnit;
91       rx51device.addSingle(biphaseUnit); // bit 1 space;
92       duration += biphaseUnit;
93       rx51device.addSingle(biphaseUnit); // bit 2 pulse;
94       duration += biphaseUnit;
95       rx51device.addSingle(2 * biphaseUnit); // bit 2 space + bit 3 space;
96       duration += 2 * biphaseUnit;
97       rx51device.addSingle(biphaseUnit); // bit 3 pulse;
98       duration += biphaseUnit;
99       rx51device.addSingle(2 * biphaseUnit); // trailer space
100       duration += 2 * biphaseUnit;
101       buffer = 2 * biphaseUnit; // trailer pulse goes into the buffer
102       bufferContainsPulse = true;
103
104       // Now, we can start the normal buffering process:
105
106       // push the "OEM" data:
107       duration += pushBits(oemBits, rx51device);
108
109       // The next bit is the MCE toggle bit:
110       if (keypressCount % 2)
111       {
112         pushOne(rx51device);
113       }
114       else
115       {
116         pushZero(rx51device);
117       }
118
119       // push the device address data:
120       duration += pushBits(preData, rx51device);
121
122       // push the command data:
123       duration += pushBits((*i).second.firstCode, rx51device);
124
125       // Flush out the buffer, if necessary:
126       if (buffer)
127       {
128         rx51device.addSingle(buffer);
129         duration += buffer;
130         buffer = 0;
131       }
132
133       // Actually send out the command:
134       rx51device.sendCommandToDevice();
135
136       // Sleep for an amount of time.  (RC6 demands an addtional 6 unit space
137       // at the end of any command...)
138       sleepUntilRepeat(duration + 6 * biphaseUnit);
139
140       // Have we been told to stop yet?
141       if (checkRepeatFlag())
142       {
143         // Yes, we can now quit repeating:
144         ++keypressCount;
145         QMutexLocker ciflocker(&commandIFMutex);
146         commandInFlight = false;
147         return;
148       }
149     }
150   }
151   catch (PIRException e)
152   {
153     emit commandFailed(e.getError().c_str());
154   }
155
156   ++keypressCount;
157   QMutexLocker cifLocker(&commandIFMutex);
158   commandInFlight = false;
159 }
160
161
162 int MCEProtocol::pushBits(
163   const CommandSequence &bits,
164   PIRRX51Hardware &rx51device)
165 {
166   int duration = 0;
167
168   CommandSequence::const_iterator i = bits.begin();
169
170   while (i != bits.end())
171   {
172     if (*i)
173     {
174       duration += pushOne(rx51device);
175     }
176     else
177     {
178       duration += pushZero(rx51device);
179     }
180
181     ++i;
182   }
183
184   return duration;
185 }
186
187
188 // This should be part of a general RC6 parent maybe?
189 int MCEProtocol::pushZero(
190   PIRRX51Hardware &rx51device)
191 {
192   // Need to add a space, then a pulse.
193   int duration = 0;
194
195   if (bufferContainsSpace)
196   {
197     // Merge this space and the previous one, and send to device:
198     rx51device.addSingle(buffer + biphaseUnit);
199     duration += (buffer + biphaseUnit);
200     buffer = 0;
201      bufferContainsSpace = false;
202   }
203   else
204   {
205     if (bufferContainsPulse)
206     {
207       // Flush out the buffer:
208       rx51device.addSingle(buffer);
209       duration += buffer;
210       buffer = 0;
211       bufferContainsPulse = false;
212     }
213
214     // push a space onto the device:
215     rx51device.addSingle(biphaseUnit);
216     duration += biphaseUnit;
217   }
218
219   // Put a pulse into the buffer to wait:
220   buffer = biphaseUnit;
221   bufferContainsPulse = true;
222
223   return duration;
224 }
225
226
227 int MCEProtocol::pushOne(
228   PIRRX51Hardware &rx51device)
229 {
230   // Need to add a pulse, then a space.
231   int duration = 0;
232
233   // First, the pulse:
234   if (bufferContainsPulse)
235   {
236     rx51device.addSingle(buffer + biphaseUnit);
237     duration += (buffer + biphaseUnit);
238     buffer = 0;
239     bufferContainsPulse = false;
240   }
241   else
242   {
243     if (bufferContainsSpace)
244     {
245       // Flush the buffer:
246       rx51device.addSingle(buffer);
247       duration += buffer;
248       buffer = 0;
249       bufferContainsSpace = false;
250     }
251     // Now, add the pulse:
252     rx51device.addSingle(biphaseUnit);
253     duration += biphaseUnit;
254   }
255
256   // Next, push a space onto the buffer:
257   buffer = biphaseUnit;
258   bufferContainsSpace = true;
259
260   return duration;
261 }