summaryrefslogtreecommitdiff
path: root/apps/plugins/clock/clock_draw_binary.c
blob: 611ab1b3714b37d8815396e7ef04777eb903c9d8 (plain)
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
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2007 Copyright Kévin Ferrare
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "clock_draw_binary.h"
#include "clock_bitmap_strings.h"
#include "clock_bitmaps.h"
#include "lib/picture.h"

const struct picture* binary_skin[]={binary,digits,segments};

static void print_binary(char* buffer, int number, int nb_bits){
    int i;
    int mask=1;
    buffer[nb_bits]='\0';
    for(i=0; i<nb_bits; i++){
        if((number & mask) !=0)
            buffer[nb_bits-i-1]='1';
        else
            buffer[nb_bits-i-1]='0';
        mask=mask<<1;
    }
}

void binary_clock_draw(struct screen* display, struct time* time, int skin){
    int lines_values[]={
        time->hour,time->minute,time->second
    };
    char buffer[9];
    int i;
    const struct picture* binary_bitmaps = &(binary_skin[skin][display->screen_type]);
    int y_offset=(display->getheight()-(binary_bitmaps->slide_height*3))/2;
    int x_offset=(display->getwidth()-(binary_bitmaps->width*6))/2;
    for(i=0;i<3;i++){
        print_binary(buffer, lines_values[i], 6);
        draw_string(display, binary_bitmaps, buffer, x_offset,
                    binary_bitmaps->slide_height*i+y_offset);
    }
}
/a> 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
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2010 Amaury Pouly
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb.h>
#include <stdint.h>
#include <stdbool.h>
#include <getopt.h>

bool g_debug = false;

#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

static void put32be(uint8_t *buf, uint32_t i)
{
    *buf++ = (i >> 24) & 0xff;
    *buf++ = (i >> 16) & 0xff;
    *buf++ = (i >> 8) & 0xff;
    *buf++ = i & 0xff;
}

enum dev_type_t
{
    PROBE_DEVICE,
    HID_DEVICE,
    RECOVERY_DEVICE,
};

struct dev_info_t
{
    uint16_t vendor_id;
    uint16_t product_id;
    unsigned xfer_size;
    enum dev_type_t dev_type;
};

struct dev_info_t g_dev_info[] =
{
    {0x066f, 0x3780, 1024, HID_DEVICE}, /* i.MX233 / STMP3780 */
    {0x066f, 0x3770, 48, HID_DEVICE}, /* STMP3770 */
    {0x15A2, 0x004F, 1024, HID_DEVICE}, /* i.MX28 */
    {0x066f, 0x3600, 4096, RECOVERY_DEVICE}, /* STMP36xx */
};

/* Command Block Descriptor (CDB) */
struct hid_cdb_t
{
    uint8_t command;
    uint32_t length; // big-endian!
    uint8_t reserved[11];
} __attribute__((packed));

// command
#define BLTC_DOWNLOAD_FW    2

/* Command Block Wrapper (CBW) */
struct hid_cbw_t
{
    uint32_t signature; // BLTC or PITC
    uint32_t tag; // returned in CSW
    uint32_t length; // number of bytes to transfer
    uint8_t flags;
    uint8_t reserved[2];
    struct hid_cdb_t cdb;
} __attribute__((packed));

/* HID Command Report */
struct hid_cmd_report_t
{
    uint8_t report_id;
    struct hid_cbw_t cbw;
} __attribute__((packed));

// report id
#define HID_BLTC_DATA_REPORT    2
#define HID_BLTC_CMD_REPORT     1

// signature
#define CBW_BLTC    0x43544C42  /* "BLTC" */
#define CBW_PITC    0x43544950  /* "PITC" */
// flags
#define CBW_DIR_IN  0x80
#define CBW_DIR_OUT 0x00

/* Command Status Wrapper (CSW) */
struct hid_csw_t
{
    uint32_t signature; // BLTS or PITS
    uint32_t tag; // given in CBW
    uint32_t residue; // number of bytes not transferred
    uint8_t status;
} __attribute__((packed));
// signature
#define CSW_BLTS    0x53544C42 /* "BLTS" */
#define CSW_PITS    0x53544950 /* "PITS" */
// status
#define CSW_PASSED      0x00
#define CSW_FAILED      0x01
#define CSW_PHASE_ERROR 0x02

/* HID Status Report */
struct hid_status_report_t
{
    uint8_t report_id;
    struct hid_csw_t csw;
} __attribute__((packed));

#define HID_BLTC_STATUS_REPORT  4

