mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-07 21:52:05 +00:00
Merge 9a3d35c274
into ba296db701
This commit is contained in:
commit
55aafb9b54
@ -8,6 +8,9 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <libpinedio-usb.h>
|
#include <libpinedio-usb.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
extern "C" {
|
||||||
|
#include "platform/portduino/ch341a_i2c.h"
|
||||||
|
}
|
||||||
|
|
||||||
// include the library for Raspberry GPIO pins
|
// include the library for Raspberry GPIO pins
|
||||||
|
|
||||||
@ -47,6 +50,9 @@ class Ch341Hal : public RadioLibHal
|
|||||||
std::string s = "Could not open SPI: ";
|
std::string s = "Could not open SPI: ";
|
||||||
throw(s + std::to_string(ret));
|
throw(s + std::to_string(ret));
|
||||||
}
|
}
|
||||||
|
// How to read the eeprom
|
||||||
|
// uint8_t buffer[1024] = {0};
|
||||||
|
// ch341readEEPROM_param(buffer, 0, 128, 128, 8, 1, 0x50, pinedio.handle);
|
||||||
|
|
||||||
pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0);
|
pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0);
|
||||||
pinedio_set_pin_mode(&pinedio, 3, true);
|
pinedio_set_pin_mode(&pinedio, 3, true);
|
||||||
|
204
src/platform/portduino/ch341a_i2c.c
Normal file
204
src/platform/portduino/ch341a_i2c.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
//
|
||||||
|
// ch341eeprom programmer version 0.1 (Beta)
|
||||||
|
//
|
||||||
|
// Programming tool for the 24Cxx serial EEPROMs using the Winchiphead CH341A IC
|
||||||
|
//
|
||||||
|
// (c) December 2011 asbokid <ballymunboy@gmail.com>
|
||||||
|
// (c) August 2023 Mikhail Medvedev <e-ink-reader@yandex.ru>
|
||||||
|
// 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 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program 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.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "ch341a_i2c.h"
|
||||||
|
|
||||||
|
// extern struct libusb_device_handle *handle;
|
||||||
|
unsigned char *readbuf;
|
||||||
|
uint32_t getnextpkt; // set by the callback function
|
||||||
|
uint32_t syncackpkt; // synch / ack flag used by BULK OUT cb function
|
||||||
|
uint32_t byteoffset;
|
||||||
|
|
||||||
|
// callback functions for async USB transfers
|
||||||
|
static void cbBulkIn(struct libusb_transfer *transfer);
|
||||||
|
static void cbBulkOut(struct libusb_transfer *transfer);
|
||||||
|
|
||||||
|
void ch341ReadCmdMarshall(uint8_t *buffer, uint32_t addr, struct EEPROM *eeprom_info, uint32_t i2c_address)
|
||||||
|
{
|
||||||
|
uint8_t *ptr = buffer;
|
||||||
|
uint8_t msb_addr;
|
||||||
|
uint32_t size_kb;
|
||||||
|
|
||||||
|
*ptr++ = CH341_CMD_I2C_STREAM; // 0
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_STA; // 1
|
||||||
|
// Write address
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_OUT | ((*eeprom_info).addr_size + 1); // 2: I2C bus adddress + EEPROM address
|
||||||
|
if ((*eeprom_info).addr_size >= 2) {
|
||||||
|
// 24C32 and more
|
||||||
|
msb_addr = addr >> 16 & (*eeprom_info).i2c_addr_mask;
|
||||||
|
*ptr++ = (i2c_address | msb_addr) << 1; // 3
|
||||||
|
*ptr++ = (addr >> 8 & 0xFF); // 4
|
||||||
|
*ptr++ = (addr >> 0 & 0xFF); // 5
|
||||||
|
} else {
|
||||||
|
// 24C16 and less
|
||||||
|
msb_addr = addr >> 8 & (*eeprom_info).i2c_addr_mask;
|
||||||
|
*ptr++ = (i2c_address | msb_addr) << 1; // 3
|
||||||
|
*ptr++ = (addr >> 0 & 0xFF); // 4
|
||||||
|
}
|
||||||
|
// Read
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_STA; // 6/5
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_OUT | 1; // 7/6
|
||||||
|
*ptr++ = ((i2c_address | msb_addr) << 1) | 1; // 8/7: Read command
|
||||||
|
|
||||||
|
// Configuration?
|
||||||
|
*ptr++ = 0xE0; // 9/8
|
||||||
|
*ptr++ = 0x00; // 10/9
|
||||||
|
if ((*eeprom_info).addr_size < 2)
|
||||||
|
*ptr++ = 0x10; // x/10
|
||||||
|
memcpy(ptr, "\x00\x06\x04\x00\x00\x00\x00\x00\x00", 9);
|
||||||
|
ptr += 9; // 10
|
||||||
|
size_kb = (*eeprom_info).size / 1024;
|
||||||
|
*ptr++ = size_kb & 0xFF; // 19
|
||||||
|
*ptr++ = (size_kb >> 8) & 0xFF; // 20
|
||||||
|
memcpy(ptr, "\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc", 10);
|
||||||
|
ptr += 10;
|
||||||
|
|
||||||
|
// Frame 2
|
||||||
|
*ptr++ = CH341_CMD_I2C_STREAM;
|
||||||
|
memcpy(ptr,
|
||||||
|
"\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00"
|
||||||
|
"\xd9\x8b\x41\x7e\x00\xe0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e",
|
||||||
|
31);
|
||||||
|
ptr += 31;
|
||||||
|
|
||||||
|
// Frame 3
|
||||||
|
*ptr++ = CH341_CMD_I2C_STREAM;
|
||||||
|
memcpy(ptr,
|
||||||
|
"\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77"
|
||||||
|
"\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00",
|
||||||
|
31);
|
||||||
|
ptr += 31;
|
||||||
|
|
||||||
|
// Finalize
|
||||||
|
*ptr++ = CH341_CMD_I2C_STREAM; // 0xAA
|
||||||
|
*ptr++ = 0xDF; // ???
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_IN; // 0xC0
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_STO; // 0x75
|
||||||
|
*ptr++ = CH341_CMD_I2C_STM_END; // 0x00
|
||||||
|
|
||||||
|
assert(ptr - buffer == CH341_EEPROM_READ_CMD_SZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// ch341readEEPROM()
|
||||||
|
// read n bytes from device (in packets of 32 bytes)
|
||||||
|
int32_t ch341readEEPROM_param(uint8_t *buffer, uint32_t offset, uint32_t bytestoread, uint32_t ic_size, uint32_t block_size,
|
||||||
|
uint8_t algorithm, uint32_t i2c_address, struct libusb_device_handle *handle)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t ch341outBuffer[EEPROM_READ_BULKOUT_BUF_SZ];
|
||||||
|
uint8_t ch341inBuffer[IN_BUF_SZ]; // 0x100 bytes
|
||||||
|
int32_t ret = 0, readpktcount = 0;
|
||||||
|
struct libusb_transfer *xferBulkIn, *xferBulkOut;
|
||||||
|
struct timeval tv = {0, 100}; // our async polling interval
|
||||||
|
struct EEPROM eeprom_info;
|
||||||
|
eeprom_info.name = "24c01";
|
||||||
|
eeprom_info.size = ic_size;
|
||||||
|
eeprom_info.page_size = (uint16_t)block_size;
|
||||||
|
eeprom_info.addr_size = 0x0f & algorithm;
|
||||||
|
eeprom_info.i2c_addr_mask = (0xf0 & algorithm) / 16;
|
||||||
|
|
||||||
|
xferBulkIn = libusb_alloc_transfer(0);
|
||||||
|
xferBulkOut = libusb_alloc_transfer(0);
|
||||||
|
|
||||||
|
if (!xferBulkIn || !xferBulkOut) {
|
||||||
|
printf("Couldn't allocate USB transfer structures\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
byteoffset = 0;
|
||||||
|
|
||||||
|
memset(ch341inBuffer, 0, EEPROM_READ_BULKIN_BUF_SZ);
|
||||||
|
ch341ReadCmdMarshall(ch341outBuffer, offset, &eeprom_info, i2c_address); // Fill output buffer
|
||||||
|
|
||||||
|
libusb_fill_bulk_transfer(xferBulkIn, handle, BULK_READ_ENDPOINT, ch341inBuffer, EEPROM_READ_BULKIN_BUF_SZ, cbBulkIn, NULL,
|
||||||
|
DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
libusb_fill_bulk_transfer(xferBulkOut, handle, BULK_WRITE_ENDPOINT, ch341outBuffer, EEPROM_READ_BULKOUT_BUF_SZ, cbBulkOut,
|
||||||
|
NULL, DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
libusb_submit_transfer(xferBulkIn);
|
||||||
|
libusb_submit_transfer(xferBulkOut);
|
||||||
|
|
||||||
|
readbuf = buffer;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ret = libusb_handle_events_timeout(NULL, &tv);
|
||||||
|
|
||||||
|
if (ret < 0 || getnextpkt == -1) { // indicates an error
|
||||||
|
printf("ret from libusb_handle_timeout = %d\n", ret);
|
||||||
|
printf("getnextpkt = %u\n", getnextpkt);
|
||||||
|
if (ret < 0)
|
||||||
|
printf("USB read error : %s\n", strerror(-ret));
|
||||||
|
libusb_free_transfer(xferBulkIn);
|
||||||
|
libusb_free_transfer(xferBulkOut);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (getnextpkt == 1) { // callback function reports a new BULK IN packet received
|
||||||
|
getnextpkt = 0; // reset the flag
|
||||||
|
readpktcount++; // increment the read packet counter
|
||||||
|
byteoffset += EEPROM_READ_BULKIN_BUF_SZ;
|
||||||
|
if (byteoffset == bytestoread)
|
||||||
|
break;
|
||||||
|
|
||||||
|
libusb_submit_transfer(xferBulkIn); // re-submit request for next BULK IN packet of EEPROM data
|
||||||
|
if (syncackpkt)
|
||||||
|
syncackpkt = 0;
|
||||||
|
// if 4th packet received, we are at end of 0x80 byte data block,
|
||||||
|
// if it is not the last block, then resubmit request for data
|
||||||
|
if (readpktcount == 4) {
|
||||||
|
readpktcount = 0;
|
||||||
|
|
||||||
|
ch341ReadCmdMarshall(ch341outBuffer, byteoffset, &eeprom_info, i2c_address); // Fill output buffer
|
||||||
|
libusb_fill_bulk_transfer(xferBulkOut, handle, BULK_WRITE_ENDPOINT, ch341outBuffer, EEPROM_READ_BULKOUT_BUF_SZ,
|
||||||
|
cbBulkOut, NULL, DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
libusb_submit_transfer(xferBulkOut); // update transfer struct (with new EEPROM page offset)
|
||||||
|
// and re-submit next transfer request to BULK OUT endpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libusb_free_transfer(xferBulkIn);
|
||||||
|
libusb_free_transfer(xferBulkOut);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback function for async bulk in comms
|
||||||
|
void cbBulkIn(struct libusb_transfer *transfer)
|
||||||
|
{
|
||||||
|
switch (transfer->status) {
|
||||||
|
case LIBUSB_TRANSFER_COMPLETED:
|
||||||
|
// copy read data to our EEPROM buffer
|
||||||
|
memcpy(readbuf + byteoffset, transfer->buffer, transfer->actual_length);
|
||||||
|
getnextpkt = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("\ncbBulkIn: error : %d\n", transfer->status);
|
||||||
|
getnextpkt = -1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback function for async bulk out comms
|
||||||
|
void cbBulkOut(struct libusb_transfer *transfer)
|
||||||
|
{
|
||||||
|
syncackpkt = 1;
|
||||||
|
return;
|
||||||
|
}
|
43
src/platform/portduino/ch341a_i2c.h
Normal file
43
src/platform/portduino/ch341a_i2c.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// libUSB driver for the ch341a in i2c mode
|
||||||
|
//
|
||||||
|
// Copyright 2011 asbokid <ballymunboy@gmail.com>
|
||||||
|
#ifndef __CH341A_I2C_H__
|
||||||
|
#define __CH341A_I2C_H__
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libusb-1.0/libusb.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define BULK_WRITE_ENDPOINT 0x02 /* bEndpointAddress 0x02 EP 2 OUT (Bulk)*/
|
||||||
|
#define BULK_READ_ENDPOINT 0x82 /* bEndpointAddress 0x82 EP 2 IN (Bulk)*/
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT 300 // 300mS for USB timeouts
|
||||||
|
|
||||||
|
#define IN_BUF_SZ 0x100
|
||||||
|
#define EEPROM_READ_BULKIN_BUF_SZ 0x20
|
||||||
|
#define EEPROM_READ_BULKOUT_BUF_SZ 0x65
|
||||||
|
|
||||||
|
#define CH341_CMD_I2C_STREAM 0xAA
|
||||||
|
#define CH341_CMD_I2C_STM_STA 0x74
|
||||||
|
#define CH341_CMD_I2C_STM_STO 0x75
|
||||||
|
#define CH341_CMD_I2C_STM_OUT 0x80
|
||||||
|
#define CH341_CMD_I2C_STM_IN 0xC0
|
||||||
|
#define CH341_CMD_I2C_STM_END 0x00
|
||||||
|
|
||||||
|
#define CH341_EEPROM_READ_CMD_SZ 0x65 /* Same size for all 24cXX read setup and next packets*/
|
||||||
|
|
||||||
|
struct EEPROM {
|
||||||
|
char *name;
|
||||||
|
uint32_t size;
|
||||||
|
uint16_t page_size;
|
||||||
|
uint8_t addr_size; // Length of address in bytes
|
||||||
|
uint8_t i2c_addr_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
int32_t ch341readEEPROM_param(uint8_t *buffer, uint32_t offset, uint32_t bytestoread, uint32_t ic_size, uint32_t block_size,
|
||||||
|
uint8_t algorithm, uint32_t i2c_address, struct libusb_device_handle *handle);
|
||||||
|
#endif /* __CH341A_I2C_H__ */
|
Loading…
Reference in New Issue
Block a user