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