1 /*
2 * Copyright (C) 2006 Paul Davis
3 * Copyright (C) 2007 Michael Taht
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 */
20
21 #include <tranzport_common.h>
22 #include <tranzport_control_protocol.h>
23
24 using namespace ARDOUR;
25 using namespace std;
26 using namespace sigc;
27 using namespace PBD;
28
29 #include "pbd/i18n.h"
30
31 #include <pbd/abstract_ui.cc>
32
33 void*
_monitor_work(void * arg)34 TranzportControlProtocol::_monitor_work (void* arg)
35 {
36 return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
37 }
38
~TranzportControlProtocol()39 TranzportControlProtocol::~TranzportControlProtocol ()
40 {
41 set_active (false);
42 }
43
rtpriority_set(int priority)44 int TranzportControlProtocol::rtpriority_set(int priority)
45 {
46 char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b';
47 // Note - try SCHED_RR with a low limit
48 // - we don't care if we can't write everything this ms
49 // and it will help if we lose the device
50 if (set_thread_priority (SCHED_FIFO, priority)) {
51 PBD::info << string_compose (_("%1: thread not running with realtime scheduling."), name(), strerror (errno)) << endmsg;
52 return 1;
53 }
54 return 0;
55 }
56
57 // Running with realtime privs is bad when you have problems
58
rtpriority_unset(int priority)59 int TranzportControlProtocol::rtpriority_unset(int priority)
60 {
61 struct sched_param rtparam;
62 int err;
63 memset (&rtparam, 0, sizeof (rtparam));
64 rtparam.sched_priority = priority;
65 if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
66 PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
67 return 1;
68 }
69 PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
70 return 0;
71 }
72
73
74 int
set_active(bool yn)75 TranzportControlProtocol::set_active (bool yn)
76 {
77 if (yn != _active) {
78
79 if (yn) {
80
81 if (open ()) {
82 return -1;
83 }
84
85 if (pthread_create_and_store (X_("tranzport monitor"), &thread, _monitor_work, this) == 0) {
86 _active = true;
87 } else {
88 return -1;
89 }
90
91 } else {
92 cerr << "Begin tranzport shutdown\n";
93 // if we got here due to an error, prettifying things will only make it worse
94 // And with threads involved, oh boy...
95 if(!(last_write_error || last_read_error)) {
96 bling_mode = BlingExit;
97 enter_bling_mode();
98 // thread FIXME - wait til all writes are done
99 for(int x = 0; (x < 20/MAX_TRANZPORT_INFLIGHT) && flush(); x++) { usleep(100); }
100 }
101
102 pthread_cancel_one (thread);
103
104 cerr << "Tranzport Thread dead\n";
105 close ();
106 _active = false;
107 cerr << "End tranzport shutdown\n";
108 }
109 }
110
111 return 0;
112 }
113
TranzportControlProtocol(Session & s)114 TranzportControlProtocol::TranzportControlProtocol (Session& s)
115 : ControlProtocol (s, X_("Tranzport"))
116 {
117 /* tranzport controls one track at a time */
118
119 set_route_table_size (1);
120 timeout = 6000; // what is this for?
121 buttonmask = 0;
122 _datawheel = 0;
123 _device_status = STATUS_OFFLINE;
124 udev = 0;
125 current_track_id = 0;
126 last_where = max_samples;
127 wheel_mode = WheelTimeline;
128 wheel_shift_mode = WheelShiftGain;
129 wheel_increment = WheelIncrScreen;
130 bling_mode = BlingEnter;
131 last_notify_msg[0] = '\0';
132 last_notify = 0;
133 timerclear (&last_wheel_motion);
134 last_wheel_dir = 1;
135 last_track_gain = FLT_MAX;
136 last_write_error = 0;
137 last_read_error = 0;
138 display_mode = DisplayBling;
139 gain_fraction = 0.0;
140 invalidate();
141 screen_init();
142 lights_init();
143 // FIXME: Wait til device comes online somewhere
144 // About 3 reads is enough
145 // enter_bling_mode();
146
147 }
148
149 void*
monitor_work()150 TranzportControlProtocol::monitor_work ()
151 {
152 uint8_t buf[8]; // = { 0,0,0,0,0,0,0,0 };
153 int val = 0, pending = 0;
154 bool first_time = true;
155 uint8_t offline = 0;
156
157 register_thread (X_("Tranzport"));
158 pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
159 pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
160 rtpriority_set();
161 inflight=0;
162 //int intro = 20;
163
164 // wait for the device to come online
165 invalidate();
166 screen_init();
167 lights_init();
168 update_state();
169 // There has to be some specific command to enable the device!!
170 // while((val = read(buf,DEFAULT_USB_TIMEOUT*5)) == -110 && pending !=0) {
171 // pending = lights_flush(); // poke the device for a while
172 // }
173
174 // pending = 1;
175 // while(intro-- > 0 && pending != 0) {
176 // usleep(1000);
177 // pending = screen_flush(); // kinder, gentler init
178 // }
179 // usleep(1000);
180 // lights_on();
181 // while(flush()!=0) ;
182 // lights_off();
183 display_mode = DisplayNormal;
184
185 while (true) {
186
187 /* bInterval for this beastie is 10ms */
188
189 if (_device_status == STATUS_OFFLINE) {
190 first_time = true; offline++;
191 #if TRANZPORT_DEBUG > 3
192 if(offline == 1) {
193 cerr << "Transport has gone offline\n";
194 }
195 #endif
196 } else {
197 offline = 0; // hate writing this
198 }
199 unsigned int s = (last_write_error == 0) | ((last_read_error == 0) << 1);
200 switch (s) {
201 case 0: val = read(buf,DEFAULT_USB_TIMEOUT); break;
202 case 1: val = read(buf,DEFAULT_USB_TIMEOUT); break;
203 case 2: val = read(buf,DEFAULT_USB_TIMEOUT); break;
204 case 3: val = read(buf,DEFAULT_USB_TIMEOUT*2); break; // Hoo, boy, we're in trouble
205 default: break; // not reached
206 }
207
208 #if DEBUG_TRANZPORT_BITS > 9
209 if(_device_status != STATUS_OFFLINE && _device_status != STATUS_ONLINE && _device_status != STATUS_OK) {
210 printf("The device has more status bits than off or online: %d\n",_device_status);
211 }
212 #endif
213
214 #if DEBUG_TRANZPORT_BITS > 99
215 if (val != 8) {
216 printf("val = %d errno = %d\n",val,errno);
217 buf[0] = buf[1] = buf[2] = buf[3] =
218 buf[4] = buf[5] = buf[6] = buf[7] =
219 buf[8] = 0;
220 }
221 #endif
222
223 if(val == 8) {
224 last_write_error = 0;
225 process (buf);
226 }
227
228 #if DEBUG_TRANZPORT > 9
229 if(inflight > 1) printf("Inflight: %d\n", inflight);
230 #endif
231
232 if (_device_status == STATUS_ONLINE) {
233 if (first_time) {
234 invalidate();
235 lcd_clear ();
236 lights_off ();
237 first_time = false;
238 last_write_error = 0;
239 offline = 0;
240 pending = 3; // Give some time for the device to recover
241 }
242 #if DEBUG_TRANZPORT_BITS > 10
243 // Perhaps an online message indicates something
244
245 if(_device_status != buf[1]) {
246 printf("WTF- val: %d, device status != buf! %d != %d \n",val,_device_status,buf[1]); _device_status = buf[1];
247 }
248 #endif
249
250 }
251
252 #if DEBUG_TRANZPORT_BITS > 10
253
254 if(val == 8) {
255
256 if(_device_status == STATUS_ONLINE) {
257 printf("ONLINE : %02x %02x %02x %02x %02x %02x %02x %02x\n",
258 buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
259 }
260 if(_device_status == STATUS_OFFLINE) {
261 printf("OFFLINE : %02x %02x %02x %02x %02x %02x %02x %02x\n",
262 buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
263 }
264
265 if(_device_status == STATUS_OK) {
266 printf("OK : %02x %02x %02x %02x %02x %02x %02x %02x\n",
267 buf[0],buf[1],buf[2], buf[3], buf[4], buf[5],buf[6],buf[7]);
268 }
269
270 }
271
272 #endif
273
274 /* update whatever needs updating */
275 if(last_write_error == 0 && (_device_status == STATUS_ONLINE || _device_status == STATUS_OK)) {
276 update_state ();
277
278 /* still struggling with a good means of exerting flow control without having to create threads */
279 // pending = flush();
280
281 if(pending == 0) {
282 pending = flush();
283 } else {
284 if(inflight > 0) {
285 pending = --inflight; // we just did a whole bunch of writes so wait
286 } else {
287 pending = 0;
288 }
289 }
290 }
291 // pending = 0;
292 }
293 return (void*) 0;
294 }
295
296