Commit 76a02d8725b71848f32204dbc48816db06fc488d

nushor 2012-08-03T12:15:03

Update debian package configs to v2.6.2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
diff --git a/debian/changelog b/debian/changelog
index 85339a9..df3ef2c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,112 +1,146 @@
+cgminer (2.6.2-1) precise; urgency=low
+  Version 2.6.2 - August 3, 2012
+
+  * Scrypt mining does not support block testing yet so don't try to print it.
+  * Clear the bitforce buffer whenever we get an unexpected result as it has
+    likely throttled and we are getting cached responses out of order, and use the
+    temperature monitoring as a kind of watchdog to flush unexpected results.
+  * It is not critical getting the temperature response in bitforce so don't
+    mandatorily wait on the mutex lock.
+  * Check there is a cutoff temp actually set in bitforce before using it as a cut
+    off value otherwise it may think it's set to zero degrees.
+  * We dropped the temporary stopping of curl recruiting on submit_fail by
+    mistake, reinstate it.
+  * Make threads report in either side of the scanhash function in case we miss
+    reporting in when restarting work.
+  * Don't make mandatory work and its clones last forever.
+  * Make test work for pool_active mandatory work items to smooth out staged work
+    counts when in failover-only mode.
+  * Add debugging output when work is found stale as to why.
+  * Print the 3 parameters that are passed to applog for a debug line in
+    bitforce.c
+  * Clear bitforce buffer on init as previously.
+  * Add some headroom to the number of curls available per pool to allow for
+    longpoll and sendwork curls.
+  * Revert "Revert "Change BFL driver thread initialising to a constant 100ms
+    delay between devices instead of a random arrangement.""
+  * Revert "Remove bitforce_thread_init"
+  * Show the correct base units on GPU summary.
+  * Differentiate between the send return value being a bool and the get return
+    value when managing them in bitforce scanhash.
+  * 23a8c60 Revert "bitforce: Skip out of sending work if work restart requested" 
+
+ -- nushor <nushor@nushor-desktop>  Fri, 03 Aug 2012 11:27:44 -0500
+
 cgminer (2.4.2-1) stable; urgency=medium
   Version 2.4.2 - June 2, 2012
 
-  - API.class compiled with Java SE 6.0_03 - works with Win7x64
-  - miner.php highlight devs too slow finding shares (possibly failing)
-  - API update version to V1.11 and document changes
-  - API save default config file if none specified
-  - api.c save success incorrectly returns error
-  - api.c replace BUFSIZ (linux/windows have different values)
-  - Move RPC API content out of README to API-README
-  - Open a longpoll connection if a pool is in the REJECTING state as it's the
+  * API.class compiled with Java SE 6.0_03 - works with Win7x64
+  * miner.php highlight devs too slow finding shares (possibly failing)
+  * API update version to V1.11 and document changes
+  * API save default config file if none specified
+  * api.c save success incorrectly returns error
+  * api.c replace BUFSIZ (linux/windows have different values)
+  * Move RPC API content out of README to API-README
+  * Open a longpoll connection if a pool is in the REJECTING state as it's the
     only way to re-enable it automatically.
-  - Use only one longpoll as much as possible by using a pthread conditional
+  * Use only one longpoll as much as possible by using a pthread conditional
     broadcast that each longpoll thread waits on and checks if it's the current pool
     before
-  - If shares are known stale, don't use them to decide to disable a pool for
+  * If shares are known stale, don't use them to decide to disable a pool for
     sequential rejects.
-  - Restarting cgminer from within after ADL has been corrupted only leads to a
+  * Restarting cgminer from within after ADL has been corrupted only leads to a
     crash. Display a warning only and disable fanspeed monitoring.
-  - Icarus: fix abort calculation/allow user specified abort
-  - Icarus: make --icarus-timing hidden and document it in FPGA-README
-  - Icarus: high accuracy timing and other bitstream speed support
-  - add-MIPSEB-to-icarus-for-BIG_ENDIAN
-  - work_decode only needs swab32 on midstate under BIG ENDIAN
-  - add compile command to api-example.c
-  - save config bugfix: writing an extra ',' when no gpus
-  - Add dpkg-source commits
+  * Icarus: fix abort calculation/allow user specified abort
+  * Icarus: make --icarus-timing hidden and document it in FPGA-README
+  * Icarus: high accuracy timing and other bitstream speed support
+  * add-MIPSEB-to-icarus-for-BIG_ENDIAN
+  * work_decode only needs swab32 on midstate under BIG ENDIAN
+  * add compile command to api-example.c
+  * save config bugfix: writing an extra ',' when no gpus
+  * Add dpkg-source commits
 
  -- nushor <nushor11@gmail.com>  Sun, 03 Jun 2012 22:02:03 -0500
 
 cgminer (2.4.1-1) stable; urgency=low
   Version 2.4.1-1 - May 6, 2012
-  - In the unlikely event of finding a block, display the block solved count with
+  * In the unlikely event of finding a block, display the block solved count with
     the pool it came from for auditing.
-  - Display the device summary on exit even if a device has been disabled.
-  - Use correct pool enabled enums in api.c.
-  - Import Debian packaging configs
-  - Ensure we test for a pool recovering from idle so long as it's not set to
+  * Display the device summary on exit even if a device has been disabled.
+  * Use correct pool enabled enums in api.c.
+  * Import Debian packaging configs
+  * Ensure we test for a pool recovering from idle so long as it's not set to
     disabled.
-  - Fix pool number display.
-  - Give cgminer -T message only if curses is in use.
-  - Reinit_adl is no longer used.
-  - API 'stats' allow devices to add their own stats also for testing/debug
-  - API add getwork stats to cgminer - accesable from API 'stats'
-  - Don't initialise variables to zero when in global scope since they're already
+  * Fix pool number display.
+  * Give cgminer -T message only if curses is in use.
+  * Reinit_adl is no longer used.
+  * API 'stats' allow devices to add their own stats also for testing/debug
+  * API add getwork stats to cgminer - accesable from API 'stats'
+  * Don't initialise variables to zero when in global scope since they're already
     initialised.
-  - Get rid of unitialised variable warning when it's false.
-  - Move a pool to POOL_REJECTING to be disabled only after 3 minutes of
+  * Get rid of unitialised variable warning when it's false.
+  * Move a pool to POOL_REJECTING to be disabled only after 3 minutes of
     continuous rejected shares.
-  - Some tweaks to reporting and logging.
-  - Change FPGA detection order since BFL hangs on an ICA
-  - API support new pool status
-  - Add a temporarily disabled state for enabled pools called POOL_REJECTING and
+  * Some tweaks to reporting and logging.
+  * Change FPGA detection order since BFL hangs on an ICA
+  * API support new pool status
+  * Add a temporarily disabled state for enabled pools called POOL_REJECTING and
     use the work from each longpoll to help determine when a rejecting pool has
     started working again. Switch pools based on the multipool strategy once a pool
     is re-enabled.
-  - Removing extra debug
-  - Fix the benchmark feature by bypassing the new networking code.
-  - Reset sequential reject counter after a pool is disabled for when it is
+  * Removing extra debug
+  * Fix the benchmark feature by bypassing the new networking code.
+  * Reset sequential reject counter after a pool is disabled for when it is
     re-enabled.
-  - Icarus - correct MH/s and U: with work restart set at 8 seconds
-  - ztex updateFreq was always reporting on fpga 0
-  - Trying harder to get 1.15y working
-  - Specifying threads on multi fpga boards extra cgpu
-  - Missing the add cgpu per extra fpga on 1.15y boards
-  - API add last share time to each pool
-  - Don't try to reap curls if benchmarking is enabled.
+  * Icarus - correct MH/s and U: with work restart set at 8 seconds
+  * ztex updateFreq was always reporting on fpga 0
+  * Trying harder to get 1.15y working
+  * Specifying threads on multi fpga boards extra cgpu
+  * Missing the add cgpu per extra fpga on 1.15y boards
+  * API add last share time to each pool
+  * Don't try to reap curls if benchmarking is enabled.
 
  -- nushor <nushor11@gmail.com>  Sun, 06 May 2012 11:09:46 -0500
 
 cgminer (2.4.0-1) stable; urgency=low
   Version 2.4.0 - May 3, 2012
 
-  - Only show longpoll warning once when it has failed.
-  - Convert hashes to an unsigned long long as well.
-  - Detect pools that have issues represented by endless rejected shares and
+  * Only show longpoll warning once when it has failed.
+  * Convert hashes to an unsigned long long as well.
+  * Detect pools that have issues represented by endless rejected shares and
     disable them, with a parameter to optionally disable this feature.
-  - Bugfix: Use a 64-bit type for hashes_done (miner_thread) since it can overflow
+  * Bugfix: Use a 64-bit type for hashes_done (miner_thread) since it can overflow
     32-bit on some FPGAs
-  - Implement an older header fix for a label existing before the pthread_cleanup
+  * Implement an older header fix for a label existing before the pthread_cleanup
     macro.
-  - Limit the number of curls we recruit on communication failures and with
+  * Limit the number of curls we recruit on communication failures and with
     delaynet enabled to 5 by maintaining a per-pool curl count, and using a pthread
     conditional that wakes up when one is returned to the ring buffer.
-  - Generalise add_pool() functions since they're repeated in add_pool_details.
-  - Bugfix: Return failure, rather than quit, if BFwrite fails
-  - Disable failing devices such that the user can attempt to re-enable them
-  - Bugfix: thread_shutdown shouldn't try to free the device, since it's needed
+  * Generalise add_pool() functions since they're repeated in add_pool_details.
+  * Bugfix: Return failure, rather than quit, if BFwrite fails
+  * Disable failing devices such that the user can attempt to re-enable them
+  * Bugfix: thread_shutdown shouldn't try to free the device, since it's needed
     afterward
-  - API bool's and 1TBS fixes
-  - Icarus - minimise code delays and name timer variables
-  - api.c V1.9 add 'restart' + redesign 'quit' so thread exits cleanly
-  - api.c bug - remove extra ']'s in notify command
-  - Increase pool watch interval to 30 seconds.
-  - Reap curls that are unused for over a minute. This allows connections to be
+  * API bool's and 1TBS fixes
+  * Icarus - minimise code delays and name timer variables
+  * api.c V1.9 add 'restart' + redesign 'quit' so thread exits cleanly
+  * api.c bug - remove extra ']'s in notify command
+  * Increase pool watch interval to 30 seconds.
+  * Reap curls that are unused for over a minute. This allows connections to be
     closed, thereby allowing the number of curl handles to always be the minimum
     necessary to not delay networking.
-  - Use the ringbuffer of curls from the same pool for submit as well as getwork
+  * Use the ringbuffer of curls from the same pool for submit as well as getwork
     threads. Since the curl handles were already connected to the same pool and are
     immediately available, share submission will not be delayed by getworks.
-  - Implement a scaleable networking framework designed to cope with any sized
+  * Implement a scaleable networking framework designed to cope with any sized
     network requirements, yet minimise the number of connections being reopened. Do
     this by create a ring buffer linked list of curl handles to be used by getwork,
     recruiting extra handles when none is immediately available.
-  - There is no need for the submit and getwork curls to be tied to the pool
+  * There is no need for the submit and getwork curls to be tied to the pool
     struct.
-  - Do not recruit extra connection threads if there have been connection errors
+  * Do not recruit extra connection threads if there have been connection errors
     to the pool in question.
-  - We should not retry submitting shares indefinitely or we may end up with a
+  * We should not retry submitting shares indefinitely or we may end up with a
     huge backlog during network outages, so discard stale shares if we failed to
     submit them and they've become stale in the interim.
 
@@ -114,32 +148,32 @@ cgminer (2.4.0-1) stable; urgency=low
 
 cgminer (2.3.6-3) stable; urgency=low
   Version 2.3.6-3 - may 3, 2012
-  - More bug fixes, Pre 2.4.1 release.
+  * More bug fixes, Pre 2.4.1 release.
 
  -- nushor <nushor11@gmail.com>  Thurs, 03 May 2012 00:36:50 -0500
 
 cgminer (2.3.6-2) stable; urgency=low
   Version 2.3.6-2 - May 2, 2012
-  - Various bug fixes, latest build from repository.
+  * Various bug fixes, latest build from repository.
 
  -- nushor <nushor11@gmail.com>  Wed, 02 May 2012 18:17:49 -0500
 
 cgminer (2.3.6-1) stable; urgency=low
 
   Version 2.3.6 - April 29, 2012
-  - Shorten stale share messages slightly.
-  - Protect the freeing of current_hash under mutex_lock to prevent racing on it
+  * Shorten stale share messages slightly.
+  * Protect the freeing of current_hash under mutex_lock to prevent racing on it
     when set_curblock is hit concurrently.
-  - Change default behaviour to submitting stale, removing the --submit-stale
+  * Change default behaviour to submitting stale, removing the --submit-stale
     option and adding a --no-submit-stale option.
-  - Make sure to start the getwork and submit threads when a pool is added on the
+  * Make sure to start the getwork and submit threads when a pool is added on the
     fly. This fixes a crash when a pool is added to running cgminer and then
     switched to.
-  - Faster hardware can easily outstrip the speed we can get work and submit
+  * Faster hardware can easily outstrip the speed we can get work and submit
     shares when using only one connection per pool.
-  - Test the queued list to see if any get/submits are already queued and if they
+  * Test the queued list to see if any get/submits are already queued and if they
     are, start recruiting extra connections by generating new threads.
