xfel/fel.c
2021-06-10 18:18:03 +08:00

283 lines
6.2 KiB
C

#include <fel.h>
extern struct chip_t a40i;
extern struct chip_t d1;
extern struct chip_t f1c100s;
extern struct chip_t h3;
extern struct chip_t r329;
extern struct chip_t t507;
extern struct chip_t v3s;
extern struct chip_t v831;
static struct chip_t * chips[] = {
&a40i,
&d1,
&f1c100s,
&h3,
&r329,
&t507,
&v3s,
&v831,
};
struct usb_request_t {
char magic[8];
uint32_t length;
uint32_t unknown1;
uint16_t request;
uint32_t length2;
char pad[10];
} __attribute__((packed));
struct fel_request_t {
uint32_t request;
uint32_t address;
uint32_t length;
uint32_t pad;
} __attribute__((packed));
static inline void usb_bulk_send(libusb_device_handle * hdl, int ep, const char * buf, size_t len)
{
size_t max_chunk = 128 * 1024;
size_t chunk;
int r, bytes;
while(len > 0)
{
chunk = len < max_chunk ? len : max_chunk;
r = libusb_bulk_transfer(hdl, ep, (void *)buf, chunk, &bytes, 10000);
if(r != 0)
{
printf("usb bulk send error\r\n");
exit(-1);
}
len -= bytes;
buf += bytes;
}
}
static inline void usb_bulk_recv(libusb_device_handle * hdl, int ep, char * buf, size_t len)
{
int r, bytes;
while(len > 0)
{
r = libusb_bulk_transfer(hdl, ep, (void *)buf, len, &bytes, 10000);
if(r != 0)
{
printf("usb bulk recv error\r\n");
exit(-1);
}
len -= bytes;
buf += bytes;
}
}
static inline void send_usb_request(struct xfel_ctx_t * ctx, int type, size_t length)
{
struct usb_request_t req = {
.magic = "AWUC",
.request = cpu_to_le16(type),
.length = cpu_to_le32(length),
.unknown1 = cpu_to_le32(0x0c000000)
};
req.length2 = req.length;
usb_bulk_send(ctx->hdl, ctx->epout, (const char *)&req, sizeof(struct usb_request_t));
}
static inline void read_usb_response(struct xfel_ctx_t * ctx)
{
char buf[13];
usb_bulk_recv(ctx->hdl, ctx->epin, (char *)buf, sizeof(buf));
assert(strcmp(buf, "AWUS") == 0);
}
static inline void usb_write(struct xfel_ctx_t * ctx, const void * buf, size_t len)
{
send_usb_request(ctx, 0x12, len);
usb_bulk_send(ctx->hdl, ctx->epout, (const char *)buf, len);
read_usb_response(ctx);
}
static inline void usb_read(struct xfel_ctx_t * ctx, const void * data, size_t len)
{
send_usb_request(ctx, 0x11, len);
usb_bulk_send(ctx->hdl, ctx->epin, (const char *)data, len);
read_usb_response(ctx);
}
static inline void send_fel_request(struct xfel_ctx_t * ctx, int type, uint32_t addr, uint32_t length)
{
struct fel_request_t req = {
.request = cpu_to_le32(type),
.address = cpu_to_le32(addr),
.length = cpu_to_le32(length)
};
usb_write(ctx, &req, sizeof(struct fel_request_t));
}
static inline void read_fel_status(struct xfel_ctx_t * ctx)
{
char buf[8];
usb_read(ctx, buf, sizeof(buf));
}
static inline void fel_version(struct xfel_ctx_t * ctx)
{
int i;
send_fel_request(ctx, 0x001, 0, 0);
usb_read(ctx, &ctx->version, sizeof(ctx->version));
read_fel_status(ctx);
ctx->version.id = le32_to_cpu(ctx->version.id);
ctx->version.firmware = le32_to_cpu(ctx->version.firmware);
ctx->version.protocol = le16_to_cpu(ctx->version.protocol);
ctx->version.scratchpad = le32_to_cpu(ctx->version.scratchpad);
ctx->chip = NULL;
for(i = 0; i < ARRAY_SIZE(chips); i++)
{
if(chips[i]->id == ctx->version.id)
{
ctx->chip = chips[i];
break;
}
}
}
int fel_init(struct xfel_ctx_t * ctx)
{
if(ctx && ctx->hdl)
{
struct libusb_config_descriptor * config;
int if_idx, set_idx, ep_idx;
const struct libusb_interface * iface;
const struct libusb_interface_descriptor * setting;
const struct libusb_endpoint_descriptor * ep;
if(libusb_claim_interface(ctx->hdl, 0) == 0)
{
if(libusb_get_active_config_descriptor(libusb_get_device(ctx->hdl), &config) == 0)
{
for(if_idx = 0; if_idx < config->bNumInterfaces; if_idx++)
{
iface = config->interface + if_idx;
for(set_idx = 0; set_idx < iface->num_altsetting; set_idx++)
{
setting = iface->altsetting + set_idx;
for(ep_idx = 0; ep_idx < setting->bNumEndpoints; ep_idx++)
{
ep = setting->endpoint + ep_idx;
if((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != LIBUSB_TRANSFER_TYPE_BULK)
continue;
if((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
ctx->epin = ep->bEndpointAddress;
else
ctx->epout = ep->bEndpointAddress;
}
}
}
libusb_free_config_descriptor(config);
fel_version(ctx);
return 1;
}
}
}
return 0;
}
void fel_exec(struct xfel_ctx_t * ctx, uint32_t addr)
{
send_fel_request(ctx, 0x102, addr, 0);
read_fel_status(ctx);
}
static inline void fel_read_raw(struct xfel_ctx_t * ctx, uint32_t addr, void * buf, size_t len)
{
send_fel_request(ctx, 0x103, addr, len);
usb_read(ctx, buf, len);
read_fel_status(ctx);
}
static inline void fel_write_raw(struct xfel_ctx_t * ctx, uint32_t addr, void * buf, size_t len)
{
send_fel_request(ctx, 0x101, addr, len);
usb_write(ctx, buf, len);
read_fel_status(ctx);
}
uint32_t fel_read32(struct xfel_ctx_t * ctx, uint32_t addr)
{
uint32_t val;
fel_read_raw(ctx, addr, &val, sizeof(uint32_t));
return val;
}
void fel_write32(struct xfel_ctx_t * ctx, uint32_t addr, uint32_t val)
{
fel_write_raw(ctx, addr, &val, sizeof(uint32_t));
}
void fel_read(struct xfel_ctx_t * ctx, uint32_t addr, void * buf, size_t len)
{
size_t n;
while(len > 0)
{
n = len > 65536 ? 65536 : len;
fel_read_raw(ctx, addr, buf, n);
addr += n;
buf += n;
len -= n;
}
}
void fel_write(struct xfel_ctx_t * ctx, uint32_t addr, void * buf, size_t len)
{
size_t n;
while(len > 0)
{
n = len > 65536 ? 65536 : len;
fel_write_raw(ctx, addr, buf, n);
addr += n;
buf += n;
len -= n;
}
}
void fel_read_progress(struct xfel_ctx_t * ctx, uint32_t addr, void * buf, size_t len)
{
struct progress_t p;
size_t n;
progress_start(&p, len);
while(len > 0)
{
n = len > 65536 ? 65536 : len;
fel_read_raw(ctx, addr, buf, n);
addr += n;
buf += n;
len -= n;
progress_update(&p, n);
}
progress_stop(&p);
}
void fel_write_progress(struct xfel_ctx_t * ctx, uint32_t addr, void * buf, size_t len)
{
struct progress_t p;
size_t n;
progress_start(&p, len);
while(len > 0)
{
n = len > 65536 ? 65536 : len;
fel_write_raw(ctx, addr, buf, n);
addr += n;
buf += n;
len -= n;
progress_update(&p, n);
}
progress_stop(&p);
}