├── src ├── res.h ├── Makefile ├── timer.h ├── types.h ├── mw_eeprom_api.h ├── snorcmd_api.h ├── i2c_eeprom_api.h ├── ch341a_gpio.h ├── nandcmd_api.h ├── flashcmd_api.h ├── ch341a_spi.h ├── timer.c ├── flashcmd_api.c ├── spi_controller.c ├── bitbang_microwire.h ├── ch341a_gpio.c ├── i2c_eeprom.c ├── mw_eeprom.c ├── spi_controller.h ├── main.c ├── bitbang_microwire.c ├── spi_nand_flash.h ├── ch341a_i2c.c ├── ch341a_spi.c ├── ch341a_i2c.h └── spi_nor_flash.c ├── README.md ├── .github └── workflows │ └── microsnander.yml └── LICENSE /src/res.h: -------------------------------------------------------------------------------- 1 | #define snander 128 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MicroSNANDer 2 | Stripped down and modified version of Serial Nor/nAND/Eeprom programmeR (based on CH341A) 3 | 4 | ## Links 5 | 6 | * Original sourses of [SNANDer](https://github.com/McMCCRU/SNANDer) 7 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # To assist in cross-compiling 2 | # apt-get inatall libusb-1.0-0 libusb-1.0-0-dev 3 | # 4 | 5 | LDFLAGS=-pthread -lusb-1.0 6 | TOPDIR=$(shell pwd) 7 | 8 | EEPROM_SUPPORT = n 9 | 10 | BIGFILES=-D_FILE_OFFSET_BITS=64 11 | CFLAGS=-Os -std=gnu99 -Wall $(BIGFILES) 12 | 13 | OBJS= flashcmd_api.o spi_controller.o spi_nand_flash.o spi_nor_flash.o ch341a_spi.o timer.o main.o 14 | 15 | ifeq ($(EEPROM_SUPPORT),y) 16 | CFLAGS += -DEEPROM_SUPPORT 17 | OBJS += ch341a_i2c.o i2c_eeprom.o 18 | OBJS += bitbang_microwire.o mw_eeprom.o ch341a_gpio.o 19 | endif 20 | 21 | all: microsnander 22 | 23 | microsnander: $(OBJS) 24 | $(CC) $(CFLAGS) -s -o $@ $(OBJS) $(LDFLAGS) 25 | 26 | .c.o: 27 | $(CC) $(CFLAGS) -c $< 28 | 29 | clean: 30 | rm -f *.o microsnander* 31 | -------------------------------------------------------------------------------- /src/timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * timer.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __TIMER_H__ 16 | #define __TIMER_H__ 17 | 18 | void timer_start(void); 19 | void timer_end(void); 20 | int timer_progress(void); 21 | 22 | #endif /* __TIMER_H__ */ 23 | /* End of [timer.h] package */ 24 | -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * types.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __TYPES_H__ 16 | #define __TYPES_H__ 17 | 18 | typedef unsigned char u8; 19 | typedef unsigned short u16; 20 | typedef unsigned int u32; 21 | typedef unsigned long long u64; 22 | typedef u16 __le16; 23 | typedef u16 __be16; 24 | typedef u32 __le32; 25 | typedef u32 __be32; 26 | 27 | #endif /* __TYPES_H__ */ 28 | /* End of [types.h] package */ 29 | -------------------------------------------------------------------------------- /src/mw_eeprom_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * mw_eeprom_api.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __MW_EEPROM_API_H__ 16 | #define __MW_EEPROM_API_H__ 17 | 18 | int mw_eeprom_read(unsigned char *buf, unsigned long from, unsigned long len); 19 | int mw_eeprom_erase(unsigned long offs, unsigned long len); 20 | int mw_eeprom_write(unsigned char *buf, unsigned long to, unsigned long len); 21 | long mw_init(void); 22 | void support_mw_eeprom_list(void); 23 | 24 | #endif /* __MW_EEPROM_API_H__ */ 25 | /* End of [mw_eeprom_api.h] package */ 26 | -------------------------------------------------------------------------------- /src/snorcmd_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * snorcmd_api.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __SNORCMD_API_H__ 16 | #define __SNORCMD_API_H__ 17 | 18 | int snor_read(unsigned char *buf, unsigned long from, unsigned long len); 19 | int snor_erase(unsigned long offs, unsigned long len); 20 | int snor_write(unsigned char *buf, unsigned long to, unsigned long len); 21 | int snor_unprotect(void); 22 | long snor_init(void); 23 | void support_snor_list(void); 24 | 25 | #endif /* __SNORCMD_API_H__ */ 26 | /* End of [snorcmd_api.h] package */ 27 | -------------------------------------------------------------------------------- /src/i2c_eeprom_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * i2c_eeprom_api.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __I2C_EEPROM_API_H__ 16 | #define __I2C_EEPROM_API_H__ 17 | 18 | int i2c_eeprom_read(unsigned char *buf, unsigned long from, unsigned long len); 19 | int i2c_eeprom_erase(unsigned long offs, unsigned long len); 20 | int i2c_eeprom_write(unsigned char *buf, unsigned long to, unsigned long len); 21 | long i2c_init(void); 22 | void support_i2c_eeprom_list(void); 23 | 24 | #endif /* __I2C_EEPROM_API_H__ */ 25 | /* End of [i2c_eeprom_api.h] package */ 26 | -------------------------------------------------------------------------------- /src/ch341a_gpio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * ch341a_gpio.h 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | #ifndef __CH341A_GPIO_H__ 20 | #define __CH341A_GPIO_H__ 21 | 22 | #include 23 | 24 | int ch341a_gpio_setdir(void); 25 | int ch341a_gpio_setbits(uint8_t bits); 26 | int ch341a_gpio_getbits(uint8_t *data); 27 | 28 | #endif /* __CH341A_GPIO_H__ */ 29 | /* End of [ch341a_gpio.h] package */ 30 | -------------------------------------------------------------------------------- /src/nandcmd_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * nandcmd_api.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __NANDCMD_API_H__ 16 | #define __NANDCMD_API_H__ 17 | 18 | int snand_read(unsigned char *buf, unsigned long from, unsigned long len); 19 | int snand_erase(unsigned long offs, unsigned long len); 20 | int snand_write(unsigned char *buf, unsigned long to, unsigned long len); 21 | long snand_init(void); 22 | void support_snand_list(void); 23 | 24 | extern int ECC_fcheck; 25 | extern int ECC_ignore; 26 | extern unsigned char _ondie_ecc_flag; 27 | 28 | #endif /* __NANDCMD_API_H__ */ 29 | /* End of [nandcmd_api.h] package */ 30 | -------------------------------------------------------------------------------- /src/flashcmd_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * flashcmd_api.h 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | #ifndef __FLASHCMD_API_H__ 16 | #define __FLASHCMD_API_H__ 17 | 18 | #include "nandcmd_api.h" 19 | #include "snorcmd_api.h" 20 | #ifdef EEPROM_SUPPORT 21 | #include "i2c_eeprom_api.h" 22 | #include "mw_eeprom_api.h" 23 | #endif 24 | 25 | struct flash_cmd { 26 | int (*flash_read)(unsigned char *buf, unsigned long from, unsigned long len); 27 | int (*flash_erase)(unsigned long offs, unsigned long len); 28 | int (*flash_write)(unsigned char *buf, unsigned long to, unsigned long len); 29 | int (*flash_unprotect)(void); 30 | }; 31 | 32 | long flash_cmd_init(struct flash_cmd *cmd); 33 | void support_flash_list(void); 34 | 35 | #endif /* __FLASHCMD_API_H__ */ 36 | /* End of [flashcmd_api.h] package */ 37 | -------------------------------------------------------------------------------- /src/ch341a_spi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | #ifndef __CH341_SPI_H__ 19 | #define __CH341_SPI_H__ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #define min(a,b) (((a)<(b))?(a):(b)) 26 | #define max(a,b) (((a)>(b))?(a):(b)) 27 | 28 | int ch341a_spi_init(void); 29 | int ch341a_spi_shutdown(void); 30 | int ch341a_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); 31 | int enable_pins(bool enable); 32 | int config_stream(unsigned int speed); 33 | 34 | #endif /* __CH341_SPI_H__ */ 35 | /* End of [ch341a_spi.h] package */ 36 | -------------------------------------------------------------------------------- /src/timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * timer.c 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #include "timer.h" 20 | 21 | static time_t start_time = 0; 22 | static time_t print_time = 0; 23 | 24 | void timer_start(void) 25 | { 26 | start_time = time(0); 27 | } 28 | 29 | void timer_end(void) 30 | { 31 | time_t end_time = 0, elapsed_seconds = 0; 32 | 33 | time(&end_time); 34 | elapsed_seconds = difftime(end_time, start_time); 35 | printf("Elapsed time: %d seconds\n", (int)elapsed_seconds); 36 | print_time = 0; 37 | } 38 | 39 | int timer_progress(void) 40 | { 41 | time_t end_time = 0; 42 | int elapsed_seconds = 0; 43 | 44 | if (!print_time) 45 | print_time = time(0); 46 | 47 | time(&end_time); 48 | 49 | elapsed_seconds = (int)difftime(end_time, print_time); 50 | 51 | if (elapsed_seconds == 1) { 52 | print_time = 0; 53 | return 1; 54 | } 55 | return 0; 56 | } 57 | /* End of [timer.c] package */ 58 | -------------------------------------------------------------------------------- /src/flashcmd_api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * flashcmd_api.c 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include 17 | #include "flashcmd_api.h" 18 | 19 | #ifdef EEPROM_SUPPORT 20 | #define __EEPROM___ "or EEPROM" 21 | extern int eepromsize; 22 | extern int mw_eepromsize; 23 | #else 24 | #define __EEPROM___ "" 25 | #endif 26 | 27 | long flash_cmd_init(struct flash_cmd *cmd) 28 | { 29 | long flen = -1; 30 | 31 | #ifdef EEPROM_SUPPORT 32 | if ((eepromsize <= 0) && (mw_eepromsize <= 0)) { 33 | #endif 34 | if ((flen = snand_init()) > 0) { 35 | cmd->flash_erase = snand_erase; 36 | cmd->flash_write = snand_write; 37 | cmd->flash_read = snand_read; 38 | } else if ((flen = snor_init()) > 0) { 39 | cmd->flash_unprotect = snor_unprotect; 40 | cmd->flash_erase = snor_erase; 41 | cmd->flash_write = snor_write; 42 | cmd->flash_read = snor_read; 43 | } 44 | #ifdef EEPROM_SUPPORT 45 | } else if ((eepromsize > 0) || (mw_eepromsize > 0)) { 46 | if ((eepromsize > 0) && (flen = i2c_init()) > 0) { 47 | cmd->flash_erase = i2c_eeprom_erase; 48 | cmd->flash_write = i2c_eeprom_write; 49 | cmd->flash_read = i2c_eeprom_read; 50 | } else if ((mw_eepromsize > 0) && (flen = mw_init()) > 0) { 51 | cmd->flash_erase = mw_eeprom_erase; 52 | cmd->flash_write = mw_eeprom_write; 53 | cmd->flash_read = mw_eeprom_read; 54 | } 55 | } 56 | #endif 57 | else 58 | printf("\nFlash" __EEPROM___ " not found!!!!\n\n"); 59 | 60 | return flen; 61 | } 62 | 63 | void support_flash_list(void) 64 | { 65 | support_snand_list(); 66 | printf("\n"); 67 | support_snor_list(); 68 | #ifdef EEPROM_SUPPORT 69 | printf("\n"); 70 | support_i2c_eeprom_list(); 71 | printf("\n"); 72 | support_mw_eeprom_list(); 73 | #endif 74 | } 75 | /* End of [flashcmd.c] package */ 76 | -------------------------------------------------------------------------------- /src/spi_controller.c: -------------------------------------------------------------------------------- 1 | /*====================================================================================== 2 | * MODULE NAME: spi 3 | * FILE NAME: spi_controller.c 4 | * 5 | * FUNCTIONS 6 | * 7 | * SPI_CONTROLLER_Enable_Manual_Mode To provide interface for Enable SPI Controller Manual Mode. 8 | * SPI_CONTROLLER_Write_One_Byte To provide interface for write one byte to SPI bus. 9 | * SPI_CONTROLLER_Write_NByte To provide interface for write N bytes to SPI bus. 10 | * SPI_CONTROLLER_Read_NByte To provide interface for read N bytes from SPI bus. 11 | * SPI_CONTROLLER_Chip_Select_Low To provide interface for set chip select low in SPI bus. 12 | * SPI_CONTROLLER_Chip_Select_High To provide interface for set chip select high in SPI bus. 13 | * 14 | * DEPENDENCIES 15 | * 16 | * * $History: $ 17 | * MODIFICTION HISTORY: 18 | * 19 | *====================================================================================== 20 | */ 21 | #include "ch341a_spi.h" 22 | #include "spi_controller.h" 23 | 24 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Enable_Manual_Mode( void ) 25 | { 26 | return 0; 27 | } 28 | 29 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Write_One_Byte( u8 data ) 30 | { 31 | return (SPI_CONTROLLER_RTN_T)ch341a_spi_send_command(1, 0, &data, NULL); 32 | } 33 | 34 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Chip_Select_High( void ) 35 | { 36 | return (SPI_CONTROLLER_RTN_T)enable_pins(false); 37 | } 38 | 39 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Chip_Select_Low( void ) 40 | { 41 | return (SPI_CONTROLLER_RTN_T)enable_pins(true); 42 | } 43 | 44 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Read_NByte( u8 *ptr_rtn_data, u32 len, SPI_CONTROLLER_SPEED_T speed ) 45 | { 46 | return (SPI_CONTROLLER_RTN_T)ch341a_spi_send_command(0, len, NULL, ptr_rtn_data); 47 | } 48 | 49 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Write_NByte( u8 *ptr_data, u32 len, SPI_CONTROLLER_SPEED_T speed ) 50 | { 51 | return (SPI_CONTROLLER_RTN_T)ch341a_spi_send_command(len, 0, ptr_data, NULL); 52 | } 53 | 54 | #if 0 55 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Xfer_NByte( u8 *ptr_data_in, u32 len_in, u8 *ptr_data_out, u32 len_out, SPI_CONTROLLER_SPEED_T speed ) 56 | { 57 | return (SPI_CONTROLLER_RTN_T)ch341a_spi_send_command(len_out, len_in, ptr_data_out, ptr_data_in); 58 | } 59 | #endif 60 | /* End of [spi_controller.c] package */ 61 | -------------------------------------------------------------------------------- /src/bitbang_microwire.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2005-2021 Mokrushin I.V. aka McMCC mcmcc@mail.ru 2 | A simple bitbang protocol for Microwire 8-pin serial EEPROMs 3 | (93XX devices). Support organization 8bit and 16bit(8bit emulation). 4 | 5 | bitbang_microwire.h 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ 20 | /* ------------------------------------------------------------------------- */ 21 | 22 | #ifndef _BITBANG_MICROWIRE_H 23 | #define _BITBANG_MICROWIRE_H 24 | 25 | #if 0 26 | extern unsigned char ORG; /* organization 0 = 8 bit and 1 = 16 bit */ 27 | #endif 28 | extern unsigned char CLK; 29 | extern unsigned char DO; 30 | extern unsigned char DI; 31 | extern unsigned char CSEL; 32 | 33 | extern int org; 34 | extern int mw_eepromsize; 35 | extern int fix_addr_len; 36 | 37 | struct MW_EEPROM { 38 | char *name; 39 | unsigned int size; 40 | }; 41 | 42 | struct gpio_cmd { 43 | int (*gpio_setdir)(void); 44 | int (*gpio_setbits)(unsigned char bit); 45 | int (*gpio_getbits)(unsigned char *data); 46 | }; 47 | 48 | void Erase_EEPROM_3wire(int size_eeprom); 49 | int Read_EEPROM_3wire(unsigned char *buffer, int size_eeprom); 50 | int Write_EEPROM_3wire(unsigned char *buffer, int size_eeprom); 51 | int deviceSize_3wire(char *eepromname); 52 | 53 | const static struct MW_EEPROM mw_eepromlist[] = { 54 | { "93c06", 32 }, 55 | { "93c16", 64 }, 56 | { "93c46", 128 }, 57 | { "93c56", 256 }, 58 | { "93c66", 512 }, 59 | { "93c76", 1024 }, 60 | { "93c86", 2048 }, 61 | { "93c96", 4096 }, 62 | { 0, 0 } 63 | }; 64 | 65 | #define MAX_MW_EEPROM_SIZE 4096 66 | 67 | #endif /* _BITBANG_MICROWIRE_H */ 68 | /* End of [bitbang_microwire.h] package */ 69 | -------------------------------------------------------------------------------- /.github/workflows/microsnander.yml: -------------------------------------------------------------------------------- 1 | name: MicroSNANDERer for Linux 2 | 3 | on: 4 | push: 5 | # branches: 6 | # - main 7 | tags: 8 | - "v*" 9 | 10 | jobs: 11 | image_build: 12 | name: images build 13 | runs-on: ubuntu-18.04 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | 19 | - name: Install build dependencies 20 | id: install 21 | run: | 22 | DEBIAN_FRONTEND=noninteractive sudo apt-get update && sudo apt -y autoremove && \ 23 | sudo apt-get -y install build-essential curl libusb-1.0-0 libusb-1.0-0-dev 24 | 25 | - name: Build MicroSNANDer binary 26 | id: build 27 | run: | 28 | HEAD_TAG=$(git tag --points-at HEAD) 29 | GIT_HASH=$(git rev-parse --short $GITHUB_SHA) 30 | BRANCH_NAME=$(echo $GITHUB_REF | cut -d'/' -f 3) 31 | 32 | if [ -z "$HEAD_TAG" ]; then 33 | TAG_NAME="latest" 34 | RELEASE_NAME="Development Build" 35 | PRERELEASE=true 36 | else 37 | TAG_NAME=${{ github.ref }} 38 | RELEASE_NAME="Release ${{ github.ref }}" 39 | PRERELEASE=false 40 | fi 41 | 42 | ARCHIVE="output/microsnander" 43 | echo "ARCHIVE=$ARCHIVE" >> $GITHUB_ENV 44 | echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV 45 | echo "GIT_HASH=$GIT_HASH" >> $GITHUB_ENV 46 | echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV 47 | echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV 48 | echo "PRERELEASE=$PRERELEASE" >> $GITHUB_ENV 49 | 50 | cd src 51 | make -j1 52 | mkdir output 53 | mv -v microsnander output/ 54 | 55 | continue-on-error: true 56 | 57 | 58 | - name: Create release 59 | uses: actions/create-release@v1 60 | continue-on-error: true 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | with: 64 | tag_name: ${{ env.TAG_NAME }} 65 | release_name: ${{ env.RELEASE_NAME }} 66 | draft: false 67 | prerelease: ${{ env.PRERELEASE }} 68 | 69 | - name: Upload binaries to release 70 | uses: svenstaro/upload-release-action@v2 71 | with: 72 | repo_token: ${{ secrets.GITHUB_TOKEN }} 73 | file_glob: true 74 | file: ${{ env.ARCHIVE }} 75 | asset_name: ${{ env.ARCHIVE }} 76 | tag: ${{ env.TAG_NAME }} 77 | overwrite: true 78 | -------------------------------------------------------------------------------- /src/ch341a_gpio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * ch341a_gpio.c 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define DEFAULT_TIMEOUT 1000 28 | #define BULK_WRITE_ENDPOINT 0x02 29 | #define BULK_READ_ENDPOINT 0x82 30 | 31 | #define CH341A_CMD_UIO_STREAM 0xAB 32 | #define CH341A_CMD_UIO_STM_IN 0x00 33 | #define CH341A_CMD_UIO_STM_DIR 0x40 34 | #define CH341A_CMD_UIO_STM_OUT 0x80 35 | #define CH341A_CMD_UIO_STM_US 0xC0 36 | #define CH341A_CMD_UIO_STM_END 0x20 37 | 38 | #define DIR_MASK 0x3F /* D6,D7 - input, D0-D5 - output */ 39 | 40 | extern struct libusb_device_handle *handle; 41 | 42 | static int usb_transf(const char *func, uint8_t type, uint8_t *buf, int len) 43 | { 44 | int ret, actuallen = 0; 45 | 46 | if (handle == NULL) 47 | return -1; 48 | 49 | ret = libusb_bulk_transfer(handle, type, buf, len, &actuallen, DEFAULT_TIMEOUT); 50 | if (ret < 0) { 51 | printf("%s: Failed to %s %d bytes '%s'\n", func, 52 | (type == BULK_WRITE_ENDPOINT) ? "write" : "read", len, strerror(-ret)); 53 | return -1; 54 | } 55 | 56 | return actuallen; 57 | } 58 | 59 | int ch341a_gpio_setdir(void) 60 | { 61 | uint8_t buf[] = { 62 | CH341A_CMD_UIO_STREAM, 63 | CH341A_CMD_UIO_STM_DIR | DIR_MASK, 64 | CH341A_CMD_UIO_STM_END 65 | }; 66 | 67 | return usb_transf(__func__, BULK_WRITE_ENDPOINT, buf, 3); 68 | } 69 | 70 | int ch341a_gpio_setbits(uint8_t bits) 71 | { 72 | uint8_t buf[] = { 73 | CH341A_CMD_UIO_STREAM, 74 | CH341A_CMD_UIO_STM_OUT | bits, 75 | CH341A_CMD_UIO_STM_END 76 | }; 77 | 78 | return usb_transf(__func__, BULK_WRITE_ENDPOINT, buf, 3); 79 | } 80 | 81 | int ch341a_gpio_getbits(uint8_t *data) 82 | { 83 | int ret; 84 | uint8_t buf[] = { 85 | CH341A_CMD_UIO_STREAM, 86 | CH341A_CMD_UIO_STM_IN, 87 | CH341A_CMD_UIO_STM_END 88 | }; 89 | 90 | ret = usb_transf(__func__, BULK_WRITE_ENDPOINT, buf, 3); 91 | if (ret < 0) 92 | return -1; 93 | 94 | ret = usb_transf(__func__, BULK_READ_ENDPOINT, buf, 1); 95 | if (ret < 0) 96 | return -1; 97 | 98 | *data = buf[0]; 99 | 100 | return ret; 101 | } 102 | /* End of [ch341a_gpio.c] package */ 103 | -------------------------------------------------------------------------------- /src/i2c_eeprom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * i2c_eeprom.c 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include "ch341a_spi.h" 17 | #include "ch341a_i2c.h" 18 | #include "timer.h" 19 | 20 | extern unsigned int bsize; 21 | struct EEPROM eeprom_info; 22 | char eepromname[12]; 23 | int eepromsize = 0; 24 | 25 | int i2c_eeprom_read(unsigned char *buf, unsigned long from, unsigned long len) 26 | { 27 | unsigned char *pbuf, ebuf[MAX_EEPROM_SIZE]; 28 | 29 | if (len == 0) 30 | return -1; 31 | 32 | timer_start(); 33 | memset(ebuf, 0, sizeof(ebuf)); 34 | pbuf = ebuf; 35 | 36 | if (ch341readEEPROM(pbuf, eepromsize, &eeprom_info) < 0) { 37 | printf("Couldnt read [%d] bytes from [%s] EEPROM address 0x%08lu\n", (int)len, eepromname, from); 38 | return -1; 39 | } 40 | 41 | memcpy(buf, pbuf + from, len); 42 | 43 | printf("Read [%d] bytes from [%s] EEPROM address 0x%08lu\n", (int)len, eepromname, from); 44 | timer_end(); 45 | 46 | return (int)len; 47 | } 48 | 49 | int i2c_eeprom_erase(unsigned long offs, unsigned long len) 50 | { 51 | unsigned char *pbuf, ebuf[MAX_EEPROM_SIZE]; 52 | 53 | if (len == 0) 54 | return -1; 55 | 56 | timer_start(); 57 | memset(ebuf, 0xff, sizeof(ebuf)); 58 | pbuf = ebuf; 59 | 60 | if (offs || len < eepromsize) { 61 | if (ch341readEEPROM(pbuf, eepromsize, &eeprom_info) < 0) { 62 | printf("Couldnt read [%d] bytes from [%s] EEPROM\n", eepromsize, eepromname); 63 | return -1; 64 | } 65 | memset(pbuf + offs, 0xff, len); 66 | } 67 | 68 | if(ch341writeEEPROM(pbuf, eepromsize, &eeprom_info) < 0) { 69 | printf("Failed to erase [%d] bytes of [%s] EEPROM address 0x%08lu\n", (int)len, eepromname, offs); 70 | return -1; 71 | } 72 | 73 | printf("Erased [%d] bytes of [%s] EEPROM address 0x%08lu\n", (int)len, eepromname, offs); 74 | timer_end(); 75 | 76 | return 0; 77 | } 78 | 79 | int i2c_eeprom_write(unsigned char *buf, unsigned long to, unsigned long len) 80 | { 81 | unsigned char *pbuf, ebuf[MAX_EEPROM_SIZE]; 82 | 83 | if (len == 0) 84 | return -1; 85 | 86 | timer_start(); 87 | memset(ebuf, 0xff, sizeof(ebuf)); 88 | pbuf = ebuf; 89 | 90 | if (to || len < eepromsize) { 91 | if (ch341readEEPROM(pbuf, eepromsize, &eeprom_info) < 0) { 92 | printf("Couldnt read [%d] bytes from [%s] EEPROM\n", (int)len, eepromname); 93 | return -1; 94 | } 95 | } 96 | memcpy(pbuf + to, buf, len); 97 | 98 | if(ch341writeEEPROM(pbuf, eepromsize, &eeprom_info) < 0) { 99 | printf("Failed to write [%d] bytes of [%s] EEPROM address 0x%08lu\n", (int)len, eepromname, to); 100 | return -1; 101 | } 102 | 103 | printf("Wrote [%d] bytes to [%s] EEPROM address 0x%08lu\n", (int)len, eepromname, to); 104 | timer_end(); 105 | 106 | return (int)len; 107 | } 108 | 109 | long i2c_init(void) 110 | { 111 | if (config_stream(CH341_I2C_STANDARD_SPEED) < 0) 112 | return -1; 113 | 114 | if (eepromsize <= 0) { 115 | printf("I2C EEPROM Not Detected!\n"); 116 | return -1; 117 | } 118 | 119 | bsize = 1; 120 | 121 | printf("I2C EEPROM chip: %s, Size: %d bytes\n", eepromname, eepromsize); 122 | 123 | return (long)eepromsize; 124 | } 125 | 126 | void support_i2c_eeprom_list(void) 127 | { 128 | int i; 129 | 130 | printf("I2C EEPROM Support List:\n"); 131 | for ( i = 0; i < (sizeof(eepromlist)/sizeof(struct EEPROM)); i++) 132 | { 133 | if (!eepromlist[i].size) 134 | break; 135 | printf("%03d. %s\n", i + 1, eepromlist[i].name); 136 | } 137 | } 138 | /* End of [i2c_eeprom.c] package */ 139 | -------------------------------------------------------------------------------- /src/mw_eeprom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 McMCC 3 | * mw_eeprom.c 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "bitbang_microwire.h" 26 | #include "ch341a_gpio.h" 27 | #include "timer.h" 28 | 29 | extern struct gpio_cmd bb_func; 30 | extern char eepromname[12]; 31 | extern unsigned int bsize; 32 | 33 | int mw_eeprom_read(unsigned char *buf, unsigned long from, unsigned long len) 34 | { 35 | unsigned char *pbuf, ebuf[MAX_MW_EEPROM_SIZE]; 36 | 37 | if (len == 0) 38 | return -1; 39 | 40 | timer_start(); 41 | memset(ebuf, 0, sizeof(ebuf)); 42 | pbuf = ebuf; 43 | 44 | Read_EEPROM_3wire(pbuf, mw_eepromsize); 45 | memcpy(buf, pbuf + from, len); 46 | 47 | printf("Read [%lu] bytes from [%s] EEPROM address 0x%08lu\n", len, eepromname, from); 48 | timer_end(); 49 | 50 | return (int)len; 51 | } 52 | 53 | int mw_eeprom_erase(unsigned long offs, unsigned long len) 54 | { 55 | unsigned char *pbuf, ebuf[MAX_MW_EEPROM_SIZE]; 56 | 57 | if (len == 0) 58 | return -1; 59 | 60 | timer_start(); 61 | memset(ebuf, 0xff, sizeof(ebuf)); 62 | pbuf = ebuf; 63 | 64 | if (offs || len < mw_eepromsize) { 65 | Read_EEPROM_3wire(pbuf, mw_eepromsize); 66 | memset(pbuf + offs, 0xff, len); 67 | } 68 | 69 | Erase_EEPROM_3wire(mw_eepromsize); 70 | 71 | if (offs || len < mw_eepromsize) { 72 | if (Write_EEPROM_3wire(pbuf, mw_eepromsize) < 0) { 73 | printf("Failed to erase [%lu] bytes of [%s] EEPROM address 0x%08lu\n", len, eepromname, offs); 74 | return -1; 75 | } 76 | } 77 | 78 | printf("Erased [%lu] bytes of [%s] EEPROM address 0x%08lu\n", len, eepromname, offs); 79 | timer_end(); 80 | 81 | return 0; 82 | } 83 | 84 | int mw_eeprom_write(unsigned char *buf, unsigned long to, unsigned long len) 85 | { 86 | unsigned char *pbuf, ebuf[MAX_MW_EEPROM_SIZE]; 87 | 88 | if (len == 0) 89 | return -1; 90 | 91 | timer_start(); 92 | memset(ebuf, 0xff, sizeof(ebuf)); 93 | pbuf = ebuf; 94 | 95 | if (to || len < mw_eepromsize) { 96 | Read_EEPROM_3wire(pbuf, mw_eepromsize); 97 | } 98 | memcpy(pbuf + to, buf, len); 99 | 100 | Erase_EEPROM_3wire(mw_eepromsize); 101 | 102 | if (Write_EEPROM_3wire(pbuf, mw_eepromsize) < 0) { 103 | printf("Failed to write [%lu] bytes of [%s] EEPROM address 0x%08lu\n", len, eepromname, to); 104 | return -1; 105 | } 106 | 107 | printf("Wrote [%lu] bytes to [%s] EEPROM address 0x%08lu\n", len, eepromname, to); 108 | timer_end(); 109 | 110 | return (int)len; 111 | } 112 | 113 | /* 114 | 25xx 93xx 115 | PIN 22 - D7 - MISO DO 116 | PIN 21 - D6 - 117 | PIN 20 - D5 - MOSI DI 118 | PIN 19 - D4 - 119 | PIN 18 - D3 - CLK CLK 120 | PIN 17 - D2 - 121 | PIN 16 - D1 - 122 | PIN 15 - D0 - CS CS 123 | */ 124 | 125 | static int mw_gpio_init(void) 126 | { 127 | int ret = 0; 128 | 129 | CLK = 1 << 3; 130 | DO = 1 << 7; 131 | DI = 1 << 5; 132 | CSEL = 1 << 0; 133 | 134 | bb_func.gpio_setdir = ch341a_gpio_setdir; 135 | bb_func.gpio_setbits = ch341a_gpio_setbits; 136 | bb_func.gpio_getbits = ch341a_gpio_getbits; 137 | 138 | if(bb_func.gpio_setdir) 139 | ret = bb_func.gpio_setdir(); 140 | else 141 | return -1; 142 | 143 | if(ret < 0) 144 | return -1; 145 | 146 | return 0; 147 | } 148 | 149 | static char *__itoa(int a) 150 | { 151 | char *p, tmp[32]; 152 | p = tmp; 153 | snprintf(p, sizeof(tmp), "%d", a); 154 | return p; 155 | } 156 | 157 | 158 | long mw_init(void) 159 | { 160 | if (mw_eepromsize <= 0) { 161 | printf("Microwire EEPROM Not Detected!\n"); 162 | return -1; 163 | } 164 | 165 | if (mw_gpio_init() < 0) 166 | return -1; 167 | 168 | bsize = 1; 169 | 170 | printf("Microwire EEPROM chip: %s, Size: %d bytes, Org: %d bits, fix addr len: %s\n", eepromname, mw_eepromsize / (org ? 2 : 1), 171 | org ? 16 : 8, fix_addr_len ? __itoa(fix_addr_len) : "Auto"); 172 | 173 | return (long)mw_eepromsize; 174 | } 175 | 176 | void support_mw_eeprom_list(void) 177 | { 178 | int i; 179 | 180 | printf("Microwire EEPROM Support List:\n"); 181 | for ( i = 0; i < (sizeof(mw_eepromlist)/sizeof(struct MW_EEPROM)); i++) 182 | { 183 | if (!mw_eepromlist[i].size) 184 | break; 185 | printf("%03d. %s\n", i + 1, mw_eepromlist[i].name); 186 | } 187 | } 188 | /* End of [mw_eeprom.c] package */ 189 | -------------------------------------------------------------------------------- /src/spi_controller.h: -------------------------------------------------------------------------------- 1 | /*====================================================================================== 2 | * MODULE NAME: spi 3 | * FILE NAME: spi_controller.h 4 | * 5 | * FUNCTIONS 6 | * 7 | * SPI_CONTROLLER_Enable_Manual_Mode To provide interface for Enable SPI Controller Manual Mode. 8 | * SPI_CONTROLLER_Write_One_Byte To provide interface for write one byte to SPI bus. 9 | * SPI_CONTROLLER_Write_NByte To provide interface for write N bytes to SPI bus. 10 | * SPI_CONTROLLER_Read_NByte To provide interface for read N bytes from SPI bus. 11 | * SPI_CONTROLLER_Chip_Select_Low To provide interface for set chip select low in SPI bus. 12 | * SPI_CONTROLLER_Chip_Select_High To provide interface for set chip select high in SPI bus. 13 | * 14 | * DEPENDENCIES 15 | * 16 | * * $History: $ 17 | * MODIFICTION HISTORY: 18 | * 19 | *====================================================================================== 20 | */ 21 | 22 | #ifndef __SPI_CONTROLLER_H__ 23 | #define __SPI_CONTROLLER_H__ 24 | 25 | /* INCLUDE FILE DECLARATIONS --------------------------------------------------------- */ 26 | #include "types.h" 27 | 28 | /* NAMING CONSTANT DECLARATIONS ------------------------------------------------------ */ 29 | 30 | /* MACRO DECLARATIONS ---------------------------------------------------------------- */ 31 | 32 | /* TYPE DECLARATIONS ----------------------------------------------------------------- */ 33 | typedef enum{ 34 | SPI_CONTROLLER_SPEED_SINGLE = 0, 35 | SPI_CONTROLLER_SPEED_DUAL, 36 | SPI_CONTROLLER_SPEED_QUAD 37 | 38 | } SPI_CONTROLLER_SPEED_T; 39 | 40 | typedef enum{ 41 | SPI_CONTROLLER_RTN_NO_ERROR = 0, 42 | SPI_CONTROLLER_RTN_SET_OPFIFO_ERROR, 43 | SPI_CONTROLLER_RTN_READ_DATAPFIFO_ERROR, 44 | SPI_CONTROLLER_RTN_WRITE_DATAPFIFO_ERROR, 45 | SPI_CONTROLLER_RTN_DEF_NO 46 | } SPI_CONTROLLER_RTN_T; 47 | 48 | 49 | typedef enum{ 50 | SPI_CONTROLLER_MODE_AUTO = 0, 51 | SPI_CONTROLLER_MODE_MANUAL, 52 | SPI_CONTROLLER_MODE_NO 53 | } SPI_CONTROLLER_MODE_T; 54 | 55 | /*------------------------------------------------------------------------------------ 56 | * FUNCTION: SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Enable_Manual_Mode( void ) 57 | * PURPOSE : To provide interface for enable SPI Controller Manual Mode Enable. 58 | * AUTHOR : 59 | * CALLED BY 60 | * - 61 | * CALLS 62 | * - 63 | * PARAMs : 64 | * INPUT : None 65 | * OUTPUT: None 66 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 67 | * NOTES : 68 | * MODIFICTION HISTORY: 69 | *------------------------------------------------------------------------------------ 70 | */ 71 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Enable_Manual_Mode( void ); 72 | 73 | /*------------------------------------------------------------------------------------ 74 | * FUNCTION: SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Write_One_Byte( u8 data ) 75 | * PURPOSE : To provide interface for write one byte to SPI bus. 76 | * AUTHOR : 77 | * CALLED BY 78 | * - 79 | * CALLS 80 | * - 81 | * PARAMs : 82 | * INPUT : data - The data variable of this function. 83 | * OUTPUT: None 84 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 85 | * NOTES : 86 | * MODIFICTION HISTORY: 87 | *------------------------------------------------------------------------------------ 88 | */ 89 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Write_One_Byte( u8 data ); 90 | 91 | /*------------------------------------------------------------------------------------ 92 | * FUNCTION: SPI_CONTROLLER_RTN_T SPI_CONTROLLER_WRITE_NBYTES( u8 *ptr_data, 93 | * u32 len, 94 | * SPI_CONTROLLER_SPEED_T speed ) 95 | * PURPOSE : To provide interface for write N bytes to SPI bus. 96 | * AUTHOR : 97 | * CALLED BY 98 | * - 99 | * CALLS 100 | * - 101 | * PARAMs : 102 | * INPUT : ptr_data - The data variable of this function. 103 | * len - The len variable of this function. 104 | * speed - The speed variable of this function. 105 | * OUTPUT: None 106 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 107 | * NOTES : 108 | * MODIFICTION HISTORY: 109 | *------------------------------------------------------------------------------------ 110 | */ 111 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Write_NByte( u8 *ptr_data, u32 len, SPI_CONTROLLER_SPEED_T speed ); 112 | 113 | /*------------------------------------------------------------------------------------ 114 | * FUNCTION: SPI_CONTROLLER_RTN_T SPI_CONTROLLER_READ_NBYTES( u8 *ptr_rtn_data, 115 | * u8 len, 116 | * SPI_CONTROLLER_SPEED_T speed ) 117 | * PURPOSE : To provide interface for read N bytes from SPI bus. 118 | * AUTHOR : 119 | * CALLED BY 120 | * - 121 | * CALLS 122 | * - 123 | * PARAMs : 124 | * INPUT : len - The len variable of this function. 125 | * speed - The speed variable of this function. 126 | * OUTPUT: ptr_rtn_data - The ptr_rtn_data variable of this function. 127 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 128 | * NOTES : 129 | * MODIFICTION HISTORY: 130 | *------------------------------------------------------------------------------------ 131 | */ 132 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Read_NByte( u8 *ptr_rtn_data, u32 len, SPI_CONTROLLER_SPEED_T speed ); 133 | 134 | /*------------------------------------------------------------------------------------ 135 | * FUNCTION: SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Chip_Select_Low( void ) 136 | * PURPOSE : To provide interface for set chip select low in SPI bus. 137 | * AUTHOR : 138 | * CALLED BY 139 | * - 140 | * CALLS 141 | * - 142 | * PARAMs : 143 | * INPUT : None 144 | * OUTPUT: None 145 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 146 | * NOTES : 147 | * MODIFICTION HISTORY: 148 | *------------------------------------------------------------------------------------ 149 | */ 150 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Chip_Select_Low( void ); 151 | 152 | /*------------------------------------------------------------------------------------ 153 | * FUNCTION: SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Chip_Select_High( void ) 154 | * PURPOSE : To provide interface for set chip select high in SPI bus. 155 | * AUTHOR : 156 | * CALLED BY 157 | * - 158 | * CALLS 159 | * - 160 | * PARAMs : 161 | * INPUT : None 162 | * OUTPUT: None 163 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 164 | * NOTES : 165 | * MODIFICTION HISTORY: 166 | *------------------------------------------------------------------------------------ 167 | */ 168 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Chip_Select_High( void ); 169 | 170 | #if 0 171 | SPI_CONTROLLER_RTN_T SPI_CONTROLLER_Xfer_NByte( u8 *ptr_data_in, u32 len_in, u8 *ptr_data_out, u32 len_out, SPI_CONTROLLER_SPEED_T speed ); 172 | #endif 173 | 174 | #endif /* ifndef __SPI_CONTROLLER_H__ */ 175 | /* End of [spi_controller.h] package */ 176 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "flashcmd_api.h" 29 | #include "ch341a_spi.h" 30 | #include "spi_nand_flash.h" 31 | 32 | struct flash_cmd prog; 33 | extern unsigned int bsize; 34 | 35 | #ifdef EEPROM_SUPPORT 36 | #include "ch341a_i2c.h" 37 | #include "bitbang_microwire.h" 38 | extern struct EEPROM eeprom_info; 39 | extern char eepromname[12]; 40 | extern int eepromsize; 41 | extern int mw_eepromsize; 42 | extern int org; 43 | #define EHELP " -E select I2C EEPROM {24c01|24c02|24c04|24c08|24c16|24c32|24c64|24c128|24c256|24c512|24c1024}\n" \ 44 | " select Microwire EEPROM {93c06|93c16|93c46|93c56|93c66|93c76|93c86|93c96} (need SPI-to-MW adapter)\n" \ 45 | " -8 set organization 8-bit for Microwire EEPROM(default 16-bit) and set jumper on SPI-to-MW adapter\n" \ 46 | " -f set manual address size in bits for Microwire EEPROM(default auto)\n" 47 | #else 48 | #define EHELP "" 49 | #endif 50 | 51 | #define _VER "1.7.4" 52 | 53 | void title(void) 54 | { 55 | #ifdef EEPROM_SUPPORT 56 | printf("\nSNANDer - Serial Nor/nAND/Eeprom programmeR v." _VER " by McMCC \n\n"); 57 | #else 58 | printf("\nSNANDer - Spi Nor/nAND programmER v." _VER " by McMCC \n\n"); 59 | #endif 60 | } 61 | 62 | void usage(void) 63 | { 64 | const char use[] = 65 | " Usage:\n"\ 66 | " -h display this message\n"\ 67 | " -d disable internal ECC(use read and write page size + OOB size)\n"\ 68 | " -I ECC ignore errors(for read test only)\n"\ 69 | " -L print list support chips\n"\ 70 | " -i read the chip ID info\n"\ 71 | "" EHELP ""\ 72 | " -e erase chip(full or use with -a [-l])\n"\ 73 | " -l manually set length\n"\ 74 | " -a
manually set address\n"\ 75 | " -w write chip with data from filename\n"\ 76 | " -r read chip and save data to filename\n"\ 77 | " -u unprotect chip\n"\ 78 | " -v verify after write on chip\n"; 79 | printf(use); 80 | exit(0); 81 | } 82 | 83 | int main(int argc, char* argv[]) 84 | { 85 | int c, vr = 0, svr = 0, ret = 0; 86 | char *str, *fname = NULL, op = 0; 87 | unsigned char *buf; 88 | int long long len = 0, addr = 0, flen = 0, wlen = 0; 89 | FILE *fp; 90 | 91 | title(); 92 | 93 | #ifdef EEPROM_SUPPORT 94 | while ((c = getopt(argc, argv, "diIhveLl:a:w:r:E:f:8")) != -1) 95 | #else 96 | while ((c = getopt(argc, argv, "diIhveuLl:a:w:r:")) != -1) 97 | #endif 98 | { 99 | switch(c) 100 | { 101 | #ifdef EEPROM_SUPPORT 102 | case 'E': 103 | if ((eepromsize = parseEEPsize(optarg, &eeprom_info)) > 0) { 104 | memset(eepromname, 0, sizeof(eepromname)); 105 | strncpy(eepromname, optarg, 10); 106 | if (len > eepromsize) { 107 | printf("Error set size %lld, max size %d for EEPROM %s!!!\n", len, eepromsize, eepromname); 108 | exit(0); 109 | } 110 | } else if ((mw_eepromsize = deviceSize_3wire(optarg)) > 0) { 111 | memset(eepromname, 0, sizeof(eepromname)); 112 | strncpy(eepromname, optarg, 10); 113 | org = 1; 114 | if (len > mw_eepromsize) { 115 | printf("Error set size %lld, max size %d for EEPROM %s!!!\n", len, mw_eepromsize, eepromname); 116 | exit(0); 117 | } 118 | } else { 119 | printf("Unknown EEPROM chip %s!!!\n", optarg); 120 | exit(0); 121 | } 122 | break; 123 | case '8': 124 | if (mw_eepromsize <= 0) 125 | { 126 | printf("-8 option only for Microwire EEPROM chips!!!\n"); 127 | exit(0); 128 | } 129 | org = 0; 130 | break; 131 | case 'f': 132 | if (mw_eepromsize <= 0) 133 | { 134 | printf("-f option only for Microwire EEPROM chips!!!\n"); 135 | exit(0); 136 | } 137 | str = strdup(optarg); 138 | fix_addr_len = strtoll(str, NULL, *str && *(str + 1) == 'x' ? 16 : 10); 139 | if (fix_addr_len > 32) { 140 | printf("Address len is very big!!!\n"); 141 | exit(0); 142 | } 143 | break; 144 | #endif 145 | case 'I': 146 | ECC_ignore = 1; 147 | break; 148 | case 'd': 149 | ECC_fcheck = 0; 150 | _ondie_ecc_flag = 0; 151 | break; 152 | case 'l': 153 | str = strdup(optarg); 154 | len = strtoll(str, NULL, *str && *(str + 1) == 'x' ? 16 : 10); 155 | break; 156 | case 'a': 157 | str = strdup(optarg); 158 | addr = strtoll(str, NULL, *str && *(str + 1) == 'x' ? 16 : 10); 159 | break; 160 | case 'v': 161 | vr = 1; 162 | break; 163 | case 'i': 164 | case 'e': 165 | if(!op) 166 | op = c; 167 | else 168 | op = 'x'; 169 | break; 170 | case 'r': 171 | case 'w': 172 | if(!op) { 173 | op = c; 174 | fname = strdup(optarg); 175 | } else 176 | op = 'x'; 177 | break; 178 | case 'u': 179 | op = c; 180 | break; 181 | case 'L': 182 | support_flash_list(); 183 | exit(0); 184 | case 'h': 185 | default: 186 | usage(); 187 | } 188 | } 189 | 190 | if (op == 0) usage(); 191 | 192 | if (op == 'x' || (ECC_ignore && !ECC_fcheck) || (op == 'w' && ECC_ignore)) { 193 | printf("Conflicting options, only one option at a time.\n\n"); 194 | return -1; 195 | } 196 | 197 | if (ch341a_spi_init() < 0) { 198 | printf("Programmer device not found!\n\n"); 199 | return -1; 200 | } 201 | 202 | if((flen = flash_cmd_init(&prog)) <= 0) 203 | goto out; 204 | 205 | #ifdef EEPROM_SUPPORT 206 | if ((eepromsize || mw_eepromsize) && op == 'i') { 207 | printf("Programmer not supported auto detect EEPROM!\n\n"); 208 | goto out; 209 | } 210 | #else 211 | if (op == 'i') goto out; 212 | #endif 213 | 214 | if (op == 'e') { 215 | printf("ERASE:\n"); 216 | if(addr && !len) 217 | len = flen - addr; 218 | else if(!addr && !len) { 219 | len = flen; 220 | printf("Set full erase chip!\n"); 221 | } 222 | if(len % bsize) { 223 | printf("Please set len = 0x%016llX multiple of the block size 0x%08X\n", len, bsize); 224 | goto out; 225 | } 226 | printf("Erase addr = 0x%016llX, len = 0x%016llX\n", addr, len); 227 | ret = prog.flash_erase(addr, len); 228 | if(!ret) 229 | printf("Status: OK\n"); 230 | else 231 | printf("Status: BAD(%d)\n", ret); 232 | goto out; 233 | } 234 | 235 | if (op == 'u') { 236 | printf("UNLOCK:\n"); 237 | ret = prog.flash_unprotect(); 238 | if(!ret) 239 | printf("Status: OK\n"); 240 | else 241 | printf("Status: BAD(%d)\n", ret); 242 | goto out; 243 | } 244 | 245 | if ((op == 'r') || (op == 'w')) { 246 | if(addr && !len) 247 | len = flen - addr; 248 | else if(!addr && !len) { 249 | len = flen; 250 | } 251 | buf = (unsigned char *)malloc(len + 1); 252 | if (!buf) { 253 | printf("Malloc failed for read buffer.\n"); 254 | goto out; 255 | } 256 | } 257 | 258 | if (op == 'w') { 259 | printf("WRITE:\n"); 260 | fp = fopen(fname, "rb"); 261 | if (!fp) { 262 | printf("Couldn't open file %s for reading.\n", fname); 263 | free(buf); 264 | goto out; 265 | } 266 | wlen = fread(buf, 1, len, fp); 267 | if (ferror(fp)) { 268 | printf("Error reading file [%s]\n", fname); 269 | if (fp) 270 | fclose(fp); 271 | free(buf); 272 | goto out; 273 | } 274 | if(len == flen) 275 | len = wlen; 276 | printf("Write addr = 0x%016llX, len = 0x%016llX\n", addr, len); 277 | ret = prog.flash_write(buf, addr, len); 278 | if(ret > 0) { 279 | printf("Status: OK\n"); 280 | if (vr) { 281 | op = 'r'; 282 | svr = 1; 283 | printf("VERIFY:\n"); 284 | goto very; 285 | } 286 | } 287 | else 288 | printf("Status: BAD(%d)\n", ret); 289 | fclose(fp); 290 | free(buf); 291 | } 292 | 293 | very: 294 | if (op == 'r') { 295 | if (!svr) printf("READ:\n"); 296 | else memset(buf, 0, len); 297 | printf("Read addr = 0x%016llX, len = 0x%016llX\n", addr, len); 298 | ret = prog.flash_read(buf, addr, len); 299 | if (ret < 0) { 300 | printf("Status: BAD(%d)\n", ret); 301 | free(buf); 302 | goto out; 303 | } 304 | if (svr) { 305 | unsigned char ch1; 306 | int i = 0; 307 | 308 | fseek(fp, 0, SEEK_SET); 309 | ch1 = (unsigned char)getc(fp); 310 | 311 | while ((ch1 != EOF) && (i < len - 1) && (ch1 == buf[i++])) 312 | ch1 = (unsigned char)getc(fp); 313 | 314 | if (ch1 == buf[i]) 315 | printf("Status: OK\n"); 316 | else 317 | printf("Status: BAD\n"); 318 | fclose(fp); 319 | free(buf); 320 | goto out; 321 | } 322 | fp = fopen(fname, "wb"); 323 | if (!fp) { 324 | printf("Couldn't open file %s for writing.\n", fname); 325 | free(buf); 326 | goto out; 327 | } 328 | fwrite(buf, 1, len, fp); 329 | if (ferror(fp)) 330 | printf("Error writing file [%s]\n", fname); 331 | fclose(fp); 332 | free(buf); 333 | printf("Status: OK\n"); 334 | } 335 | 336 | out: 337 | ch341a_spi_shutdown(); 338 | return 0; 339 | } 340 | -------------------------------------------------------------------------------- /src/bitbang_microwire.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2005-2021 Mokrushin I.V. aka McMCC mcmcc@mail.ru 2 | A simple bitbang protocol for Microwire 8-pin serial EEPROMs 3 | (93XX devices). Support organization 8bit and 16bit(8bit emulation). 4 | 5 | bitbang_microwire.c 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ 20 | /* ------------------------------------------------------------------------- */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "bitbang_microwire.h" 27 | 28 | struct gpio_cmd bb_func; 29 | 30 | #if 0 31 | unsigned char ORG = 0; /* organization 0 = 8 bit and 1 = 16 bit */ 32 | #endif 33 | unsigned char CLK = 0; 34 | unsigned char DO = 0; 35 | unsigned char DI = 0; 36 | unsigned char CSEL = 0; 37 | 38 | int org = 0; 39 | int mw_eepromsize = 0; 40 | int fix_addr_len = 0; 41 | 42 | static unsigned char data = 0; 43 | 44 | static void delay_ms(int n) 45 | { 46 | #if 0 /* ifndef _WINDOWS */ 47 | int i; 48 | for(i = 0; i < (n * 100000); i++); 49 | #else 50 | usleep(n); 51 | #endif 52 | } 53 | 54 | static void data_1() 55 | { 56 | data = data | DI; 57 | if (bb_func.gpio_setbits) 58 | bb_func.gpio_setbits(data); 59 | } 60 | 61 | static void data_0() 62 | { 63 | data = data & (~DI); 64 | if (bb_func.gpio_setbits) 65 | bb_func.gpio_setbits(data); 66 | } 67 | 68 | static void org_1() 69 | { 70 | #if 0 71 | data = data | ORG; /* 16bit */ 72 | if (bb_func.gpio_setbits) 73 | bb_func.gpio_setbits(data); 74 | #endif 75 | } 76 | 77 | static void org_0() 78 | { 79 | #if 0 80 | data = data & (~ORG); /* 8bit */ 81 | if (bb_func.gpio_setbits) 82 | bb_func.gpio_setbits(data); 83 | #endif 84 | } 85 | 86 | static void csel_1() 87 | { 88 | data = data | CSEL; 89 | if (bb_func.gpio_setbits) 90 | bb_func.gpio_setbits(data); 91 | } 92 | 93 | static void csel_0() 94 | { 95 | data = data & (~CSEL); 96 | if (bb_func.gpio_setbits) 97 | bb_func.gpio_setbits(data); 98 | } 99 | 100 | static void clock_1() 101 | { 102 | data = data | CLK; 103 | if (bb_func.gpio_setbits) 104 | bb_func.gpio_setbits(data); 105 | } 106 | 107 | static void clock_0() 108 | { 109 | data = data & (~CLK); 110 | if (bb_func.gpio_setbits) 111 | bb_func.gpio_setbits(data); 112 | } 113 | 114 | static unsigned int get_data() 115 | { 116 | unsigned char b = 0; 117 | if (bb_func.gpio_getbits) 118 | bb_func.gpio_getbits(&b); 119 | return ((b & DO) == DO); 120 | } 121 | 122 | static int addr_nbits(const char *func, int size) 123 | { 124 | int i = 0; 125 | 126 | if (fix_addr_len) { 127 | printf("%s: Set address len %d bits\n", func, fix_addr_len); 128 | return fix_addr_len; 129 | } 130 | 131 | switch (size) { 132 | case 128: /* 93c46 */ 133 | i = org ? 6 : 7; 134 | break; 135 | case 256: /* 93c56 */ 136 | case 512: /* 93c66 */ 137 | i = org ? 8 : 9; 138 | break; 139 | case 1024: /* 93c76 */ 140 | case 2048: /* 93c86 */ 141 | i = org ? 10 : 11; 142 | break; 143 | case 4096: /* 93c96(not original name) */ 144 | i = org ? 12 : 13; 145 | break; 146 | default: 147 | i = 6; /* 93c06 and 93c16(not original name) */ 148 | break; 149 | } 150 | 151 | printf("%s: Set address len %d bits\n", func, i); 152 | 153 | return i; 154 | } 155 | 156 | static int convert_size(int eeprom_size) 157 | { 158 | int k = 1; 159 | 160 | org_0(); 161 | delay_ms(1); 162 | if (org) 163 | { 164 | org_1(); 165 | delay_ms(1); 166 | k = 2; 167 | } 168 | eeprom_size = eeprom_size / k; 169 | 170 | return eeprom_size; 171 | } 172 | 173 | static void send_to_di(unsigned int val, int nbit) 174 | { 175 | int b = 0, i = 0; 176 | 177 | while (i < nbit) 178 | { 179 | b = val & (1 << ((nbit - i++) - 1)); 180 | clock_0(); 181 | if (b) 182 | data_1(); 183 | else 184 | data_0(); 185 | delay_ms(1); 186 | clock_1(); 187 | delay_ms(1); 188 | } 189 | } 190 | 191 | static unsigned char get_from_do() 192 | { 193 | unsigned char val = 0; 194 | int b = 0, i = 0; 195 | while (i < 8) 196 | { 197 | clock_0(); 198 | delay_ms(1); 199 | b = get_data(); 200 | clock_1(); 201 | delay_ms(1); 202 | val |= (b << (7 - i++)); 203 | } 204 | return val; 205 | } 206 | 207 | static void enable_write_3wire(int num_bit) 208 | { 209 | csel_0(); 210 | clock_0(); 211 | delay_ms(1); 212 | data_1(); 213 | csel_1(); 214 | delay_ms(1); 215 | clock_1(); 216 | delay_ms(1); 217 | 218 | send_to_di(3, 4); 219 | send_to_di(0, num_bit - 2); 220 | 221 | data_0(); 222 | delay_ms(1); 223 | csel_0(); 224 | delay_ms(1); 225 | } 226 | 227 | static void disable_write_3wire(int num_bit) 228 | { 229 | csel_1(); 230 | delay_ms(1); 231 | clock_0(); 232 | delay_ms(1); 233 | data_1(); 234 | delay_ms(1); 235 | clock_1(); 236 | delay_ms(1); 237 | send_to_di(0, 4); 238 | send_to_di(0, num_bit - 2); 239 | csel_0(); 240 | delay_ms(1); 241 | } 242 | 243 | static void chip_busy(void) 244 | { 245 | printf("Error: Always BUSY! Communication problem...The broken microwire chip?\n"); 246 | } 247 | 248 | void Erase_EEPROM_3wire(int size_eeprom) 249 | { 250 | int i, num_bit; 251 | 252 | num_bit = addr_nbits(__func__, size_eeprom); 253 | 254 | enable_write_3wire(num_bit); 255 | csel_0(); 256 | clock_0(); 257 | delay_ms(1); 258 | data_1(); 259 | csel_1(); 260 | delay_ms(1); 261 | clock_1(); 262 | delay_ms(1); 263 | 264 | send_to_di(2, 4); 265 | send_to_di(0, num_bit - 2); 266 | clock_0(); 267 | data_0(); 268 | delay_ms(1); 269 | csel_0(); 270 | delay_ms(1); 271 | csel_1(); 272 | delay_ms(1); 273 | clock_1(); 274 | delay_ms(1); 275 | i = 0; 276 | while (!get_data() && i < 10000) 277 | { 278 | clock_0(); 279 | delay_ms(1); 280 | clock_1(); 281 | delay_ms(1); 282 | i++; 283 | } 284 | 285 | if (i == 10000) 286 | { 287 | chip_busy(); 288 | return; 289 | } 290 | 291 | csel_0(); 292 | delay_ms(1); 293 | clock_0(); 294 | delay_ms(1); 295 | clock_1(); 296 | delay_ms(1); 297 | 298 | disable_write_3wire(num_bit); 299 | } 300 | 301 | int Read_EEPROM_3wire(unsigned char *buffer, int size_eeprom) 302 | { 303 | int address, num_bit, l; 304 | 305 | num_bit = addr_nbits(__func__, size_eeprom); 306 | size_eeprom = convert_size(size_eeprom); 307 | 308 | address = 0; 309 | 310 | for (l = 0; l < size_eeprom; l++) 311 | { 312 | csel_0(); 313 | clock_0(); 314 | delay_ms(1); 315 | data_1(); 316 | csel_1(); 317 | delay_ms(1); 318 | clock_1(); 319 | delay_ms(1); 320 | 321 | send_to_di(2, 2); 322 | send_to_di(l, num_bit); 323 | 324 | data_0(); 325 | clock_0(); 326 | delay_ms(1); 327 | clock_1(); 328 | delay_ms(1); 329 | buffer[address] = get_from_do(); 330 | if (org) 331 | { 332 | address++; 333 | buffer[address] = get_from_do(); 334 | } 335 | csel_0(); 336 | delay_ms(1); 337 | clock_0(); 338 | delay_ms(1); 339 | clock_1(); 340 | delay_ms(1); 341 | printf("\bRead %d%% [%d] of [%d] bytes ", 100 * l / size_eeprom, l, size_eeprom); 342 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 343 | fflush(stdout); 344 | address++; 345 | } 346 | printf("Read 100%% [%d] of [%d] bytes \n", l, size_eeprom); 347 | return 0; 348 | } 349 | 350 | int Write_EEPROM_3wire(unsigned char *buffer, int size_eeprom) 351 | { 352 | int i, l, address, num_bit; 353 | 354 | num_bit = addr_nbits(__func__, size_eeprom); 355 | size_eeprom = convert_size(size_eeprom); 356 | 357 | enable_write_3wire(num_bit); 358 | address = 0; 359 | 360 | for (l = 0; l < size_eeprom; l++) 361 | { 362 | csel_0(); 363 | clock_0(); 364 | delay_ms(1); 365 | data_1(); 366 | csel_1(); 367 | delay_ms(1); 368 | clock_1(); 369 | delay_ms(1); 370 | send_to_di(1, 2); 371 | send_to_di(l, num_bit); 372 | send_to_di(buffer[address], 8); 373 | if (org) 374 | { 375 | address++; 376 | send_to_di(buffer[address], 8); 377 | } 378 | clock_0(); 379 | data_0(); 380 | delay_ms(1); 381 | csel_0(); 382 | delay_ms(1); 383 | csel_1(); 384 | delay_ms(1); 385 | clock_1(); 386 | delay_ms(1); 387 | i = 0; 388 | while (!get_data() && i < 10000) 389 | { 390 | clock_0(); 391 | delay_ms(1); 392 | clock_1(); 393 | delay_ms(1); 394 | i++; 395 | } 396 | if (i == 10000) 397 | { 398 | printf("\n"); 399 | chip_busy(); 400 | return -1; 401 | } 402 | 403 | csel_0(); 404 | delay_ms(1); 405 | clock_0(); 406 | delay_ms(1); 407 | clock_1(); 408 | delay_ms(1); 409 | printf("\bWritten %d%% [%d] of [%d] bytes ", 100 * l / size_eeprom, l, size_eeprom); 410 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 411 | fflush(stdout); 412 | address++; 413 | } 414 | printf("Written 100%% [%d] of [%d] bytes \n", l, size_eeprom); 415 | disable_write_3wire(num_bit); 416 | 417 | return 0; 418 | } 419 | 420 | int deviceSize_3wire(char *eepromname) 421 | { 422 | int i; 423 | 424 | for (i = 0; mw_eepromlist[i].size; i++) { 425 | if (strstr(mw_eepromlist[i].name, eepromname)) { 426 | return (mw_eepromlist[i].size); 427 | } 428 | } 429 | 430 | return -1; 431 | } 432 | /* End of [bitbang_microwire.c] package */ 433 | -------------------------------------------------------------------------------- /src/spi_nand_flash.h: -------------------------------------------------------------------------------- 1 | /*====================================================================================== 2 | * MODULE NAME: spi 3 | * FILE NAME: spi_nand_flash.h 4 | * DATE: 5 | * VERSION: 1.00 6 | * PURPOSE: To Provide SPI NAND Access interface. 7 | * NOTES: 8 | * 9 | * AUTHOR : 10 | * 11 | * FUNCTIONS 12 | * SPI_NAND_Flash_Init To provide interface for SPI NAND init. 13 | * SPI_NAND_Flash_Get_Flash_Info To get system current flash info. 14 | * SPI_NAND_Flash_Write_Nbyte To provide interface for Write N Bytes into SPI NAND Flash. 15 | * SPI_NAND_Flash_Read_NByte To provide interface for Read N Bytes from SPI NAND Flash. 16 | * SPI_NAND_Flash_Erase To provide interface for Erase SPI NAND Flash. 17 | * SPI_NAND_Flash_Read_Byte To provide interface for read 1 Bytes from SPI NAND Flash. 18 | * SPI_NAND_Flash_Read_DWord To provide interface for read Double Word from SPI NAND Flash. 19 | * 20 | * DEPENDENCIES 21 | * 22 | * * $History: $ 23 | * MODIFICTION HISTORY: 24 | *====================================================================================== 25 | */ 26 | 27 | #ifndef __SPI_NAND_FLASH_H__ 28 | #define __SPI_NAND_FLASH_H__ 29 | 30 | /* INCLUDE FILE DECLARATIONS --------------------------------------------------------- */ 31 | #include "types.h" 32 | #include "ch341a_spi.h" 33 | 34 | /* MACRO DECLARATIONS ---------------------------------------------------------------- */ 35 | #define SPI_NAND_FLASH_OOB_FREE_ENTRY_MAX 32 36 | 37 | /* TYPE DECLARATIONS ----------------------------------------------------------------- */ 38 | typedef enum{ 39 | SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND, 40 | SPI_NAND_FLASH_READ_DUMMY_BYTE_APPEND, 41 | SPI_NAND_FLASH_READ_DUMMY_BYTE_DEF_NO 42 | } SPI_NAND_FLASH_READ_DUMMY_BYTE_T; 43 | 44 | typedef enum{ 45 | SPI_NAND_FLASH_RTN_NO_ERROR = 0, 46 | SPI_NAND_FLASH_RTN_PROBE_ERROR, 47 | SPI_NAND_FLASH_RTN_ALIGNED_CHECK_FAIL, 48 | SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK, 49 | SPI_NAND_FLASH_RTN_ERASE_FAIL, 50 | SPI_NAND_FLASH_RTN_PROGRAM_FAIL, 51 | SPI_NAND_FLASH_RTN_DEF_NO 52 | } SPI_NAND_FLASH_RTN_T; 53 | 54 | typedef enum{ 55 | SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE = 0, 56 | SPI_NAND_FLASH_READ_SPEED_MODE_DUAL, 57 | SPI_NAND_FLASH_READ_SPEED_MODE_QUAD, 58 | SPI_NAND_FLASH_READ_SPEED_MODE_DEF_NO 59 | } SPI_NAND_FLASH_READ_SPEED_MODE_T; 60 | 61 | 62 | typedef enum{ 63 | SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE = 0, 64 | SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD, 65 | SPI_NAND_FLASH_WRITE_SPEED_MODE_DEF_NO 66 | } SPI_NAND_FLASH_WRITE_SPEED_MODE_T; 67 | 68 | 69 | typedef enum{ 70 | SPI_NAND_FLASH_DEBUG_LEVEL_0 = 0, 71 | SPI_NAND_FLASH_DEBUG_LEVEL_1, 72 | SPI_NAND_FLASH_DEBUG_LEVEL_2, 73 | SPI_NAND_FLASH_DEBUG_LEVEL_DEF_NO 74 | } SPI_NAND_FLASH_DEBUG_LEVEL_T; 75 | 76 | /* Bitwise */ 77 | #define SPI_NAND_FLASH_FEATURE_NONE ( 0x00 ) 78 | #define SPI_NAND_FLASH_PLANE_SELECT_HAVE ( 0x01 << 0 ) 79 | #define SPI_NAND_FLASH_DIE_SELECT_1_HAVE ( 0x01 << 1 ) 80 | #define SPI_NAND_FLASH_DIE_SELECT_2_HAVE ( 0x01 << 2 ) 81 | 82 | struct spi_nand_flash_oobfree{ 83 | unsigned long offset; 84 | unsigned long len; 85 | }; 86 | 87 | struct spi_nand_flash_ooblayout 88 | { unsigned long oobsize; 89 | struct spi_nand_flash_oobfree oobfree[SPI_NAND_FLASH_OOB_FREE_ENTRY_MAX]; 90 | }; 91 | 92 | struct SPI_NAND_FLASH_INFO_T { 93 | u8 mfr_id; 94 | u8 dev_id; 95 | const char *ptr_name; 96 | u32 device_size; /* Flash total Size */ 97 | u32 page_size; /* Page Size */ 98 | u32 erase_size; /* Block Size */ 99 | u32 oob_size; /* Spare Area (OOB) Size */ 100 | SPI_NAND_FLASH_READ_DUMMY_BYTE_T dummy_mode; 101 | SPI_NAND_FLASH_READ_SPEED_MODE_T read_mode; 102 | SPI_NAND_FLASH_WRITE_SPEED_MODE_T write_mode; 103 | struct spi_nand_flash_ooblayout *oob_free_layout; 104 | u32 feature; 105 | }; 106 | 107 | struct nand_info { 108 | int mfr_id; 109 | int dev_id; 110 | char *name; 111 | int numchips; 112 | int chip_shift; 113 | int page_shift; 114 | int erase_shift; 115 | int oob_shift; 116 | int badblockpos; 117 | int opcode_type; 118 | }; 119 | 120 | struct ra_nand_chip { 121 | struct nand_info *flash; 122 | }; 123 | 124 | /* EXPORTED SUBPROGRAM SPECIFICATION ------------------------------------------------- */ 125 | 126 | /*------------------------------------------------------------------------------------ 127 | * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Init( long rom_base ) 128 | * PURPOSE : To provide interface for SPI NAND init. 129 | * AUTHOR : 130 | * CALLED BY 131 | * - 132 | * CALLS 133 | * - 134 | * PARAMs : 135 | * INPUT : rom_base - The rom_base variable of this function. 136 | * OUTPUT: None 137 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 138 | * NOTES : 139 | * MODIFICTION HISTORY: 140 | * 141 | *------------------------------------------------------------------------------------ 142 | */ 143 | SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Init( u32 rom_base ); 144 | 145 | /*------------------------------------------------------------------------------------ 146 | * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Get_Flash_Info( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_into_t ) 147 | * PURPOSE : To get system current flash info. 148 | * AUTHOR : 149 | * CALLED BY 150 | * - 151 | * CALLS 152 | * - 153 | * PARAMs : 154 | * INPUT : None 155 | * OUTPUT: ptr_rtn_into_t - A pointer to the structure of the ptr_rtn_into_t variable. 156 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 157 | * NOTES : 158 | * MODIFICTION HISTORY: 159 | * 160 | *------------------------------------------------------------------------------------ 161 | */ 162 | SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Get_Flash_Info( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_into_t); 163 | 164 | /*------------------------------------------------------------------------------------ 165 | * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Write_Nbyte( u32 dst_addr, 166 | * u32 len, 167 | * u32 *ptr_rtn_len, 168 | * u8* ptr_buf ) 169 | * PURPOSE : To provide interface for Write N Bytes into SPI NAND Flash. 170 | * AUTHOR : 171 | * CALLED BY 172 | * - 173 | * CALLS 174 | * - 175 | * PARAMs : 176 | * INPUT : dst_addr - The dst_addr variable of this function. 177 | * len - The len variable of this function. 178 | * buf - The buf variable of this function. 179 | * OUTPUT: rtn_len - The rtn_len variable of this function. 180 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 181 | * NOTES : 182 | * MODIFICTION HISTORY: 183 | * 184 | *------------------------------------------------------------------------------------ 185 | */ 186 | SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Write_Nbyte( u32 dst_addr, 187 | u32 len, 188 | u32 *ptr_rtn_len, 189 | u8 *ptr_buf, 190 | SPI_NAND_FLASH_WRITE_SPEED_MODE_T speed_mode ); 191 | 192 | /*------------------------------------------------------------------------------------ 193 | * FUNCTION: int SPI_NAND_Flash_Read_NByte( long addr, 194 | * long len, 195 | * long *retlen, 196 | * char *buf ) 197 | * PURPOSE : To provide interface for Read N Bytes from SPI NAND Flash. 198 | * AUTHOR : 199 | * CALLED BY 200 | * - 201 | * CALLS 202 | * - 203 | * PARAMs : 204 | * INPUT : addr - The addr variable of this function. 205 | * len - The len variable of this function. 206 | * retlen - The retlen variable of this function. 207 | * buf - The buf variable of this function. 208 | * OUTPUT: None 209 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 210 | * NOTES : 211 | * MODIFICTION HISTORY: 212 | * 213 | *------------------------------------------------------------------------------------ 214 | */ 215 | u32 SPI_NAND_Flash_Read_NByte( u32 addr, 216 | u32 len, 217 | u32 *retlen, 218 | u8 *buf, 219 | SPI_NAND_FLASH_READ_SPEED_MODE_T speed_mode, 220 | SPI_NAND_FLASH_RTN_T *status ); 221 | 222 | /*------------------------------------------------------------------------------------ 223 | * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Erase( u32 dst_addr, 224 | * u32 len ) 225 | * PURPOSE : To provide interface for Erase SPI NAND Flash. 226 | * AUTHOR : 227 | * CALLED BY 228 | * - 229 | * CALLS 230 | * - 231 | * PARAMs : 232 | * INPUT : dst_addr - The dst_addr variable of this function. 233 | * len - The len variable of this function. 234 | * OUTPUT: None 235 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 236 | * NOTES : 237 | * MODIFICTION HISTORY: 238 | * 239 | *------------------------------------------------------------------------------------ 240 | */ 241 | SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Erase( u32 dst_addr, 242 | u32 len ); 243 | 244 | /*------------------------------------------------------------------------------------ 245 | * FUNCTION: char SPI_NAND_Flash_Read_Byte( long addr ) 246 | * PURPOSE : To provide interface for read 1 Bytes from SPI NAND Flash. 247 | * AUTHOR : 248 | * CALLED BY 249 | * - 250 | * CALLS 251 | * - 252 | * PARAMs : 253 | * INPUT : addr - The addr variable of this function. 254 | * OUTPUT: None 255 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 256 | * NOTES : 257 | * MODIFICTION HISTORY: 258 | * 259 | *------------------------------------------------------------------------------------ 260 | */ 261 | unsigned char SPI_NAND_Flash_Read_Byte( unsigned long addr, SPI_NAND_FLASH_RTN_T *status); 262 | 263 | /*------------------------------------------------------------------------------------ 264 | * FUNCTION: long SPI_NAND_Flash_Read_DWord( long addr ) 265 | * PURPOSE : To provide interface for read Double Word from SPI NAND Flash. 266 | * AUTHOR : 267 | * CALLED BY 268 | * - 269 | * CALLS 270 | * - 271 | * PARAMs : 272 | * INPUT : addr - The addr variable of this function. 273 | * OUTPUT: None 274 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 275 | * NOTES : 276 | * MODIFICTION HISTORY: 277 | * 278 | *------------------------------------------------------------------------------------ 279 | */ 280 | unsigned long SPI_NAND_Flash_Read_DWord( unsigned long addr, SPI_NAND_FLASH_RTN_T *status); 281 | 282 | /*------------------------------------------------------------------------------------ 283 | * FUNCTION: void SPI_NAND_Flash_Clear_Read_Cache_Data( void ) 284 | * PURPOSE : To clear the cache data for read. 285 | * (The next time to read data will get data from flash chip certainly.) 286 | * AUTHOR : 287 | * CALLED BY 288 | * - 289 | * CALLS 290 | * - 291 | * PARAMs : 292 | * INPUT : None 293 | * OUTPUT: None 294 | * RETURN : SPI_RTN_NO_ERROR - Successful. Otherwise - Failed. 295 | * NOTES : 296 | * MODIFICTION HISTORY: 297 | * 298 | *------------------------------------------------------------------------------------ 299 | */ 300 | void SPI_NAND_Flash_Clear_Read_Cache_Data( void ); 301 | 302 | SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Enable_OnDie_ECC( void ); 303 | 304 | SPI_NAND_FLASH_RTN_T spi_nand_erase_block ( u32 block_index); 305 | 306 | #endif /* ifndef __SPI_NAND_FLASH_H__ */ 307 | /* End of [spi_nand_flash.h] package */ 308 | -------------------------------------------------------------------------------- /src/ch341a_i2c.c: -------------------------------------------------------------------------------- 1 | // 2 | // ch341eeprom programmer version 0.1 (Beta) 3 | // 4 | // Programming tool for the 24Cxx serial EEPROMs using the Winchiphead CH341A IC 5 | // 6 | // (c) December 2011 asbokid 7 | // 8 | // This program is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // This program is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with this program. If not, see . 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "ch341a_i2c.h" 29 | 30 | #define dprintf(args...) 31 | // #define dprintf(args...) do { if (1) printf(args); } while(0) 32 | 33 | extern struct libusb_device_handle *handle; 34 | unsigned char *readbuf; 35 | uint32_t getnextpkt; // set by the callback function 36 | uint32_t syncackpkt; // synch / ack flag used by BULK OUT cb function 37 | uint32_t byteoffset; 38 | 39 | // callback functions for async USB transfers 40 | static void cbBulkIn(struct libusb_transfer *transfer); 41 | static void cbBulkOut(struct libusb_transfer *transfer); 42 | 43 | void ch341ReadCmdMarshall(uint8_t *buffer, uint32_t addr, struct EEPROM *eeprom_info) 44 | { 45 | uint8_t *ptr = buffer; 46 | uint8_t msb_addr; 47 | uint32_t size_kb; 48 | 49 | *ptr++ = mCH341A_CMD_I2C_STREAM; // 0 50 | *ptr++ = mCH341A_CMD_I2C_STM_STA; // 1 51 | // Write address 52 | *ptr++ = mCH341A_CMD_I2C_STM_OUT | ((*eeprom_info).addr_size + 1); // 2: I2C bus adddress + EEPROM address 53 | if ((*eeprom_info).addr_size >= 2) { 54 | // 24C32 and more 55 | msb_addr = addr >> 16 & (*eeprom_info).i2c_addr_mask; 56 | *ptr++ = (EEPROM_I2C_BUS_ADDRESS | msb_addr) << 1; // 3 57 | *ptr++ = (addr >> 8 & 0xFF); // 4 58 | *ptr++ = (addr >> 0 & 0xFF); // 5 59 | } else { 60 | // 24C16 and less 61 | msb_addr = addr >> 8 & (*eeprom_info).i2c_addr_mask; 62 | *ptr++ = (EEPROM_I2C_BUS_ADDRESS | msb_addr) << 1; // 3 63 | *ptr++ = (addr >> 0 & 0xFF); // 4 64 | } 65 | // Read 66 | *ptr++ = mCH341A_CMD_I2C_STM_STA; // 6/5 67 | *ptr++ = mCH341A_CMD_I2C_STM_OUT | 1; // 7/6 68 | *ptr++ = ((EEPROM_I2C_BUS_ADDRESS | msb_addr) << 1) | 1; // 8/7: Read command 69 | 70 | // Configuration? 71 | *ptr++ = 0xE0; // 9/8 72 | *ptr++ = 0x00; // 10/9 73 | if ((*eeprom_info).addr_size < 2) 74 | *ptr++ = 0x10; // x/10 75 | memcpy(ptr, "\x00\x06\x04\x00\x00\x00\x00\x00\x00", 9); 76 | ptr += 9; // 10 77 | size_kb = (*eeprom_info).size/1024; 78 | *ptr++ = size_kb & 0xFF; // 19 79 | *ptr++ = (size_kb >> 8) & 0xFF; // 20 80 | memcpy(ptr, "\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc", 10); 81 | ptr += 10; 82 | 83 | // Frame 2 84 | *ptr++ = mCH341A_CMD_I2C_STREAM; 85 | memcpy(ptr, "\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 86 | "\xd9\x8b\x41\x7e\x00\xe0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e", 31); 87 | ptr += 31; 88 | 89 | // Frame 3 90 | *ptr++ = mCH341A_CMD_I2C_STREAM; 91 | memcpy(ptr, "\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 92 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00", 31); 93 | ptr += 31; 94 | 95 | // Finalize 96 | *ptr++ = mCH341A_CMD_I2C_STREAM; // 0xAA 97 | *ptr++ = 0xDF; // ??? 98 | *ptr++ = mCH341A_CMD_I2C_STM_IN; // 0xC0 99 | *ptr++ = mCH341A_CMD_I2C_STM_STO; // 0x75 100 | *ptr++ = mCH341A_CMD_I2C_STM_END; // 0x00 101 | 102 | assert(ptr - buffer == CH341_EEPROM_READ_CMD_SZ); 103 | } 104 | 105 | // -------------------------------------------------------------------------- 106 | // ch341readEEPROM() 107 | // read n bytes from device (in packets of 32 bytes) 108 | int32_t ch341readEEPROM(uint8_t *buffer, uint32_t bytestoread, struct EEPROM *eeprom_info) 109 | { 110 | uint8_t ch341outBuffer[EEPROM_READ_BULKOUT_BUF_SZ]; 111 | uint8_t ch341inBuffer[IN_BUF_SZ]; // 0x100 bytes 112 | int32_t ret = 0, readpktcount = 0; 113 | struct libusb_transfer *xferBulkIn, *xferBulkOut; 114 | struct timeval tv = {0, 100}; // our async polling interval 115 | 116 | xferBulkIn = libusb_alloc_transfer(0); 117 | xferBulkOut = libusb_alloc_transfer(0); 118 | 119 | if (!xferBulkIn || !xferBulkOut) { 120 | printf("Couldnt allocate USB transfer structures\n"); 121 | return -1; 122 | } 123 | 124 | byteoffset = 0; 125 | 126 | dprintf("Allocated USB transfer structures\n"); 127 | 128 | memset(ch341inBuffer, 0, EEPROM_READ_BULKIN_BUF_SZ); 129 | ch341ReadCmdMarshall(ch341outBuffer, 0, eeprom_info); // Fill output buffer 130 | 131 | libusb_fill_bulk_transfer(xferBulkIn, handle, BULK_READ_ENDPOINT, ch341inBuffer, 132 | EEPROM_READ_BULKIN_BUF_SZ, cbBulkIn, NULL, DEFAULT_TIMEOUT); 133 | 134 | libusb_fill_bulk_transfer(xferBulkOut, handle, BULK_WRITE_ENDPOINT, 135 | ch341outBuffer, EEPROM_READ_BULKOUT_BUF_SZ, cbBulkOut, NULL, DEFAULT_TIMEOUT); 136 | 137 | dprintf("Filled USB transfer structures\n"); 138 | 139 | libusb_submit_transfer(xferBulkIn); 140 | dprintf("Submitted BULK IN start packet\n"); 141 | libusb_submit_transfer(xferBulkOut); 142 | dprintf("Submitted BULK OUT setup packet\n"); 143 | 144 | readbuf = buffer; 145 | 146 | while (1) { 147 | printf("Read %d%% [%d] of [%d] bytes ", 100 * byteoffset / bytestoread, byteoffset, bytestoread); 148 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 149 | fflush(stdout); 150 | ret = libusb_handle_events_timeout(NULL, &tv); 151 | 152 | if (ret < 0 || getnextpkt == -1) { // indicates an error 153 | printf("ret from libusb_handle_timeout = %d\n", ret); 154 | printf("getnextpkt = %d\n", getnextpkt); 155 | if (ret < 0) 156 | printf("USB read error : %s\n", strerror(-ret)); 157 | libusb_free_transfer(xferBulkIn); 158 | libusb_free_transfer(xferBulkOut); 159 | return -1; 160 | } 161 | if (getnextpkt == 1) { // callback function reports a new BULK IN packet received 162 | getnextpkt = 0; // reset the flag 163 | readpktcount++; // increment the read packet counter 164 | byteoffset += EEPROM_READ_BULKIN_BUF_SZ; 165 | if (byteoffset == bytestoread) 166 | break; 167 | 168 | dprintf("\nRe-submitting transfer request to BULK IN endpoint\n"); 169 | libusb_submit_transfer(xferBulkIn); // re-submit request for next BULK IN packet of EEPROM data 170 | if (syncackpkt) 171 | syncackpkt = 0; 172 | // if 4th packet received, we are at end of 0x80 byte data block, 173 | // if it is not the last block, then resubmit request for data 174 | if (readpktcount == 4) { 175 | dprintf("\nSubmitting next transfer request to BULK OUT endpoint\n"); 176 | readpktcount = 0; 177 | 178 | ch341ReadCmdMarshall(ch341outBuffer, byteoffset, eeprom_info); // Fill output buffer 179 | libusb_fill_bulk_transfer(xferBulkOut, handle, BULK_WRITE_ENDPOINT, ch341outBuffer, 180 | EEPROM_READ_BULKOUT_BUF_SZ, cbBulkOut, NULL, DEFAULT_TIMEOUT); 181 | 182 | libusb_submit_transfer(xferBulkOut); // update transfer struct (with new EEPROM page offset) 183 | // and re-submit next transfer request to BULK OUT endpoint 184 | } 185 | } 186 | } 187 | printf("Read 100%% [%d] of [%d] bytes \n", byteoffset, bytestoread); 188 | 189 | libusb_free_transfer(xferBulkIn); 190 | libusb_free_transfer(xferBulkOut); 191 | return 0; 192 | } 193 | 194 | // Callback function for async bulk in comms 195 | void cbBulkIn(struct libusb_transfer *transfer) 196 | { 197 | int i; 198 | 199 | switch (transfer->status) { 200 | case LIBUSB_TRANSFER_COMPLETED: 201 | // display the contents of the BULK IN data buffer 202 | dprintf("\ncbBulkIn(): status %d - Read %d bytes\n",transfer->status,transfer->actual_length); 203 | 204 | for (i = 0; i < transfer->actual_length; i++) { 205 | if(!(i % 16)) 206 | dprintf("\n "); 207 | dprintf("%02x ", transfer->buffer[i]); 208 | } 209 | dprintf("\n"); 210 | // copy read data to our EEPROM buffer 211 | memcpy(readbuf + byteoffset, transfer->buffer, transfer->actual_length); 212 | getnextpkt = 1; 213 | break; 214 | default: 215 | printf("\ncbBulkIn: error : %d\n", transfer->status); 216 | getnextpkt = -1; 217 | } 218 | return; 219 | } 220 | 221 | // Callback function for async bulk out comms 222 | void cbBulkOut(struct libusb_transfer *transfer) 223 | { 224 | syncackpkt = 1; 225 | dprintf("\ncbBulkOut(): Sync/Ack received: status %d\n", transfer->status); 226 | return; 227 | } 228 | 229 | // -------------------------------------------------------------------------- 230 | // ch341writeEEPROM() 231 | // write n bytes to 24c32/24c64 device (in packets of 32 bytes) 232 | int32_t ch341writeEEPROM(uint8_t *buffer, uint32_t bytesum, struct EEPROM *eeprom_info) 233 | { 234 | uint8_t ch341outBuffer[512/*EEPROM_WRITE_BUF_SZ*/]; 235 | uint8_t *outptr, *bufptr; 236 | uint8_t i2cCmdBuffer[256]; 237 | int32_t ret = 0, i; 238 | uint32_t payload_size, byteoffset = 0; 239 | uint32_t bytes = bytesum; 240 | uint8_t addrbytecount = (*eeprom_info).addr_size + 1; // 24c32 and 24c64 (and other 24c??) use 3 bytes for addressing 241 | int32_t actuallen = 0; 242 | uint16_t page_size = (*eeprom_info).page_size; 243 | uint16_t page_size_left; 244 | uint8_t part_no; 245 | uint8_t *i2cBufPtr; 246 | 247 | bufptr = buffer; 248 | 249 | while (bytes) { 250 | outptr = i2cCmdBuffer; 251 | if ((*eeprom_info).addr_size >= 2) { 252 | *outptr++ = (uint8_t) (0xa0 | (byteoffset >> 16 & (*eeprom_info).i2c_addr_mask) << 1); // EEPROM device address 253 | *outptr++ = (uint8_t) (byteoffset >> 8 & 0xff); // MSB (big-endian) byte address 254 | } else { 255 | *outptr++ = (uint8_t) (0xa0 | (byteoffset >> 8 & (*eeprom_info).i2c_addr_mask) << 1); // EEPROM device address 256 | } 257 | *outptr++ = (uint8_t) (byteoffset & 0xff); // LSB of 16-bit byte address 258 | 259 | memcpy(outptr, bufptr, page_size); // Copy one page 260 | 261 | byteoffset += page_size; 262 | bufptr += page_size; 263 | bytes -= page_size; 264 | 265 | outptr = ch341outBuffer; 266 | page_size_left = page_size + addrbytecount; 267 | part_no = 0; 268 | i2cBufPtr = i2cCmdBuffer; 269 | while (page_size_left) { 270 | uint8_t to_write = MIN(page_size_left, 28); 271 | *outptr++ = mCH341A_CMD_I2C_STREAM; 272 | if (part_no == 0) { // Start packet 273 | *outptr++ = mCH341A_CMD_I2C_STM_STA; 274 | } 275 | *outptr++ = mCH341A_CMD_I2C_STM_OUT | to_write; 276 | memcpy(outptr, i2cBufPtr, to_write); 277 | outptr += to_write; 278 | i2cBufPtr += to_write; 279 | page_size_left -= to_write; 280 | 281 | if (page_size_left == 0) { // Stop packet 282 | *outptr++ = mCH341A_CMD_I2C_STM_STO; 283 | } 284 | *outptr++ = mCH341A_CMD_I2C_STM_END; 285 | part_no++; 286 | } 287 | payload_size = outptr - ch341outBuffer; 288 | 289 | for (i = 0; i < payload_size; i++) { 290 | if(!(i % 0x10)) 291 | dprintf("\n%04x : ", i); 292 | dprintf("%02x ", ch341outBuffer[i]); 293 | } 294 | dprintf("\n"); 295 | 296 | ret = libusb_bulk_transfer(handle, BULK_WRITE_ENDPOINT, 297 | ch341outBuffer, payload_size, &actuallen, DEFAULT_TIMEOUT); 298 | 299 | if (ret < 0) { 300 | printf("Failed to write to EEPROM: '%s'\n", strerror(-ret)); 301 | return -1; 302 | } 303 | 304 | dprintf("Writing [aa 5a 00] to EEPROM\n"); // Magic CH341a packet! Undocumented, unknown purpose 305 | 306 | outptr = ch341outBuffer; 307 | *outptr++ = mCH341A_CMD_I2C_STREAM; 308 | *outptr++ = 0x5a; // what is this 0x5a?? 309 | *outptr++ = mCH341A_CMD_I2C_STM_END; 310 | 311 | ret = libusb_bulk_transfer(handle, BULK_WRITE_ENDPOINT, ch341outBuffer, 3, &actuallen, DEFAULT_TIMEOUT); 312 | 313 | if (ret < 0) { 314 | printf("Failed to write to EEPROM: '%s'\n", strerror(-ret)); 315 | return -1; 316 | } 317 | 318 | /* 319 | struct timeval tv = {0, 100}; // our async polling interval 320 | ret = libusb_handle_events_timeout(NULL, &tv); 321 | if (ret < 0) { // indicates an error 322 | fprintf(stderr, "ret from libusb_handle_timeout = %d\n", ret); 323 | fprintf(stderr, "USB read error : %s\n", strerror(-ret)); 324 | return -1; 325 | } 326 | */ 327 | printf("\bWritten %d%% [%d] of [%d] bytes ", 100 * (bytesum - bytes) / bytesum, bytesum - bytes, bytesum); 328 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 329 | fflush(stdout); 330 | } 331 | printf("Written 100%% [%d] of [%d] bytes \n", bytesum - bytes, bytesum); 332 | return 0; 333 | } 334 | 335 | // -------------------------------------------------------------------------- 336 | // parseEEPsize() 337 | // passed an EEPROM name (case-sensitive), returns its byte size 338 | int32_t parseEEPsize(char *eepromname, struct EEPROM *eeprom) 339 | { 340 | int i; 341 | 342 | for (i = 0; eepromlist[i].size; i++) { 343 | if (strstr(eepromlist[i].name, eepromname)) { 344 | memcpy(eeprom, &(eepromlist[i]), sizeof(struct EEPROM)); 345 | return (eepromlist[i].size); 346 | } 347 | } 348 | 349 | return -1; 350 | } 351 | -------------------------------------------------------------------------------- /src/ch341a_spi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the flashrom project. 3 | * 4 | * Copyright (C) 2011 asbokid 5 | * Copyright (C) 2014 Pluto Yang 6 | * Copyright (C) 2015-2016 Stefan Tauner 7 | * Copyright (C) 2015 Urja Rannikko 8 | * Copyright (C) 2018-2021 McMCC 9 | * 10 | * This program is free software; you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation; either version 2 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | */ 24 | #include 25 | #include 26 | #include "ch341a_spi.h" 27 | #include 28 | #include 29 | 30 | /* LIBUSB_CALL ensures the right calling conventions on libusb callbacks. 31 | * However, the macro is not defined everywhere. m( 32 | */ 33 | #ifndef LIBUSB_CALL 34 | #define LIBUSB_CALL 35 | #endif 36 | 37 | #define USB_TIMEOUT 1000 /* 1000 ms is plenty and we have no backup strategy anyway. */ 38 | #define WRITE_EP 0x02 39 | #define READ_EP 0x82 40 | 41 | #define CH341_PACKET_LENGTH 0x20 42 | #define CH341_MAX_PACKETS 256 43 | #define CH341_MAX_PACKET_LEN (CH341_PACKET_LENGTH * CH341_MAX_PACKETS) 44 | 45 | #define CH341A_CMD_SET_OUTPUT 0xA1 46 | #define CH341A_CMD_IO_ADDR 0xA2 47 | #define CH341A_CMD_PRINT_OUT 0xA3 48 | #define CH341A_CMD_SPI_STREAM 0xA8 49 | #define CH341A_CMD_SIO_STREAM 0xA9 50 | #define CH341A_CMD_I2C_STREAM 0xAA 51 | #define CH341A_CMD_UIO_STREAM 0xAB 52 | 53 | #define CH341A_CMD_I2C_STM_START 0x74 54 | #define CH341A_CMD_I2C_STM_STOP 0x75 55 | #define CH341A_CMD_I2C_STM_OUT 0x80 56 | #define CH341A_CMD_I2C_STM_IN 0xC0 57 | #define CH341A_CMD_I2C_STM_MAX ( min( 0x3F, CH341_PACKET_LENGTH ) ) 58 | #define CH341A_CMD_I2C_STM_SET 0x60 // bit 2: SPI with two data pairs D5,D4=out, D7,D6=in 59 | #define CH341A_CMD_I2C_STM_US 0x40 60 | #define CH341A_CMD_I2C_STM_MS 0x50 61 | #define CH341A_CMD_I2C_STM_DLY 0x0F 62 | #define CH341A_CMD_I2C_STM_END 0x00 63 | 64 | #define CH341A_CMD_UIO_STM_IN 0x00 65 | #define CH341A_CMD_UIO_STM_DIR 0x40 66 | #define CH341A_CMD_UIO_STM_OUT 0x80 67 | #define CH341A_CMD_UIO_STM_US 0xC0 68 | #define CH341A_CMD_UIO_STM_END 0x20 69 | 70 | #define CH341A_STM_I2C_20K 0x00 71 | #define CH341A_STM_I2C_100K 0x01 72 | #define CH341A_STM_I2C_400K 0x02 73 | #define CH341A_STM_I2C_750K 0x03 74 | #define CH341A_STM_SPI_DBL 0x04 75 | 76 | 77 | /* Number of parallel IN transfers. 32 seems to produce the most stable throughput on Windows. */ 78 | #define USB_IN_TRANSFERS 32 79 | 80 | struct dev_entry { 81 | uint16_t vendor_id; 82 | uint16_t device_id; 83 | const char *vendor_name; 84 | const char *device_name; 85 | }; 86 | 87 | /* We need to use many queued IN transfers for any resemblance of performance (especially on Windows) 88 | * because USB spec says that transfers end on non-full packets and the device sends the 31 reply 89 | * data bytes to each 32-byte packet with command + 31 bytes of data... */ 90 | static struct libusb_transfer *transfer_out = NULL; 91 | static struct libusb_transfer *transfer_ins[USB_IN_TRANSFERS] = {0}; 92 | struct libusb_device_handle *handle = NULL; 93 | 94 | const struct dev_entry devs_ch341a_spi[] = { 95 | {0x1A86, 0x5512, "WinChipHead (WCH)", "CH341A"}, 96 | {0}, 97 | }; 98 | 99 | enum trans_state {TRANS_ACTIVE = -2, TRANS_ERR = -1, TRANS_IDLE = 0}; 100 | 101 | #if 0 102 | static void print_hex(const void *buf, size_t len) 103 | { 104 | size_t i; 105 | for (i = 0; i < len; i++) { 106 | printf(" %02x", ((uint8_t *)buf)[i]); 107 | if (i % CH341_PACKET_LENGTH == CH341_PACKET_LENGTH - 1) 108 | printf("\n"); 109 | } 110 | } 111 | #endif 112 | 113 | static void cb_common(const char *func, struct libusb_transfer *transfer) 114 | { 115 | int *transfer_cnt = (int*)transfer->user_data; 116 | 117 | if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { 118 | /* Silently ACK and exit. */ 119 | *transfer_cnt = TRANS_IDLE; 120 | return; 121 | } 122 | 123 | if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { 124 | printf("\n%s: error: %s\n", func, libusb_error_name(transfer->status)); 125 | *transfer_cnt = TRANS_ERR; 126 | } else { 127 | *transfer_cnt = transfer->actual_length; 128 | } 129 | } 130 | 131 | /* callback for bulk out async transfer */ 132 | static void LIBUSB_CALL cb_out(struct libusb_transfer *transfer) 133 | { 134 | cb_common(__func__, transfer); 135 | } 136 | 137 | /* callback for bulk in async transfer */ 138 | static void LIBUSB_CALL cb_in(struct libusb_transfer *transfer) 139 | { 140 | cb_common(__func__, transfer); 141 | } 142 | 143 | static int32_t usb_transfer(const char *func, unsigned int writecnt, unsigned int readcnt, const uint8_t *writearr, uint8_t *readarr) 144 | { 145 | if (handle == NULL) 146 | return -1; 147 | 148 | int state_out = TRANS_IDLE; 149 | transfer_out->buffer = (uint8_t*)writearr; 150 | transfer_out->length = writecnt; 151 | transfer_out->user_data = &state_out; 152 | 153 | /* Schedule write first */ 154 | if (writecnt > 0) { 155 | state_out = TRANS_ACTIVE; 156 | int ret = libusb_submit_transfer(transfer_out); 157 | if (ret) { 158 | printf("%s: failed to submit OUT transfer: %s\n", func, libusb_error_name(ret)); 159 | state_out = TRANS_ERR; 160 | goto err; 161 | } 162 | } 163 | 164 | /* Handle all asynchronous packets as long as we have stuff to write or read. The write(s) simply need 165 | * to complete but we need to scheduling reads as long as we are not done. */ 166 | unsigned int free_idx = 0; /* The IN transfer we expect to be free next. */ 167 | unsigned int in_idx = 0; /* The IN transfer we expect to be completed next. */ 168 | unsigned int in_done = 0; 169 | unsigned int in_active = 0; 170 | unsigned int out_done = 0; 171 | uint8_t *in_buf = readarr; 172 | int state_in[USB_IN_TRANSFERS] = {0}; 173 | do { 174 | /* Schedule new reads as long as there are free transfers and unscheduled bytes to read. */ 175 | while ((in_done + in_active) < readcnt && state_in[free_idx] == TRANS_IDLE) { 176 | unsigned int cur_todo = min(CH341_PACKET_LENGTH - 1, readcnt - in_done - in_active); 177 | transfer_ins[free_idx]->length = cur_todo; 178 | transfer_ins[free_idx]->buffer = in_buf; 179 | transfer_ins[free_idx]->user_data = &state_in[free_idx]; 180 | int ret = libusb_submit_transfer(transfer_ins[free_idx]); 181 | if (ret) { 182 | state_in[free_idx] = TRANS_ERR; 183 | printf("%s: failed to submit IN transfer: %s\n", 184 | func, libusb_error_name(ret)); 185 | goto err; 186 | } 187 | in_buf += cur_todo; 188 | in_active += cur_todo; 189 | state_in[free_idx] = TRANS_ACTIVE; 190 | free_idx = (free_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */ 191 | } 192 | 193 | /* Actually get some work done. */ 194 | libusb_handle_events_timeout(NULL, &(struct timeval){1, 0}); 195 | 196 | /* Check for the write */ 197 | if (out_done < writecnt) { 198 | if (state_out == TRANS_ERR) { 199 | goto err; 200 | } else if (state_out > 0) { 201 | out_done += state_out; 202 | state_out = TRANS_IDLE; 203 | } 204 | } 205 | /* Check for completed transfers. */ 206 | while (state_in[in_idx] != TRANS_IDLE && state_in[in_idx] != TRANS_ACTIVE) { 207 | if (state_in[in_idx] == TRANS_ERR) { 208 | goto err; 209 | } 210 | /* If a transfer is done, record the number of bytes read and reuse it later. */ 211 | in_done += state_in[in_idx]; 212 | in_active -= state_in[in_idx]; 213 | state_in[in_idx] = TRANS_IDLE; 214 | in_idx = (in_idx + 1) % USB_IN_TRANSFERS; /* Increment (and wrap around). */ 215 | } 216 | } while ((out_done < writecnt) || (in_done < readcnt)); 217 | #if 0 218 | if (out_done > 0) { 219 | printf("Wrote %d bytes:\n", out_done); 220 | print_hex(writearr, out_done); 221 | printf("\n\n"); 222 | } 223 | if (in_done > 0) { 224 | printf("Read %d bytes:\n", in_done); 225 | print_hex(readarr, in_done); 226 | printf("\n\n"); 227 | } 228 | #endif 229 | return 0; 230 | err: 231 | /* Clean up on errors. */ 232 | printf("%s: Failed to %s %d bytes\n", func, (state_out == TRANS_ERR) ? "write" : "read", 233 | (state_out == TRANS_ERR) ? writecnt : readcnt); 234 | /* First, we must cancel any ongoing requests and wait for them to be canceled. */ 235 | if ((writecnt > 0) && (state_out == TRANS_ACTIVE)) { 236 | if (libusb_cancel_transfer(transfer_out) != 0) 237 | state_out = TRANS_ERR; 238 | } 239 | if (readcnt > 0) { 240 | unsigned int i; 241 | for (i = 0; i < USB_IN_TRANSFERS; i++) { 242 | if (state_in[i] == TRANS_ACTIVE) 243 | if (libusb_cancel_transfer(transfer_ins[i]) != 0) 244 | state_in[i] = TRANS_ERR; 245 | } 246 | } 247 | 248 | /* Wait for cancellations to complete. */ 249 | while (1) { 250 | bool finished = true; 251 | if ((writecnt > 0) && (state_out == TRANS_ACTIVE)) 252 | finished = false; 253 | if (readcnt > 0) { 254 | unsigned int i; 255 | for (i = 0; i < USB_IN_TRANSFERS; i++) { 256 | if (state_in[i] == TRANS_ACTIVE) 257 | finished = false; 258 | } 259 | } 260 | if (finished) 261 | break; 262 | libusb_handle_events_timeout(NULL, &(struct timeval){1, 0}); 263 | } 264 | return -1; 265 | } 266 | 267 | /* Set the I2C bus speed (speed(b1b0): 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz). 268 | * Set the SPI bus data width (speed(b2): 0 = Single, 1 = Double). */ 269 | int config_stream(unsigned int speed) 270 | { 271 | if (handle == NULL) 272 | return -1; 273 | 274 | uint8_t buf[] = { 275 | CH341A_CMD_I2C_STREAM, 276 | CH341A_CMD_I2C_STM_SET | (speed & 0x7), 277 | CH341A_CMD_I2C_STM_END 278 | }; 279 | 280 | int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL); 281 | if (ret < 0) { 282 | printf("Could not configure stream interface.\n"); 283 | } 284 | return ret; 285 | } 286 | 287 | /* ch341 requires LSB first, swap the bit order before send and after receive */ 288 | static uint8_t swap_byte(uint8_t x) 289 | { 290 | x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa); 291 | x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); 292 | x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); 293 | return x; 294 | } 295 | 296 | /* The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip: 297 | * UIO CH341A SPI CH341A SPI name 298 | * 0 D0/15 CS/1 (CS0) 299 | * 1 D1/16 unused (CS1) 300 | * 2 D2/17 unused (CS2) 301 | * 3 D3/18 SCK/6 (DCK) 302 | * 4 D4/19 unused (DOUT2) 303 | * 5 D5/20 SI/5 (DOUT) 304 | * - The UIO stream commands seem to only have 6 bits of output, and D6/D7 are the SPI inputs, 305 | * mapped as follows: 306 | * D6/21 unused (DIN2) 307 | * D7/22 SO/2 (DIN) 308 | */ 309 | int enable_pins(bool enable) 310 | { 311 | uint8_t buf[] = { 312 | CH341A_CMD_UIO_STREAM, 313 | CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1 314 | CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1 315 | CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1 316 | CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1 317 | CH341A_CMD_UIO_STM_OUT | 0x37, // CS high (all of them), SCK=0, DOUT*=1 318 | CH341A_CMD_UIO_STM_OUT | 0x36, // CS low (all of them), SCK=0, DOUT*=1 319 | CH341A_CMD_UIO_STM_DIR | (enable ? 0x3F : 0x00), // Interface output enable / disable 320 | CH341A_CMD_UIO_STM_END, 321 | }; 322 | 323 | int32_t ret = usb_transfer(__func__, sizeof(buf), 0, buf, NULL); 324 | if (ret < 0) { 325 | printf("Could not %sable output pins.\n", enable ? "en" : "dis"); 326 | } 327 | return ret; 328 | } 329 | 330 | int ch341a_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr) 331 | { 332 | int32_t ret = 0; 333 | 334 | if (handle == NULL) 335 | return -1; 336 | 337 | /* How many packets ... */ 338 | const size_t packets = (writecnt + readcnt + CH341_PACKET_LENGTH - 2) / (CH341_PACKET_LENGTH - 1); 339 | 340 | /* We pluck CS/timeout handling into the first packet thus we need to allocate one extra package. */ 341 | uint8_t wbuf[packets+1][CH341_PACKET_LENGTH]; 342 | uint8_t rbuf[writecnt + readcnt]; 343 | /* Initialize the write buffer to zero to prevent writing random stack contents to device. */ 344 | memset(wbuf[0], 0, CH341_PACKET_LENGTH); 345 | 346 | uint8_t *ptr = wbuf[0]; 347 | /* CS usage is optimized by doing both transitions in one packet. 348 | * Final transition to deselected state is in the pin disable. */ 349 | unsigned int write_left = writecnt; 350 | unsigned int read_left = readcnt; 351 | unsigned int p; 352 | for (p = 0; p < packets; p++) { 353 | unsigned int write_now = min(CH341_PACKET_LENGTH - 1, write_left); 354 | unsigned int read_now = min ((CH341_PACKET_LENGTH - 1) - write_now, read_left); 355 | ptr = wbuf[p + 1]; 356 | *ptr++ = CH341A_CMD_SPI_STREAM; 357 | unsigned int i; 358 | for (i = 0; i < write_now; ++i) 359 | *ptr++ = swap_byte(*writearr++); 360 | if (read_now) { 361 | memset(ptr, 0xFF, read_now); 362 | read_left -= read_now; 363 | } 364 | write_left -= write_now; 365 | } 366 | 367 | ret = usb_transfer(__func__, CH341_PACKET_LENGTH + packets + writecnt + readcnt, 368 | writecnt + readcnt, wbuf[0], rbuf); 369 | 370 | if (ret < 0) 371 | return -1; 372 | 373 | unsigned int i; 374 | for (i = 0; i < readcnt; i++) { 375 | *readarr++ = swap_byte(rbuf[writecnt + i]); 376 | } 377 | 378 | return 0; 379 | } 380 | 381 | int ch341a_spi_shutdown(void) 382 | { 383 | if (handle == NULL) 384 | return -1; 385 | 386 | enable_pins(false); 387 | libusb_free_transfer(transfer_out); 388 | transfer_out = NULL; 389 | int i; 390 | for (i = 0; i < USB_IN_TRANSFERS; i++) { 391 | libusb_free_transfer(transfer_ins[i]); 392 | transfer_ins[i] = NULL; 393 | } 394 | libusb_release_interface(handle, 0); 395 | libusb_close(handle); 396 | libusb_exit(NULL); 397 | handle = NULL; 398 | return 0; 399 | } 400 | 401 | int ch341a_spi_init(void) 402 | { 403 | if (handle != NULL) { 404 | printf("%s: handle already set!\n", __func__); 405 | return -1; 406 | } 407 | 408 | int32_t ret = libusb_init(NULL); 409 | if (ret < 0) { 410 | printf("Couldnt initialize libusb!\n"); 411 | return -1; 412 | } 413 | #if LIBUSB_API_VERSION >= 0x01000106 414 | libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, 3); 415 | #else 416 | libusb_set_debug(NULL, 3); // Enable information, warning and error messages (only). 417 | #endif 418 | uint16_t vid = devs_ch341a_spi[0].vendor_id; 419 | uint16_t pid = devs_ch341a_spi[0].device_id; 420 | handle = libusb_open_device_with_vid_pid(NULL, vid, pid); 421 | if (handle == NULL) { 422 | printf("Couldn't open device %04x:%04x.\n", vid, pid); 423 | return -1; 424 | } 425 | printf("Found programmer device: %s - %s\n", devs_ch341a_spi[0].vendor_name, devs_ch341a_spi[0].device_name); 426 | 427 | #ifdef __gnu_linux__ 428 | /* libusb_detach_kernel_driver() and friends basically only work on Linux. We simply try to detach on Linux 429 | * without a lot of passion here. If that works fine else we will fail on claiming the interface anyway. */ 430 | ret = libusb_detach_kernel_driver(handle, 0); 431 | if (ret == LIBUSB_ERROR_NOT_SUPPORTED) { 432 | printf("Detaching kernel drivers is not supported. Further accesses may fail.\n"); 433 | } else if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND) { 434 | printf("Failed to detach kernel driver: '%s'. Further accesses will probably fail.\n", 435 | libusb_error_name(ret)); 436 | } 437 | #endif 438 | 439 | ret = libusb_claim_interface(handle, 0); 440 | if (ret != 0) { 441 | printf("Failed to claim interface 0: '%s'\n", libusb_error_name(ret)); 442 | goto close_handle; 443 | } 444 | 445 | struct libusb_device *dev; 446 | if (!(dev = libusb_get_device(handle))) { 447 | printf("Failed to get device from device handle.\n"); 448 | goto close_handle; 449 | } 450 | 451 | struct libusb_device_descriptor desc; 452 | ret = libusb_get_device_descriptor(dev, &desc); 453 | if (ret < 0) { 454 | printf("Failed to get device descriptor: '%s'\n", libusb_error_name(ret)); 455 | goto release_interface; 456 | } 457 | 458 | printf("Device revision is %d.%01d.%01d\n", 459 | (desc.bcdDevice >> 8) & 0x00FF, 460 | (desc.bcdDevice >> 4) & 0x000F, 461 | (desc.bcdDevice >> 0) & 0x000F); 462 | 463 | /* Allocate and pre-fill transfer structures. */ 464 | transfer_out = libusb_alloc_transfer(0); 465 | if (!transfer_out) { 466 | printf("Failed to alloc libusb OUT transfer\n"); 467 | goto release_interface; 468 | } 469 | int i; 470 | for (i = 0; i < USB_IN_TRANSFERS; i++) { 471 | transfer_ins[i] = libusb_alloc_transfer(0); 472 | if (transfer_ins[i] == NULL) { 473 | printf("Failed to alloc libusb IN transfer %d\n", i); 474 | goto dealloc_transfers; 475 | } 476 | } 477 | /* We use these helpers but dont fill the actual buffer yet. */ 478 | libusb_fill_bulk_transfer(transfer_out, handle, WRITE_EP, NULL, 0, cb_out, NULL, USB_TIMEOUT); 479 | for (i = 0; i < USB_IN_TRANSFERS; i++) 480 | libusb_fill_bulk_transfer(transfer_ins[i], handle, READ_EP, NULL, 0, cb_in, NULL, USB_TIMEOUT); 481 | 482 | if ((config_stream(CH341A_STM_I2C_750K) < 0) || (enable_pins(true) < 0)) 483 | goto dealloc_transfers; 484 | 485 | return 0; 486 | 487 | dealloc_transfers: 488 | for (i = 0; i < USB_IN_TRANSFERS; i++) { 489 | if (transfer_ins[i] == NULL) 490 | break; 491 | libusb_free_transfer(transfer_ins[i]); 492 | transfer_ins[i] = NULL; 493 | } 494 | libusb_free_transfer(transfer_out); 495 | transfer_out = NULL; 496 | release_interface: 497 | libusb_release_interface(handle, 0); 498 | close_handle: 499 | libusb_close(handle); 500 | handle = NULL; 501 | return -1; 502 | } 503 | /* End of [ch341a_spi.c] package */ 504 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/ch341a_i2c.h: -------------------------------------------------------------------------------- 1 | // libUSB driver for the ch341a in i2c mode 2 | // 3 | // Copyright 2011 asbokid 4 | #ifndef __CH341A_I2C_H__ 5 | #define __CH341A_I2C_H__ 6 | 7 | #include 8 | 9 | #define MAX_EEPROM_SIZE 131072 /* For 24c1024*/ 10 | 11 | #define EEPROM_I2C_BUS_ADDRESS 0x50 12 | 13 | #define BULK_WRITE_ENDPOINT 0x02 /* bEndpointAddress 0x02 EP 2 OUT (Bulk)*/ 14 | #define BULK_READ_ENDPOINT 0x82 /* bEndpointAddress 0x82 EP 2 IN (Bulk)*/ 15 | #define DEFAULT_INTERFACE 0x00 16 | 17 | #define DEFAULT_CONFIGURATION 0x01 18 | #define DEFAULT_TIMEOUT 300 // 300mS for USB timeouts 19 | 20 | #define IN_BUF_SZ 0x100 21 | #define EEPROM_WRITE_BUF_SZ 0x2b // only for 24c64 / 24c32 ?? 22 | #define EEPROM_READ_BULKIN_BUF_SZ 0x20 23 | #define EEPROM_READ_BULKOUT_BUF_SZ 0x65 24 | 25 | /* Based on (closed-source) DLL V1.9 for USB by WinChipHead (c) 2005. 26 | Supports USB chips: CH341, CH341A 27 | This can be a problem for copyright, sure asbokid can't release this part on any GPL licence*/ 28 | 29 | #define mCH341_PACKET_LENGTH 32 /* wMaxPacketSize 0x0020 1x 32 bytes, unused on the source*/ 30 | #define mCH341_PKT_LEN_SHORT 8 /* wMaxPacketSize 0x0008 1x 8 bytes, unused on the source*/ 31 | 32 | #define mCH341_ENDP_INTER_UP 0x81 /* bEndpointAddress 0x81 EP 1 IN (Interrupt), unused on the source*/ 33 | #define mCH341_ENDP_INTER_DOWN 0x01 /* This endpoint isn't list on my lsusb -v output, unused on the source*/ 34 | #define mCH341_ENDP_DATA_UP 0x82 /* ==BULK_READ_ENDPOINT Why repeat it?*/ 35 | #define mCH341_ENDP_DATA_DOWN 0x02 /* ==BULK_WRITE_ENDPOINT Why repeat it?*/ 36 | 37 | #define mCH341_VENDOR_READ 0xC0 /* Unused on the source*/ 38 | #define mCH341_VENDOR_WRITE 0x40 /* Unused on the source*/ 39 | 40 | #define mCH341_PARA_INIT 0xB1 /* Unused on the source*/ 41 | #define mCH341_I2C_STATUS 0x52 /* Unused on the source*/ 42 | #define mCH341_I2C_COMMAND 0x53 /* Unused on the source*/ 43 | 44 | #define mCH341_PARA_CMD_R0 0xAC /* Unused on the source*/ 45 | #define mCH341_PARA_CMD_R1 0xAD /* Unused on the source*/ 46 | #define mCH341_PARA_CMD_W0 0xA6 /* Unused on the source*/ 47 | #define mCH341_PARA_CMD_W1 0xA7 /* Unused on the source*/ 48 | #define mCH341_PARA_CMD_STS 0xA0 /* Unused on the source*/ 49 | 50 | #define mCH341A_CMD_SET_OUTPUT 0xA1 /* Unused on the source*/ 51 | #define mCH341A_CMD_IO_ADDR 0xA2 /* Unused on the source*/ 52 | #define mCH341A_CMD_PRINT_OUT 0xA3 /* Unused on the source*/ 53 | #define mCH341A_CMD_SPI_STREAM 0xA8 /* Unused on the source*/ 54 | #define mCH341A_CMD_SIO_STREAM 0xA9 /* Unused on the source*/ 55 | #define mCH341A_CMD_I2C_STREAM 0xAA 56 | #define mCH341A_CMD_UIO_STREAM 0xAB /* Unused on the source*/ 57 | 58 | #define mCH341A_BUF_CLEAR 0xB2 /* Unused on the source*/ 59 | #define mCH341A_I2C_CMD_X 0x54 /* Unused on the source*/ 60 | #define mCH341A_DELAY_MS 0x5E /* Unused on the source*/ 61 | #define mCH341A_GET_VER 0x5F /* Unused on the source*/ 62 | 63 | #define mCH341_EPP_IO_MAX ( mCH341_PACKET_LENGTH - 1 ) /* Unused on the source*/ 64 | #define mCH341A_EPP_IO_MAX 0xFF /* Unused on the source*/ 65 | 66 | #define mCH341A_CMD_IO_ADDR_W 0x00 /* Unused on the source*/ 67 | #define mCH341A_CMD_IO_ADDR_R 0x80 /* Unused on the source*/ 68 | 69 | #define mCH341A_CMD_I2C_STM_STA 0x74 70 | #define mCH341A_CMD_I2C_STM_STO 0x75 71 | #define mCH341A_CMD_I2C_STM_OUT 0x80 72 | #define mCH341A_CMD_I2C_STM_IN 0xC0 73 | #define mCH341A_CMD_I2C_STM_MAX ( min( 0x3F, mCH341_PACKET_LENGTH ) ) /* Unused on the source*/ 74 | #define mCH341A_CMD_I2C_STM_SET 0x60 75 | #define mCH341A_CMD_I2C_STM_US 0x40 /* Unused on the source*/ 76 | #define mCH341A_CMD_I2C_STM_MS 0x50 /* Unused on the source*/ 77 | #define mCH341A_CMD_I2C_STM_DLY 0x0F /* Unused on the source*/ 78 | #define mCH341A_CMD_I2C_STM_END 0x00 79 | 80 | #define mCH341A_CMD_UIO_STM_IN 0x00 /* Unused on the source*/ 81 | #define mCH341A_CMD_UIO_STM_DIR 0x40 /* Unused on the source*/ 82 | #define mCH341A_CMD_UIO_STM_OUT 0x80 /* Unused on the source*/ 83 | #define mCH341A_CMD_UIO_STM_US 0xC0 /* Unused on the source*/ 84 | #define mCH341A_CMD_UIO_STM_END 0x20 /* Unused on the source*/ 85 | 86 | #define mCH341_PARA_MODE_EPP 0x00 /* Unused on the source*/ 87 | #define mCH341_PARA_MODE_EPP17 0x00 /* Unused on the source*/ 88 | #define mCH341_PARA_MODE_EPP19 0x01 /* Unused on the source*/ 89 | #define mCH341_PARA_MODE_MEM 0x02 /* Unused on the source*/ 90 | 91 | /* End of part based on (closed-source) DLL V1.9 for USB by WinChipHead (c) 2005. 92 | Since is largely unused we can replace it */ 93 | 94 | 95 | #define CH341_I2C_LOW_SPEED 0 // low speed - 20kHz 96 | #define CH341_I2C_STANDARD_SPEED 1 // standard speed - 100kHz 97 | #define CH341_I2C_FAST_SPEED 2 // fast speed - 400kHz 98 | #define CH341_I2C_HIGH_SPEED 3 // high speed - 750kHz 99 | 100 | #define CH341_EEPROM_READ_CMD_SZ 0x65 /* Same size for all 24cXX read setup and next packets*/ 101 | 102 | /* CH341a READ EEPROM setup packet for the 24c01 103 | this needs putting into a struct to allow convenient access to individual elements*/ 104 | #define CH341_EEPROM_24c01_READ_SETUP_CMD "\xaa\x74\x82\xa0\x00\x74\x81\xa1\xe0\x00\x0f\x00\x06\x04\x00\x00" \ 105 | "\x00\x00\x00\x00\x01\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 106 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 107 | "\xd9\x8b\x41\x7e\x00\xd0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 108 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 109 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 110 | "\xaa\xdf\xc0\x75\x00" 111 | /* please se file /wiresharkusbsniffing/sniffed.txt 112 | this is the read setup packet for 24c01 113 | 114 | 0040 aa 74 82 a0 00 74 81 a1 e0 00 0f 00 06 04 00 00 .t...t.. ........ 115 | 0050 00 00 00 00 01 00 00 00 11 4d 40 77 cd ab ba dc ........ .M@w.... 116 | 0060 aa e0 00 00 c4 f1 12 00 11 4d 40 77 f0 f1 12 00 ........ .M@w.... 117 | 0070 d9 8b 41 7e 00 d0 fd 7f f0 f1 12 00 5a 88 41 7e ..A~.... ....Z.A~ 118 | 0080 aa e0 00 00 2a 88 41 7e 06 04 00 00 11 4d 40 77 ....*.A~ .....M@w 119 | 0090 e8 f3 12 00 14 00 00 00 01 00 00 00 00 00 00 00 ........ ........ 120 | 00a0 aa df c0 75 00 ...u. 121 | */ 122 | 123 | /* CH341a READ EEPROM next packet for 24c01 (no packets!!!)*/ 124 | 125 | 126 | /* CH341a READ EEPROM setup packet for the 24c02 127 | this needs putting into a struct to allow convenient access to individual elements*/ 128 | #define CH341_EEPROM_24c02_READ_SETUP_CMD "\xaa\x74\x82\xa0\x00\x74\x81\xa1\xe0\x00\x10\x00\x06\x04\x00\x00" \ 129 | "\x00\x00\x00\x00\x02\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 130 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 131 | "\xd9\x8b\x41\x7e\x00\xf0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 132 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 133 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 134 | "\xaa\xdf\xc0\x75\x00" 135 | /* please se file /wiresharkusbsniffing/sniffed.txt 136 | this is the read setup packet for 24c02 137 | 138 | 0040 aa 74 82 a0 00 74 81 a1 e0 00 10 00 06 04 00 00 .t...t.. ........ 139 | 0050 00 00 00 00 02 00 00 00 11 4d 40 77 cd ab ba dc ........ .M@w.... 140 | 0060 aa e0 00 00 c4 f1 12 00 11 4d 40 77 f0 f1 12 00 ........ .M@w.... 141 | 0070 d9 8b 41 7e 00 f0 fd 7f f0 f1 12 00 5a 88 41 7e ..A~.... ....Z.A~ 142 | 0080 aa e0 00 00 2a 88 41 7e 06 04 00 00 11 4d 40 77 ....*.A~ .....M@w 143 | 0090 e8 f3 12 00 14 00 00 00 01 00 00 00 00 00 00 00 ........ ........ 144 | 00a0 aa df c0 75 00 ...u. 145 | */ 146 | 147 | /* CH341a READ EEPROM next packet for 24c02 (one packet)*/ 148 | #define CH341_EEPROM_24c02_READ_NEXT_CMD "\xaa\x74\x82\xa0\x80\x74\x81\xa1\xe0\x00\x00\x00\x10\x00\x00\x00" \ 149 | "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 150 | "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ 151 | "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ 152 | "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\xe4\x00\x18\x00" \ 153 | "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xe8\x67\x00" \ 154 | "\xaa\xdf\xc0\x75\x00" 155 | /*please see file /wiresharkusbsniffing/sniffed.txt 156 | this is the read next packet for 24c02 (one packet) 157 | 158 | 0040 aa 74 82 a0 80 74 81 a1 e0 00 00 00 10 00 00 00 .t...t.. ........ 159 | 0050 00 00 00 00 8c f1 12 00 01 00 00 00 00 00 00 00 ........ ........ 160 | 0060 aa e0 00 00 4c f1 12 00 5d 22 d7 5a dc f1 12 00 ....L... ]".Z.... 161 | 0070 8f 04 44 7e 30 88 41 7e ff ff ff ff 2a 88 41 7e ..D~0.A~ ....*.A~ 162 | 0080 aa e0 00 7e 00 00 00 00 69 0e 3c 00 e4 00 18 00 ...~.... i.<..... 163 | 0090 0f 00 00 00 00 00 00 00 00 00 00 00 8c e8 67 00 ........ ......g. 164 | 00a0 aa df c0 75 00 ...u. 165 | */ 166 | 167 | 168 | /* CH341a READ EEPROM setup packet for the 24c04 169 | this needs putting into a struct to allow convenient access to individual elements*/ 170 | #define CH341_EEPROM_24c04_READ_SETUP_CMD "\xaa\x74\x82\xa0\x00\x74\x81\xa1\xe0\x00\x11\x00\x06\x04\x00\x00" \ 171 | "\x00\x00\x00\x00\x04\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 172 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 173 | "\xd9\x8b\x41\x7e\x00\xd0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 174 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 175 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 176 | "\xaa\xdf\xc0\x75\x00" 177 | /* please se file /wiresharkusbsniffing/sniffed.txt 178 | this is the read setup packet for 24c04 179 | 180 | 0040 aa 74 82 a0 00 74 81 a1 e0 00 11 00 06 04 00 00 .t...t.. ........ 181 | 0050 00 00 00 00 04 00 00 00 11 4d 40 77 cd ab ba dc ........ .M@w.... 182 | 0060 aa e0 00 00 c4 f1 12 00 11 4d 40 77 f0 f1 12 00 ........ .M@w.... 183 | 0070 d9 8b 41 7e 00 d0 fd 7f f0 f1 12 00 5a 88 41 7e ..A~.... ....Z.A~ 184 | 0080 aa e0 00 00 2a 88 41 7e 06 04 00 00 11 4d 40 77 ....*.A~ .....M@w 185 | 0090 e8 f3 12 00 14 00 00 00 01 00 00 00 00 00 00 00 ........ ........ 186 | 00a0 aa df c0 75 00 ...u. 187 | */ 188 | 189 | /* CH341a READ EEPROM next packet for 24c04 (three packets)*/ 190 | #define CH341_EEPROM_24c04_READ_NEXT_CMD "\xaa\x74\x82\xa0\x80\x74\x81\xa1\xe0\x00\x00\x00\x10\x00\x00\x00" \ 191 | "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 192 | "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ 193 | "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ 194 | "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\xde\x00\x16\x00" \ 195 | "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xe8\x67\x00" \ 196 | "\xaa\xdf\xc0\x75\x00" 197 | /*please see file /wiresharkusbsniffing/sniffed.txt 198 | this is the first read next packet for 24c04 (three different packets) 199 | 200 | 0040 aa 74 82 a0 80 74 81 a1 e0 00 00 00 10 00 00 00 .t...t.. ........ 201 | 0050 00 00 00 00 8c f1 12 00 01 00 00 00 00 00 00 00 ........ ........ 202 | 0060 aa e0 00 00 4c f1 12 00 5d 22 d7 5a dc f1 12 00 ....L... ]".Z.... 203 | 0070 8f 04 44 7e 30 88 41 7e ff ff ff ff 2a 88 41 7e ..D~0.A~ ....*.A~ 204 | 0080 aa e0 00 7e 00 00 00 00 69 0e 3c 00 de 00 16 00 ...~.... i.<..... 205 | 0090 0f 00 00 00 00 00 00 00 00 00 00 00 8c e8 67 00 ........ ......g. 206 | 00a0 aa df c0 75 00 ...u. 207 | */ 208 | 209 | 210 | /* CH341a READ EEPROM setup packet for the 24c08 211 | this needs putting into a struct to allow convenient access to individual elements*/ 212 | #define CH341_EEPROM_24c08_READ_SETUP_CMD "\xaa\x74\x82\xa0\x00\x74\x81\xa1\xe0\x00\x16\x00\x06\x04\x00\x00" \ 213 | "\x00\x00\x00\x00\x08\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 214 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 215 | "\xd9\x8b\x41\x7e\x00\xd0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 216 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 217 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 218 | "\xaa\xdf\xc0\x75\x00" 219 | /* please se file /wiresharkusbsniffing/sniffed.txt 220 | this is the read setup packet for 24c08 221 | 222 | 0040 aa 74 82 a0 00 74 81 a1 e0 00 16 00 06 04 00 00 .t...t.. ........ 223 | 0050 00 00 00 00 08 00 00 00 11 4d 40 77 cd ab ba dc ........ .M@w.... 224 | 0060 aa e0 00 00 c4 f1 12 00 11 4d 40 77 f0 f1 12 00 ........ .M@w.... 225 | 0070 d9 8b 41 7e 00 d0 fd 7f f0 f1 12 00 5a 88 41 7e ..A~.... ....Z.A~ 226 | 0080 aa e0 00 00 2a 88 41 7e 06 04 00 00 11 4d 40 77 ....*.A~ .....M@w 227 | 0090 e8 f3 12 00 14 00 00 00 01 00 00 00 00 00 00 00 ........ ........ 228 | 00a0 aa df c0 75 00 ...u. 229 | */ 230 | 231 | /* CH341a READ EEPROM next packet for 24c08 (seven packets)*/ 232 | #define CH341_EEPROM_24c08_READ_NEXT_CMD "\xaa\x74\x82\xa0\x80\x74\x81\xa1\xe0\x00\x00\x00\x10\x00\x00\x00" \ 233 | "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 234 | "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ 235 | "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ 236 | "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\x00\x01\x1a\x00" \ 237 | "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xe8\x67\x00" \ 238 | "\xaa\xdf\xc0\x75\x00" 239 | /*please see file /wiresharkusbsniffing/sniffed.txt 240 | this is the first read next packet for 24c08 (seven different packets) 241 | 242 | 0040 aa 74 82 a0 80 74 81 a1 e0 00 00 00 10 00 00 00 .t...t.. ........ 243 | 0050 00 00 00 00 8c f1 12 00 01 00 00 00 00 00 00 00 ........ ........ 244 | 0060 aa e0 00 00 4c f1 12 00 5d 22 d7 5a dc f1 12 00 ....L... ]".Z.... 245 | 0070 8f 04 44 7e 30 88 41 7e ff ff ff ff 2a 88 41 7e ..D~0.A~ ....*.A~ 246 | 0080 aa e0 00 7e 00 00 00 00 69 0e 3c 00 00 01 1a 00 ...~.... i.<..... 247 | 0090 0f 00 00 00 00 00 00 00 00 00 00 00 8c e8 67 00 ........ ......g. 248 | 00a0 aa df c0 75 00 ...u. 249 | */ 250 | 251 | 252 | /* CH341a READ EEPROM setup packet for the 24c16 253 | this needs putting into a struct to allow convenient access to individual elements*/ 254 | #define CH341_EEPROM_24c16_READ_SETUP_CMD "\xaa\x74\x82\xa0\x00\x74\x81\xa1\xe0\x00\x0e\x00\x06\x04\x00\x00" \ 255 | "\x00\x00\x00\x00\x10\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 256 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 257 | "\xd9\x8b\x41\x7e\x00\xe0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 258 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 259 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 260 | "\xaa\xdf\xc0\x75\x00" 261 | /* please se file /wiresharkusbsniffing/sniffed.txt 262 | this is the read setup packet for 24c16 263 | 264 | 0040 aa 74 82 a0 00 74 81 a1 e0 00 0e 00 06 04 00 00 .t...t.. ........ 265 | 0050 00 00 00 00 10 00 00 00 11 4d 40 77 cd ab ba dc ........ .M@w.... 266 | 0060 aa e0 00 00 c4 f1 12 00 11 4d 40 77 f0 f1 12 00 ........ .M@w.... 267 | 0070 d9 8b 41 7e 00 e0 fd 7f f0 f1 12 00 5a 88 41 7e ..A~.... ....Z.A~ 268 | 0080 aa e0 00 00 2a 88 41 7e 06 04 00 00 11 4d 40 77 ....*.A~ .....M@w 269 | 0090 e8 f3 12 00 14 00 00 00 01 00 00 00 00 00 00 00 ........ ........ 270 | 00a0 aa df c0 75 00 ...u. 271 | */ 272 | 273 | /* CH341a READ EEPROM next packet for 24c16*/ 274 | #define CH341_EEPROM_24c16_READ_NEXT_CMD "\xaa\x74\x82\xa0\x80\x74\x81\xa1\xe0\x00\x00\x00\x10\x00\x00\x00" \ 275 | "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 276 | "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ 277 | "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ 278 | "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\xfa\x00\x31\x00" \ 279 | "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8c\xe8\x67\x00" \ 280 | "\xaa\xdf\xc0\x75\x00" 281 | /*please see file /wiresharkusbsniffing/sniffed.txt 282 | this is the first read next packet for 24c16 (fifteen different packets) 283 | 284 | 0040 aa 74 82 a0 80 74 81 a1 e0 00 00 00 10 00 00 00 .t...t.. ........ 285 | 0050 00 00 00 00 8c f1 12 00 01 00 00 00 00 00 00 00 ........ ........ 286 | 0060 aa e0 00 00 4c f1 12 00 5d 22 d7 5a dc f1 12 00 ....L... ]".Z.... 287 | 0070 8f 04 44 7e 30 88 41 7e ff ff ff ff 2a 88 41 7e ..D~0.A~ ....*.A~ 288 | 0080 aa e0 00 7e 00 00 00 00 69 0e 3c 00 fa 00 31 00 ...~.... i.<...1. 289 | 0090 0f 00 00 00 00 00 00 00 00 00 00 00 8c e8 67 00 ........ ......g. 290 | 00a0 aa df c0 75 00 ...u. 291 | */ 292 | 293 | 294 | /* CH341a READ EEPROM setup packet for the 24c64 295 | this needs putting into a struct to allow convenient access to individual elements*/ 296 | #define CH341_EEPROM_24c64_READ_SETUP_CMD "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x06\x04\x00\x00" \ 297 | "\x00\x00\x00\x00\x40\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 298 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 299 | "\xd9\x8b\x41\x7e\x00\xf0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 300 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 301 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 302 | "\xaa\xdf\xc0\x75\x00" 303 | /* please se file /wiresharkusbsniffing/sniffed.txt 304 | this is the read setup packet for 24c64 305 | 306 | 0040 aa 74 83 a0 00 00 74 81 a1 e0 00 00 06 04 00 00 .t....t. ........ 307 | 0050 00 00 00 00 40 00 00 00 11 4d 40 77 cd ab ba dc ....@... .M@w.... 308 | 0060 aa e0 00 00 c4 f1 12 00 11 4d 40 77 f0 f1 12 00 ........ .M@w.... 309 | 0070 d9 8b 41 7e 00 e0 fd 7f f0 f1 12 00 5a 88 41 7e ..A~.... ....Z.A~ 310 | 0080 aa e0 00 00 2a 88 41 7e 06 04 00 00 11 4d 40 77 ....*.A~ .....M@w 311 | 0090 e8 f3 12 00 14 00 00 00 01 00 00 00 00 00 00 00 ........ ........ 312 | 00a0 aa df c0 75 00 ...u. 313 | */ 314 | 315 | /* CH341a READ EEPROM next packet for 24c64 (63 packets)*/ 316 | #define CH341_EEPROM_24c64_READ_NEXT_CMD "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x10\x00\x00\x00" \ 317 | "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 318 | "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \ 319 | "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \ 320 | "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\x12\x01\x19\x00" \ 321 | "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\x2e\x68\x00" \ 322 | "\xaa\xdf\xc0\x75\x00" 323 | /* please see file /wiresharkusbsniffing/sniffed.txt 324 | this is the first read next packet for 24c64 (63 different packets) 325 | 326 | 0040 aa 74 83 a0 00 80 74 81 a1 e0 00 00 10 00 00 00 .t....t. ........ 327 | 0050 00 00 00 00 8c f1 12 00 01 00 00 00 00 00 00 00 ........ ........ 328 | 0060 aa e0 00 00 4c f1 12 00 5d 22 d7 5a dc f1 12 00 ....L... ]".Z.... 329 | 0070 8f 04 44 7e 30 88 41 7e ff ff ff ff 2a 88 41 7e ..D~0.A~ ....*.A~ 330 | 0080 aa e0 00 7e 00 00 00 00 69 0e 3c 00 3a 01 1b 00 ...~.... i.<.:... 331 | 0090 0f 00 00 00 00 00 00 00 00 00 00 00 8c e8 67 00 ........ ......g. 332 | 00a0 aa df c0 75 00 ...u. 333 | */ 334 | 335 | #define MIN(a,b) (((a)<(b))?(a):(b)) 336 | #define MAX(a,b) (((a)>(b))?(a):(b)) 337 | 338 | #define TRUE 1 339 | #define FALSE 0 340 | 341 | struct EEPROM { 342 | char *name; 343 | uint32_t size; 344 | uint16_t page_size; 345 | uint8_t addr_size; // Length of addres in bytes 346 | uint8_t i2c_addr_mask; 347 | }; 348 | 349 | const static struct EEPROM eepromlist[] = { 350 | { "24c01", 128, 8, 1, 0x00 }, // 16 pages of 8 bytes each = 128 bytes 351 | { "24c02", 256, 8, 1, 0x00 }, // 32 pages of 8 bytes each = 256 bytes 352 | { "24c04", 512, 16, 1, 0x01 }, // 32 pages of 16 bytes each = 512 bytes 353 | { "24c08", 1024, 16, 1, 0x03 }, // 64 pages of 16 bytes each = 1024 bytes 354 | { "24c16", 2048, 16, 1, 0x07 }, // 128 pages of 16 bytes each = 2048 bytes 355 | { "24c32", 4096, 32, 2, 0x00 }, // 32kbit = 4kbyte 356 | { "24c64", 8192, 32, 2, 0x00 }, 357 | { "24c128", 16384, 32/*64*/, 2, 0x00 }, 358 | { "24c256", 32768, 32/*64*/, 2, 0x00 }, 359 | { "24c512", 65536, 32/*128*/, 2, 0x00 }, 360 | { "24c1024", 131072, 32/*128*/, 2, 0x01 }, 361 | { 0, 0, 0, 0 } 362 | }; 363 | 364 | 365 | int32_t ch341readEEPROM(uint8_t *buf, uint32_t bytes, struct EEPROM *eeprom_info); 366 | int32_t ch341writeEEPROM(uint8_t *buf, uint32_t bytes, struct EEPROM *eeprom_info); 367 | int32_t parseEEPsize(char *eepromname, struct EEPROM *eeprom); 368 | 369 | #endif /* __CH341A_I2C_H__ */ 370 | -------------------------------------------------------------------------------- /src/spi_nor_flash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2021 McMCC 3 | * spi_nor_flash.c 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "spi_controller.h" 24 | #include "snorcmd_api.h" 25 | #include "types.h" 26 | #include "timer.h" 27 | 28 | #define min(a,b) (((a)<(b))?(a):(b)) 29 | 30 | /****************************************************************************** 31 | * SPI FLASH elementray definition and function 32 | ******************************************************************************/ 33 | 34 | #define FLASH_PAGESIZE 256 35 | 36 | /* Flash opcodes. */ 37 | #define OPCODE_WREN 6 /* Write enable */ 38 | #define OPCODE_WRDI 4 /* Write disable */ 39 | #define OPCODE_RDSR 5 /* Read status register */ 40 | #define OPCODE_WRSR 1 /* Write status register */ 41 | #define OPCODE_RDSR2 0x35/* Read status register 2*/ 42 | #define OPCODE_WRSR2 0x31/* Write status register 2*/ 43 | #define OPCODE_RDSR3 0x15/* Read status register 3 */ 44 | #define OPCODE_WRSR3 0x11/* Write status register 3*/ 45 | #define OPCODE_READ 3 /* Read data bytes */ 46 | #define OPCODE_PP 2 /* Page program */ 47 | #define OPCODE_SE 0xD8 /* Sector erase */ 48 | #define OPCODE_RES 0xAB /* Read Electronic Signature */ 49 | #define OPCODE_RDID 0x9F /* Read JEDEC ID */ 50 | 51 | #define OPCODE_FAST_READ 0x0B /* Fast Read */ 52 | #define OPCODE_DOR 0x3B /* Dual Output Read */ 53 | #define OPCODE_QOR 0x6B /* Quad Output Read */ 54 | #define OPCODE_DIOR 0xBB /* Dual IO High Performance Read */ 55 | #define OPCODE_QIOR 0xEB /* Quad IO High Performance Read */ 56 | #define OPCODE_READ_ID 0x90 /* Read Manufacturer and Device ID */ 57 | 58 | #define OPCODE_P4E 0x20 /* 4KB Parameter Sectore Erase */ 59 | #define OPCODE_P8E 0x40 /* 8KB Parameter Sectore Erase */ 60 | #define OPCODE_BE 0x60 /* Bulk Erase */ 61 | #define OPCODE_BE1 0xC7 /* Bulk Erase */ 62 | #define OPCODE_QPP 0x32 /* Quad Page Programing */ 63 | 64 | #define OPCODE_CLSR 0x30 65 | #define OPCODE_RCR 0x35 /* Read Configuration Register */ 66 | 67 | #define OPCODE_BRRD 0x16 68 | #define OPCODE_BRWR 0x17 69 | 70 | /* Status Register bits. */ 71 | #define SR_WIP 1 /* Write in progress */ 72 | #define SR_WEL 2 /* Write enable latch */ 73 | #define SR_BP0 4 /* Block protect 0 */ 74 | #define SR_BP1 8 /* Block protect 1 */ 75 | #define SR_BP2 0x10 /* Block protect 2 */ 76 | #define SR_EPE 0x20 /* Erase/Program error */ 77 | #define SR_SRWD 0x80 /* SR write protect */ 78 | 79 | #define SR2_SRP1 1 80 | #define SR2_QE 2 81 | #define SR2_R 4 82 | #define SR2_LB1 8 83 | #define SR2_LB2 0x10 84 | #define SR2_LB3 0x20 85 | #define SR2_CMP 0x40 86 | #define SR2_SUS 0x80 87 | 88 | #define SR3_WPS 4 89 | #define SR3_DRV0 8 90 | #define SR3_DRV1 0x10 91 | #define SR3_HOLD 0x80 92 | 93 | #define snor_dbg(args...) 94 | /* #define snor_dbg(args...) do { if (1) printf(args); } while(0) */ 95 | 96 | #define udelay(x) usleep(x) 97 | 98 | struct chip_info { 99 | char *name; 100 | u8 id; 101 | u32 jedec_id; 102 | unsigned long sector_size; 103 | unsigned int n_sectors; 104 | char addr4b; 105 | }; 106 | 107 | struct chip_info *spi_chip_info; 108 | 109 | static int snor_wait_ready(int sleep_ms); 110 | static int snor_read_sr(u8 opcode, u8 *val); 111 | static int snor_write_sr(u8 opcode, u8 *val); 112 | 113 | extern unsigned int bsize; 114 | 115 | /* 116 | * Set write enable latch with Write Enable command. 117 | * Returns negative if error occurred. 118 | */ 119 | static inline void snor_write_enable(void) 120 | { 121 | SPI_CONTROLLER_Chip_Select_Low(); 122 | SPI_CONTROLLER_Write_One_Byte(OPCODE_WREN); 123 | SPI_CONTROLLER_Chip_Select_High(); 124 | } 125 | 126 | static inline void snor_write_disable(void) 127 | { 128 | SPI_CONTROLLER_Chip_Select_Low(); 129 | SPI_CONTROLLER_Write_One_Byte(OPCODE_WRDI); 130 | SPI_CONTROLLER_Chip_Select_High(); 131 | } 132 | 133 | /* 134 | * Set all sectors (global) unprotected if they are protected. 135 | * Returns negative if error occurred. 136 | */ 137 | int snor_unprotect(void) 138 | { 139 | u8 sr = 0; 140 | 141 | if (snor_read_sr(OPCODE_RDSR, &sr) < 0) { 142 | printf("%s: read_sr fail: %x\n", __func__, sr); 143 | return -1; 144 | } 145 | 146 | if ((sr & (SR_BP0 | SR_BP1 | SR_BP2)) != 0) { 147 | sr = 0; 148 | snor_wait_ready(1); 149 | snor_write_enable(); 150 | snor_write_sr(OPCODE_WRSR, &sr); 151 | } 152 | 153 | if (snor_read_sr(OPCODE_RDSR2, &sr) < 0) { 154 | printf("%s: read_sr2 fail: %x\n", __func__, sr); 155 | return -1; 156 | } 157 | 158 | if ((sr & (SR2_QE | SR2_SRP1 | SR2_CMP))) { 159 | sr = 0; 160 | snor_wait_ready(1); 161 | snor_write_enable(); 162 | snor_write_sr(OPCODE_WRSR2, &sr); 163 | } 164 | 165 | if (snor_read_sr(OPCODE_RDSR3, &sr) < 0) { 166 | printf("%s: read_sr3 fail: %x\n", __func__, sr); 167 | return -1; 168 | } 169 | 170 | if ((sr & SR3_WPS)) { 171 | sr ^= SR3_WPS; 172 | snor_wait_ready(1); 173 | snor_write_enable(); 174 | snor_write_sr(OPCODE_WRSR3, &sr); 175 | } 176 | 177 | return 0; 178 | } 179 | 180 | /* 181 | * Service routine to read status register until ready, or timeout occurs. 182 | * Returns non-zero if error. 183 | */ 184 | static int snor_wait_ready(int sleep_ms) 185 | { 186 | int count; 187 | int sr = 0; 188 | 189 | /* one chip guarantees max 5 msec wait here after page writes, 190 | * but potentially three seconds (!) after page erase. 191 | */ 192 | for (count = 0; count < ((sleep_ms + 1) * 1000); count++) { 193 | if ((snor_read_sr(OPCODE_RDSR,(u8 *)&sr)) < 0) 194 | break; 195 | else if ((sr & (SR_WIP | SR_EPE | SR_WEL)) != sr || !sr) { 196 | return 0; 197 | } 198 | udelay(500); 199 | /* REVISIT sometimes sleeping would be best */ 200 | } 201 | printf("%s: read_sr fail: %x\n", __func__, sr); 202 | return -1; 203 | } 204 | 205 | /* 206 | * read status register 207 | */ 208 | static int snor_read_rg(u8 code, u8 *val) 209 | { 210 | int retval; 211 | 212 | SPI_CONTROLLER_Chip_Select_Low(); 213 | SPI_CONTROLLER_Write_One_Byte(code); 214 | retval = SPI_CONTROLLER_Read_NByte(val, 1, SPI_CONTROLLER_SPEED_SINGLE); 215 | SPI_CONTROLLER_Chip_Select_High(); 216 | if (retval) { 217 | printf("%s: ret: %x\n", __func__, retval); 218 | return -1; 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | /* 225 | * write status register 226 | */ 227 | static int snor_write_rg(u8 code, u8 *val) 228 | { 229 | int retval; 230 | 231 | SPI_CONTROLLER_Chip_Select_Low(); 232 | SPI_CONTROLLER_Write_One_Byte(code); 233 | retval = SPI_CONTROLLER_Write_NByte(val, 1, SPI_CONTROLLER_SPEED_SINGLE); 234 | SPI_CONTROLLER_Chip_Select_High(); 235 | if (retval) { 236 | printf("%s: ret: %x\n", __func__, retval); 237 | return -1; 238 | } 239 | 240 | return 0; 241 | } 242 | 243 | static int snor_4byte_mode(int enable) 244 | { 245 | int retval; 246 | 247 | if (snor_wait_ready(1)) 248 | return -1; 249 | 250 | if (spi_chip_info->id == 0x1) /* Spansion */ 251 | { 252 | u8 br_cfn; 253 | u8 br = enable ? 0x81 : 0; 254 | 255 | snor_write_rg(OPCODE_BRWR, &br); 256 | snor_read_rg(OPCODE_BRRD, &br_cfn); 257 | if (br_cfn != br) { 258 | printf("4B mode switch failed %s, 0x%02x, 0x%02x\n", enable ? "enable" : "disable" , br_cfn, br); 259 | return -1; 260 | } 261 | } else { 262 | u8 code = enable ? 0xb7 : 0xe9; /* B7: enter 4B, E9: exit 4B */ 263 | 264 | SPI_CONTROLLER_Chip_Select_Low(); 265 | retval = SPI_CONTROLLER_Write_One_Byte(code); 266 | SPI_CONTROLLER_Chip_Select_High(); 267 | if (retval) { 268 | printf("%s: ret: %x\n", __func__, retval); 269 | return -1; 270 | } 271 | if ((!enable) && (spi_chip_info->id == 0xef)) /* Winbond */ 272 | { 273 | code = 0; 274 | snor_write_enable(); 275 | snor_write_rg(0xc5, &code); 276 | } 277 | } 278 | return 0; 279 | } 280 | 281 | /* 282 | * Erase one sector of flash memory at offset ``offset'' which is any 283 | * address within the sector which should be erased. 284 | * 285 | * Returns 0 if successful, non-zero otherwise. 286 | */ 287 | static int snor_erase_sector(unsigned long offset) 288 | { 289 | snor_dbg("%s: offset:%x\n", __func__, offset); 290 | 291 | /* Wait until finished previous write command. */ 292 | if (snor_wait_ready(950)) 293 | return -1; 294 | 295 | if (spi_chip_info->addr4b) { 296 | snor_4byte_mode(1); 297 | } 298 | 299 | /* Send write enable, then erase commands. */ 300 | snor_write_enable(); 301 | 302 | SPI_CONTROLLER_Chip_Select_Low(); 303 | 304 | SPI_CONTROLLER_Write_One_Byte(OPCODE_SE); 305 | if (spi_chip_info->addr4b) 306 | SPI_CONTROLLER_Write_One_Byte((offset >> 24) & 0xff); 307 | SPI_CONTROLLER_Write_One_Byte((offset >> 16) & 0xff); 308 | SPI_CONTROLLER_Write_One_Byte((offset >> 8) & 0xff); 309 | SPI_CONTROLLER_Write_One_Byte(offset & 0xff); 310 | 311 | SPI_CONTROLLER_Chip_Select_High(); 312 | 313 | snor_wait_ready(950); 314 | 315 | if (spi_chip_info->addr4b) 316 | snor_4byte_mode(0); 317 | 318 | return 0; 319 | } 320 | 321 | static int full_erase_chip(void) 322 | { 323 | timer_start(); 324 | /* Wait until finished previous write command. */ 325 | if (snor_wait_ready(3)) 326 | return -1; 327 | 328 | /* Send write enable, then erase commands. */ 329 | snor_write_enable(); 330 | snor_unprotect(); 331 | 332 | SPI_CONTROLLER_Chip_Select_Low(); 333 | SPI_CONTROLLER_Write_One_Byte(OPCODE_BE1); 334 | SPI_CONTROLLER_Chip_Select_High(); 335 | 336 | snor_wait_ready(950); 337 | snor_write_disable(); 338 | timer_end(); 339 | 340 | return 0; 341 | } 342 | 343 | static struct chip_info chips_data [] = { 344 | /* REVISIT: fill in JEDEC ids, for parts that have them */ 345 | { "AT25DF321", 0x1f, 0x47000000, 64 * 1024, 64, 0 }, 346 | { "AT26DF161", 0x1f, 0x46000000, 64 * 1024, 32, 0 }, 347 | 348 | { "F25L016", 0x8c, 0x21150000, 64 * 1024, 32, 0 }, //ESMT 349 | { "F25L16QA", 0x8c, 0x41158c41, 64 * 1024, 32, 0 }, 350 | { "F25L032", 0x8c, 0x21160000, 64 * 1024, 64, 0 }, 351 | { "F25L32QA", 0x8c, 0x41168c41, 64 * 1024, 64, 0 }, 352 | { "F25L064", 0x8c, 0x21170000, 64 * 1024, 128, 0 }, 353 | { "F25L64QA", 0x8c, 0x41170000, 64 * 1024, 128, 0 }, 354 | 355 | { "GD25Q16", 0xc8, 0x40150000, 64 * 1024, 32, 0 }, 356 | { "GD25Q32", 0xc8, 0x40160000, 64 * 1024, 64, 0 }, 357 | { "GD25Q64CSIG", 0xc8, 0x4017c840, 64 * 1024, 128, 0 }, 358 | { "GD25Q128CSIG", 0xc8, 0x4018c840, 64 * 1024, 256, 0 }, 359 | { "YC25Q128", 0xd8, 0x4018d840, 64 * 1024, 256, 0 }, 360 | { "GD25Q256CSIG", 0xc8, 0x4019c840, 64 * 1024, 512, 1 }, 361 | 362 | { "MX25L1605D", 0xc2, 0x2015c220, 64 * 1024, 32, 0 }, 363 | { "MX25L3205D", 0xc2, 0x2016c220, 64 * 1024, 64, 0 }, 364 | { "MX25L6405D", 0xc2, 0x2017c220, 64 * 1024, 128, 0 }, 365 | { "MX25L12805D", 0xc2, 0x2018c220, 64 * 1024, 256, 0 }, 366 | { "MX25L25635E", 0xc2, 0x2019c220, 64 * 1024, 512, 1 }, 367 | { "MX25L51245G", 0xc2, 0x201ac220, 64 * 1024, 1024, 1 }, 368 | 369 | { "FL016AIF", 0x01, 0x02140000, 64 * 1024, 32, 0 }, 370 | { "FL064AIF", 0x01, 0x02160000, 64 * 1024, 128, 0 }, 371 | { "S25FL032P", 0x01, 0x02154D00, 64 * 1024, 64, 0 }, 372 | { "S25FL064P", 0x01, 0x02164D00, 64 * 1024, 128, 0 }, 373 | { "S25FL128P", 0x01, 0x20180301, 64 * 1024, 256, 0 }, 374 | { "S25FL129P", 0x01, 0x20184D01, 64 * 1024, 256, 0 }, 375 | { "S25FL256S", 0x01, 0x02194D01, 64 * 1024, 512, 1 }, 376 | { "S25FL116K", 0x01, 0x40150140, 64 * 1024, 32, 0 }, 377 | { "S25FL132K", 0x01, 0x40160140, 64 * 1024, 64, 0 }, 378 | { "S25FL164K", 0x01, 0x40170140, 64 * 1024, 128, 0 }, 379 | 380 | { "EN25F16", 0x1c, 0x31151c31, 64 * 1024, 32, 0 }, 381 | { "EN25Q16", 0x1c, 0x30151c30, 64 * 1024, 32, 0 }, 382 | { "EN25QH16", 0x1c, 0x70151c70, 64 * 1024, 32, 0 }, 383 | { "EN25Q32B", 0x1c, 0x30161c30, 64 * 1024, 64, 0 }, 384 | { "EN25F32", 0x1c, 0x31161c31, 64 * 1024, 64, 0 }, 385 | { "EN25F64", 0x1c, 0x20171c20, 64 * 1024, 128, 0 }, 386 | { "EN25Q64", 0x1c, 0x30171c30, 64 * 1024, 128, 0 }, 387 | { "EN25QA64A", 0x1c, 0x60170000, 64 * 1024, 128, 0 }, 388 | { "EN25QH64A", 0x1c, 0x70171c70, 64 * 1024, 128, 0 }, 389 | { "EN25Q128", 0x1c, 0x30181c30, 64 * 1024, 256, 0 }, 390 | { "EN25QA128A", 0x1c, 0x60180000, 64 * 1024, 256, 0 }, 391 | { "EN25QH128A", 0x1c, 0x70181c70, 64 * 1024, 256, 0 }, 392 | { "GM25Q64A", 0x1c, 0x40171c40, 64 * 1024, 128, 0 }, 393 | { "GM25Q128A", 0x1c, 0x40181c40, 64 * 1024, 256, 0 }, 394 | 395 | { "W25X05", 0xef, 0x30100000, 64 * 1024, 1, 0 }, 396 | { "W25X10", 0xef, 0x30110000, 64 * 1024, 2, 0 }, 397 | { "W25X20", 0xef, 0x30120000, 64 * 1024, 4, 0 }, 398 | { "W25X40", 0xef, 0x30130000, 64 * 1024, 8, 0 }, 399 | { "W25X80", 0xef, 0x30140000, 64 * 1024, 16, 0 }, 400 | { "W25X16", 0xef, 0x30150000, 64 * 1024, 32, 0 }, 401 | { "W25X32VS", 0xef, 0x30160000, 64 * 1024, 64, 0 }, 402 | { "W25X64", 0xef, 0x30170000, 64 * 1024, 128, 0 }, 403 | { "W25Q20CL", 0xef, 0x40120000, 64 * 1024, 4, 0 }, 404 | { "W25Q20BW", 0xef, 0x50120000, 64 * 1024, 4, 0 }, 405 | { "W25Q20EW", 0xef, 0x60120000, 64 * 1024, 4, 0 }, 406 | { "W25Q80", 0xef, 0x50140000, 64 * 1024, 16, 0 }, 407 | { "W25Q80BL", 0xef, 0x40140000, 64 * 1024, 16, 0 }, 408 | { "W25Q16JQ", 0xef, 0x40150000, 64 * 1024, 32, 0 }, 409 | { "W25Q16JM", 0xef, 0x70150000, 64 * 1024, 32, 0 }, 410 | { "W25Q32BV", 0xef, 0x40160000, 64 * 1024, 64, 0 }, 411 | { "W25Q32DW", 0xef, 0x60160000, 64 * 1024, 64, 0 }, 412 | { "W25Q64BV", 0xef, 0x40170000, 64 * 1024, 128, 0 }, 413 | { "W25Q64DW", 0xef, 0x60170000, 64 * 1024, 128, 0 }, 414 | { "W25Q128BV", 0xef, 0x40180000, 64 * 1024, 256, 0 }, 415 | { "W25Q128FW", 0xef, 0x60180000, 64 * 1024, 256, 0 }, 416 | { "W25Q256FV", 0xef, 0x40190000, 64 * 1024, 512, 1 }, 417 | { "W25Q512JV", 0xef, 0x71190000, 64 * 1024, 1024, 1 }, 418 | 419 | { "M25P016", 0x20, 0x20150000, 64 * 1024, 32, 0 }, 420 | { "N25Q032A", 0x20, 0xba161000, 64 * 1024, 64, 0 }, 421 | { "N25Q064A", 0x20, 0xba171000, 64 * 1024, 128, 0 }, 422 | { "M25P128", 0x20, 0x20180000, 64 * 1024, 256, 0 }, 423 | { "N25Q128A", 0x20, 0xba181000, 64 * 1024, 256, 0 }, 424 | { "XM25QH32B", 0x20, 0x40160000, 64 * 1024, 64, 0 }, 425 | { "XM25QH32A", 0x20, 0x70160000, 64 * 1024, 64, 0 }, 426 | { "XM25QH64A", 0x20, 0x70170000, 64 * 1024, 128, 0 }, 427 | { "XM25QH128A", 0x20, 0x70182070, 64 * 1024, 256, 0 }, 428 | { "XM25QH64C", 0x20, 0x40170000, 64 * 1024, 128, 0 }, 429 | { "XM25QH128C", 0x20, 0x40182070, 64 * 1024, 256, 0 }, 430 | { "N25Q256A", 0x20, 0xba191000, 64 * 1024, 512, 1 }, 431 | { "MT25QL512AB", 0x20, 0xba201044, 64 * 1024, 1024, 1 }, 432 | 433 | { "ZB25VQ16", 0x5e, 0x40150000, 64 * 1024, 32, 0 }, 434 | { "ZB25VQ32", 0x5e, 0x40160000, 64 * 1024, 64, 0 }, 435 | { "ZB25VQ64", 0x5e, 0x40170000, 64 * 1024, 128, 0 }, 436 | { "ZB25VQ128", 0x5e, 0x40180000, 64 * 1024, 256, 0 }, 437 | 438 | { "BY25Q16BS", 0x68, 0x40150000, 64 * 1024, 32, 0 }, 439 | { "BY25Q32BS", 0x68, 0x40160000, 64 * 1024, 64, 0 }, 440 | { "BY25Q64AS", 0x68, 0x40170000, 64 * 1024, 128, 0 }, 441 | { "BY25Q128AS", 0x68, 0x40180000, 64 * 1024, 256, 0 }, 442 | 443 | { "XT25F32B", 0x0b, 0x40150000, 64 * 1024, 32, 0 }, 444 | { "XT25F32B", 0x0b, 0x40160000, 64 * 1024, 64, 0 }, 445 | { "XT25F64B", 0x0b, 0x40170000, 64 * 1024, 128, 0 }, 446 | { "XT25F128B", 0x0b, 0x40180000, 64 * 1024, 256, 0 }, 447 | 448 | { "PM25LQ016", 0x7f, 0x9d450000, 64 * 1024, 32, 0 }, 449 | { "PM25LQ032", 0x7f, 0x9d460000, 64 * 1024, 64, 0 }, 450 | { "PM25LQ064", 0x7f, 0x9d470000, 64 * 1024, 128, 0 }, 451 | { "PM25LQ128", 0x7f, 0x9d480000, 64 * 1024, 256, 0 }, 452 | 453 | { "IC25LP016", 0x9d, 0x60150000, 64 * 1024, 32, 0 }, 454 | { "IC25LP032", 0x9d, 0x60160000, 64 * 1024, 64, 0 }, 455 | { "IC25LP064", 0x9d, 0x60170000, 64 * 1024, 128, 0 }, 456 | { "IC25LP128", 0x9d, 0x60180000, 64 * 1024, 256, 0 }, 457 | 458 | { "FM25Q16", 0xa1, 0x40150000, 64 * 1024, 32, 0 }, 459 | { "FM25Q32", 0xa1, 0x40160000, 64 * 1024, 64, 0 }, 460 | { "FM25Q64", 0xa1, 0x40170000, 64 * 1024, 128, 0 }, 461 | { "FM25Q128", 0xa1, 0x40180000, 64 * 1024, 256, 0 }, 462 | { "FM25W16", 0xa1, 0x28150000, 64 * 1024, 32, 0 }, 463 | { "FM25W32", 0xa1, 0x28160000, 64 * 1024, 64, 0 }, 464 | { "FM25W64", 0xa1, 0x28170000, 64 * 1024, 128, 0 }, 465 | { "FM25W128", 0xa1, 0x28180000, 64 * 1024, 256, 0 }, 466 | 467 | { "FM25Q16A", 0xf8, 0x32150000, 64 * 1024, 32, 0 }, 468 | { "FM25Q32A", 0xf8, 0x32160000, 64 * 1024, 64, 0 }, 469 | { "FM25Q64A", 0xf8, 0x32170000, 64 * 1024, 128, 0 }, 470 | { "FM25Q128A", 0xf8, 0x32180000, 64 * 1024, 256, 0 }, 471 | 472 | { "PN25F16", 0xe0, 0x40150000, 64 * 1024, 32, 0 }, 473 | { "PN25F32", 0xe0, 0x40160000, 64 * 1024, 64, 0 }, 474 | { "PN25F64", 0xe0, 0x40170000, 64 * 1024, 128, 0 }, 475 | { "PN25F128", 0xe0, 0x40180000, 64 * 1024, 256, 0 }, 476 | 477 | { "P25Q16H", 0x85, 0x60150000, 64 * 1024, 32, 0 }, 478 | { "P25Q32H", 0x85, 0x60160000, 64 * 1024, 64, 0 }, 479 | { "P25Q64H", 0x85, 0x60170000, 64 * 1024, 128, 0 }, 480 | { "P25Q128H", 0x85, 0x60180000, 64 * 1024, 256, 0 }, 481 | { "PY25Q128HA", 0x85, 0x20180000, 64 * 1024, 256, 0 }, 482 | 483 | { "SK25P32", 0x25, 0x60162560, 64 * 1024, 64, 0 }, 484 | { "SK25P64", 0x25, 0x60172560, 64 * 1024, 128, 0 }, 485 | { "SK25P128", 0x25, 0x60182560, 64 * 1024, 256, 0 }, 486 | 487 | { "NM25Q64E", 0x52, 0x22170000, 64 * 1024, 128, 0 }, 488 | { "NM25Q128E", 0x52, 0x21180000, 64 * 1024, 256, 0 }, 489 | { "NM25L256F", 0x52, 0x10190000, 64 * 1024, 512, 1 }, 490 | 491 | }; 492 | 493 | /* 494 | * read SPI flash device ID 495 | */ 496 | static int snor_read_devid(u8 *rxbuf, int n_rx) 497 | { 498 | int retval = 0; 499 | 500 | SPI_CONTROLLER_Chip_Select_Low(); 501 | SPI_CONTROLLER_Write_One_Byte(OPCODE_RDID); 502 | 503 | retval = SPI_CONTROLLER_Read_NByte(rxbuf, n_rx, SPI_CONTROLLER_SPEED_SINGLE); 504 | SPI_CONTROLLER_Chip_Select_High(); 505 | if (retval) { 506 | printf("%s: ret: %x\n", __func__, retval); 507 | return retval; 508 | } 509 | 510 | return 0; 511 | } 512 | 513 | /* 514 | * read status register 515 | */ 516 | static int snor_read_sr(u8 opcode, u8 *val) 517 | { 518 | int retval = 0; 519 | 520 | SPI_CONTROLLER_Chip_Select_Low(); 521 | SPI_CONTROLLER_Write_One_Byte(opcode); 522 | 523 | retval = SPI_CONTROLLER_Read_NByte(val, 1, SPI_CONTROLLER_SPEED_SINGLE); 524 | SPI_CONTROLLER_Chip_Select_High(); 525 | if (retval) { 526 | printf("%s: ret: %x\n", __func__, retval); 527 | return retval; 528 | } 529 | 530 | return 0; 531 | } 532 | 533 | /* 534 | * write status register 535 | */ 536 | static int snor_write_sr(u8 opcode, u8 *val) 537 | { 538 | int retval = 0; 539 | 540 | SPI_CONTROLLER_Chip_Select_Low(); 541 | SPI_CONTROLLER_Write_One_Byte(opcode); 542 | 543 | retval = SPI_CONTROLLER_Write_NByte(val, 1, SPI_CONTROLLER_SPEED_SINGLE); 544 | SPI_CONTROLLER_Chip_Select_High(); 545 | if (retval) { 546 | printf("%s: ret: %x\n", __func__, retval); 547 | return retval; 548 | } 549 | return 0; 550 | } 551 | 552 | struct chip_info *chip_prob(void) 553 | { 554 | struct chip_info *info = NULL, *match = NULL; 555 | u8 buf[5]; 556 | u32 jedec, jedec_strip, weight; 557 | int i; 558 | 559 | snor_read_devid(buf, 5); 560 | jedec = (u32)((u32)(buf[1] << 24) | ((u32)buf[2] << 16) | ((u32)buf[3] <<8) | (u32)buf[4]); 561 | jedec_strip = jedec & 0xffff0000; 562 | 563 | printf("spi device id: %x %x %x %x %x (%x)\n", buf[0], buf[1], buf[2], buf[3], buf[4], jedec); 564 | 565 | // FIXME, assign default as AT25D 566 | weight = 0xffffffff; 567 | match = &chips_data[0]; 568 | for (i = 0; i < sizeof(chips_data)/sizeof(chips_data[0]); i++) { 569 | info = &chips_data[i]; 570 | if (info->id == buf[0]) { 571 | if ((info->jedec_id == jedec) || ((info->jedec_id & 0xffff0000) == jedec_strip)) { 572 | u8 sr,sr2,sr3=0; 573 | snor_read_sr(OPCODE_RDSR, &sr); 574 | snor_read_sr(OPCODE_RDSR2, &sr2); 575 | snor_read_sr(OPCODE_RDSR3, &sr3); 576 | 577 | printf("Detected SPI NOR Flash: %s, Flash Size: %ld MB, SR: [%02x] [%02x] [%02x]\n", info->name, (info->sector_size * info->n_sectors) >> 20, sr, sr2, sr3); 578 | return info; 579 | } 580 | 581 | if (weight > (info->jedec_id ^ jedec)) { 582 | weight = info->jedec_id ^ jedec; 583 | match = info; 584 | } 585 | } 586 | } 587 | printf("SPI NOR Flash Not Detected!\n"); 588 | match = NULL; /* Not support JEDEC calculate info */ 589 | 590 | return match; 591 | } 592 | 593 | long snor_init(void) 594 | { 595 | spi_chip_info = chip_prob(); 596 | 597 | if(spi_chip_info == NULL) 598 | return -1; 599 | 600 | bsize = spi_chip_info->sector_size; 601 | 602 | return spi_chip_info->sector_size * spi_chip_info->n_sectors; 603 | } 604 | 605 | int snor_erase(unsigned long offs, unsigned long len) 606 | { 607 | unsigned long plen = len; 608 | snor_dbg("%s: offs:%x len:%x\n", __func__, offs, len); 609 | 610 | /* sanity checks */ 611 | if (len == 0) 612 | return -1; 613 | 614 | if(!offs && len == (spi_chip_info->sector_size * spi_chip_info->n_sectors)) 615 | { 616 | printf("Please Wait......\n"); 617 | return full_erase_chip(); 618 | } 619 | 620 | timer_start(); 621 | 622 | snor_unprotect(); 623 | 624 | /* now erase those sectors */ 625 | while (len > 0) { 626 | if (snor_erase_sector(offs)) { 627 | return -1; 628 | } 629 | 630 | offs += spi_chip_info->sector_size; 631 | len -= spi_chip_info->sector_size; 632 | if( timer_progress() ) 633 | { 634 | printf("\bErase %ld%% [%lu] of [%lu] bytes ", 100 * (plen - len) / plen, plen - len, plen); 635 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 636 | fflush(stdout); 637 | } 638 | } 639 | printf("Erase 100%% [%lu] of [%lu] bytes \n", plen - len, plen); 640 | timer_end(); 641 | 642 | return 0; 643 | } 644 | 645 | int snor_read(unsigned char *buf, unsigned long from, unsigned long len) 646 | { 647 | u32 read_addr, physical_read_addr, remain_len, data_offset; 648 | 649 | snor_dbg("%s: from:%x len:%x \n", __func__, from, len); 650 | 651 | /* sanity checks */ 652 | if (len == 0) 653 | return 0; 654 | 655 | timer_start(); 656 | /* Wait till previous write/erase is done. */ 657 | if (snor_wait_ready(1)) { 658 | /* REVISIT status return?? */ 659 | return -1; 660 | } 661 | 662 | read_addr = from; 663 | remain_len = len; 664 | 665 | while(remain_len > 0) { 666 | 667 | physical_read_addr = read_addr; 668 | data_offset = (physical_read_addr % (spi_chip_info->sector_size)); 669 | 670 | if (spi_chip_info->addr4b) 671 | snor_4byte_mode(1); 672 | 673 | SPI_CONTROLLER_Chip_Select_Low(); 674 | 675 | /* Set up the write data buffer. */ 676 | SPI_CONTROLLER_Write_One_Byte(OPCODE_READ); 677 | 678 | if (spi_chip_info->addr4b) 679 | SPI_CONTROLLER_Write_One_Byte((physical_read_addr >> 24) & 0xff); 680 | SPI_CONTROLLER_Write_One_Byte((physical_read_addr >> 16) & 0xff); 681 | SPI_CONTROLLER_Write_One_Byte((physical_read_addr >> 8) & 0xff); 682 | SPI_CONTROLLER_Write_One_Byte(physical_read_addr & 0xff); 683 | 684 | if( (data_offset + remain_len) < spi_chip_info->sector_size ) 685 | { 686 | if(SPI_CONTROLLER_Read_NByte(&buf[len - remain_len], remain_len, SPI_CONTROLLER_SPEED_SINGLE)) { 687 | SPI_CONTROLLER_Chip_Select_High(); 688 | if (spi_chip_info->addr4b) 689 | snor_4byte_mode(0); 690 | len = -1; 691 | break; 692 | } 693 | remain_len = 0; 694 | } else { 695 | if(SPI_CONTROLLER_Read_NByte(&buf[len - remain_len], spi_chip_info->sector_size - data_offset, SPI_CONTROLLER_SPEED_SINGLE)) { 696 | SPI_CONTROLLER_Chip_Select_High(); 697 | if (spi_chip_info->addr4b) 698 | snor_4byte_mode(0); 699 | len = -1; 700 | break; 701 | } 702 | remain_len -= spi_chip_info->sector_size - data_offset; 703 | read_addr += spi_chip_info->sector_size - data_offset; 704 | if( timer_progress() ) { 705 | printf("\bRead %ld%% [%lu] of [%lu] bytes ", 100 * (len - remain_len) / len, len - remain_len, len); 706 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 707 | fflush(stdout); 708 | } 709 | } 710 | 711 | SPI_CONTROLLER_Chip_Select_High(); 712 | 713 | if (spi_chip_info->addr4b) 714 | snor_4byte_mode(0); 715 | } 716 | printf("Read 100%% [%lu] of [%lu] bytes \n", len - remain_len, len); 717 | timer_end(); 718 | 719 | return len; 720 | } 721 | 722 | int snor_write(unsigned char *buf, unsigned long to, unsigned long len) 723 | { 724 | u32 page_offset, page_size; 725 | int rc = 0, retlen = 0; 726 | unsigned long plen = len; 727 | 728 | snor_dbg("%s: to:%x len:%x \n", __func__, to, len); 729 | 730 | /* sanity checks */ 731 | if (len == 0) 732 | return 0; 733 | 734 | if (to + len > spi_chip_info->sector_size * spi_chip_info->n_sectors) 735 | return -1; 736 | 737 | timer_start(); 738 | /* Wait until finished previous write command. */ 739 | if (snor_wait_ready(2)) { 740 | return -1; 741 | } 742 | 743 | 744 | /* what page do we start with? */ 745 | page_offset = to % FLASH_PAGESIZE; 746 | 747 | if (spi_chip_info->addr4b) 748 | snor_4byte_mode(1); 749 | 750 | /* write everything in PAGESIZE chunks */ 751 | while (len > 0) { 752 | page_size = min(len, FLASH_PAGESIZE - page_offset); 753 | page_offset = 0; 754 | /* write the next page to flash */ 755 | 756 | snor_wait_ready(3); 757 | snor_write_enable(); 758 | snor_unprotect(); 759 | 760 | SPI_CONTROLLER_Chip_Select_Low(); 761 | /* Set up the opcode in the write buffer. */ 762 | SPI_CONTROLLER_Write_One_Byte(OPCODE_PP); 763 | 764 | if (spi_chip_info->addr4b) 765 | SPI_CONTROLLER_Write_One_Byte((to >> 24) & 0xff); 766 | SPI_CONTROLLER_Write_One_Byte((to >> 16) & 0xff); 767 | SPI_CONTROLLER_Write_One_Byte((to >> 8) & 0xff); 768 | SPI_CONTROLLER_Write_One_Byte(to & 0xff); 769 | 770 | if(!SPI_CONTROLLER_Write_NByte(buf, page_size, SPI_CONTROLLER_SPEED_SINGLE)) 771 | rc = page_size; 772 | else 773 | rc = 1; 774 | 775 | SPI_CONTROLLER_Chip_Select_High(); 776 | 777 | snor_dbg("%s: to:%x page_size:%x ret:%x\n", __func__, to, page_size, rc); 778 | 779 | if( timer_progress() ) { 780 | printf("\bWritten %ld%% [%lu] of [%lu] bytes ", 100 * (plen - len) / plen, plen - len, plen); 781 | printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); 782 | fflush(stdout); 783 | } 784 | 785 | if (rc > 0) { 786 | retlen += rc; 787 | if (rc < page_size) { 788 | printf("%s: rc:%x page_size:%x\n", 789 | __func__, rc, page_size); 790 | snor_write_disable(); 791 | return retlen - rc; 792 | } 793 | } 794 | 795 | len -= page_size; 796 | to += page_size; 797 | buf += page_size; 798 | } 799 | 800 | if (spi_chip_info->addr4b) 801 | snor_4byte_mode(0); 802 | 803 | snor_write_disable(); 804 | 805 | printf("Written 100%% [%ld] of [%ld] bytes \n", plen - len, plen); 806 | timer_end(); 807 | 808 | return retlen; 809 | } 810 | 811 | void support_snor_list(void) 812 | { 813 | int i; 814 | 815 | printf("SPI NOR Flash Support List:\n"); 816 | for ( i = 0; i < (sizeof(chips_data)/sizeof(struct chip_info)); i++) 817 | { 818 | printf("%03d. %s\n", i + 1, chips_data[i].name); 819 | } 820 | } 821 | /* End of [spi_nor_flash.c] package */ 822 | --------------------------------------------------------------------------------