Commit b419053d authored by Brian Johnson's avatar Brian Johnson

Add basic support for TL866II+ programmers

parent c6f09a78
......@@ -48,7 +48,7 @@ ifeq ($(PKG_CONFIG),)
ERROR := $(error "pkg-config utility not found")
endif
COMMON_OBJECTS=byte_utils.o database.o minipro.o tl866a.o fuses.o easyconfig.o version.o
COMMON_OBJECTS=byte_utils.o database.o minipro.o tl866a.o tl866iiplus.o fuses.o easyconfig.o version.o
OBJECTS=$(COMMON_OBJECTS) main.o
PROGS=minipro
MINIPRO=minipro
......
......@@ -28,6 +28,7 @@
#endif
#include "minipro.h"
#include "tl866a.h"
#include "tl866iiplus.h"
#include "byte_utils.h"
#include "error.h"
......@@ -110,6 +111,14 @@ minipro_handle_t * minipro_open(device_t *device)
break;
case MP_TL866IIPLUS:
strcpy(handle->model, "TL866II+");
handle->minipro_begin_transaction = tl866iiplus_begin_transaction;
handle->minipro_end_transaction = tl866iiplus_end_transaction;
handle->minipro_get_chip_id = tl866iiplus_get_chip_id;
handle->minipro_read_block = tl866iiplus_read_block;
handle->minipro_write_block = tl866iiplus_write_block;
handle->minipro_protect_off = tl866iiplus_protect_off;
handle->minipro_protect_on = tl866iiplus_protect_on;
handle->minipro_erase = tl866iiplus_erase;
break;
}
......@@ -204,6 +213,7 @@ uint32_t msg_send(minipro_handle_t *handle, uint8_t *buf, size_t length)
{
uint32_t bytes_transferred = msg_transfer(handle, buf, length,
LIBUSB_ENDPOINT_OUT);
if (bytes_transferred != length)
{
minipro_close(handle);
......@@ -268,6 +278,10 @@ uint32_t minipro_get_ovc_status(minipro_handle_t *handle,
{
assert(handle != NULL);
if (status){
memset (status, 0x00, sizeof(*status));
}
if (handle->minipro_get_ovc_status) {
return handle->minipro_get_ovc_status(handle, status);
}
......
# TL866II+ Protocol Documentation #
The TL866II+ programmers have 3 USB bulk endpoints. Endpoint 1 is used to send commands to the programmer and receive status information back from the programmer. Endpoints 2 and 3 are used when reading or writing a program to a chip, presumably to help increase performance.
## Commands Bytes ##
* 0x00 - Get system(prgorammer) info
* 0x03 - Begin transaction
* 0x04 - End trasaction
* 0x05 - Read chipid
* 0x06 - Read USER
* 0x07 - Write USER
* 0x08 - Read Fuses
* 0x09 - Write Fuses
* 0x0A - Write payload to data2 memory
* 0x0B - Read payload from data2 memory
* 0x0C - Write payload to code memory
* 0x0D - Read payload from code memory
* 0x0E - Erase chip
* 0x10 - Read payload from data memory
* 0x11 - Write payload to data memory
* 0x14 - Write LOCK bits
* 0x15 - Read LOCK bits
* 0x16 - Read RC calibration
* 0x18 - Protect off
* 0x19 - Protect on
* 0x31 - ???
* 0x32 - ???
* 0x34 - ???
* 0x35 - ???
* 0x36 - ???
* 0x39 - ???
## Get Programmer Information ##
Sending command 0x00 to the programmer will return a block of information about programmer, including firmware version and the serial number. This command works almost identically to the TL866A/CS series except the returned data is 41 bytes long instead of 40. The updated data structure looks like the following:
```
struct minipro_report_info
{
uint8_t echo;
uint8_t device_status;
uint16_t report_size;
uint8_t firmware_version_minor;
uint8_t firmware_version_major;
uint16_t device_version; // changed from byte to word.
uint8_t device_code[8];
uint8_t serial_number[24];
uint8_t hardware_version;
};
```
For the TL866II+ the device version is 5.
## Pin Detect ##
The pin detect feature of the TL866II+ devices appears to use the following command bytes 0x31, 0x32, 0x34, 0x35, and 0x36. All command bytes except for 0x35 are outputs, 0x35 returns 48 bytes of data over endpoint 1.
## Transactions ##
A transaction needs to be started before you can read, write or erase a chip. The command to start a transaction is 0x03 and is used to give the programmer information about the chip being programmed. It is a 64 byte structure that looks like the following.
```
struct begin_transaction {
uint8_t cmd; // 0x00
uint8_t protocol; // 0x01
uint8_t variant; // 0x02
uint16_t unknown1; // 0x03
uint16_t opts1; // 0x05
uint8_t unknown2; // 0x07
uint16_t data_memory_size; // 0x08
uint16_t opts2; // 0x0A
uint16_t opts3; // 0x0C
uint16_t data_memory2_size; // 0X0E
uint32_t code_memory_size; // 0x10
uint8_t zero1[20]; // 0x14
uint32_t package_details; // 0x28
uint32_t zero2[20] // 0x2C
};
```
When finished you need to send an end transaction command of 0x04. This has a simple structure as follows:
```
struct end_transaction {
uint8_t cmd; // 0x00
uint8_t zero[7] // 0x01
};
```
## Reading/Writing ##
To initiate a read or write you must for send an 8 byte command to EP1 that tells the programmer the protocol to used as well as the length of data being transferred and the offset in memory to start the transfer at.
#### Example of reading 512 bytes from a AT28C64B EEPROM at offset 0 ####
| cmd | protocol | length |offset |
|:----:|:--------:|:-------:|:------------------:|
|0x0D |0x07 |0x00 0x02| 0x00 0x00 0x00 0x00|
Actually reading or writing the data depends on the amount of data being read or written. If you are transferring less then 64 bytes of data then you will use endpoint 1 to transfer that data to or from the chip. This works much like the TL866A/CS series.
If however the data is 64 bytes or more you will need to send or receive the data over endpoints 2 and 3. In this case half the data will be transfered using endoint 2 and the other half will use endpoint 3. The data will be broken up into 64 byte blocks and alternate between the two endpoints.
For example if you were trying to read 512 bytes from an EEPROM, you would expect 256 bytes to be returned over endpoint 2 and another 256 bytes returned over endpoint 3. The data over endpoint 2 would contain blocks [0,2,4,6] while endpoint 3 would give you blocks [1,3,5,7].
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "byte_utils.h"
#include "error.h"
#include "minipro.h"
#include "tl866iiplus.h"
static void msg_init(minipro_handle_t *handle, uint8_t command, uint8_t *buf, size_t length)
{
assert(length >= 8);
memset(buf, 0x00, length);
buf[0] = command;
buf[1] = handle->device->protocol_id;
buf[2] = handle->device->variant;
}
static void payload_transfer_cb(struct libusb_transfer *transfer)
{
int *completed = transfer->user_data;
if (completed != NULL) {
*completed = 1;
}
}
static uint32_t payload_transfer(minipro_handle_t *handle, uint8_t direction,
uint8_t *ep2_buffer, size_t ep2_length,
uint8_t *ep3_buffer, size_t ep3_length)
{
struct libusb_transfer *ep2_urb;
struct libusb_transfer *ep3_urb;
int32_t ret;
int32_t ep2_completed = 0;
int32_t ep3_completed = 0;
ep2_urb = libusb_alloc_transfer(0);
ep3_urb = libusb_alloc_transfer(0);
if (ep2_urb == NULL || ep3_urb == NULL) {
ERROR("Out of memory");
}
libusb_fill_bulk_transfer(ep2_urb, handle->usb_handle, (0x02 | direction), ep2_buffer, ep2_length,
payload_transfer_cb, &ep2_completed, 5000);
libusb_fill_bulk_transfer(ep3_urb, handle->usb_handle, (0x03 | direction), ep3_buffer, ep3_length,
payload_transfer_cb, &ep3_completed, 5000);
ret = libusb_submit_transfer(ep2_urb);
if (ret < 0) {
minipro_close(handle);
ERROR2("IO error: submit_transfer: %s\n", libusb_error_name(ret));
}
ret = libusb_submit_transfer(ep3_urb);
if (ret < 0) {
minipro_close(handle);
ERROR2("IO error: submit_transfer: %s\n", libusb_error_name(ret));
}
while (!ep2_completed) {
ret = libusb_handle_events_completed(handle->ctx, &ep2_completed);
if (ret < 0) {
if (ret == LIBUSB_ERROR_INTERRUPTED)
continue;
libusb_cancel_transfer(ep2_urb);
libusb_cancel_transfer(ep3_urb);
continue;
}
}
while (!ep3_completed) {
ret = libusb_handle_events_completed(handle->ctx, &ep3_completed);
if (ret < 0) {
if (ret == LIBUSB_ERROR_INTERRUPTED)
continue;
libusb_cancel_transfer(ep2_urb);
libusb_cancel_transfer(ep3_urb);
continue;
}
}
if (ep2_urb->status != 0 || ep3_urb->status != 0) {
minipro_close(handle);
ERROR("IO Error: Async transfer failed.");
}
ret = ep2_urb->actual_length + ep3_urb->actual_length;
libusb_free_transfer(ep2_urb);
libusb_free_transfer(ep3_urb);
return ret;
}
static uint32_t write_payload(minipro_handle_t *handle, uint8_t *buffer, size_t length)
{
uint8_t *data;
int32_t ret;
size_t ep_length = length/2;
size_t blocks = length/64;
data = malloc(length);
if (data == NULL) {
ERROR("Out of memory");
}
for (int i = 0; i < blocks; ++i) {
uint8_t *ep_buf;
if (i % 2 == 0) {
ep_buf = data;
} else {
ep_buf = data + ep_length;
}
memcpy(ep_buf + ((i/2)*64), buffer+(i*64), 64);
}
ret = payload_transfer(handle, LIBUSB_ENDPOINT_OUT, data, ep_length, data+ep_length, ep_length);
free(data);
return ret;
}
static uint32_t read_payload(minipro_handle_t *handle, uint8_t *buffer, size_t length)
{
uint8_t *data;
int32_t ret;
size_t ep_length = length/2;
size_t blocks = length/64;
data = malloc(length);
if (data == NULL) {
ERROR("Out of memory");
}
ret = payload_transfer(handle, LIBUSB_ENDPOINT_IN, data, ep_length, data+ep_length, ep_length);
for (int i = 0; i < blocks; ++i) {
uint8_t *ep_buf;
if (i % 2 == 0) {
ep_buf = data;
} else {
ep_buf = data + ep_length;
}
memcpy(buffer+(i*64), ep_buf + ((i/2)*64), 64);
}
free(data);
return ret;
}
void tl866iiplus_begin_transaction(minipro_handle_t *handle)
{
uint8_t msg[64];
msg_init(handle, TL866IIPLUS_BEGIN_TRANS, msg, sizeof(msg));
format_int(&(msg[40]), handle->device->package_details, 4, MP_LITTLE_ENDIAN);
format_int(&(msg[16]), handle->device->code_memory_size, 4, MP_LITTLE_ENDIAN);
format_int(&(msg[14]), handle->device->data_memory2_size, 2, MP_LITTLE_ENDIAN);
format_int(&(msg[12]), handle->device->opts3, 2, MP_LITTLE_ENDIAN);
format_int(&(msg[10]), handle->device->opts2, 2, MP_LITTLE_ENDIAN);
format_int(&(msg[8]), handle->device->data_memory_size, 2, MP_LITTLE_ENDIAN);
format_int(&(msg[5]), handle->device->opts1, 2, MP_LITTLE_ENDIAN);
msg_send(handle, msg, sizeof(msg));
}
void tl866iiplus_end_transaction(minipro_handle_t *handle)
{
uint8_t msg[8];
msg_init(handle, TL866IIPLUS_END_TRANS, msg, sizeof(msg));
msg_send(handle, msg, sizeof(msg));
}
void tl866iiplus_read_block(minipro_handle_t *handle, uint32_t type,
uint32_t addr, uint8_t *buf, size_t len)
{
uint8_t msg[64];
if (type == MP_CODE) {
type = TL866IIPLUS_READ_CODE;
} else if (type == MP_DATA) {
type = TL866IIPLUS_READ_DATA;
} else {
ERROR2("Unknown type for read_block (%d)\n", type);
}
msg_init(handle, type, msg, sizeof(msg));
format_int(&(msg[2]), len, 2, MP_LITTLE_ENDIAN);
format_int(&(msg[4]), addr, 4, MP_LITTLE_ENDIAN);
msg_send(handle, msg, 8);
if (len < 64) {
msg_recv(handle, buf, len);
} else {
read_payload(handle, buf, len);
}
}
void tl866iiplus_write_block(minipro_handle_t *handle, uint32_t type,
uint32_t addr, uint8_t *buf, size_t len)
{
uint8_t msg[72];
if (type == MP_CODE) {
type = TL866IIPLUS_WRITE_CODE;
} else if (type == MP_DATA) {
type = TL866IIPLUS_WRITE_DATA;
} else {
ERROR2("Unknown type for write_block (%d)\n", type);
}
msg_init(handle, type, msg, sizeof(msg));
format_int(&(msg[2]), len, 2, MP_LITTLE_ENDIAN);
format_int(&(msg[4]), addr, 4, MP_LITTLE_ENDIAN);
if (len < 64) {
memcpy(&(msg[8]), buf, len);
msg_send(handle, msg, 8 + len);
} else {
msg_send(handle, msg, 8);
write_payload(handle, buf, len);
}
}
uint32_t tl866iiplus_get_chip_id(minipro_handle_t *handle, uint8_t *type)
{
uint8_t msg[8];
msg_init(handle, TL866IIPLUS_READID, msg, sizeof(msg));
msg_send(handle, msg, sizeof(msg));
msg_recv(handle, msg, 6);
*type = msg[0]; //The Chip ID type (1-5)
msg[1] &= 0x03; //The length byte is always 1-4 but never know, truncate to max. 4 bytes.
return (msg[1] ? load_int(&(msg[2]), msg[1], MP_BIG_ENDIAN) : 0); //Check for positive length.
}
void tl866iiplus_protect_off(minipro_handle_t *handle)
{
uint8_t msg[8];
msg_init(handle, TL866IIPLUS_PROTECT_OFF, msg, sizeof(msg));
msg_send(handle, msg, sizeof(msg));
}
void tl866iiplus_protect_on(minipro_handle_t *handle)
{
uint8_t msg[8];
msg_init(handle, TL866IIPLUS_PROTECT_ON, msg, sizeof(msg));
msg_send(handle, msg, sizeof(msg));
}
uint32_t tl866iiplus_erase(minipro_handle_t *handle)
{
uint8_t msg[8];
msg_init(handle, TL866IIPLUS_ERASE, msg, sizeof(msg));
format_int(&(msg[2]), handle->device->write_unlock, 2, MP_LITTLE_ENDIAN);
msg_send(handle, msg, sizeof(msg));
msg_recv(handle, msg, sizeof(msg));
return msg[0] != TL866IIPLUS_ERASE;
}
/*
* tl866iplusa.h - Low level ops for TL866II+ declarations and definations
*
* This file is a part of Minipro.
*
* Minipro 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 3 of the License, or
* (at your option) any later version.
*
* Minipro is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __TL866IIPLUS_H
#define __TL866IIPLUS_H
#define TL866IIPLUS_BEGIN_TRANS 0x03
#define TL866IIPLUS_END_TRANS 0x04
#define TL866IIPLUS_READID 0x05
#define TL866IIPLUS_WRITE_CODE 0x0C
#define TL866IIPLUS_READ_CODE 0x0D
#define TL866IIPLUS_ERASE 0x0E
#define TL866IIPLUS_READ_DATA 0x10
#define TL866IIPLUS_WRITE_DATA 0x11
#define TL866IIPLUS_PROTECT_OFF 0x18
#define TL866IIPLUS_PROTECT_ON 0x19
void tl866iiplus_begin_transaction(minipro_handle_t *handle);
void tl866iiplus_end_transaction(minipro_handle_t *handle);
uint32_t tl866iiplus_get_chip_id(minipro_handle_t *handle, uint8_t *type);
void tl866iiplus_read_block(minipro_handle_t *handle, uint32_t type, uint32_t addr, uint8_t *buf, size_t len);
void tl866iiplus_write_block(minipro_handle_t *handle, uint32_t type, uint32_t addr, uint8_t *buf, size_t len);
void tl866iiplus_protect_off(minipro_handle_t *handle);
void tl866iiplus_protect_on(minipro_handle_t *handle);
uint32_t tl866iiplus_erase(minipro_handle_t *handle);
#endif
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment