1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id$ */
3
4 /* libticalcs - Ti Calculator library, a part of the TiLP project
5 * Copyright (C) 1999-2005 Romain Liévin
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /*
23 Probing support.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "ticalcs.h"
31 #include "internal.h"
32 #include "logging.h"
33 #include "dbus_pkt.h"
34 #include "error.h"
35 #include "gettext.h"
36 #include "calc_xx.h"
37 #include "dusb_vpkt.h"
38 #include "dusb_cmd.h"
39
40 #define DEAD_TIME 250
41
42 /*
43 Get the first byte sent by the calc (Machine ID)
44 */
tixx_recv_ACK(CalcHandle * handle,uint8_t * mid)45 static int tixx_recv_ACK(CalcHandle* handle, uint8_t* mid)
46 {
47 uint8_t host, cmd;
48 uint16_t length;
49 uint8_t buffer[5];
50 int ret;
51
52 ret = dbus_recv(handle, &host, &cmd, &length, buffer);
53 if (!ret)
54 {
55 ticalcs_info(" TI->PC: ACK");
56
57 *mid = host;
58
59 if (cmd == DBUS_CMD_SKP)
60 {
61 ret = ERR_VAR_REJECTED;
62 }
63 }
64
65 return ret;
66 }
67
68 /**
69 * ticalcs_probe_calc_2:
70 * @handle: a previously allocated handle
71 * @type: the calculator model
72 *
73 * This function tries and detect the calculator type for non-silent models
74 * by requesting a screedump and analyzing the Machine ID.
75 * It supposes your calc is on and plugged.
76 *
77 * PC: 08 6D 00 00 PC request a screen dump
78 * TI: MId 56 00 00 TI reply OK
79 *
80 * Beware: the call sequence is very important: 86, 85, 73, 83, 82 !!!
81 *
82 * Return value: 0 if successful, an error code otherwise.
83 **/
ticalcs_probe_calc_2(CalcHandle * handle,CalcModel * model)84 static int ticalcs_probe_calc_2(CalcHandle* handle, CalcModel* model)
85 {
86 int err;
87 uint8_t data;
88
89 do
90 {
91 ticalcs_info(_("Probing calculator...\n"));
92 *model = CALC_NONE;
93
94 /* Test for a TI86 before a TI85 */
95 ticalcs_info(_("Check for TI86... "));
96 err = dbus_send(handle, DBUS_MID_PC_TI86, DBUS_CMD_SCR, 2, NULL);
97 if (err)
98 {
99 break;
100 }
101 err = tixx_recv_ACK(handle, &data);
102
103 ticalcs_info("<%02X-%02X> ", DBUS_MID_PC_TI86, data);
104
105 if (!err && (data == DBUS_MID_TI86_PC))
106 {
107 ticalcs_info("OK !\n");
108 *model = CALC_TI86;
109 break;
110 }
111 else
112 {
113 ticalcs_info("NOK.\n");
114 ticables_cable_reset(handle->cable);
115 PAUSE(DEAD_TIME);
116 }
117
118 /* Test for a TI85 */
119 ticalcs_info(_("Check for TI85... "));
120 err = dbus_send(handle, DBUS_MID_PC_TI85, DBUS_CMD_SCR, 2, NULL);
121 if (err)
122 {
123 break;
124 }
125 err = tixx_recv_ACK(handle, &data);
126
127 ticalcs_info("<%02X-%02X> ", DBUS_MID_PC_TI85, data);
128
129 if (!err && (data == DBUS_MID_TI85_PC))
130 {
131 ticalcs_info("OK !\n");
132 *model = CALC_TI85;
133 break;
134 }
135 else
136 {
137 ticalcs_info("NOK.\n");
138 ticables_cable_reset(handle->cable);
139 PAUSE(DEAD_TIME);
140 }
141
142 /* Test for a TI73 before a TI83 */
143 ticalcs_info(_("Check for TI73... "));
144 err = dbus_send(handle, DBUS_MID_PC_TI73, DBUS_CMD_SCR, 2, NULL);
145 if (err)
146 {
147 break;
148 }
149 err = tixx_recv_ACK(handle, &data);
150
151 ticalcs_info("<%02X-%02X> ", DBUS_MID_PC_TI73, data);
152
153 if (!err && (data == DBUS_MID_TI73_PC))
154 {
155 ticalcs_info("OK !\n");
156 *model = CALC_TI73;
157 break;
158 }
159 else
160 {
161 ticalcs_info("NOK.\n");
162 ticables_cable_reset(handle->cable);
163 PAUSE(DEAD_TIME);
164 }
165
166 /* Test for a TI83 before a TI82 */
167 ticalcs_info(_("Check for TI83... "));
168 err = dbus_send(handle, DBUS_MID_PC_TI83, DBUS_CMD_SCR, 2, NULL);
169 if (err)
170 {
171 break;
172 }
173 err = tixx_recv_ACK(handle, &data);
174
175 ticalcs_info("<%02X-%02X> ", DBUS_MID_PC_TI83, data);
176
177 if (!err && (data == DBUS_MID_TI83_PC))
178 {
179 ticalcs_info("OK !\n");
180 *model = CALC_TI83;
181 break;
182 }
183 else
184 {
185 ticalcs_info("NOK.\n");
186 ticables_cable_reset(handle->cable);
187 PAUSE(DEAD_TIME);
188 }
189
190 /* Test for a TI82 */
191 ticalcs_info(_("Check for TI82... "));
192 err = dbus_send(handle, DBUS_MID_PC_TI83, DBUS_CMD_SCR, 2, NULL);
193 if (err)
194 {
195 break;
196 }
197 err = tixx_recv_ACK(handle, &data);
198
199 ticalcs_info("<%02X-%02X> ", DBUS_MID_PC_TI82, data);
200
201 if (!err && (data == DBUS_MID_TI82_PC))
202 {
203 ticalcs_info("OK !\n");
204 *model = CALC_TI82;
205 break;
206 }
207 else
208 {
209 ticalcs_info("NOK.\n");
210 ticables_cable_reset(handle->cable);
211 PAUSE(DEAD_TIME);
212 }
213
214 #if 0
215 /* Test for a TI80 */
216 #warning TI-80 DETECTION FAILS
217 ticalcs_info(_("Check for TI80... "));
218 err = dbus_send(handle, DBUS_MID_PC_TI80, DBUS_CMD_SCR, 0, NULL);
219 if (err)
220 {
221 break;
222 }
223 err = tixx_recv_ACK(handle, &data);
224
225 ticalcs_info("<%02X-%02X> ", DBUS_MID_PC_TI80, data);
226
227 if (!err && (data == DBUS_MID_TI80_PC))
228 {
229 ticalcs_info("OK !\n");
230 *model = CALC_TI80;
231 break;
232 }
233 else
234 {
235 ticalcs_info("NOK.\n");
236 ticables_cable_reset(handle->cable);
237 PAUSE(DEAD_TIME);
238 }
239 #endif
240 } while(0);
241
242 return (*model == CALC_NONE) ? ERR_NO_CALC : 0;
243 }
244
245 /**
246 * ticalcs_probe_calc_1:
247 * @handle: a previously allocated handle
248 * @type: the calculator model
249 *
250 * Check if the calculator is ready and detect the type.
251 * Works only on FLASH calculators with an AMS2.08 or OS2.00 by requesting the
252 * version. A previous version was based on MID but TI83+/84+, TI89/TI89t, TI92+/V200
253 * could not be distinguished ;-(
254 *
255 * Return value: 0 if successful, an error code otherwise.
256 **/
ticalcs_probe_calc_1(CalcHandle * handle,CalcModel * model)257 static int ticalcs_probe_calc_1(CalcHandle* handle, CalcModel* model)
258 {
259 uint8_t host, cmd;
260 uint16_t status;
261 uint8_t buffer[256];
262 int i, ret;
263
264 // init value
265 *model = CALC_NONE;
266
267 do
268 {
269 CalcInfos infos;
270
271 // test for FLASH hand-helds (00 68 00 00 -> XX 56 00 00)
272 // where XX is 0x98: TI89/89t, 0x88: TI92+/V200, 0x73: TI83+/84+, 0x74: TI73
273 ticalcs_info(_("Check for TIXX... "));
274 for (i = 0; i < 2; i++)
275 {
276 ticalcs_info(" PC->TI: RDY?");
277 ret = dbus_send(handle, DBUS_MID_PC_TIXX, DBUS_CMD_RDY, 2, NULL);
278 if (ret)
279 {
280 continue;
281 }
282
283 ret = dbus_recv(handle, &host, &cmd, &status, buffer);
284 ticalcs_info(" TI->PC: ACK");
285 if (ret)
286 {
287 continue;
288 }
289
290 break;
291 }
292
293 // test for TI73
294 if (!ret)
295 {
296 if (host == DBUS_MID_TI73_PC)
297 {
298 *model = CALC_TI73;
299 break;
300 }
301 else if (host == DBUS_MID_TI92_PC)
302 {
303 *model = CALC_TI92;
304 break;
305 }
306 }
307
308 // test for TI92 (09 68 00 00 -> 89 56 00 00)
309 else if (ret)
310 {
311 ticalcs_info(_("Check for TI92... "));
312 ticables_cable_reset(handle->cable);
313 PAUSE(DEAD_TIME); // needed !
314
315 for (i = 0; i < 2; i++)
316 {
317 ticalcs_info(" PC->TI: RDY?");
318 ret = dbus_send(handle, DBUS_MID_PC_TI92, DBUS_CMD_RDY, 2, NULL);
319 if (ret)
320 {
321 continue;
322 }
323
324 ret = dbus_recv(handle, &host, &cmd, &status, buffer);
325 ticalcs_info(" TI->PC: ACK");
326 if (ret)
327 {
328 continue;
329 }
330
331 break;
332 }
333
334 if (!ret)
335 {
336 *model = CALC_TI92;
337 }
338 }
339
340 if (cmd != DBUS_CMD_ACK)
341 {
342 ret = ERR_INVALID_CMD;
343 break;
344 }
345
346 if ((status & 1) != 0)
347 {
348 ret = ERR_NOT_READY;
349 break;
350 }
351
352 // test for TI9x FLASH hand-helds again (request version and analyze HW_ID)
353 if(!ret && (host != DBUS_MID_TI73_PC) && (host != DBUS_MID_TI83p_PC))
354 {
355 ticalcs_info(_("Check for TI9X... "));
356
357 handle->model = CALC_TI89;
358 handle->calc = (CalcFncts *)&calc_89;
359
360 memset(&infos, 0, sizeof(CalcInfos));
361 ret = ticalcs_calc_get_version(handle, &infos);
362 if (ret)
363 {
364 break;
365 }
366 *model = infos.model;
367 }
368 else
369 {
370 ticalcs_info(_("Check for TI8X... "));
371
372 handle->model = CALC_TI83P;
373 handle->calc = (CalcFncts *)&calc_83p;
374
375 memset(&infos, 0, sizeof(CalcInfos));
376 ret = ticalcs_calc_get_version(handle, &infos);
377 if (ret)
378 {
379 break;
380 }
381 *model = infos.model;
382 }
383 } while(0);
384
385 ticalcs_info(_("Calculator type: %s"), tifiles_model_to_string(*model));
386
387 if (!ret)
388 {
389 ret = (*model == CALC_NONE) ? ERR_NO_CALC : 0;
390 }
391
392 return ret;
393 }
394
395 extern const CalcUpdate default_update;
396
397 /**
398 * ticalcs_probe_calc:
399 * @cable: a valid (=opened/attached) link cable handle
400 * @model: the calculator model which have been detected
401 *
402 * This function attempts to detect the calculator model plugged onto the cable.
403 * It works in a heuristic fashion.
404 *
405 * Return value: 0 if successful, an error code otherwise.
406 **/
ticalcs_probe_calc(CableHandle * cable,CalcModel * model)407 TIEXPORT3 int TICALL ticalcs_probe_calc(CableHandle* cable, CalcModel* model)
408 {
409 CalcHandle calc;
410 int ret = 0;
411
412 VALIDATE_NONNULL(cable);
413 VALIDATE_NONNULL(model);
414
415 do
416 {
417 // Hack: we construct the structure here because we don't really need it.
418 // I want to use ticalcs functions with a non-fixed calculator
419 memset(&calc, 0, sizeof(CalcHandle));
420 calc.model = *model = CALC_NONE;
421 calc.updat = (CalcUpdate *)&default_update;
422 calc.buffer = (uint8_t *)g_malloc(65536 + 6);
423 calc.cable = cable;
424 calc.open = !0;
425
426 // first: search for FLASH hand-helds (fast)
427 ret = ticalcs_probe_calc_1(&calc, model);
428 if (!ret && (*model != CALC_NONE))
429 {
430 g_free(calc.buffer);
431 break;
432 }
433
434 // second: search for other calcs (slow)
435 ret = ticalcs_probe_calc_2(&calc, model);
436 g_free(calc.buffer);
437 } while(0);
438
439 if (!ret)
440 {
441 ret = (*model == CALC_NONE) ? ERR_NO_CALC : 0;
442 }
443
444 return ret;
445 }
446
447 /**
448 * ticalcs_probe_usb_calc:
449 * @cable: a valid (=opened/attached) link cable handle
450 * @model: the calculator model which have been detected
451 *
452 * This function attempts to detect the calculator model plugged onto the cable.
453 * It works in a heuristic fashion and with FLASH hand-helds only.
454 *
455 * Return value: 0 if successful, an error code otherwise.
456 **/
ticalcs_probe_usb_calc(CableHandle * cable,CalcModel * model)457 TIEXPORT3 int TICALL ticalcs_probe_usb_calc(CableHandle* cable, CalcModel* model)
458 {
459 CalcHandle calc;
460 int ret = ERR_NO_CALC;
461
462 VALIDATE_NONNULL(cable);
463 VALIDATE_NONNULL(model);
464
465 do
466 {
467 // Hack: we construct the structure here because we don't really need it.
468 // I want to use ticalcs functions with a non-fixed calculator
469 memset(&calc, 0, sizeof(CalcHandle));
470 calc.model = *model = CALC_NONE;
471 calc.updat = (CalcUpdate *)&default_update;
472 calc.buffer = (uint8_t *)g_malloc(65536 + 6);
473 calc.cable = cable;
474 calc.open = !0;
475
476 if (cable->model == CABLE_SLV)
477 {
478 ret = ticalcs_probe_calc_1(&calc, model);
479 }
480 else if (cable->model == CABLE_USB)
481 {
482 CableDeviceInfo info;
483
484 ret = ticables_cable_get_device_info(cable, &info);
485 if (!ret)
486 {
487 *model = ticalcs_device_info_to_model(&info);
488 }
489 }
490
491 g_free(calc.buffer);
492 } while(0);
493
494 return ret;
495 }
496
497 /**
498 * ticalcs_probe:
499 * @c_model: link cable model
500 * @c_port: link cable port
501 * @model: hand-held model detected/found [out]
502 * @all: which hand-helds to detect (FLASH only or all)
503 *
504 * This function attempts to detect the calculator model plugged onto a
505 * given link cable model/port. It works in a heuristic fashion.
506 * This function handles device opening/closing for you.
507 *
508 * Return value: 0 if successful, an error code otherwise.
509 **/
ticalcs_probe(CableModel c_model,CablePort c_port,CalcModel * model,int all)510 TIEXPORT3 int TICALL ticalcs_probe(CableModel c_model, CablePort c_port, CalcModel* model, int all)
511 {
512 CableHandle *handle;
513 int ret = 0;
514 CalcHandle calc;
515
516 VALIDATE_NONNULL(model);
517
518 // get handle
519 handle = ticables_handle_new(c_model, c_port);
520 ticables_options_set_timeout(handle, 10);
521
522 // hack: we construct the structure here because we don't really need it.
523 // I want to use ticalcs functions with a non-fixed calculator
524 memset(&calc, 0, sizeof(CalcHandle));
525 calc.model = *model = CALC_NONE;
526 calc.updat = (CalcUpdate *)&default_update;
527 calc.buffer = (uint8_t *)g_malloc(65536 + 6);
528 calc.cable = handle;
529 calc.open = !0;
530
531 do
532 {
533 // open cable
534 ret = ticables_cable_open(handle);
535 if (ret)
536 {
537 ticables_handle_del(handle);
538 break;
539 }
540
541
542 // probe
543 if (c_model == CABLE_USB)
544 {
545 ret = ticalcs_probe_usb_calc(handle, model);
546 }
547 else
548 {
549 if (all)
550 {
551 ret = ticalcs_probe_calc(handle, model);
552 }
553 else
554 {
555 ret = ticalcs_probe_calc_1(&calc, model);
556 }
557 }
558
559 // close
560 ticables_cable_close(handle);
561 ticables_handle_del(handle);
562 } while(0);
563
564 return ret;
565 }
566
567 /**
568 * ticalcs_device_info_to_model:
569 * @info: pointer to device info
570 *
571 * Converts a cable device info to a CalcModel, if possible.
572 *
573 * Return value: != CALC_NONE if a precise calculator model can be determined through the cable+device info, CALC_NONE if invalid argument or SilverLink cable.
574 */
ticalcs_device_info_to_model(const CableDeviceInfo * info)575 TIEXPORT3 CalcModel TICALL ticalcs_device_info_to_model(const CableDeviceInfo *info)
576 {
577 CalcModel model = CALC_NONE;
578
579 if (info != NULL)
580 {
581 if (info->family == CABLE_FAMILY_DBUS)
582 {
583 // Can't determine calc model through the sole cable model. That's a job for the upper layers.
584 // Fall through.
585 }
586 else if (info->family == CABLE_FAMILY_USB_TI8X)
587 {
588 if (info->variant == CABLE_VARIANT_TI84P || info->variant == CABLE_VARIANT_TI84PSE)
589 {
590 model = CALC_TI84P_USB;
591 }
592 else if (info->variant == CABLE_VARIANT_TI84PCSE)
593 {
594 model = CALC_TI84PC_USB;
595 }
596 else if (info->variant == CABLE_VARIANT_TI84PCE)
597 {
598 model = CALC_TI84PCE_USB;
599 }
600 else if (info->variant == CABLE_VARIANT_TI83PCE)
601 {
602 model = CALC_TI83PCE_USB;
603 }
604 else if (info->variant == CABLE_VARIANT_TI82A)
605 {
606 model = CALC_TI82A_USB;
607 }
608 else if (info->variant == CABLE_VARIANT_TI84PT)
609 {
610 model = CALC_TI84PT_USB;
611 }
612 else
613 {
614 ticalcs_warning("Unexpected variant for TI-(e)Z80 USB cable, assuming 84+CE");
615 model = CALC_TI84PCE_USB;
616 }
617 }
618 else if (info->family == CABLE_FAMILY_USB_TI9X)
619 {
620 // This cable family has a single corresponding calc model.
621 if (info->variant != CABLE_VARIANT_TI89TM)
622 {
623 ticalcs_warning("Unexpected variant for TI-68k USB cable");
624 }
625 model = CALC_TI89T_USB;
626 }
627 else if (info->family == CABLE_FAMILY_USB_NSPIRE)
628 {
629 // This cable family has a single corresponding calc model.
630 if (info->variant != CABLE_VARIANT_NSPIRE)
631 {
632 ticalcs_warning("Unexpected variant for Nspire USB cable");
633 }
634 model = CALC_NSPIRE;
635 }
636 else
637 {
638 ticalcs_critical("Unexpected cable family");
639 // Fall through.
640 }
641 }
642 // else fall through.
643
644 return model;
645 }
646
647 /**
648 * ticalcs_remap_model_from_usb:
649 * @cable: a cable model model taken in #CableModel.
650 * @cable: a calc model model taken in #CalcModel.
651 *
652 * This function remaps a CALC_*_USB model to the corresponding CALC_* model, if said CALC_* model exists and the selected cable is USB.
653 *
654 * Return value: the new calculator model, which can be the same as the passed one.
655 **/
ticalcs_remap_model_from_usb(CableModel cable,CalcModel calc)656 TIEXPORT3 CalcModel TICALL ticalcs_remap_model_from_usb(CableModel cable, CalcModel calc)
657 {
658 if (cable == CABLE_USB)
659 {
660 if (calc == CALC_TI84P_USB)
661 {
662 return CALC_TI84P;
663 }
664 else if (calc == CALC_TI84PC_USB)
665 {
666 return CALC_TI84PC;
667 }
668 else if (calc == CALC_TI89T_USB)
669 {
670 return CALC_TI89T;
671 }
672 // else fall through.
673 }
674 // else fall through.
675
676 return calc;
677 }
678
679 /**
680 * ticalcs_remap_model_from_usb:
681 * @cable: a cable model model taken in #CableModel.
682 * @cable: a calc model model taken in #CalcModel.
683 *
684 * This function remaps a CALC_* model to the corresponding CALC_*_USB model, if said CALC_*_USB model exists and the selected cable is USB.
685 *
686 * Return value: the new calculator model, which can be the same as the passed one.
687 **/
ticalcs_remap_model_to_usb(CableModel cable,CalcModel calc)688 TIEXPORT3 CalcModel TICALL ticalcs_remap_model_to_usb(CableModel cable, CalcModel calc)
689 {
690 if (cable == CABLE_USB)
691 {
692 if (calc == CALC_TI84P)
693 {
694 return CALC_TI84P_USB;
695 }
696 else if (calc == CALC_TI84PC)
697 {
698 return CALC_TI84PC_USB;
699 }
700 else if (calc== CALC_TI89T)
701 {
702 return CALC_TI89T_USB;
703 }
704 // else fall through.
705 }
706 // else fall through.
707
708 return calc;
709 }
710