static int send_hid(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers)
{
    libusb_detach_kernel_driver(dev, 0);
    libusb_claim_interface(dev, 0);

    int recv_size;
    uint32_t my_tag = 0xcafebabe;
    uint8_t *xfer_buf = malloc(1 + xfer_size);
    struct hid_cmd_report_t cmd;
    memset(&cmd, 0, sizeof(cmd));
    cmd.report_id = HID_BLTC_CMD_REPORT;
    cmd.cbw.signature = CBW_BLTC;
    cmd.cbw.tag = my_tag;
    cmd.cbw.length = size;
    cmd.cbw.flags = CBW_DIR_OUT;
    cmd.cbw.cdb.command = BLTC_DOWNLOAD_FW;
    put32be((void *)&cmd.cbw.cdb.length, size);

    int ret = libusb_control_transfer(dev,
        LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, 0x9, 0x201, 0,
        (void *)&cmd, sizeof(cmd), 1000);
    if(ret < 0)
    {
        printf("transfer error at init step\n");
        goto Lstatus;
    }

    for(int i = 0; i < nr_xfers; i++)
    {
        xfer_buf[0] = HID_BLTC_DATA_REPORT;
        memcpy(&xfer_buf[1], &data[i * xfer_size], xfer_size);

        ret = libusb_control_transfer(dev,
            LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
            0x9, 0x202, 0, xfer_buf, xfer_size + 1, 1000);
        if(ret < 0)
        {
            printf("transfer error at send step %d\n", i);
            goto Lstatus;
        }
    }

    Lstatus:
    ret = libusb_interrupt_transfer(dev, 0x81, xfer_buf, xfer_size, 
        &recv_size, 1000);
    if(ret == 0 && recv_size == sizeof(struct hid_status_report_t))
    {
        struct hid_status_report_t *report = (void *)xfer_buf;
        if(report->report_id != HID_BLTC_STATUS_REPORT)
        {
            printf("Error: got non-status report\n");
            return -1;
        }
        if(report->csw.signature != CSW_BLTS)
        {
            printf("Error: status report signature mismatch\n");
            return -2;
        }
        if(report->csw.tag != my_tag)
        {
            printf("Error: status report tag mismtahc\n");
            return -3;
        }
        if(report->csw.residue != 0)
            printf("Warning: %d byte were not transferred\n", report->csw.residue);
        switch(report->csw.status)
        {
            case CSW_PASSED:
                printf("Status: Passed\n");
                return 0;
            case CSW_FAILED:
                printf("Status: Failed\n");
                return -1;
            case CSW_PHASE_ERROR:
                printf("Status: Phase Error\n");
                return -2;
            default:
                printf("Status: Unknown Error\n");
                return -3;
        }
    }
    else if(ret < 0)
        printf("Error: cannot get status report\n");
    else
        printf("Error: status report has wrong size\n");

    return ret;
}

static int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size, int nr_xfers)
{
    (void) nr_xfers;
    // there should be no kernel driver attached but in doubt...
    libusb_detach_kernel_driver(dev, 0);
    libusb_claim_interface(dev, 0);

    int sent = 0;
    while(sent < size)
    {
        int xfered;
        int len = MIN(size - sent, xfer_size);
        int ret = libusb_bulk_transfer(dev, 1, data + sent, len, &xfered, 1000);
        if(ret < 0)
        {
            printf("transfer error at send offset %d\n", sent);
            return 1;
        }
        if(xfered == 0)
        {
            printf("empty transfer at step offset %d\n", sent);
            return 2;
        }
        sent += xfered;
    }
    return 0;
}

static void usage(void)
{
    printf("sbloader [options] file\n");
    printf("options:\n");
    printf("  -h/-?/--help    Display this help\n");
    printf("  -d/--debug      Enable debug output\n");
    printf("  -x <size>       Force transfer size\n");
    printf("  -u <vid>:<pid>  Force USB PID and VID\n");
    printf("  -b <bus>:<dev>  Force USB bus and device\n");
    printf("  -p <protocol>   Force protocol ('hid' or 'recovery')\n");
    printf("The following devices are known to this tool:\n");
    for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++)
    {
        const char *type = "unk";
        if(g_dev_info[i].dev_type == HID_DEVICE)
            type = "hid";
        else if(g_dev_info[i].dev_type == RECOVERY_DEVICE)
            type = "recovery";
        else if(g_dev_info[i].dev_type == PROBE_DEVICE)
            type = "probe";
        printf("  %04x:%04x %s (%d bytes/xfer)\n", g_dev_info[i].vendor_id,
            g_dev_info[i].product_id, type, g_dev_info[i].xfer_size);
    }
    printf("You can select a particular device by USB PID and VID.\n");
    printf("In case this is ambiguous, use bus and device number.\n");
    printf("Protocol is infered if possible and unspecified.\n");
    printf("Transfer size is infered if possible.\n");
    exit(1);
}