-  - This allows us to reuse network connections at low loads but recuit new open
+  * This allows us to reuse network connections at low loads but recuit new open
     connections as they're needed, so that cgminer can scale to hardware of any
     size.
 
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..539cc48
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,3 @@
+v2.4.1
+v2.4.2
+v2.6.2
diff --git a/debian/patches/v2.6.2 b/debian/patches/v2.6.2
new file mode 100644
index 0000000..2223a00
--- /dev/null
+++ b/debian/patches/v2.6.2
@@ -0,0 +1,1275 @@
+--- a/FPGA-README
++++ b/FPGA-README
+@@ -16,7 +16,25 @@
+ 
+ Icarus
+ 
+-There is a hidden option in cgminer when Icarus support is compiled in:
++There are two hidden options in cgminer when Icarus support is compiled in:
++
++--icarus-options <arg> Set specific FPGA board configurations - one set of values for all or comma separated
++           baud:work_division:fpga_count
++
++           baud           The Serial/USB baud rate - 115200 or 57600 only - default 115200
++           work_division  The fraction of work divided up for each FPGA chip - 1, 2, 4 or 8
++                          e.g. 2 means each FPGA does half the nonce range - default 2
++           fpga_count     The actual number of FPGA working - this would normally be the same
++                          as work_division - range is from 1 up to 'work_division'
++                          It defaults to the value of work_division - or 2 if you don't specify
++                          work_division
++
++If you define fewer comma seperated values than Icarus devices, the last values will be used
++for all extra devices
++
++An example would be: --icarus-options 57600:2:1
++This would mean: use 57600 baud, the FPGA board divides the work in half however
++only 1 FPGA actually runs on the board (e.g. like an early CM1 Icarus copy bitstream)
+ 
+ --icarus-timing <arg> Set how the Icarus timing is calculated - one setting/value for all or comma separated
+            default[=N]   Use the default Icarus hash time (2.6316ns)
+@@ -24,6 +42,9 @@
+            long          Re-calculate the hash time continuously
+            value[=N]     Specify the hash time in nanoseconds (e.g. 2.6316) and abort time (e.g. 2.6316=80)
+ 
++If you define fewer comma seperated values than Icarus devices, the last values will be used
++for all extra devices
++
+ Icarus timing is required for devices that do not exactly match a default Icarus Rev3 in
+ processing speed
+ If you have an Icarus Rev3 you should not normally need to use --icarus-timing since the
+@@ -55,9 +76,9 @@
+ 'short' mode and take note of the final hash time value (Hs) calculated
+ You can also use the RPC API 'stats' command to see the current hash time (Hs) at any time
+ 
+-The Icarus code currently only works with a dual FPGA device that supports the same commands as
++The Icarus code currently only works with an FPGA device that supports the same commands as
+ Icarus Rev3 requires and also is less than ~840MH/s and greater than 2MH/s
+-If a dual FPGA device does hash faster than ~840MH/s it should work correctly if you supply the
++If an FPGA device does hash faster than ~840MH/s it should work correctly if you supply the
+ correct hash time nanoseconds value
+ 
+ The timing code itself will affect the Icarus performance since it increases the delay after
+--- a/NEWS
++++ b/NEWS
+@@ -1,7 +1,42 @@
++Version 2.6.2 - August 3, 2012
++
++- Scrypt mining does not support block testing yet so don't try to print it.
++- Clear the bitforce buffer whenever we get an unexpected result as it has
++likely throttled and we are getting cached responses out of order, and use the
++temperature monitoring as a kind of watchdog to flush unexpected results.
++- It is not critical getting the temperature response in bitforce so don't
++mandatorily wait on the mutex lock.
++- Check there is a cutoff temp actually set in bitforce before using it as a cut
++off value otherwise it may think it's set to zero degrees.
++- We dropped the temporary stopping of curl recruiting on submit_fail by
++mistake, reinstate it.
++- Make threads report in either side of the scanhash function in case we miss
++reporting in when restarting work.
++- Don't make mandatory work and its clones last forever.
++- Make test work for pool_active mandatory work items to smooth out staged work
++counts when in failover-only mode.
++- Add debugging output when work is found stale as to why.
++- Print the 3 parameters that are passed to applog for a debug line in
++bitforce.c
++- Clear bitforce buffer on init as previously.
++- Add some headroom to the number of curls available per pool to allow for
++longpoll and sendwork curls.
++- Revert "Revert "Change BFL driver thread initialising to a constant 100ms
++delay between devices instead of a random arrangement.""
++- Revert "Remove bitforce_thread_init"
++- Show the correct base units on GPU summary.
++- Differentiate between the send return value being a bool and the get return
++value when managing them in bitforce scanhash.
++- 23a8c60 Revert "bitforce: Skip out of sending work if work restart requested"
++
++
+ Version 2.6.1 - July 30, 2012
+ 
++- Display scrypt as being built in as well.
++- Fix build warning about KL_SCRYPT when built without scrypt support.
+ - Remove the low hash count determinant of hardware being sick. A low hash rate
+-can be for poor network connectivity or scrypt mining, neither of which a
++can be for poor network connectivity or scrypt mining, neither of which are due
++to a sick device.
+ - api.c poolpriority changes
+ 
+ 
+--- a/cgminer.c
++++ b/cgminer.c
+@@ -142,6 +142,7 @@
+ bool opt_api_network;
+ bool opt_delaynet;
+ bool opt_disable_pool = true;
++char *opt_icarus_options = NULL;
+ char *opt_icarus_timing = NULL;
+ 
+ char *opt_kernel_path;
+@@ -710,6 +711,13 @@
+ }
+ 
+ #ifdef USE_ICARUS
++static char *set_icarus_options(const char *arg)
++{
++	opt_set_charp(arg, &opt_icarus_options);
++
++	return NULL;
++}
++
+ static char *set_icarus_timing(const char *arg)
+ {
+ 	opt_set_charp(arg, &opt_icarus_timing);
+@@ -873,6 +881,9 @@
+ 		     "Override sha256 kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated"),
+ #endif
+ #ifdef USE_ICARUS
++	OPT_WITH_ARG("--icarus-options",
++		     set_icarus_options, NULL, NULL,
++		     opt_hidden),
+ 	OPT_WITH_ARG("--icarus-timing",
+ 		     set_icarus_timing, NULL, NULL,
+ 		     opt_hidden),
+@@ -1770,10 +1781,9 @@
+ 
+ 	if (!QUIET) {
+ 		hash32 = (uint32_t *)(work->hash);
+-		if (opt_scrypt) {
+-			sprintf(hashshow, "%08lx.%08lx%s", (unsigned long)(hash32[7]), (unsigned long)(hash32[6]),
+-				work->block? " BLOCK!" : "");
+-		} else {
++		if (opt_scrypt)
++			sprintf(hashshow, "%08lx.%08lx", (unsigned long)(hash32[7]), (unsigned long)(hash32[6]));
++		else {
+ 			sprintf(hashshow, "%08lx.%08lx%s", (unsigned long)(hash32[6]), (unsigned long)(hash32[5]),
+ 				work->block? " BLOCK!" : "");
+ 		}
+@@ -2169,14 +2179,14 @@
+  * network delays/outages. */
+ static struct curl_ent *pop_curl_entry(struct pool *pool)
+ {
+-	int curl_limit = opt_delaynet ? 5 : mining_threads;
++	int curl_limit = opt_delaynet ? 5 : mining_threads * 4 / 3;
+ 	struct curl_ent *ce;
+ 
+ 	mutex_lock(&pool->pool_lock);
+ 	if (!pool->curls)
+ 		recruit_curl(pool);
+ 	else if (list_empty(&pool->curlring)) {
+-		if (pool->curls >= curl_limit)
++		if (pool->submit_fail || pool->curls >= curl_limit)
+ 			pthread_cond_wait(&pool->cr_cond, &pool->pool_lock);
+ 		else
+ 			recruit_curl(pool);
+@@ -2278,9 +2288,6 @@
+ 	struct pool *pool;
+ 	int getwork_delay;
+ 
+-	if (work->mandatory)
+-		return false;
+-
+ 	if (share) {
+ 		/* Technically the rolltime should be correct but some pools
+ 		 * advertise a broken expire= that is lower than a meaningful
+@@ -2306,14 +2313,20 @@
+ 		work_expiry = 5;
+ 
+ 	gettimeofday(&now, NULL);
+-	if ((now.tv_sec - work->tv_staged.tv_sec) >= work_expiry)
++	if ((now.tv_sec - work->tv_staged.tv_sec) >= work_expiry) {
++		applog(LOG_DEBUG, "Work stale due to expiry");
+ 		return true;
++	}
+ 
+-	if (work->work_block != work_block)
++	if (work->work_block != work_block) {
++		applog(LOG_DEBUG, "Work stale due to block mismatch");
+ 		return true;
++	}
+ 
+-	if (opt_fail_only && !share && pool != current_pool() && pool->enabled != POOL_REJECTING)
++	if (opt_fail_only && !share && pool != current_pool() && !work->mandatory) {
++		applog(LOG_DEBUG, "Work stale due to fail only pool mismatch");
+ 		return true;
++	}
+ 
+ 	return false;
+ }
+@@ -3011,6 +3024,8 @@
+ 		fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
+ 	if (opt_api_groups)
+ 		fprintf(fcfg, ",\n\"api-groups\" : \"%s\"", opt_api_groups);
++	if (opt_icarus_options)
++		fprintf(fcfg, ",\n\"icarus-options\" : \"%s\"", opt_icarus_options);
+ 	if (opt_icarus_timing)
+ 		fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", opt_icarus_timing);
+ 	fputs("\n}", fcfg);
+@@ -3584,6 +3599,7 @@
+ 		struct work *work = make_work();
+ 		bool rc;
+ 
++		work->mandatory = true;
+ 		rc = work_decode(json_object_get(val, "result"), work);
+ 		if (rc) {
+ 			applog(LOG_DEBUG, "Successfully retrieved and deciphered work from pool %u %s",
+@@ -3835,6 +3851,7 @@
+ 	memcpy(work_clone, work, sizeof(struct work));
+ 	work_clone->clone = true;
+ 	work_clone->longpoll = false;
++	work_clone->mandatory = false;
+ 	/* Make cloned work appear slightly older to bias towards keeping the
+ 	 * master work item which can be further rolled */
+ 	work_clone->tv_staged.tv_sec -= 1;
+@@ -4192,7 +4209,9 @@
+ 			}
+ 			pool_stats->getwork_calls++;
+ 
++			thread_reportin(mythr);
+ 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
++			thread_reportin(mythr);
+ 
+ 			gettimeofday(&getwork_start, NULL);
+ 
+--- a/configure.ac
++++ b/configure.ac
+@@ -2,7 +2,7 @@
+ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+ m4_define([v_maj], [2])
+ m4_define([v_min], [6])
+-m4_define([v_mic], [1])
++m4_define([v_mic], [2])
+ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
+ m4_define([v_ver], [v_maj.v_min.v_mic])
+ m4_define([lt_rev], m4_eval(v_maj + v_min))
+--- a/debian/changelog
++++ b/debian/changelog
+@@ -1,112 +1,151 @@
++cgminer (2.4.3-1) stable; urgency=medium
++  Version 2.4.3 - June 14, 2012
++
++  * can_roll and should_roll should have no bearing on the cycle period within the 
++    miner_thread so remove it.
++  * Check for strategy being changed to load balance when enabling LPs.
++  * Check that all threads on the device that called get_work are waiting on getwork 
++    before considering the pool lagging.
++  * Iterate over each thread belonging to each device in the hashmeter instead of 
++    searching for them now that they're a list.
++  * When using rotate pool strategy, ensure we only select from alive enabled pools.
++  * Start longpoll from every pool when load balance strategy is in use.
++  * Add mandatory and block fields to the work struct. Flag any shares that are 
++    detected as blocks as mandatory to submit, along with longpoll work from a previously 
++    rejecting pool.
++  * Consider the fan optimal if fanspeed is dropping but within the optimal speed window.
++  * Fix typo in some API messages (succeess/success)
++  * api.c MMQ stat bugs
++  * Bugfix: Fix warnings when built without libudev support
++  * Bugfix: slay a variety of warnings
++  * Bugfix: modminer: Fix unsigned/signed comparison and similar warnings
++  * API add ModMinerQuad support
++  * Bugfix: Honour forceauto parameter in serial_detect functions
++  * modminer: Temperature sensor improvements
++  * modminer: Make log messages more consistent in format
++  * Only adjust GPU speed up if the fanspeed is within the normal fanrange and hasn't been
++    turned to maximum speed under overheat conditions.
++  * ModMiner use valid .name
++  * New driver: BTCFPGA ModMiner
++  * Abstract generally useful FPGA code into fpgautils.c
++  * API add stats for pool getworks
++  * miner.php option to hide specific fields from the display
++  * miner.php add version numbers to the summary page
++  * Update debian configs to v2.4.2
++  * Add API and FPGA READMEs into Makefile to be included in source distribution.
++  * Icarus - fix unit64_t printf warnings
++
++ -- nushor <nushor11@gmail.com>  Fri, 15 Jun 2012 11:31:51 -0500
++
+ cgminer (2.4.2-1) stable; urgency=medium
+   Version 2.4.2 - June 2, 2012
+ 
+-  - API.class compiled with Java SE 6.0_03 - works with Win7x64
+-  - miner.php highlight devs too slow finding shares (possibly failing)
+-  - API update version to V1.11 and document changes
+-  - API save default config file if none specified
+-  - api.c save success incorrectly returns error
+-  - api.c replace BUFSIZ (linux/windows have different values)
+-  - Move RPC API content out of README to API-README
+-  - Open a longpoll connection if a pool is in the REJECTING state as it's the
++  * API.class compiled with Java SE 6.0_03 - works with Win7x64
++  * miner.php highlight devs too slow finding shares (possibly failing)
++  * API update version to V1.11 and document changes
++  * API save default config file if none specified
++  * api.c save success incorrectly returns error
++  * api.c replace BUFSIZ (linux/windows have different values)
++  * Move RPC API content out of README to API-README
++  * Open a longpoll connection if a pool is in the REJECTING state as it's the
+     only way to re-enable it automatically.
+-  - Use only one longpoll as much as possible by using a pthread conditional
++  * Use only one longpoll as much as possible by using a pthread conditional
+     broadcast that each longpoll thread waits on and checks if it's the current pool
+     before
+-  - If shares are known stale, don't use them to decide to disable a pool for
++  * If shares are known stale, don't use them to decide to disable a pool for
+     sequential rejects.
+-  - Restarting cgminer from within after ADL has been corrupted only leads to a
++  * Restarting cgminer from within after ADL has been corrupted only leads to a
+     crash. Display a warning only and disable fanspeed monitoring.
+-  - Icarus: fix abort calculation/allow user specified abort
+-  - Icarus: make --icarus-timing hidden and document it in FPGA-README
+-  - Icarus: high accuracy timing and other bitstream speed support
+-  - add-MIPSEB-to-icarus-for-BIG_ENDIAN
+-  - work_decode only needs swab32 on midstate under BIG ENDIAN
+-  - add compile command to api-example.c
+-  - save config bugfix: writing an extra ',' when no gpus
+-  - Add dpkg-source commits
++  * Icarus: fix abort calculation/allow user specified abort
++  * Icarus: make --icarus-timing hidden and document it in FPGA-README
++  * Icarus: high accuracy timing and other bitstream speed support
++  * add-MIPSEB-to-icarus-for-BIG_ENDIAN
++  * work_decode only needs swab32 on midstate under BIG ENDIAN
++  * add compile command to api-example.c
++  * save config bugfix: writing an extra ',' when no gpus
++  * Add dpkg-source commits
+ 
+  -- nushor <nushor11@gmail.com>  Sun, 03 Jun 2012 22:02:03 -0500
+ 
+ cgminer (2.4.1-1) stable; urgency=low
+   Version 2.4.1-1 - May 6, 2012
+-  - In the unlikely event of finding a block, display the block solved count with
++  * In the unlikely event of finding a block, display the block solved count with
+     the pool it came from for auditing.
+-  - Display the device summary on exit even if a device has been disabled.
+-  - Use correct pool enabled enums in api.c.
+-  - Import Debian packaging configs
+-  - Ensure we test for a pool recovering from idle so long as it's not set to
++  * Display the device summary on exit even if a device has been disabled.
++  * Use correct pool enabled enums in api.c.
++  * Import Debian packaging configs
++  * Ensure we test for a pool recovering from idle so long as it's not set to
+     disabled.
+-  - Fix pool number display.
+-  - Give cgminer -T message only if curses is in use.
+-  - Reinit_adl is no longer used.
+-  - API 'stats' allow devices to add their own stats also for testing/debug
+-  - API add getwork stats to cgminer - accesable from API 'stats'
+-  - Don't initialise variables to zero when in global scope since they're already
++  * Fix pool number display.
++  * Give cgminer -T message only if curses is in use.
++  * Reinit_adl is no longer used.
++  * API 'stats' allow devices to add their own stats also for testing/debug
++  * API add getwork stats to cgminer - accesable from API 'stats'
++  * Don't initialise variables to zero when in global scope since they're already
+     initialised.
+-  - Get rid of unitialised variable warning when it's false.
+-  - Move a pool to POOL_REJECTING to be disabled only after 3 minutes of
++  * Get rid of unitialised variable warning when it's false.
++  * Move a pool to POOL_REJECTING to be disabled only after 3 minutes of
+     continuous rejected shares.
+-  - Some tweaks to reporting and logging.
+-  - Change FPGA detection order since BFL hangs on an ICA
+-  - API support new pool status
+-  - Add a temporarily disabled state for enabled pools called POOL_REJECTING and
++  * Some tweaks to reporting and logging.
++  * Change FPGA detection order since BFL hangs on an ICA
++  * API support new pool status
++  * Add a temporarily disabled state for enabled pools called POOL_REJECTING and
+     use the work from each longpoll to help determine when a rejecting pool has
+     started working again. Switch pools based on the multipool strategy once a pool
+     is re-enabled.
+-  - Removing extra debug
+-  - Fix the benchmark feature by bypassing the new networking code.
+-  - Reset sequential reject counter after a pool is disabled for when it is
++  * Removing extra debug
++  * Fix the benchmark feature by bypassing the new networking code.
++  * Reset sequential reject counter after a pool is disabled for when it is
+     re-enabled.
+-  - Icarus - correct MH/s and U: with work restart set at 8 seconds
+-  - ztex updateFreq was always reporting on fpga 0
+-  - Trying harder to get 1.15y working
+-  - Specifying threads on multi fpga boards extra cgpu
+-  - Missing the add cgpu per extra fpga on 1.15y boards
+-  - API add last share time to each pool
+-  - Don't try to reap curls if benchmarking is enabled.
++  * Icarus - correct MH/s and U: with work restart set at 8 seconds
++  * ztex updateFreq was always reporting on fpga 0
++  * Trying harder to get 1.15y working
++  * Specifying threads on multi fpga boards extra cgpu
++  * Missing the add cgpu per extra fpga on 1.15y boards
++  * API add last share time to each pool
++  * Don't try to reap curls if benchmarking is enabled.
+ 
+  -- nushor <nushor11@gmail.com>  Sun, 06 May 2012 11:09:46 -0500
+ 
+ cgminer (2.4.0-1) stable; urgency=low
+   Version 2.4.0 - May 3, 2012
+ 
+-  - Only show longpoll warning once when it has failed.
+-  - Convert hashes to an unsigned long long as well.
+-  - Detect pools that have issues represented by endless rejected shares and
++  * Only show longpoll warning once when it has failed.
++  * Convert hashes to an unsigned long long as well.
++  * Detect pools that have issues represented by endless rejected shares and
+     disable them, with a parameter to optionally disable this feature.
+-  - Bugfix: Use a 64-bit type for hashes_done (miner_thread) since it can overflow
++  * Bugfix: Use a 64-bit type for hashes_done (miner_thread) since it can overflow
+     32-bit on some FPGAs
+-  - Implement an older header fix for a label existing before the pthread_cleanup
++  * Implement an older header fix for a label existing before the pthread_cleanup
+     macro.
+-  - Limit the number of curls we recruit on communication failures and with
++  * Limit the number of curls we recruit on communication failures and with
+     delaynet enabled to 5 by maintaining a per-pool curl count, and using a pthread
+     conditional that wakes up when one is returned to the ring buffer.
+-  - Generalise add_pool() functions since they're repeated in add_pool_details.
+-  - Bugfix: Return failure, rather than quit, if BFwrite fails
+-  - Disable failing devices such that the user can attempt to re-enable them
+-  - Bugfix: thread_shutdown shouldn't try to free the device, since it's needed
++  * Generalise add_pool() functions since they're repeated in add_pool_details.
++  * Bugfix: Return failure, rather than quit, if BFwrite fails
++  * Disable failing devices such that the user can attempt to re-enable them
++  * Bugfix: thread_shutdown shouldn't try to free the device, since it's needed
+     afterward
+-  - API bool's and 1TBS fixes
+-  - Icarus - minimise code delays and name timer variables
+-  - api.c V1.9 add 'restart' + redesign 'quit' so thread exits cleanly
+-  - api.c bug - remove extra ']'s in notify command
+-  - Increase pool watch interval to 30 seconds.
+-  - Reap curls that are unused for over a minute. This allows connections to be
++  * API bool's and 1TBS fixes
++  * Icarus - minimise code delays and name timer variables
++  * api.c V1.9 add 'restart' + redesign 'quit' so thread exits cleanly
++  * api.c bug - remove extra ']'s in notify command
++  * Increase pool watch interval to 30 seconds.
++  * Reap curls that are unused for over a minute. This allows connections to be
+     closed, thereby allowing the number of curl handles to always be the minimum
+     necessary to not delay networking.
+-  - Use the ringbuffer of curls from the same pool for submit as well as getwork
++  * Use the ringbuffer of curls from the same pool for submit as well as getwork
+     threads. Since the curl handles were already connected to the same pool and are
+     immediately available, share submission will not be delayed by getworks.
+-  - Implement a scaleable networking framework designed to cope with any sized
++  * Implement a scaleable networking framework designed to cope with any sized
+     network requirements, yet minimise the number of connections being reopened. Do
+     this by create a ring buffer linked list of curl handles to be used by getwork,
+     recruiting extra handles when none is immediately available.
+-  - There is no need for the submit and getwork curls to be tied to the pool
++  * There is no need for the submit and getwork curls to be tied to the pool
+     struct.
+-  - Do not recruit extra connection threads if there have been connection errors
++  * Do not recruit extra connection threads if there have been connection errors
+     to the pool in question.
+-  - We should not retry submitting shares indefinitely or we may end up with a
++  * We should not retry submitting shares indefinitely or we may end up with a
+     huge backlog during network outages, so discard stale shares if we failed to
+     submit them and they've become stale in the interim.
+ 
+@@ -114,32 +153,32 @@
+ 
+ cgminer (2.3.6-3) stable; urgency=low
+   Version 2.3.6-3 - may 3, 2012
+-  - More bug fixes, Pre 2.4.1 release.
++  * More bug fixes, Pre 2.4.1 release.
+ 
+  -- nushor <nushor11@gmail.com>  Thurs, 03 May 2012 00:36:50 -0500
+ 
+ cgminer (2.3.6-2) stable; urgency=low
+   Version 2.3.6-2 - May 2, 2012
+-  - Various bug fixes, latest build from repository.
++  * Various bug fixes, latest build from repository.
+ 
+  -- nushor <nushor11@gmail.com>  Wed, 02 May 2012 18:17:49 -0500
+ 
+ cgminer (2.3.6-1) stable; urgency=low
+ 
+   Version 2.3.6 - April 29, 2012
+-  - Shorten stale share messages slightly.
+-  - Protect the freeing of current_hash under mutex_lock to prevent racing on it
++  * Shorten stale share messages slightly.
++  * Protect the freeing of current_hash under mutex_lock to prevent racing on it
+     when set_curblock is hit concurrently.
+-  - Change default behaviour to submitting stale, removing the --submit-stale
++  * Change default behaviour to submitting stale, removing the --submit-stale
+     option and adding a --no-submit-stale option.
+-  - Make sure to start the getwork and submit threads when a pool is added on the
++  * Make sure to start the getwork and submit threads when a pool is added on the
+     fly. This fixes a crash when a pool is added to running cgminer and then
+     switched to.
+-  - Faster hardware can easily outstrip the speed we can get work and submit
++  * Faster hardware can easily outstrip the speed we can get work and submit
+     shares when using only one connection per pool.
+-  - Test the queued list to see if any get/submits are already queued and if they
++  * Test the queued list to see if any get/submits are already queued and if they
+     are, start recruiting extra connections by generating new threads.
+-  - This allows us to reuse network connections at low loads but recuit new open
++  * This allows us to reuse network connections at low loads but recuit new open
+     connections as they're needed, so that cgminer can scale to hardware of any
+     size.
+ 
+--- a/driver-bitforce.c
++++ b/driver-bitforce.c
+@@ -157,7 +157,7 @@
+ 	return true;
+ }
+ 
+-static void biforce_clear_buffer(struct cgpu_info *bitforce)
++static void bitforce_clear_buffer(struct cgpu_info *bitforce)
+ {
+ 	int fdDev = bitforce->device_fd;
+ 	char pdevbuf[0x100];
+@@ -185,6 +185,8 @@
+ 
+ 	applog(LOG_WARNING, "BFL%i: Re-initialising", bitforce->device_id);
+ 
++	bitforce_clear_buffer(bitforce);
++
+ 	mutex_lock(&bitforce->device_mutex);
+ 	if (fdDev) {
+ 		BFclose(fdDev);
+@@ -239,7 +241,11 @@
+ 	if (!fdDev)
+ 		return false;
+ 
+-	mutex_lock(&bitforce->device_mutex);
++	/* It is not critical getting temperature so don't get stuck if  we
++	 * can't grab the mutex here */
++	if (mutex_trylock(&bitforce->device_mutex))
++		return false;
++
+ 	BFwrite(fdDev, "ZLX", 3);
+ 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+ 	mutex_unlock(&bitforce->device_mutex);
+@@ -255,7 +261,7 @@
+ 
+ 		if (temp > 0) {
+ 			bitforce->temp = temp;
+-			if (temp > bitforce->cutofftemp) {
++			if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {
+ 				applog(LOG_WARNING, "BFL%i: Hit thermal cutoff limit, disabling!", bitforce->device_id);
+ 				bitforce->deven = DEV_RECOVER;
+ 
+@@ -264,7 +270,15 @@
+ 				bitforce->dev_thermal_cutoff_count++;
+ 			}
+ 		}
++	} else {
++		/* Use the temperature monitor as a kind of watchdog for when
++		 * our responses are out of sync and flush the buffer to
++		 * hopefully recover */
++		applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer");
++		bitforce_clear_buffer(bitforce);
++		return false;;
+ 	}
++
+ 	return true;
+ }
+ 
+@@ -287,8 +301,7 @@
+ 	BFgets(pdevbuf, sizeof(pdevbuf), fdDev);
+ 	if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) {
+ 		mutex_unlock(&bitforce->device_mutex);
+-		if (!restart_wait(WORK_CHECK_INTERVAL_MS))
+-			return false;
++		nmsleep(WORK_CHECK_INTERVAL_MS);
+ 		goto re_send;
+ 	} else if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
+ 		mutex_unlock(&bitforce->device_mutex);
+@@ -300,6 +313,7 @@
+ 			goto re_send;
+ 		}
+ 		applog(LOG_ERR, "BFL%i: Error: Send work reports: %s", bitforce->device_id, pdevbuf);
++		bitforce_clear_buffer(bitforce);
+ 		return false;
+ 	}
+ 
+@@ -340,6 +354,7 @@
+ 
+ 	if (unlikely(strncasecmp(pdevbuf, "OK", 2))) {
+ 		applog(LOG_ERR, "BFL%i: Error: Send block data reports: %s", bitforce->device_id, pdevbuf);
++		bitforce_clear_buffer(bitforce);
+ 		return false;
+ 	}
+ 
+@@ -414,7 +429,7 @@
+ 		}
+ 
+ 		if (delay_time_ms != bitforce->sleep_ms)
+-			  applog(LOG_DEBUG, "BFL%i: Wait time changed to: %d", bitforce->device_id, bitforce->sleep_ms, bitforce->wait_ms);
++			  applog(LOG_DEBUG, "BFL%i: Wait time changed to: %d, waited %u", bitforce->device_id, bitforce->sleep_ms, bitforce->wait_ms);
+ 
+ 		/* Work out the average time taken. Float for calculation, uint for display */
+ 		bitforce->avg_wait_f += (tv_to_ms(elapsed) - bitforce->avg_wait_f) / TIME_AVG_CONSTANT;
+@@ -428,6 +443,7 @@
+ 		return 0;	/* Device idle */
+ 	else if (strncasecmp(pdevbuf, "NONCE-FOUND", 11)) {
+ 		applog(LOG_WARNING, "BFL%i: Error: Get result reports: %s", bitforce->device_id, pdevbuf);
++		bitforce_clear_buffer(bitforce);
+ 		return 0;
+ 	}
+ 
+@@ -475,9 +491,10 @@
+ {
+ 	struct cgpu_info *bitforce = thr->cgpu;
+ 	unsigned int sleep_time;
++	bool send_ret;
+ 	int64_t ret;
+ 
+-	ret = bitforce_send_work(thr, work);
++	send_ret = bitforce_send_work(thr, work);
+ 
+ 	if (!bitforce->nonce_range) {
+ 		/* Initially wait 2/3 of the average cycle time so we can request more
+@@ -503,8 +520,10 @@
+ 		bitforce->wait_ms = sleep_time;
+ 	}
+ 
+-	if (ret)
++	if (send_ret)
+ 		ret = bitforce_get_result(thr, work);
++	else
++		ret = -1;
+ 
+ 	if (ret == -1) {
+ 		ret = 0;
+@@ -513,7 +532,7 @@
+ 		bitforce->device_not_well_reason = REASON_DEV_COMMS_ERROR;
+ 		bitforce->dev_comms_error_count++;
+ 		/* empty read buffer */
+-		biforce_clear_buffer(bitforce);
++		bitforce_clear_buffer(bitforce);
+ 	}
+ 	return ret;
+ }
+@@ -523,6 +542,20 @@
+ 	return bitforce_get_temp(bitforce);
+ }
+ 
++static bool bitforce_thread_init(struct thr_info *thr)
++{
++	struct cgpu_info *bitforce = thr->cgpu;
++	unsigned int wait;
++
++	/* Pause each new thread at least 100ms between initialising
++	 * so the devices aren't making calls all at the same time. */
++	wait = thr->id * MAX_START_DELAY_US;
++	applog(LOG_DEBUG, "BFL%i: Delaying start by %dms", bitforce->device_id, wait / 1000);
++	usleep(wait);
++
++	return true;
++}
++
+ static struct api_data *bitforce_api_stats(struct cgpu_info *cgpu)
+ {
+ 	struct api_data *root = NULL;
+@@ -546,6 +579,7 @@
+ 	.get_statline_before = get_bitforce_statline_before,
+ 	.get_stats = bitforce_get_stats,
+ 	.thread_prepare = bitforce_thread_prepare,
++	.thread_init = bitforce_thread_init,
+ 	.scanhash = bitforce_scanhash,
+ 	.thread_shutdown = bitforce_shutdown,
+ 	.thread_enable = biforce_thread_enable
+--- a/driver-icarus.c
++++ b/driver-icarus.c
+@@ -65,7 +65,7 @@
+ #define ASSERT1(condition) __maybe_unused static char sizeof_uint32_t_must_be_4[(condition)?1:-1]
+ ASSERT1(sizeof(uint32_t) == 4);
+ 
+-#define ICARUS_READ_TIME ((double)ICARUS_READ_SIZE * (double)8.0 / (double)ICARUS_IO_SPEED)
++#define ICARUS_READ_TIME(baud) ((double)ICARUS_READ_SIZE * (double)8.0 / (double)(baud))
+ 
+ // Fraction of a second, USB timeout is measured in
+ // i.e. 10 means 1/10 of a second
+@@ -176,11 +176,36 @@
+ 	// (which will only affect W)
+ 	uint64_t history_count;
+ 	struct timeval history_time;
++
++	// icarus-options
++	int baud;
++	int work_division;
++	int fpga_count;
++	uint32_t nonce_mask;
+ };
+ 
++#define END_CONDITION 0x0000ffff
++
+ // One for each possible device
+ static struct ICARUS_INFO **icarus_info;
+ 
++// Looking for options in --icarus-timing and --icarus-options:
++//
++// Code increments this each time we start to look at a device
++// However, this means that if other devices are checked by
++// the Icarus code (e.g. BFL) they will count in the option offset
++//
++// This, however, is deterministic so that's OK
++//
++// If we were to increment after successfully finding an Icarus
++// that would be random since an Icarus may fail and thus we'd
++// not be able to predict the option order
++//
++// This also assumes that serial_detect() checks them sequentially
++// and in the order specified on the command line
++//
++static int option_offset = -1;
++
+ struct device_api icarus_api;
+ 
+ static void rev(unsigned char *s, size_t l)
+@@ -195,8 +220,8 @@
+ 	}
+ }
+ 
+-#define icarus_open2(devpath, purge)  serial_open(devpath, 115200, ICARUS_READ_FAULT_DECISECONDS, purge)
+-#define icarus_open(devpath)  icarus_open2(devpath, false)
++#define icarus_open2(devpath, baud, purge)  serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge)
++#define icarus_open(devpath, baud)  icarus_open2(devpath, baud, false)
+ 
+ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count)
+ {
+@@ -272,7 +297,7 @@
+ 	}
+ }
+ 
+-static void set_timing_mode(struct cgpu_info *icarus)
++static void set_timing_mode(int this_option_offset, struct cgpu_info *icarus)
+ {
+ 	struct ICARUS_INFO *info = icarus_info[icarus->device_id];
+ 	double Hs;
+@@ -285,7 +310,7 @@
+ 		buf[0] = '\0';
+ 	else {
+ 		ptr = opt_icarus_timing;
+-		for (i = 0; i < icarus->device_id; i++) {
++		for (i = 0; i < this_option_offset; i++) {
+ 			comma = strchr(ptr, ',');
+ 			if (comma == NULL)
+ 				break;
+@@ -354,11 +379,123 @@
+ 
+ 	applog(LOG_DEBUG, "Icarus: Init: %d mode=%s read_count=%d Hs=%e",
+ 		icarus->device_id, timing_mode_str(info->timing_mode), info->read_count, info->Hs);
++}
++
++static uint32_t mask(int work_division)
++{
++	char err_buf[BUFSIZ+1];
++	uint32_t nonce_mask = 0x7fffffff;
+ 
++	// yes we can calculate these, but this way it's easy to see what they are
++	switch (work_division) {
++	case 1:
++		nonce_mask = 0xffffffff;
++		break;
++	case 2:
++		nonce_mask = 0x7fffffff;
++		break;
++	case 4:
++		nonce_mask = 0x3fffffff;
++		break;
++	case 8:
++		nonce_mask = 0x1fffffff;
++		break;
++	default:
++		sprintf(err_buf, "Invalid2 icarus-options for work_division (%d) must be 1, 2, 4 or 8", work_division);
++		quit(1, err_buf);
++	}
++
++	return nonce_mask;
++}
++
++static void get_options(int this_option_offset, int *baud, int *work_division, int *fpga_count)
++{
++	char err_buf[BUFSIZ+1];
++	char buf[BUFSIZ+1];
++	char *ptr, *comma, *colon, *colon2;
++	size_t max;
++	int i, tmp;
++
++	if (opt_icarus_options == NULL)
++		buf[0] = '\0';
++	else {
++		ptr = opt_icarus_options;
++		for (i = 0; i < this_option_offset; i++) {
++			comma = strchr(ptr, ',');
++			if (comma == NULL)
++				break;
++			ptr = comma + 1;
++		}
++
++		comma = strchr(ptr, ',');
++		if (comma == NULL)
++			max = strlen(ptr);
++		else
++			max = comma - ptr;
++
++		if (max > BUFSIZ)
++			max = BUFSIZ;
++		strncpy(buf, ptr, max);
++		buf[max] = '\0';
++	}
++
++	*baud = ICARUS_IO_SPEED;
++	*work_division = 2;
++	*fpga_count = 2;
++
++	if (*buf) {
++		colon = strchr(buf, ':');
++		if (colon)
++			*(colon++) = '\0';
++
++		if (*buf) {
++			tmp = atoi(buf);
++			switch (tmp) {
++			case 115200:
++				*baud = 115200;
++				break;
++			case 57600:
++				*baud = 57600;
++				break;
++			default:
++				sprintf(err_buf, "Invalid icarus-options for baud (%s) must be 115200 or 57600", buf);
++				quit(1, err_buf);
++			}
++		}
++
++		if (colon && *colon) {
++			colon2 = strchr(colon, ':');
++			if (colon2)
++				*(colon2++) = '\0';
++
++			if (*colon) {
++				tmp = atoi(colon);
++				if (tmp == 1 || tmp == 2 || tmp == 4 || tmp == 8) {
++					*work_division = tmp;
++					*fpga_count = tmp;	// default to the same
++				} else {
++					sprintf(err_buf, "Invalid icarus-options for work_division (%s) must be 1, 2, 4 or 8", colon);
++					quit(1, err_buf);
++				}
++			}
++
++			if (colon2 && *colon2) {
++				tmp = atoi(colon2);
++				if (tmp > 0 && tmp <= *work_division)
++					*fpga_count = tmp;
++				else {
++					sprintf(err_buf, "Invalid icarus-options for fpga_count (%s) must be >0 and <=work_division (%d)", colon2, *work_division);
++					quit(1, err_buf);
++				}
++			}
++		}
++	}
+ }
+ 
+ static bool icarus_detect_one(const char *devpath)
+ {
++	int this_option_offset = ++option_offset;
++
+ 	struct ICARUS_INFO *info;
+ 	struct timeval tv_start, tv_finish;
+ 	int fd;
+@@ -379,9 +516,13 @@
+ 	unsigned char ob_bin[64], nonce_bin[ICARUS_READ_SIZE];
+ 	char *nonce_hex;
+ 
++	int baud, work_division, fpga_count;
++
++	get_options(this_option_offset, &baud, &work_division, &fpga_count);
++
+ 	applog(LOG_DEBUG, "Icarus Detect: Attempting to open %s", devpath);
+ 
+-	fd = icarus_open2(devpath, true);
++	fd = icarus_open2(devpath, baud, true);
+ 	if (unlikely(fd == -1)) {
+ 		applog(LOG_ERR, "Icarus Detect: Failed to open %s", devpath);
+ 		return false;
+@@ -429,6 +570,9 @@
+ 	applog(LOG_INFO, "Found Icarus at %s, mark as %d",
+ 		devpath, icarus->device_id);
+ 
++	applog(LOG_DEBUG, "Icarus: Init: %d baud=%d work_division=%d fpga_count=%d",
++		icarus->device_id, baud, work_division, fpga_count);
++
+ 	// Since we are adding a new device on the end it needs to always be allocated
+ 	icarus_info[icarus->device_id] = (struct ICARUS_INFO *)malloc(sizeof(struct ICARUS_INFO));
+ 	if (unlikely(!(icarus_info[icarus->device_id])))
+@@ -439,10 +583,15 @@
+ 	// Initialise everything to zero for a new device
+ 	memset(info, 0, sizeof(struct ICARUS_INFO));
+ 
+-	info->golden_hashes = (golden_nonce_val & 0x7fffffff) << 1;
++	info->baud = baud;
++	info->work_division = work_division;
++	info->fpga_count = fpga_count;
++	info->nonce_mask = mask(work_division);
++
++	info->golden_hashes = (golden_nonce_val & info->nonce_mask) * fpga_count;
+ 	timersub(&tv_finish, &tv_start, &(info->golden_tv));
+ 
+-	set_timing_mode(icarus);
++	set_timing_mode(this_option_offset, icarus);
+ 
+ 	return true;
+ }
+@@ -458,7 +607,7 @@
+ 
+ 	struct timeval now;
+ 
+-	int fd = icarus_open(icarus->device_path);
++	int fd = icarus_open(icarus->device_path, icarus_info[icarus->device_id]->baud);
+ 	if (unlikely(-1 == fd)) {
+ 		applog(LOG_ERR, "Failed to open Icarus on %s",
+ 		       icarus->device_path);
+@@ -565,11 +714,9 @@
+ 
+ 	submit_nonce(thr, work, nonce);
+ 
+-	hash_count = (nonce & 0x7fffffff);
+-	if (hash_count++ == 0x7fffffff)
+-		hash_count = 0xffffffff;
+-	else
+-		hash_count <<= 1;
++	hash_count = (nonce & info->nonce_mask);
++	hash_count++;
++	hash_count *= info->fpga_count;
+ 
+ 	if (opt_debug || info->do_icarus_timing)
+ 		timersub(&tv_finish, &tv_start, &elapsed);
+@@ -580,7 +727,9 @@
+ 	}
+ 
+ 	// ignore possible end condition values
+-	if (info->do_icarus_timing && (nonce & 0x7fffffff) > 0x000fffff && (nonce & 0x7fffffff) < 0x7ff00000) {
++	if (info->do_icarus_timing
++	&&  ((nonce & info->nonce_mask) > END_CONDITION)
++	&&  ((nonce & info->nonce_mask) < (info->nonce_mask & ~END_CONDITION))) {
+ 		gettimeofday(&tv_history_start, NULL);
+ 
+ 		history0 = &(info->history[0]);
+@@ -590,7 +739,7 @@
+ 
+ 		Ti = (double)(elapsed.tv_sec)
+ 			+ ((double)(elapsed.tv_usec))/((double)1000000)
+-			- ICARUS_READ_TIME;
++			- ((double)ICARUS_READ_TIME(info->baud));
+ 		Xi = (double)hash_count;
+ 		history0->sumXiTi += Xi * Ti;
+ 		history0->sumXi += Xi;
+@@ -700,6 +849,9 @@
+ 	root = api_add_uint(root, "timing_values", &(info->history[0].values), false);
+ 	root = api_add_const(root, "timing_mode", timing_mode_str(info->timing_mode), false);
+ 	root = api_add_bool(root, "is_timing", &(info->do_icarus_timing), false);
++	root = api_add_int(root, "baud", &(info->baud), false);
++	root = api_add_int(root, "work_division", &(info->work_division), false);
++	root = api_add_int(root, "fpga_count", &(info->fpga_count), false);
+ 
+ 	return root;
+ }
+--- a/driver-opencl.c
++++ b/driver-opencl.c
+@@ -660,9 +660,19 @@
+ 
+ 	for (gpu = 0; gpu < nDevs; gpu++) {
+ 		struct cgpu_info *cgpu = &gpus[gpu];
++		double displayed_rolling, displayed_total;
++		bool mhash_base = true;
+ 
+-		wlog("GPU %d: %.1f / %.1f Mh/s | A:%d  R:%d  HW:%d  U:%.2f/m  I:%d\n",
+-			gpu, cgpu->rolling, cgpu->total_mhashes / total_secs,
++		displayed_rolling = cgpu->rolling;
++		displayed_total = cgpu->total_mhashes / total_secs;
++		if (displayed_rolling < 1) {
++			displayed_rolling *= 1000;
++			displayed_total *= 1000;
++			mhash_base = false;
++		}
++
++		wlog("GPU %d: %.1f / %.1f %sh/s | A:%d  R:%d  HW:%d  U:%.2f/m  I:%d\n",
++			gpu, displayed_rolling, displayed_total, mhash_base ? "M" : "K",
+ 			cgpu->accepted, cgpu->rejected, cgpu->hw_errors,
+ 			cgpu->utility, cgpu->intensity);
+ #ifdef HAVE_ADL
+@@ -710,7 +720,10 @@
+ 			if (thr->cgpu != cgpu)
+ 				continue;
+ 			get_datestamp(checkin, &thr->last);
+-			wlog("Thread %d: %.1f Mh/s %s ", i, thr->rolling, cgpu->deven != DEV_DISABLED ? "Enabled" : "Disabled");
++			displayed_rolling = thr->rolling;
++			if (!mhash_base)
++				displayed_rolling *= 1000;
++			wlog("Thread %d: %.1f %sh/s %s ", i, displayed_rolling, mhash_base ? "M" : "K" , cgpu->deven != DEV_DISABLED ? "Enabled" : "Disabled");
+ 			switch (cgpu->status) {
+ 				default:
+ 				case LIFE_WELL:
+--- a/miner.h
++++ b/miner.h
+@@ -500,6 +500,11 @@
+ 		quit(1, "WTF MUTEX ERROR ON UNLOCK!");
+ }
+ 
++static inline int mutex_trylock(pthread_mutex_t *lock)
++{
++	return pthread_mutex_trylock(lock);
++}
++
+ static inline void wr_lock(pthread_rwlock_t *lock)
+ {
+ 	if (unlikely(pthread_rwlock_wrlock(lock)))
+@@ -557,6 +562,7 @@
+ extern bool opt_api_network;
+ extern bool opt_delaynet;
+ extern bool opt_restart;
++extern char *opt_icarus_options;
+ extern char *opt_icarus_timing;
+ #ifdef USE_BITFORCE
+ extern bool opt_bfl_noncerange;
+--- a/miner.php
++++ b/miner.php
+@@ -87,11 +87,13 @@
+  'DATE' => null,
+  'RIGS' => null,
+  'SUMMARY' => array('Elapsed', 'MHS av', 'Found Blocks=Blks', 'Accepted', 'Rejected=Rej', 'Utility'),
+- 'DEVS' => array('ID', 'Name', 'GPU', 'Status', 'MHS av', 'Accepted', 'Rejected=Rej', 'Utility'),
++ 'DEVS+NOTIFY' => array('DEVS.Name=Name', 'DEVS.ID=ID', 'DEVS.Status=Status', 'DEVS.Temperature=Temp',
++			'DEVS.MHS av=MHS av', 'DEVS.Accepted=Accept', 'DEVS.Rejected=Rej',
++			'DEVS.Utility=Utility', 'NOTIFY.Last Not Well=Not Well'),
+  'POOL' => array('POOL', 'Status', 'Accepted', 'Rejected=Rej', 'Last Share Time'));
+ $mobilesum = array(
+  'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted', 'Rejected', 'Utility'),
+- 'DEVS' => array('MHS av', 'Accepted', 'Rejected', 'Utility'),
++ 'DEVS+NOTIFY' => array('DEVS.MHS av', 'DEVS.Accepted', 'DEVS.Rejected', 'DEVS.Utility'),
+  'POOL' => array('Accepted', 'Rejected'));
+ #
+ # customsummarypages is an array of these Custom Summary Pages
+@@ -716,6 +718,9 @@
+  if ($class == '' && ($rownum % 2) == 0)
+ 	$class = $c2class;
+ 
++ if ($ret == '')
++	$ret = $b;
++
+  return array($ret, $class);
+ }
+ #
+@@ -1274,8 +1279,171 @@
+ 	'GPU' => 'devs',	// You would normally use DEVS
+ 	'PGA' => 'devs',	// You would normally use DEVS
+ 	'NOTIFY' => 'notify',
++	'DEVDETAILS' => 'devdetails',
++	'STATS' => 'stats',
+ 	'CONFIG' => 'config');
+ #
++function joinfields($section1, $section2, $join, $results)
++{
++ global $sectionmap;
++
++ $name1 = $sectionmap[$section1];
++ $name2 = $sectionmap[$section2];
++ $newres = array();
++
++ // foreach rig in section1
++ foreach ($results[$name1] as $rig => $result)
++ {
++	$status = null;
++
++	// foreach answer section in the rig api call
++	foreach ($result as $name1b => $fields1b)
++	{
++		if ($name1b == 'STATUS')
++		{
++			// remember the STATUS from section1
++			$status = $result[$name1b];
++			continue;
++		}
++
++		// foreach answer section in the rig api call (for the other api command)
++		foreach ($results[$name2][$rig] as $name2b => $fields2b)
++		{
++			if ($name2b == 'STATUS')
++				continue;
++
++			// If match the same field values of fields in $join
++			$match = true;
++			foreach ($join as $field)
++				if ($fields1b[$field] != $fields2b[$field])
++				{
++					$match = false;
++					break;
++				}
++
++			if ($match === true)
++			{
++				if ($status != null)
++				{
++					$newres[$rig]['STATUS'] = $status;
++					$status = null;
++				}
++
++				$subsection = $section1.'+'.$section2;
++				$subsection .= preg_replace('/[^0-9]/', '', $name1b.$name2b);
++
++				foreach ($fields1b as $nam => $val)
++					$newres[$rig][$subsection]["$section1.$nam"] = $val;
++				foreach ($fields2b as $nam => $val)
++					$newres[$rig][$subsection]["$section2.$nam"] = $val;
++			}
++		}
++	}
++ }
++ return $newres;
++}
++#
++function joinall($section1, $section2, $results)
++{
++ global $sectionmap;
++
++ $name1 = $sectionmap[$section1];
++ $name2 = $sectionmap[$section2];
++ $newres = array();
++
++ // foreach rig in section1
++ foreach ($results[$name1] as $rig => $result)
++ {
++	// foreach answer section in the rig api call
++	foreach ($result as $name1b => $fields1b)
++	{
++		if ($name1b == 'STATUS')
++		{
++			// copy the STATUS from section1
++			$newres[$rig][$name1b] = $result[$name1b];
++			continue;
++		}
++
++		// foreach answer section in the rig api call (for the other api command)
++		foreach ($results[$name2][$rig] as $name2b => $fields2b)
++		{
++			if ($name2b == 'STATUS')
++				continue;
++
++			$subsection = $section1.'+'.$section2;
++			$subsection .= preg_replace('/[^0-9]/', '', $name1b.$name2b);
++
++			foreach ($fields1b as $nam => $val)
++				$newres[$rig][$subsection]["$section1.$nam"] = $val;
++			foreach ($fields2b as $nam => $val)
++				$newres[$rig][$subsection]["$section2.$nam"] = $val;
++		}
++	}
++ }
++ return $newres;
++}
++#
++function joinsections($sections, $results, $errors)
++{
++ global $sectionmap;
++
++#echo "results['pools']=".print_r($results['pools'],true)."<br>";
++
++ // GPU's don't have Name,ID fields - so create them
++ foreach ($results as $section => $res)
++	foreach ($res as $rig => $result)
++		foreach ($result as $name => $fields)
++		{
++			$subname = preg_replace('/[0-9]/', '', $name);
++			if ($subname == 'GPU' and isset($result[$name]['GPU']))
++			{
++				$results[$section][$rig][$name]['Name'] = 'GPU';
++				$results[$section][$rig][$name]['ID'] = $result[$name]['GPU'];
++			}
++		}
++
++ foreach ($sections as $section => $fields)
++	if ($section != 'DATE' && !isset($sectionmap[$section]))
++	{
++		$both = explode('+', $section, 2);
++		if (count($both) > 1)
++		{
++			switch($both[0])
++			{
++			case 'SUMMARY':
++				switch($both[1])
++				{
++				case 'POOL':
++				case 'DEVS':
++				case 'CONFIG':
++					$sectionmap[$section] = $section;
++					$results[$section] = joinall($both[0], $both[1], $results);
++					break;
++				}
++				break;
++			case 'DEVS':
++				$join = array('Name', 'ID');
++				switch($both[1])
++				{
++				case 'NOTIFY':
++				case 'DEVDETAILS':
++					$sectionmap[$section] = $section;
++					$results[$section] = joinfields($both[0], $both[1], $join, $results);
++					break;
++				}
++				break;
++			default:
++				$errors[] = "Error: Invalid section '$section'";
++				break;
++			}
++		}
++		else
++			$errors[] = "Error: Invalid section '$section'";
++	}
++
++ return array($results, $errors);
++}
++#
+ function secmatch($section, $field)
+ {
+  if ($section == $field)
+@@ -1335,7 +1503,14 @@
+ 				$value = null;
+ 		}
+ 
+-		list($showvalue, $class) = fmt($secname, $name, $value, $when, $row);
++		if (strpos($secname, '+') === false)
++			list($showvalue, $class) = fmt($secname, $name, $value, $when, $row);
++		else
++		{
++			$parts = explode('.', $name, 2);
++			list($showvalue, $class) = fmt($parts[0], $parts[1], $value, $when, $row);
++		}
++
+ 		echo "<td$class align=right>$showvalue</td>";
+ 	}
+ 	endrow();
+@@ -1356,15 +1531,19 @@
+  $errors = array();
+  foreach ($sections as $section => $fields)
+  {
+-	if (isset($sectionmap[$section]))
++	$all = explode('+', $section);
++	foreach ($all as $section)
+ 	{
+-		$cmd = $sectionmap[$section];
+-		if (!isset($cmds[$cmd]))
+-			$cmds[$cmd] = 1;
++		if (isset($sectionmap[$section]))
++		{
++			$cmd = $sectionmap[$section];
++			if (!isset($cmds[$cmd]))
++				$cmds[$cmd] = 1;
++		}
++		else
++			if ($section != 'DATE')
++				$errors[] = "Error: unknown section '$section' in custom summary page '$pagename'";
+ 	}
+-	else
+-		if ($section != 'DATE')
+-			$errors[] = "Error: unknown section '$section' in custom summary page '$pagename'";
+  }
+ 
+  $results = array();
+@@ -1399,6 +1578,7 @@
+  $shownsomething = false;
+  if (count($results) > 0)
+  {
++	list($results, $errors) = joinsections($sections, $results, $errors);
+ 	$first = true;
+ 	foreach ($sections as $section => $fields)
+ 	{