1
0
Fork 0
forked from len0rd/rockbox
foxbox/utils/imxtools/hwemul/lib/hwemul.c
Amaury Pouly f44d95630c imxtools: introduce hwemul
The hwemul tool is a small binary blob running on the device
that can received commands over USB. It is mainly intended to be
loaded using the recory mode and allows to read/write registers,
memory, use the OTP device, ... The tool is split into three
parts: dev/ contains the actual blob (which handles both imx233
and stmp3700), lib/ contains the communication library and can
also use the register description produced by the regtools/
to ease register by name, tools/ contains an interactive tool
to send commands to the device when running the blob.

Change-Id: Ie8cb32e987f825d8ed750d48071e43415b4dacb3
2012-11-14 12:51:51 +01:00

175 lines
6.1 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2012 by 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 "hwemul.h"
#include "hwemul_soc.h"
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
/* requires then ->handle field only */
int hwemul_probe(struct hwemul_device_t *dev)
{
libusb_device *mydev = libusb_get_device(dev->handle);
int config_id;
libusb_get_configuration(dev->handle, &config_id);
struct libusb_config_descriptor *config;
libusb_get_active_config_descriptor(mydev, &config);
const struct libusb_endpoint_descriptor *endp = NULL;
int intf;
for(intf = 0; intf < config->bNumInterfaces; intf++)
{
if(config->interface[intf].num_altsetting != 1)
continue;
const struct libusb_interface_descriptor *interface =
&config->interface[intf].altsetting[0];
if(interface->bNumEndpoints != 3 ||
interface->bInterfaceClass != HWEMUL_CLASS ||
interface->bInterfaceSubClass != HWEMUL_SUBCLASS ||
interface->bInterfaceProtocol != HWEMUL_PROTOCOL)
continue;
dev->intf = intf;
dev->bulk_in = dev->bulk_out = dev->int_in = -1;
for(int ep = 0; ep < interface->bNumEndpoints; ep++)
{
endp = &interface->endpoint[ep];
if((endp->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT &&
(endp->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
dev->int_in = endp->bEndpointAddress;
if((endp->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK &&
(endp->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
dev->bulk_in = endp->bEndpointAddress;
if((endp->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_BULK &&
(endp->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT)
dev->bulk_out = endp->bEndpointAddress;
}
if(dev->bulk_in == -1 || dev->bulk_out == -1 || dev->int_in == -1)
continue;
break;
}
if(intf == config->bNumInterfaces)
return 1;
return libusb_claim_interface(dev->handle, intf);
}
int hwemul_release(struct hwemul_device_t *dev)
{
return libusb_release_interface(dev->handle, dev->intf);
}
int hwemul_get_info(struct hwemul_device_t *dev, uint16_t idx, void *info, size_t sz)
{
return libusb_control_transfer(dev->handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
HWEMUL_GET_INFO, 0, idx, info, sz, 1000);
}
int hwemul_get_log(struct hwemul_device_t *dev, void *buf, size_t sz)
{
return libusb_control_transfer(dev->handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
HWEMUL_GET_LOG, 0, 0, buf, sz, 1000);
}
int hwemul_rw_mem(struct hwemul_device_t *dev, int read, uint32_t addr, void *buf, size_t sz)
{
size_t tot_sz = 0;
while(sz)
{
uint16_t xfer = MIN(1 * 1024, sz);
int ret = libusb_control_transfer(dev->handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE |
(read ? LIBUSB_ENDPOINT_IN : LIBUSB_ENDPOINT_OUT),
HWEMUL_RW_MEM, addr & 0xffff, addr >> 16, buf, xfer, 1000);
if(ret != xfer)
return ret;
sz -= xfer;
addr += xfer;
buf += xfer;
tot_sz += xfer;
}
return tot_sz;
}
int hwemul_call(struct hwemul_device_t *dev, uint32_t addr)
{
return libusb_control_transfer(dev->handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, HWEMUL_CALL, addr & 0xffff, addr >> 16, NULL, 0,
1000);
}
int hwemul_jump(struct hwemul_device_t *dev, uint32_t addr)
{
return libusb_control_transfer(dev->handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, HWEMUL_JUMP, addr & 0xffff, addr >> 16, NULL, 0,
1000);
}
const char *hwemul_get_product_string(struct usb_resp_info_stmp_t *stmp)
{
switch(stmp->chipid)
{
case 0x3700: return "STMP 3700";
case 0x37b0: return "STMP 3770";
case 0x3780: return "STMP 3780 / i.MX233";
default: return "unknown";
}
}
const char *hwemul_get_rev_string(struct usb_resp_info_stmp_t *stmp)
{
switch(stmp->chipid)
{
case 0x37b0:
case 0x3780:
switch(stmp->rev)
{
case 0: return "TA1";
case 1: return "TA2";
case 2: return "TA3";
case 3: return "TA4";
default: return "unknown";
}
break;
default:
return "unknown";
}
}
int hwemul_aes_otp(struct hwemul_device_t *dev, void *buf, size_t sz, uint16_t param)
{
int ret = libusb_control_transfer(dev->handle,
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE |
LIBUSB_ENDPOINT_OUT, HWEMUL_AES_OTP, param, 0, buf, sz,
1000);
if(ret <0 || (unsigned)ret != sz)
return -1;
int xfer;
ret = libusb_interrupt_transfer(dev->handle, dev->int_in, buf, sz, &xfer, 1000);
if(ret < 0 || (unsigned)xfer != sz)
return -1;
return ret;
}