static bool dev_match(libusb_device *dev, struct dev_info_t *arg_di,
    int usb_bus, int usb_dev, int *db_idx)
{
    // match bus/dev
    if(usb_bus != -1)
        return libusb_get_bus_number(dev) == usb_bus && libusb_get_device_address(dev) == usb_dev;
    // get device descriptor
    struct libusb_device_descriptor desc;
    if(libusb_get_device_descriptor(dev, &desc))
        return false;
    // match command line vid/pid if specified
    if(arg_di->vendor_id != 0)
        return desc.idVendor == arg_di->vendor_id && desc.idProduct == arg_di->product_id;
    // match known vid/pid
    for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++)
        if(desc.idVendor == g_dev_info[i].vendor_id && desc.idProduct == g_dev_info[i].product_id)
        {
            if(db_idx)
                *db_idx = i;
            return true;
        }
    return false;
}

static void print_match(libusb_device *dev)
{
    struct libusb_device_descriptor desc;
    if(libusb_get_device_descriptor(dev, &desc))
        printf("????:????");
    else
        printf("%04x:%04x", desc.idVendor, desc.idProduct);
    printf(" @ %d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev));
}

static bool is_hid_dev(struct libusb_config_descriptor *desc)
{
    if(desc->bNumInterfaces != 1)
        return false;
    if(desc->interface[0].num_altsetting != 1)
        return false;
    const struct libusb_interface_descriptor *intf = &desc->interface[0].altsetting[0];
    if(intf->bNumEndpoints != 1)
        return false;
    if(intf->bInterfaceClass != LIBUSB_CLASS_HID  || intf->bInterfaceSubClass != 0 ||
            intf->bInterfaceProtocol != 0)
        return false;
    return true;
}

static bool is_recovery_dev(struct libusb_config_descriptor *desc)
{
    (void) desc;
    return false;
}

static enum dev_type_t probe_protocol(libusb_device_handle *dev)
{
    struct libusb_config_descriptor *desc;
    if(libusb_get_config_descriptor(libusb_get_device(dev), 0, &desc))
        goto Lerr;
    if(is_hid_dev(desc))
        return HID_DEVICE;
    if(is_recovery_dev(desc))
        return RECOVERY_DEVICE;
    Lerr:
    printf("Cannot probe protocol, please specify it on command line.\n");
    exit(11);
    return PROBE_DEVICE;
}

struct hid_item_t
{
    int tag;
    int type;
    int total_size;
    int data_offset;
    int data_size;
};

static bool hid_parse_short_item(uint8_t *buf, int size, struct hid_item_t *item)
{
    if(size == 0)
        return false;
    item->tag = buf[0] >> 4;
    item->data_size = buf[0] & 3;
    item->type = (buf[0] >> 2) & 3;
    item->data_offset = 1;
    item->total_size = 1 + item->data_size;
    return size >= item->total_size;
}

static bool hid_parse_item(uint8_t *buf, int size, struct hid_item_t *item)
{
    if(!hid_parse_short_item(buf, size, item))
        return false;
    /* long item ? */
    if(item->data_size == 2 && item->type == 3 && item->tag == 15)
    {
        item->tag = buf[2];
        item->data_size = buf[1];
        item->total_size = 3 + item->data_size;
        return size >= item->total_size;
    }
    else
        return true;
}

static int probe_hid_xfer_size(libusb_device_handle *dev)
{
    // FIXME detahc kernel and claim interface here ?
    /* get HID descriptor */
    uint8_t buffer[1024];
    int ret = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_RECIPIENT_INTERFACE,
        LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8) | 0, 0, buffer,
        sizeof(buffer), 1000);
    if(ret <= 0)
        goto Lerr;
    /* this is not a real parser, since the HID descriptor of the device is
     * is mostly trivial, we assume that all reports are made up of one item
     * and simply compute the maximum of report size * report count */
    int xfer_size = 0;
    int report_size = 0;
    int report_count = 0;
    uint8_t *buf = buffer;
    int size = ret;
    while(true)
    {
        struct hid_item_t item;
        if(!hid_parse_item(buf, size, &item))
            break;
        if(item.type == /*global*/1)
        {
            if(item.tag == /*report count*/9)
                report_count = buf[item.data_offset];
            if(item.tag == /*report size*/7)
                report_size = buf[item.data_offset];
        }
        else if(item.type == /*main*/0)
        {
            if(item.tag == /*output*/9)
                xfer_size = MAX(xfer_size, report_count * report_size);
        }
        buf += item.total_size;
        size -= item.total_size;
    }
    return xfer_size / 8;

    Lerr:
    printf("Cannot probe transfer size, using default.\n");
    return 0;
}

static int probe_xfer_size(enum dev_type_t prot, libusb_device_handle *dev)
{
    if(prot == HID_DEVICE)
        return probe_hid_xfer_size(dev);
    printf("Cannot probe transfer size, using default.\n");
    return 0;
}