├── .gitignore ├── .travis.yml ├── README.md ├── SCR-imx-uuc.txt ├── Makefile ├── SBOM.spdx.json ├── linuxrc ├── sdimage.c ├── ufb.c ├── LICENSE └── uu.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | sdimage 3 | ufb 4 | uuc 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | script: 4 | - make 5 | 6 | compiler: 7 | - clang 8 | - gcc 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imx-uuc 2 | 3 | A Daemon wait for mfgtools host's command 4 | 5 | linuxrc: boot script to launch uuc deamon. 6 | -------------------------------------------------------------------------------- /SCR-imx-uuc.txt: -------------------------------------------------------------------------------- 1 | Package: imx-uuc.git 2 | Outgoing License: GPL-2.0 3 | License File: COPYING 4 | Type of Content: source 5 | Description and comments: A daemon for mfgtools. 6 | Release Location: https://github.com/NXPmicro/imx-uuc -b master 7 | Origin: NXP (GPL-2.0) 8 | 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= $(CROSS_COMPILE)gcc 2 | BINDIR ?= /usr/bin 3 | PROGRAMS = sdimage ufb 4 | LIBS ?= -lpthread 5 | 6 | all: $(PROGRAMS) 7 | 8 | uuc: uu.c 9 | $(CC) $(CFLAGS) $(CPPFLAGS) uu.c -o uuc $(LDFLAGS) $(LIBS) 10 | 11 | sdimage: sdimage.c 12 | $(CC) $(CFLAGS) $(CPPFLAGS) sdimage.c -o sdimage $(LDFLAGS) 13 | 14 | ufb: ufb.c 15 | $(CC) $(CFLAGS) $(CPPFLAGS) ufb.c -o ufb $(LDFLAGS) $(LIBS) 16 | 17 | install: 18 | install -d $(DESTDIR)$(BINDIR) 19 | install -m 755 linuxrc $(DESTDIR) 20 | install -m 755 $(PROGRAMS) $(DESTDIR)$(BINDIR) 21 | dd if=/dev/zero of=$(DESTDIR)/fat bs=1M count=1 22 | mkfs.vfat $(DESTDIR)/fat 23 | 24 | clean: 25 | rm -f $(PROGRAMS) 26 | -------------------------------------------------------------------------------- /SBOM.spdx.json: -------------------------------------------------------------------------------- 1 | { 2 | "SPDXID": "SPDXRef-DOCUMENT", 3 | "spdxVersion": "SPDX-2.3", 4 | "creationInfo": { 5 | "created": "2025-08-24T08:12:29Z", 6 | "creators": [ 7 | "Organization: NXP" 8 | ], 9 | "licenseListVersion": "3.20" 10 | }, 11 | "name": "imx-uuc-LF_6.12.34_2.1.0", 12 | "dataLicense": "CC0-1.0", 13 | "documentNamespace": "https://nxp.com/spdx/f0a83dcc-b7f1-450e-a3fe-2ef4fc0a38f6", 14 | "packages": [ 15 | { 16 | "SPDXID": "SPDXRef-package-f0a83dcc-b7f1-450e-a3fe-2ef4fc0a38f6", 17 | "name": "imx-uuc", 18 | "versionInfo": "LF_6.12.34_2.1.0", 19 | "licenseConcluded": "(GPL-2.0-only)", 20 | "licenseDeclared": "(GPL-2.0-only)", 21 | "downloadLocation": "https://github.com/nxp-imx/imx-uuc -b master", 22 | "originator": "Organization: NXP", 23 | "supplier": "NOASSERTION", 24 | "externalRefs": [], 25 | "filesAnalyzed": false 26 | } 27 | ], 28 | "relationships": [ 29 | { 30 | "spdxElementId": "SPDXRef-DOCUMENT", 31 | "relationshipType": "DESCRIBES", 32 | "relatedSpdxElement": "SPDXRef-package-f0a83dcc-b7f1-450e-a3fe-2ef4fc0a38f6" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /linuxrc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export PATH=/sbin:/bin:/usr/sbin:/usr/bin 3 | 4 | mount -t sysfs none /sys 5 | mount -t proc none /proc 6 | mount -t devtmpfs none /dev 7 | mount -t configfs none /sys/kernel/config 8 | 9 | cmdline=`cat /proc/cmdline` 10 | 11 | # disable turn off display 12 | echo -e "\033[9;0]" > /dev/tty0 13 | 14 | 15 | last=0; 16 | 17 | UDC_DIR=/sys/class/udc 18 | 19 | function launch_dhcpd() { 20 | 21 | touch -f /var/lib/misc/udhcpd.leases 22 | echo start 168.1.1.50 > /conf 23 | echo end 168.1.1.253 >> /conf 24 | echo interface usb$1 >>/conf 25 | 26 | ifconfig usb$1 168.1.1.1 27 | udhcpd /conf 28 | 29 | } 30 | 31 | function contains() { 32 | for i in "${files[@]}" 33 | do 34 | if [ "$i" == "$1" ]; then 35 | return 1; 36 | fi 37 | done 38 | return 0 39 | } 40 | 41 | function launch_uuc() { 42 | echo $1 $2 43 | mkdir /sys/kernel/config/usb_gadget/$1 44 | cd /sys/kernel/config/usb_gadget/$1 45 | echo 0x066F > idVendor 46 | 47 | if [[ ${cmdline} == *nfsroot* ]]; then 48 | echo 0x9CFF > idProduct 49 | else 50 | echo 0x9BFF > idProduct 51 | fi 52 | 53 | mkdir strings/0x409 54 | 55 | if [ -e /sys/devices/soc0/soc_uid ]; then 56 | cat /sys/devices/soc0/soc_uid > strings/0x409/serialnumber 57 | else 58 | echo 0000000000000000 > strings/0x409/serialnumber 59 | fi 60 | 61 | echo "FSL i.MX Board" > strings/0x409/product 62 | mkdir configs/c.1 63 | echo 5 > configs/c.1/MaxPower 64 | 65 | echo ffs.utp$2 66 | 67 | echo 1 > os_desc/use 68 | echo "MSFT100" > os_desc/qw_sign 69 | echo 0x40 > os_desc/b_vendor_code 70 | 71 | if [[ ${cmdline} == *nfsroot* ]]; then 72 | mkdir functions/ncm.1 73 | ln -s functions/ncm.1 configs/c.1/ 74 | # mkdir functions/acm.1 75 | # ln -s functions/acm.1 configs/c.1/ 76 | echo $1 > UDC 77 | 78 | echo "Start config network" 79 | 80 | ifconfig usb$2 up 81 | while [ ! -e /find_one ] 82 | do 83 | if [[ `ifconfig usb$2` == *inet6* ]]; then 84 | break; 85 | fi 86 | sleep 1 87 | done 88 | 89 | if [ -e /find_one ]; then 90 | exit 0 91 | fi 92 | 93 | touch /find_one 94 | 95 | launch_dhcpd $2 96 | 97 | remote="" 98 | while [[ "$remote" == "" ]]; 99 | do 100 | sleep 2 101 | echo retry get remote ipaddress 102 | ping6 -c2 -I usb$2 ff02::1 103 | 104 | remote=`ip -6 neighbor show dev usb$2` 105 | remote=(${remote}) 106 | remote=${remote[0]} 107 | done 108 | 109 | nfs=${cmdline#*nfsroot=} 110 | nfs=($nfs) 111 | nfs=${nfs[0]} 112 | 113 | nfs=${nfs##*:} 114 | 115 | echo ${remote} ${nfs} 116 | 117 | mount -t nfs [${remote}%usb$2]:${nfsroot}/${nfs} /mnt/ 118 | 119 | cd / 120 | 121 | touch /exit_scan 122 | 123 | else 124 | mkdir functions/ffs.utp$2 125 | mkdir /dev/usb-utp$2 126 | mount -t functionfs utp$2 /dev/usb-utp$2 127 | ln -s functions/ffs.utp$2 configs/c.1/ 128 | ln -s configs/c.1 os_desc 129 | 130 | ufb /dev/usb-utp$2/ep0 & 131 | 132 | echo run utp at /dev/usb-utp$2/ep0; 133 | while [ ! -e /dev/usb-utp$2/ep1 ] 134 | do 135 | echo "." 136 | sleep 1; 137 | done 138 | 139 | echo $1 > UDC 140 | 141 | fi 142 | 143 | return 0; 144 | 145 | } 146 | 147 | function launch_crrm() { 148 | if [[ ${cmdline} == *crrm_server* ]]; then 149 | server=${cmdline#*crrm_server=} 150 | server=($server) 151 | server=${server[0]} 152 | 153 | udhcpc -i eth1 154 | echo "Start tftp download" 155 | tftp -g -r flash.bin ${server} 156 | 157 | if [ -e /flash.bin ]; then 158 | 159 | mkdir /mnt/emmc_fat 160 | mount -t vfat /dev/mmcblk0p1 /mnt/emmc_fat 161 | 162 | mv /flash.bin /mnt/emmc_fat/flash_install.bin 163 | sync 164 | umount /mnt/emmc_fat 165 | 166 | echo "CRRM download finished" 167 | ele_crrm_test -r 168 | else 169 | echo "Fail to download flash.bin" 170 | fi 171 | 172 | echo "Exit to shell" 173 | /bin/sh 174 | fi 175 | 176 | return 0 177 | } 178 | 179 | launch_crrm 180 | 181 | while true; do 182 | if test "$(ls -A "$UDC_DIR")"; then 183 | cd $UDC_DIR 184 | for entry in * 185 | do 186 | if contains $entry; then 187 | files[$last]=$entry; 188 | id=$last; 189 | last=`expr $last + 1`; 190 | echo "Found New UDC: $entry"; 191 | launch_uuc $entry $id & 192 | fi 193 | 194 | done 195 | sleep 1 196 | else 197 | echo "No udc Available!" 198 | sleep 5 199 | fi 200 | 201 | if [ -e /exit_scan ]; then 202 | echo find one active ncm 203 | exec switch_root /mnt /sbin/init 204 | exit 1; 205 | fi 206 | 207 | done 208 | 209 | echo bye 210 | -------------------------------------------------------------------------------- /sdimage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. 3 | * Copyright (C) 2018 Michael Heimpold 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 along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #define max(a,b) \ 35 | ({ __typeof__ (a) _a = (a); \ 36 | __typeof__ (b) _b = (b); \ 37 | _a > _b ? _a : _b; }) 38 | 39 | #ifndef ROUND_UP 40 | #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) 41 | #endif 42 | 43 | #define __stringify_1(x...) #x 44 | #define __stringify(x...) __stringify_1(x) 45 | 46 | /* size of a sector in bytes */ 47 | #define SECTOR_SIZE 512 48 | 49 | /* calculate the count of required sectors for given byte size */ 50 | #define SECTOR_COUNT(x) (((x) + SECTOR_SIZE - 1) / SECTOR_SIZE) 51 | 52 | /* The MX23 Boot ROM does blindly load from 2048 offset while the MX28 53 | * does parse the BCB header to known where to load the image from. 54 | * We start the image at 4 sectors offset so same code can be used by 55 | * both SoCs avoiding code duplication. 56 | */ 57 | #define IMAGE_OFFSET 4 58 | 59 | /* The first image always starts at IMAGE_OFFSET defined above because of MX23 60 | * limitation. Second firmware image is placed after the first one, but it is 61 | * aligned to keep some free space after the first firmware image. 62 | * Reason for this is that we want to write the second image first with 63 | * leaving the first image intact and then write the first image. 64 | * This should guard at least a little bit against problems due to lost power 65 | * during writing one of the images. 66 | * Two cases needs to be considered: 67 | * a) The new firmware image is larger than the images which are already written. 68 | * This is the good case because we would reserve more space for the first 69 | * image and start writing after the start of the existing second one. 70 | * b) The new firmware image is smaller than the existing/installed images. 71 | * In this case we would begin overwriting the tail of the first image and 72 | * thus rendering it unusable. So aligning the second image is a compromise 73 | * on using minimal space vs. allow minor firmware image changes. 74 | * It's user's task to estimate the firmware size variations and to 75 | * tell us the desired alignment, otherwise the following default 76 | * value (in kB) will be used. 77 | */ 78 | #define DEFAULT_IMAGE_ALIGNMENT 64 79 | 80 | /* Partition Table Entry */ 81 | struct pte { 82 | uint8_t active; 83 | uint8_t chs_start[3]; 84 | uint8_t type; 85 | uint8_t chs_end[3]; 86 | uint32_t start; 87 | uint32_t count; 88 | } __attribute__ ((packed)); 89 | 90 | #define MBR_SIGNATURE 0xAA55 91 | 92 | /* Master Boot Record */ 93 | struct mbr { 94 | char bootstrap_code[446]; 95 | struct pte partition[4]; 96 | uint16_t signature; 97 | } __attribute__ ((packed)); 98 | 99 | /* Drive Info Data Structure */ 100 | struct drive_info { /* Comments from i.MX28 RM: */ 101 | uint32_t chip_num; /* chip select, ROM does not use it */ 102 | uint32_t drive_type; /* always system drive, ROM does not use it */ 103 | uint32_t tag; /* drive tag */ 104 | uint32_t first_sector_number; /* start sector/block address of firmware */ 105 | uint32_t sector_count; /* not used by ROM */ 106 | } __attribute__ ((packed)); 107 | 108 | /* (maximum) elements in following drive info array 109 | * It's a design decision that this tool only supports two elements (at the moment) 110 | */ 111 | #define MAX_DI_COUNT 2 112 | 113 | #define BCB_SIGNATURE 0x00112233 114 | 115 | /* Boot Control Block (BCB) Data Structure */ 116 | struct bcb { /* (Analogous) Comments from i.MX28 RM: */ 117 | uint32_t signature; /* signature 0x00112233 */ 118 | uint32_t primary_boot_tag; /* primary boot drive identified by this tag */ 119 | uint32_t secondary_boot_tag; /* secondary boot drive identified by this tag */ 120 | uint32_t num_copies; /* num elements in drive_info array */ 121 | struct drive_info drive_info[MAX_DI_COUNT]; 122 | /* let drive_info array be last in this data 123 | * structure to be able to add more drives in 124 | * future without changing ROM code */ 125 | } __attribute__ ((packed)); 126 | 127 | /* convert the fields of struct mbr to host byte order */ 128 | void mbr_to_host(struct mbr *mbr) 129 | { 130 | int i; 131 | 132 | mbr->signature = le16toh(mbr->signature); 133 | 134 | for (i = 0; i < 4; i++) { 135 | mbr->partition[i].start = le32toh(mbr->partition[i].start); 136 | mbr->partition[i].count = le32toh(mbr->partition[i].count); 137 | } 138 | } 139 | 140 | /* convert the fields of struct bcb to host byte order */ 141 | void bcb_to_host(struct bcb *bcb) 142 | { 143 | int i; 144 | 145 | bcb->signature = le32toh(bcb->signature); 146 | bcb->primary_boot_tag = le32toh(bcb->primary_boot_tag); 147 | bcb->secondary_boot_tag = le32toh(bcb->secondary_boot_tag); 148 | bcb->num_copies = le32toh(bcb->num_copies); 149 | 150 | /* meanwhile we have num_copies in host byte order, so we can use it */ 151 | for (i = 0; i < bcb->num_copies; i++) { 152 | bcb->drive_info[i].chip_num = le32toh(bcb->drive_info[i].chip_num); 153 | bcb->drive_info[i].drive_type = le32toh(bcb->drive_info[i].drive_type); 154 | bcb->drive_info[i].tag = le32toh(bcb->drive_info[i].tag); 155 | bcb->drive_info[i].first_sector_number = le32toh(bcb->drive_info[i].first_sector_number); 156 | bcb->drive_info[i].sector_count = le32toh(bcb->drive_info[i].sector_count); 157 | } 158 | } 159 | 160 | /* convert the fields of struct bcb to disk byte order (little endian) */ 161 | void bcb_to_disk(struct bcb *bcb) 162 | { 163 | int i; 164 | 165 | /* convert the array items first because we need num_copies in host byte order */ 166 | for (i = 0; i < bcb->num_copies; i++) { 167 | bcb->drive_info[i].chip_num = htole32(bcb->drive_info[i].chip_num); 168 | bcb->drive_info[i].drive_type = htole32(bcb->drive_info[i].drive_type); 169 | bcb->drive_info[i].tag = htole32(bcb->drive_info[i].tag); 170 | bcb->drive_info[i].first_sector_number = htole32(bcb->drive_info[i].first_sector_number); 171 | bcb->drive_info[i].sector_count = htole32(bcb->drive_info[i].sector_count); 172 | } 173 | 174 | bcb->signature = htole32(bcb->signature); 175 | bcb->primary_boot_tag = htole32(bcb->primary_boot_tag); 176 | bcb->secondary_boot_tag = htole32(bcb->secondary_boot_tag); 177 | bcb->num_copies = htole32(bcb->num_copies); 178 | } 179 | 180 | #define DEFAULT_DEVICE "/dev/mmcblk0" 181 | 182 | /* command line options */ 183 | const struct option long_options[] = { 184 | { "alignment", required_argument, 0, 'a' }, 185 | { "device", required_argument, 0, 'd' }, 186 | { "firmware", required_argument, 0, 'f' }, 187 | { "verbose", no_argument, 0, 'v' }, 188 | { "help", no_argument, 0, 'h' }, 189 | /* stop condition for iterator */ 190 | { NULL, 0, 0, 0 }, 191 | }; 192 | 193 | /* command line help descriptions */ 194 | const char *long_options_descs[] = { 195 | "align second firmware image to given offset (default: " __stringify(DEFAULT_IMAGE_ALIGNMENT) " kB)", 196 | "device to write firmware to (default: " DEFAULT_DEVICE ")", 197 | "firmware file to write", 198 | "be verbose in what's going on (give twice to print debug messages)", 199 | "print this usage and exit", 200 | /* stop condition for iterator */ 201 | NULL 202 | }; 203 | 204 | void usage(const char *progname, int exitcode) 205 | { 206 | const char **desc = long_options_descs; 207 | const struct option *op = long_options; 208 | 209 | fprintf(stderr, 210 | "%s -- tool to install i.MX23/28 bootstreams in devices or image files\n\n" 211 | "Usage: %s [options] -f \n\n" 212 | "Options:\n", 213 | progname, progname); 214 | while (op->name && desc) { 215 | fprintf(stderr, "\t-%c, --%-12s\t%s\n", op->val, op->name, *desc); 216 | op++; desc++; 217 | } 218 | fprintf(stderr, "\n"); 219 | 220 | exit(exitcode); 221 | } 222 | 223 | int parse_alignment(const char *arg) 224 | { 225 | long int value; 226 | char *endptr; 227 | 228 | value = strtol(arg, &endptr, 0); 229 | if (*endptr) { 230 | fprintf(stderr, "Error: garbage after alignment value: %s\n", endptr); 231 | exit(EXIT_FAILURE); 232 | } 233 | 234 | return value; 235 | } 236 | 237 | int main(int argc, char *argv[]) 238 | { 239 | char *devicename = DEFAULT_DEVICE; 240 | char *firmware = NULL; 241 | struct mbr mbr; 242 | struct pte *part; 243 | struct bcb bcb; 244 | int rv = EXIT_FAILURE; 245 | int dev_fd = -1, fw_fd = -1; 246 | struct stat fw_stat; 247 | char *fw = NULL; 248 | int i, mincount, sector_offset = max(SECTOR_COUNT(sizeof(struct bcb)), IMAGE_OFFSET); 249 | int image_alignment = DEFAULT_IMAGE_ALIGNMENT; /* in kB */ 250 | int offset; 251 | int verbose = 0; 252 | 253 | while (1) { 254 | int c = getopt_long(argc, argv, "a:d:f:vh", long_options, NULL); 255 | 256 | /* detect the end of the options */ 257 | if (c == -1) 258 | break; 259 | 260 | switch (c) { 261 | case 'a': 262 | image_alignment = parse_alignment(optarg); 263 | if (image_alignment < 0) { 264 | fprintf(stderr, "Warning: invalid alignment '%s' given, using " __stringify(DEFAULT_IMAGE_ALIGNMENT) " instead.\n", optarg); 265 | image_alignment = DEFAULT_IMAGE_ALIGNMENT; 266 | } 267 | break; 268 | case 'd': 269 | devicename = optarg; 270 | break; 271 | case 'f': 272 | firmware = optarg; 273 | break; 274 | case 'v': 275 | verbose += 1; 276 | break; 277 | case 'h': 278 | case '?': 279 | rv = EXIT_SUCCESS; 280 | /* fall-through */ 281 | default: 282 | usage(argv[0], rv); 283 | } 284 | } 285 | 286 | if (!firmware) 287 | usage(argv[0], rv); 288 | 289 | /* open firmware file and memory map it */ 290 | fw_fd = open(firmware, O_RDONLY); 291 | if (fw_fd == -1) { 292 | fprintf(stderr, "Can't open firmware '%s': %s\n", firmware, strerror(errno)); 293 | goto close_out; 294 | } 295 | 296 | if (fstat(fw_fd, &fw_stat) == -1) { 297 | fprintf(stderr, "fstat(%s) failed: %s\n", firmware, strerror(errno)); 298 | goto close_out; 299 | } 300 | 301 | fw = (char *)mmap(NULL, fw_stat.st_size, PROT_READ, MAP_PRIVATE, fw_fd, 0); 302 | if (fw == MAP_FAILED) { 303 | fprintf(stderr, "mmap(%s) failed: %s\n", firmware, strerror(errno)); 304 | goto close_out; 305 | } 306 | 307 | if (verbose > 1) { 308 | printf("Firmware size: %lld bytes, %lld sectors\n", 309 | (long long int)fw_stat.st_size, (long long int)SECTOR_COUNT(fw_stat.st_size)); 310 | } 311 | 312 | /* open target device and read MBR with partition table */ 313 | dev_fd = open(devicename, O_RDWR); 314 | if (dev_fd == -1) { 315 | fprintf(stderr, "Can't open device '%s': %s\n", devicename, strerror(errno)); 316 | goto close_out; 317 | } 318 | 319 | if (read(dev_fd, &mbr, sizeof(mbr)) < sizeof(mbr)) { 320 | fprintf(stderr, "Could not read MBR and partition table of '%s': %s", devicename, strerror(errno)); 321 | goto close_out; 322 | } 323 | 324 | /* partition table is little endian on disk, so convert to host byte order */ 325 | mbr_to_host(&mbr); 326 | 327 | /* safety check that we found a partition table at all */ 328 | if (mbr.signature != MBR_SIGNATURE) { 329 | fprintf(stderr, "MBR signature check failed: expected 0x%" PRIx16 ", read 0x%" PRIx16 "\n", 330 | MBR_SIGNATURE, mbr.signature); 331 | goto unmap_out; 332 | } 333 | 334 | /* search bootstream partition */ 335 | for (i = 0; i < 4; i++) { 336 | if (mbr.partition[i].type == 'S') { 337 | part = &mbr.partition[i]; 338 | if (verbose > 1) { 339 | printf("Bootstream partition found: partition %d, start=%" PRIu32 " length=%" PRIu32 " (sectors)\n", 340 | i, part->start, part->count); 341 | } 342 | break; 343 | } 344 | } 345 | 346 | if (i == 4) { 347 | fprintf(stderr, "Could not find bootstream partition.\n"); 348 | goto unmap_out; 349 | } 350 | 351 | /* we assume that we want to have at least two images of the same size 352 | * in the bootstream partition, plus the first sector containing the BCB 353 | * combined with our desired offset to boot on i.MX23/i.MX28 likewise, plus 354 | * the space we use due to image alignment; 355 | * so calculate the required minimum partition size mincount (in sectors a 512 byte) 356 | */ 357 | offset = sector_offset * SECTOR_SIZE + fw_stat.st_size; 358 | if (image_alignment > 0) 359 | offset = ROUND_UP(offset, image_alignment * 1024); 360 | else 361 | offset = ROUND_UP(offset, SECTOR_SIZE); 362 | 363 | mincount = SECTOR_COUNT(offset + fw_stat.st_size); 364 | 365 | if (part->count < mincount) { 366 | fprintf(stderr, "Bootstream partition is too small with %" PRIu32 " sectors.\n", part->count); 367 | fprintf(stderr, "With two instances of this firmware and firmware alignment to %d kB,\n", image_alignment); 368 | fprintf(stderr, "we require at least %d sectors (or %d kB).\n", mincount, mincount * SECTOR_SIZE / 1024); 369 | goto unmap_out; 370 | } 371 | 372 | 373 | /* create BCB */ 374 | memset(&bcb, 0, sizeof(bcb)); 375 | bcb.signature = BCB_SIGNATURE; 376 | bcb.primary_boot_tag = 1; 377 | bcb.secondary_boot_tag = 2; 378 | bcb.num_copies = 2; 379 | 380 | bcb.drive_info[0].chip_num = 0; 381 | bcb.drive_info[0].drive_type = 0; 382 | bcb.drive_info[0].tag = bcb.primary_boot_tag; 383 | bcb.drive_info[0].first_sector_number = part->start + sector_offset; 384 | bcb.drive_info[0].sector_count = SECTOR_COUNT(offset) - sector_offset; 385 | 386 | bcb.drive_info[1].chip_num = 0; 387 | bcb.drive_info[1].drive_type = 0; 388 | bcb.drive_info[1].tag = bcb.secondary_boot_tag; 389 | bcb.drive_info[1].first_sector_number = 390 | bcb.drive_info[0].first_sector_number + bcb.drive_info[0].sector_count; 391 | bcb.drive_info[1].sector_count = SECTOR_COUNT(fw_stat.st_size); 392 | 393 | if (verbose > 1) { 394 | fprintf(stderr, "1st bootstream:\n"); 395 | fprintf(stderr, "\tstart sector: %" PRIu32 "\n", bcb.drive_info[0].first_sector_number); 396 | fprintf(stderr, "\tsector count: %" PRIu32 "\n", bcb.drive_info[0].sector_count); 397 | fprintf(stderr, "2nd bootstream:\n"); 398 | fprintf(stderr, "\tstart sector: %" PRIu32 "\n", bcb.drive_info[1].first_sector_number); 399 | fprintf(stderr, "\tsector count: %" PRIu32 "\n", bcb.drive_info[1].sector_count); 400 | } 401 | 402 | /* convert bcb to disk byte order for writing */ 403 | bcb_to_disk(&bcb); 404 | 405 | if (verbose) { 406 | printf("Updating BCB... "); 407 | } 408 | 409 | lseek(dev_fd, part->start * SECTOR_SIZE, SEEK_SET); 410 | if (write(dev_fd, &bcb, sizeof(bcb)) != sizeof(bcb)) { 411 | if (verbose) { 412 | printf("failed: %s\n", strerror(errno)); 413 | } else { 414 | fprintf(stderr, "Writing BCB to '%s' failed: %s\n", devicename, strerror(errno)); 415 | } 416 | goto unmap_out; 417 | } else { 418 | if (verbose) { 419 | printf("ok.\n"); 420 | } 421 | } 422 | 423 | if (fsync(dev_fd) == -1) { 424 | fprintf(stderr, "fsync(%s) failed: %s\n", devicename, strerror(errno)); 425 | goto unmap_out; 426 | } 427 | 428 | /* convert bcb back to host byte order */ 429 | bcb_to_host(&bcb); 430 | 431 | if (verbose) { 432 | printf("Writing second firmware... "); 433 | } 434 | 435 | lseek(dev_fd, bcb.drive_info[1].first_sector_number * SECTOR_SIZE, SEEK_SET); 436 | if (write(dev_fd, fw, fw_stat.st_size) != fw_stat.st_size) { 437 | if (verbose) { 438 | printf("failed: %s\n", strerror(errno)); 439 | } else { 440 | fprintf(stderr, "Writing second firmware failed: %s\n", strerror(errno)); 441 | } 442 | goto unmap_out; 443 | } else { 444 | if (fsync(dev_fd) == -1) { 445 | fprintf(stderr, "fsync(%s) failed: %s\n", devicename, strerror(errno)); 446 | goto unmap_out; 447 | } 448 | 449 | if (verbose) { 450 | printf("ok.\n"); 451 | } 452 | } 453 | 454 | if (verbose) { 455 | printf("Writing first firmware... "); 456 | } 457 | 458 | lseek(dev_fd, bcb.drive_info[0].first_sector_number * SECTOR_SIZE, SEEK_SET); 459 | if (write(dev_fd, fw, fw_stat.st_size) != fw_stat.st_size) { 460 | if (verbose) { 461 | printf("failed: %s\n", strerror(errno)); 462 | } else { 463 | fprintf(stderr, "Writing first firmware failed: %s\n", strerror(errno)); 464 | } 465 | goto unmap_out; 466 | } else { 467 | if (fsync(dev_fd) == -1) { 468 | fprintf(stderr, "fsync(%s) failed: %s\n", devicename, strerror(errno)); 469 | goto unmap_out; 470 | } 471 | 472 | if (verbose) { 473 | printf("ok.\n"); 474 | } 475 | } 476 | 477 | rv = EXIT_SUCCESS; 478 | 479 | unmap_out: 480 | munmap(fw, fw_stat.st_size); 481 | 482 | close_out: 483 | if (dev_fd != -1) 484 | close(dev_fd); 485 | if (fw_fd != -1) 486 | close(fw_fd); 487 | 488 | return rv; 489 | } 490 | -------------------------------------------------------------------------------- /ufb.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Mfgtools (UUU) kernel side daemon 4 | * 5 | * Copyright (C) 2024 NXP 6 | * Author: Frank Li 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #define PACKAGE "uuu fastboot client" 25 | #define VERSION "1.0.0" 26 | 27 | #define READ 0 28 | #define WRITE 1 29 | 30 | #define cpu_to_le16(x) (x) 31 | #define cpu_to_le32(x) (x) 32 | #define le32_to_cpu(x) (x) 33 | #define le16_to_cpu(x) (x) 34 | 35 | #pragma pack(1) 36 | struct usb_fs_desc{ 37 | struct usb_functionfs_descs_head_v2 header; 38 | __le32 fs_count; 39 | __le32 hs_count; 40 | __le32 ss_count; 41 | __le32 os_count; 42 | struct { 43 | struct usb_interface_descriptor intf; 44 | struct usb_endpoint_descriptor_no_audio sink; 45 | struct usb_endpoint_descriptor_no_audio source; 46 | } __attribute__((packed)) fs_descs, hs_descs; 47 | struct { 48 | struct usb_interface_descriptor intf; 49 | struct usb_endpoint_descriptor_no_audio sink; 50 | struct usb_ss_ep_comp_descriptor sink_comp; 51 | struct usb_endpoint_descriptor_no_audio source; 52 | struct usb_ss_ep_comp_descriptor source_comp; 53 | } __attribute__((packed)) ss_descs; 54 | struct usb_os_desc_header os_header; 55 | struct usb_ext_compat_desc os_desc; 56 | struct usb_os_desc_header os_ext_header; 57 | struct usb_ext_prop_desc os_ext_desc; 58 | uint8_t property_name[20]; 59 | __le32 property_data_len; 60 | uint8_t property_data[39]; 61 | }; 62 | 63 | #define STR_INTERFACE_ "utp" 64 | 65 | static const struct { 66 | struct usb_functionfs_strings_head header; 67 | struct { 68 | __le16 code; 69 | const char str1[sizeof STR_INTERFACE_]; 70 | } __attribute__((packed)) lang0; 71 | } __attribute__((packed)) g_strings = { 72 | .header = { 73 | .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), 74 | .length = cpu_to_le32(sizeof g_strings), 75 | .str_count = cpu_to_le32(1), 76 | .lang_count = cpu_to_le32(1), 77 | }, 78 | .lang0 = { 79 | cpu_to_le16(0x0409), /* en-us */ 80 | STR_INTERFACE_, 81 | }, 82 | }; 83 | 84 | #pragma pack() 85 | 86 | static const struct usb_fs_desc g_descriptors = { 87 | .header = { 88 | .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), 89 | .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | 90 | FUNCTIONFS_HAS_HS_DESC | 91 | FUNCTIONFS_HAS_SS_DESC | 92 | FUNCTIONFS_HAS_MS_OS_DESC 93 | ), 94 | .length = cpu_to_le32(sizeof g_descriptors), 95 | }, 96 | .fs_count = cpu_to_le32(3), 97 | .fs_descs = { 98 | .intf = { 99 | .bLength = sizeof g_descriptors.fs_descs.intf, 100 | .bDescriptorType = USB_DT_INTERFACE, 101 | .bNumEndpoints = 2, 102 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 103 | .iInterface = 1, 104 | }, 105 | .sink = { 106 | .bLength = sizeof g_descriptors.fs_descs.sink, 107 | .bDescriptorType = USB_DT_ENDPOINT, 108 | .bEndpointAddress = 1 | USB_DIR_IN, 109 | .bmAttributes = USB_ENDPOINT_XFER_BULK, 110 | }, 111 | .source = { 112 | .bLength = sizeof g_descriptors.fs_descs.source, 113 | .bDescriptorType = USB_DT_ENDPOINT, 114 | .bEndpointAddress = 2 | USB_DIR_OUT, 115 | .bmAttributes = USB_ENDPOINT_XFER_BULK, 116 | /* .wMaxPacketSize = autoconfiguration (kernel) */ 117 | }, 118 | }, 119 | .hs_count = cpu_to_le32(3), 120 | .hs_descs = { 121 | .intf = { 122 | .bLength = sizeof g_descriptors.fs_descs.intf, 123 | .bDescriptorType = USB_DT_INTERFACE, 124 | .bNumEndpoints = 2, 125 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 126 | .iInterface = 1, 127 | }, 128 | .sink = { 129 | .bLength = sizeof g_descriptors.hs_descs.sink, 130 | .bDescriptorType = USB_DT_ENDPOINT, 131 | .bEndpointAddress = 1 | USB_DIR_IN, 132 | .bmAttributes = USB_ENDPOINT_XFER_BULK, 133 | .wMaxPacketSize = cpu_to_le16(512), 134 | }, 135 | .source = { 136 | .bLength = sizeof g_descriptors.hs_descs.source, 137 | .bDescriptorType = USB_DT_ENDPOINT, 138 | .bEndpointAddress = 2 | USB_DIR_OUT, 139 | .bmAttributes = USB_ENDPOINT_XFER_BULK, 140 | .wMaxPacketSize = cpu_to_le16(512), 141 | .bInterval = 1, /* NAK every 1 uframe */ 142 | }, 143 | }, 144 | .ss_count = cpu_to_le32(5), 145 | .ss_descs = { 146 | .intf = { 147 | .bLength = sizeof g_descriptors.ss_descs.intf, 148 | .bDescriptorType = USB_DT_INTERFACE, 149 | .bNumEndpoints = 2, 150 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 151 | .iInterface = 1, 152 | }, 153 | .sink = { 154 | .bLength = sizeof g_descriptors.ss_descs.sink, 155 | .bDescriptorType = USB_DT_ENDPOINT, 156 | .bEndpointAddress = 1 | USB_DIR_IN, 157 | .bmAttributes = USB_ENDPOINT_XFER_BULK, 158 | .wMaxPacketSize = cpu_to_le16(1024), 159 | }, 160 | .sink_comp = { 161 | .bLength = sizeof(g_descriptors.ss_descs.sink_comp), 162 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 163 | }, 164 | .source = { 165 | .bLength = sizeof g_descriptors.ss_descs.source, 166 | .bDescriptorType = USB_DT_ENDPOINT, 167 | .bEndpointAddress = 2 | USB_DIR_OUT, 168 | .bmAttributes = USB_ENDPOINT_XFER_BULK, 169 | .wMaxPacketSize = cpu_to_le16(1024), 170 | .bInterval = 1, /* NAK every 1 uframe */ 171 | }, 172 | .source_comp = { 173 | .bLength = sizeof(g_descriptors.ss_descs.source_comp), 174 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 175 | }, 176 | }, 177 | .os_count = cpu_to_le32(2), 178 | .os_header = { 179 | .interface = cpu_to_le32(1), 180 | .dwLength = cpu_to_le32(sizeof(g_descriptors.os_header) 181 | + sizeof(g_descriptors.os_desc)), 182 | .bcdVersion = cpu_to_le32(1), 183 | .wIndex = cpu_to_le32(4), 184 | .bCount = cpu_to_le32(1), 185 | .Reserved = cpu_to_le32(0), 186 | }, 187 | .os_desc = { 188 | .bFirstInterfaceNumber = 0, 189 | .Reserved1 = cpu_to_le32(1), 190 | .CompatibleID = {'W','I','N','U','S','B',0,0}, 191 | .SubCompatibleID = {0}, 192 | .Reserved2 = {0}, 193 | }, 194 | .os_ext_header = { 195 | .interface = cpu_to_le32(0), 196 | .dwLength = cpu_to_le32(sizeof(g_descriptors.os_header) 197 | + sizeof(g_descriptors.os_ext_desc) 198 | + sizeof(g_descriptors.property_name) 199 | + sizeof(g_descriptors.property_data_len) 200 | + sizeof(g_descriptors.property_data)), 201 | .bcdVersion = cpu_to_le32(1), 202 | .wIndex = cpu_to_le32(5), 203 | .wCount = 1, 204 | }, 205 | .os_ext_desc = { 206 | .dwSize = cpu_to_le32(sizeof(g_descriptors.os_ext_desc) 207 | + sizeof(g_descriptors.property_name) 208 | + sizeof(g_descriptors.property_data_len) 209 | + sizeof(g_descriptors.property_data)), 210 | .dwPropertyDataType = 1, 211 | .wPropertyNameLength = sizeof(g_descriptors.property_name), 212 | }, 213 | .property_name = "DeviceInterfaceGUID", 214 | .property_data_len = sizeof(g_descriptors.property_data), 215 | .property_data = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}", 216 | }; 217 | 218 | pid_t popen2(const char *command, int *infp, int *outfp) 219 | { 220 | int p_stdin[2], p_stdout[2]; 221 | pid_t pid; 222 | 223 | if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) 224 | return -1; 225 | 226 | pid = fork(); 227 | 228 | if (pid < 0) 229 | return pid; 230 | else if (pid == 0) { 231 | close(p_stdin[WRITE]); 232 | if (infp == NULL) 233 | close(p_stdin[READ]); 234 | else 235 | dup2(p_stdin[READ], READ); 236 | close(p_stdout[READ]); 237 | if (outfp == NULL) 238 | close(p_stdout[WRITE]); 239 | else 240 | dup2(p_stdout[WRITE], WRITE); 241 | 242 | execl("/bin/sh", "sh", "-c", command, NULL); 243 | perror("execl"); 244 | exit(1); 245 | } 246 | 247 | if (infp == NULL) 248 | close(p_stdin[WRITE]); 249 | else 250 | *infp = p_stdin[WRITE]; 251 | 252 | if (outfp == NULL) 253 | close(p_stdout[READ]); 254 | else 255 | *outfp = p_stdout[READ]; 256 | 257 | close(p_stdin[READ]); 258 | close(p_stdout[WRITE]); 259 | return pid; 260 | } 261 | 262 | #define MAX_FRAME_SIZE 64 263 | #define MAX_FRAME_DATA_SIZE 60 264 | union FBFrame{ 265 | uint8_t raw[MAX_FRAME_SIZE]; 266 | struct { 267 | uint32_t key; 268 | char data[MAX_FRAME_SIZE - sizeof(uint32_t)]; 269 | }; 270 | }; 271 | 272 | #define INFO (('I') | ('N' << 8) | ('F' << 16) | ('O' << 24)) 273 | #define FAIL (('F') | ('A' << 8) | ('I' << 16) | ('L' << 24)) 274 | #define OKAY (('O') | ('K' << 8) | ('A' << 16) | ('Y' << 24)) 275 | #define DATA (('D') | ('A' << 8) | ('T' << 16) | ('A' << 24)) 276 | 277 | int g_stdin = -1; 278 | int g_stdout = -1; 279 | int g_pid = -1; 280 | int g_ep_sink = -1; 281 | int g_ep_source = -1; 282 | int g_ep_0 = -1; 283 | int g_open_file = -1; 284 | 285 | size_t round_up_to_cache_line(size_t size) 286 | { 287 | return (size + 0x7f) & ~0x7f; 288 | } 289 | 290 | void send_data(void *p, size_t size) 291 | { 292 | int r; 293 | r = write(g_ep_sink, p, size); 294 | if (r < 0) 295 | printf("failure write to usb ep\n"); 296 | } 297 | 298 | ssize_t write_file(int fp, void *p, size_t size) 299 | { 300 | fd_set rfds; 301 | struct timeval tv; 302 | ssize_t sz; 303 | uint8_t *buff = (uint8_t*)p; 304 | union FBFrame fm; 305 | int flags; 306 | 307 | tv.tv_sec = 0; 308 | tv.tv_usec = 100000; 309 | 310 | flags = fcntl(fp, F_GETFL); 311 | flags |= O_NONBLOCK; 312 | if (fcntl(fp, F_SETFL, flags)) { 313 | printf("fctl failure\n"); 314 | return -1; 315 | } 316 | 317 | FD_ZERO(&rfds); 318 | FD_SET(fp, &rfds); 319 | 320 | while (size > 0) 321 | { 322 | sz = write(fp, buff, size); 323 | 324 | if (sz == -1) { 325 | if (errno == EAGAIN) 326 | sz = 0; 327 | else 328 | return -errno; 329 | } 330 | 331 | buff += sz; 332 | size -= sz; 333 | fm.key = INFO; 334 | send_data(&fm, 4); 335 | select(fp+1, NULL, &rfds, NULL, &tv); 336 | } 337 | 338 | if (size == 0) 339 | return buff - (uint8_t*)p; 340 | else 341 | return -1; 342 | } 343 | 344 | int handle_cmd(const char *cmd) 345 | { 346 | int pid; 347 | int out; 348 | union FBFrame fm; 349 | int size; 350 | int p; 351 | int pstat; 352 | int flags; 353 | struct timeval tv; 354 | fd_set rfds; 355 | tv.tv_sec = 0; 356 | tv.tv_usec = 50000; 357 | 358 | if (strncmp(cmd, "UCmd:", 5) == 0) 359 | { 360 | printf("run shell cmd: %s\n", cmd + 5); 361 | pid = popen2(cmd + 5, NULL, &out); 362 | if (pid < 0) { 363 | printf("Failure excecu cmd: %s\n", cmd + 5); 364 | memset(&fm, 0, sizeof(fm)); 365 | fm.key = FAIL; 366 | strcpy(fm.data, "Failure to folk process"); 367 | return -1; 368 | } 369 | memset(&fm, 0, sizeof(fm)); 370 | 371 | flags = fcntl(out, F_GETFL); 372 | flags |= O_NONBLOCK; 373 | if (fcntl(out, F_SETFL, flags)) { 374 | printf("fctl failure\n"); 375 | return -1; 376 | } 377 | 378 | FD_ZERO(&rfds); 379 | FD_SET(out, &rfds); 380 | do { 381 | p = waitpid(pid, &pstat, WNOHANG); 382 | select(out + 1, &rfds, NULL, NULL, &tv); 383 | do { 384 | size = read(out, fm.data, MAX_FRAME_DATA_SIZE); 385 | if (size >= 0) { 386 | fm.key = INFO; 387 | send_data(&fm, size + 4); 388 | } 389 | } while (size == MAX_FRAME_DATA_SIZE); 390 | 391 | fm.key = INFO; 392 | send_data(&fm, 4); 393 | 394 | } while (p == 0); 395 | 396 | fm.key = WEXITSTATUS(pstat) ? FAIL : OKAY; 397 | send_data(&fm, 4); 398 | 399 | close(out); 400 | 401 | } else if (strncmp(cmd, "ACmd:", 5) == 0) { 402 | printf("run shell cmd: %s\n", cmd + 5); 403 | g_pid = popen2(cmd + 5, &g_stdin, &g_stdout); 404 | if (g_pid < 0) { 405 | printf("Failure excecu cmd: %s\n", cmd + 6); 406 | memset(&fm, 0, sizeof(fm)); 407 | fm.key = FAIL; 408 | strcpy(fm.data, "Failure to folk process"); 409 | } 410 | usleep(50000); 411 | p = waitpid(g_pid, &pstat, WNOHANG); 412 | if( p > 0) 413 | fm.key = WEXITSTATUS(pstat) ? FAIL : OKAY; 414 | else if (p == 0) 415 | fm.key = OKAY; 416 | else 417 | fm.key = FAIL; 418 | g_open_file = g_stdin; 419 | 420 | flags = fcntl(g_stdout, F_GETFL); 421 | flags |= O_NONBLOCK; 422 | 423 | send_data(&fm, 4); 424 | 425 | if (fcntl(g_stdout, F_SETFL, flags)) { 426 | printf("fctl failure\n"); 427 | return -1; 428 | } 429 | 430 | } else if (strncmp(cmd, "Sync", 4) == 0) { 431 | printf("wait for async proccess finish\n"); 432 | FD_ZERO(&rfds); 433 | FD_SET(g_stdout, &rfds); 434 | do { 435 | p = waitpid(g_pid, &pstat, WNOHANG); 436 | if (g_stdout >= 0) { 437 | select(g_stdout + 1, &rfds, NULL, NULL, &tv); 438 | do { 439 | size = read(g_stdout, fm.data, MAX_FRAME_DATA_SIZE); 440 | if (size >= 0) { 441 | fm.key = INFO; 442 | send_data(&fm, size + 4); 443 | } 444 | } while (size == MAX_FRAME_DATA_SIZE); 445 | } 446 | fm.key = INFO; 447 | send_data(&fm, 4); 448 | } while (p == 0); 449 | 450 | fm.key = WEXITSTATUS(pstat) ? FAIL : OKAY; 451 | send_data(&fm, 4); 452 | 453 | close(g_stdin); 454 | close(g_stdout); 455 | g_open_file = -1; 456 | g_stdin = g_stdout = -1; 457 | 458 | } else if (strncmp(cmd, "WOpen:", 6) == 0) { 459 | int rs = 4; 460 | printf("WOpen:%s\n", cmd + 6); 461 | if (cmd[6] == '-') { 462 | g_open_file = g_stdin; 463 | } 464 | else { 465 | const char *file = cmd + 6; 466 | struct stat st; 467 | if (stat(file, &st)) { 468 | g_open_file = open(file, O_WRONLY | O_CREAT, 469 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 470 | } else { 471 | if (st.st_mode & S_IFDIR) { 472 | g_open_file = -1; 473 | sprintf(fm.data, "%s", "DIR"); 474 | rs = 7; 475 | } else { 476 | g_open_file = open(file, O_WRONLY | O_CREAT, 477 | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); 478 | } 479 | } 480 | } 481 | if (g_open_file < 0) 482 | fm.key = FAIL; 483 | else 484 | fm.key = OKAY; 485 | send_data(&fm, rs); 486 | 487 | } else if (strncmp(cmd, "ROpen:", 6) == 0) { 488 | printf("ROpen: %s\n", cmd + 6); 489 | size_t size = 0; 490 | struct stat st; 491 | int rz = 4; 492 | if (cmd[6] == '-') { 493 | g_open_file = g_stdout; 494 | } else { 495 | const char *file = cmd + 6; 496 | g_open_file = open(file, O_RDONLY); 497 | memset(&st, 0, sizeof(st)); 498 | stat(file, &st); 499 | size = st.st_size; 500 | sprintf(fm.data, "%016zX", size); 501 | rz = 4 + strlen(fm.data); 502 | } 503 | 504 | if (g_open_file < 0) 505 | fm.key = FAIL; 506 | else 507 | fm.key = OKAY; 508 | send_data(&fm, rz); 509 | 510 | } else if (strncmp(cmd, "Close", 5) == 0) { 511 | close(g_open_file); 512 | g_open_file = -1; 513 | fm.key = OKAY; 514 | send_data(&fm, 4); 515 | 516 | } else if (strncmp(cmd, "donwload:", 9) == 0) { 517 | uint32_t size; 518 | ssize_t rs; 519 | uint32_t key = OKAY; 520 | int ret = 0; 521 | 522 | size = strtoul(cmd + 9, NULL, 16); 523 | 524 | void *p = malloc(round_up_to_cache_line(size)); 525 | if (p) { 526 | fm.key = DATA; 527 | } else { 528 | fm.key = FAIL; 529 | send_data(&fm, 4); 530 | return -1; 531 | } 532 | 533 | sprintf(fm.data, "%08X", size); 534 | send_data(&fm, 4 + strlen(fm.data)); 535 | 536 | /* workaround for chipidea usb driver sg alignment issue */ 537 | if ((rs = read(g_ep_source, p, round_up_to_cache_line(size))) < 0) 538 | key = FAIL; 539 | 540 | if (rs != size) { 541 | printf("read size %zd != %d\n", rs, size); 542 | key = FAIL; 543 | } 544 | 545 | ret = write_file(g_open_file, p, rs); 546 | if (ret < 0) 547 | key = FAIL; 548 | 549 | free(p); 550 | 551 | memset(&fm, 0, sizeof(fm)); 552 | 553 | if (g_stdout >= 0) { 554 | flags = fcntl(g_stdout, F_GETFL); 555 | flags |= O_NONBLOCK; 556 | if (fcntl(g_stdout, F_SETFL, flags)) { 557 | printf("fctl failure\n"); 558 | return -1; 559 | } 560 | while ((rs = read(g_stdout, fm.data, MAX_FRAME_DATA_SIZE)) > 0) { 561 | fm.key = INFO; 562 | send_data(&fm, rs + 4); 563 | } 564 | } 565 | fm.key = key; 566 | if (ret == -EPIPE) { 567 | strcpy(fm.data, "EPIPE"); 568 | send_data(&fm, 4 + strlen("EPIPE")); 569 | } else { 570 | send_data(&fm, 4); 571 | } 572 | 573 | } else if (strncmp(cmd, "upload", 6) == 0) { 574 | int max = 0x10000; 575 | void * p = malloc(max); 576 | printf("."); 577 | int ret = 0; 578 | if (p == NULL) { 579 | fm.key = FAIL; 580 | send_data(&fm, 4); 581 | } else { 582 | do { 583 | ret = read(g_open_file, p, max); 584 | if (ret < 0) { 585 | if( errno == EAGAIN) { 586 | //retry read 587 | fm.key = DATA; 588 | sprintf(fm.data, "%08X", 0); 589 | send_data(&fm, 12); 590 | } else { 591 | fm.key = FAIL; 592 | send_data(&fm, 4); 593 | break; 594 | } 595 | } else { 596 | fm.key = DATA; 597 | sprintf(fm.data, "%08X", ret); 598 | send_data(&fm, 12); 599 | send_data(p, ret); 600 | fm.key = OKAY; 601 | send_data(&fm, 4); 602 | break; 603 | } 604 | } while (1); 605 | } 606 | free(p); 607 | } else { 608 | printf("Unknow Cmd %s\n", cmd); 609 | } 610 | 611 | return 0; 612 | } 613 | 614 | 615 | void init_usb_fs() 616 | { 617 | ssize_t ret; 618 | 619 | printf("Start init usb\n"); 620 | 621 | ret = write(g_ep_0, &g_descriptors, sizeof(g_descriptors)); 622 | if (ret < 0) { 623 | printf("write descriptor failure\n"); 624 | exit(1); 625 | } 626 | 627 | printf("write string\n"); 628 | ret = write(g_ep_0, &g_strings, sizeof(g_strings)); 629 | if (ret < 0) { 630 | printf("write string failure\n"); 631 | exit(1); 632 | } 633 | } 634 | 635 | 636 | int main(int argc, char **argv) 637 | { 638 | printf("%s %s [built %s %s]\n", PACKAGE, VERSION, __DATE__, __TIME__); 639 | 640 | char file[] = "/dev/usb-ffs/ep0"; 641 | char *usb_file = file; 642 | 643 | signal(SIGPIPE, SIG_IGN); 644 | 645 | if (argc > 1) 646 | usb_file = argv[1]; 647 | 648 | g_ep_0 = open(usb_file, O_RDWR); 649 | if (g_ep_0 < 0) { 650 | printf("Can't open file %s\n", usb_file); 651 | exit(1); 652 | } 653 | init_usb_fs(); 654 | 655 | usb_file[strlen(usb_file) - 1] = '1'; 656 | g_ep_sink = open(usb_file, O_RDWR); 657 | if (g_ep_sink < 0) { 658 | printf("can't open file %s\n", usb_file); 659 | exit(1); 660 | } 661 | 662 | usb_file[strlen(usb_file) - 1] = '2'; 663 | g_ep_source = open(usb_file, O_RDWR); 664 | if (g_ep_source < 0) { 665 | printf("can't open file %s\n", usb_file); 666 | exit(1); 667 | } 668 | 669 | printf("Start handle command\n"); 670 | while (1) { 671 | int r; 672 | char buff[512]; 673 | memset(buff, 0, 512); 674 | r = read(g_ep_source, buff, 511); 675 | if (r < 0) 676 | printf("failure read command from usb ep point\n"); 677 | if (r > 0) 678 | handle_cmd(buff); 679 | } 680 | 681 | return 0; 682 | } 683 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /uu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * iMX utp decode program 3 | * 4 | * Copyright (C) 2010-2014 Freescale Semiconductor, Inc. 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #define _GNU_SOURCE 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | /* mxc SoC will enable watchdog at USB recovery mode 44 | * so, the user must service watchdog 45 | */ 46 | #include 47 | 48 | #define UTP_TARGET_FILE "/tmp/file.utp" 49 | 50 | #define UTP_FLAG_COMMAND 0x00000001 51 | #define UTP_FLAG_DATA 0x00000002 52 | #define UTP_FLAG_STATUS 0x00000004 //indicate an error happens 53 | #define UTP_FLAG_REPORT_BUSY 0x10000000 54 | 55 | 56 | #pragma pack(1) 57 | 58 | #define PACKAGE "uuc" 59 | #define VERSION "0.6" 60 | 61 | char *utp_firmware_version = "2.6.31"; 62 | char *utp_sn = "000000000000"; 63 | char *utp_chipid = "370000A5"; 64 | /* for utp ioctl */ 65 | #define UTP_IOCTL_BASE 'U' 66 | #define UTP_GET_CPU_ID _IOR(UTP_IOCTL_BASE, 0, int) 67 | 68 | #define NEED_TO_GET_CHILD_PID 1 69 | /* 70 | * this structure should be in sync with the same in 71 | * $KERNEL/drivers/usb/gadget/fsl_updater.c 72 | */ 73 | struct utp_message { 74 | uint32_t flags; 75 | size_t size; 76 | union { 77 | struct { 78 | uint64_t payload; 79 | char command[1]; 80 | }; 81 | struct { 82 | size_t bufsize; 83 | uint8_t data[1]; 84 | }; 85 | uint32_t status; 86 | }; 87 | }; 88 | #pragma pack() 89 | 90 | static int utp_file = -1; 91 | 92 | static inline char *utp_answer_type(struct utp_message *u) 93 | { 94 | if (!u) 95 | return "UNKNOWN"; 96 | if (u->flags & UTP_FLAG_STATUS) 97 | return "Non-success"; 98 | if (u->flags & UTP_FLAG_DATA) 99 | return "Data"; 100 | if (u->flags & UTP_FLAG_REPORT_BUSY) 101 | return "Busy"; 102 | if (u->flags & UTP_FLAG_COMMAND) 103 | return "Command ?!"; 104 | return "Success"; 105 | } 106 | 107 | /* 108 | * utp_mk_devnode 109 | * 110 | * Parse the /sys entry to find major and minor for device 111 | * If/when found, create device node with type 'type' 112 | * 113 | * Example: utp_mk_devnode("block", "sda", "/dev/scsi-disk-0", S_IFBLK) 114 | */ 115 | static int utp_mk_devnode(char *class, char *name, char *node, int type) 116 | { 117 | char sys[256]; 118 | char devnode[20]; /* major:minor */ 119 | int major, minor; 120 | int len, f, rc = -EINVAL; 121 | 122 | if (access(node, F_OK) == 0) { 123 | printf("UTP: file/device node %s already exists\n", node); 124 | return 0; 125 | } 126 | 127 | snprintf(sys, sizeof(sys), "/sys/%s/%s/dev", class, name); 128 | f = open(sys, O_RDONLY); 129 | if (f >= 0) { 130 | memset(devnode, 0, sizeof(devnode)); 131 | len = read(f, devnode, sizeof(devnode)); 132 | if (len >= 0) { 133 | sscanf(devnode, "%d:%d", &major, &minor); 134 | printf("%s: creating node '%s' with %d+%d\n", __func__, node, major, minor); 135 | unlink(node); 136 | rc = mknod(node, type | 0666, makedev(major, minor)); 137 | } 138 | close(f); 139 | } 140 | return rc; 141 | } 142 | 143 | /* 144 | * utp_run 145 | * 146 | * Start the subshell and execute the command passed 147 | */ 148 | static int utp_run(char *command, ... ) 149 | { 150 | char cmd[1024]; 151 | va_list vptr; 152 | 153 | va_start(vptr, command); 154 | vsnprintf(cmd, sizeof(cmd), command, vptr); 155 | va_end(vptr); 156 | 157 | printf("UTP: executing \"%s\"\n", cmd); 158 | return system(cmd); 159 | } 160 | 161 | /* 162 | * utp_send_busy 163 | * 164 | * report the busy state to the kernel state machine 165 | */ 166 | static void utp_send_busy(int u) 167 | { 168 | struct utp_message w; 169 | 170 | w.flags = UTP_FLAG_REPORT_BUSY; 171 | w.size = sizeof(w); 172 | write(u, &w, w.size); 173 | } 174 | 175 | /* 176 | * utp_partition_mmc 177 | * 178 | * chat with fdisk to create bootable partition of type 0x53 and extended one 179 | */ 180 | static int utp_partition_mmc(char *disk) 181 | { 182 | char fc[50]; 183 | int i; 184 | char shell_cmd[256]; 185 | int fdisk; 186 | FILE *fdisk_f; 187 | 188 | sprintf(shell_cmd, "fdisk %s", disk); 189 | fdisk_f = popen(shell_cmd, "w"); 190 | if (fdisk_f < 0) 191 | return errno; 192 | 193 | fdisk = fileno(fdisk_f); 194 | for (i = 4; i >= 1 ; i--) { 195 | sprintf(fc, "d\n%d\n", i); 196 | write(fdisk, fc, strlen(fc)); 197 | } 198 | 199 | sprintf(fc, "n\np\n1\n1\n+16M\n"); 200 | write(fdisk, fc, strlen(fc)); 201 | 202 | sprintf(fc, "n\np\n2\n\n\n"); 203 | write(fdisk, fc, strlen(fc)); 204 | 205 | sprintf(fc, "t\n1\n0x%X\n\n", 0x53); 206 | write(fdisk, fc, strlen(fc)); 207 | 208 | write(fdisk, "w\nq\n", 2); 209 | 210 | pclose(fdisk_f); 211 | 212 | return 0; 213 | } 214 | 215 | /* 216 | * utp_do_selftest 217 | * 218 | * perform some diagnostics 219 | * 220 | * TBW 221 | */ 222 | static int utp_do_selftest(void) 223 | { 224 | return 0; 225 | } 226 | /* 227 | * Put the command which needs to send busy first 228 | * And the host will send poll for getting its return value 229 | * later, we call these kinds of commands as Asynchronous Commands. 230 | */ 231 | static int utp_can_busy(char *command) 232 | { 233 | char *async[] ={ 234 | "$ ", "frf", "pollpipe", NULL, 235 | }; 236 | char **ptr; 237 | 238 | ptr = async; 239 | while (*ptr) { 240 | if (strncmp(command, *ptr, strlen(*ptr)) == 0) 241 | return 1; 242 | ptr++; 243 | } 244 | return 0; 245 | } 246 | #ifdef NEED_TO_GET_CHILD_PID 247 | /* for pipe */ 248 | #define READ 0 249 | #define WRITE 1 250 | static pid_t child_pid = -1; 251 | static int utp_flush(void) 252 | { 253 | int pstat; 254 | int ret = 0; 255 | pid_t pid; 256 | if (utp_file >= 0) { 257 | fflush(NULL); 258 | ret = close(utp_file); 259 | do{ 260 | pid = waitpid(child_pid, &pstat, 0); /* wait for child finished */ 261 | }while (pid == -1 && errno == EINTR); 262 | printf("UTP: closing the file\n"); 263 | } 264 | utp_file = -1; 265 | return ret; 266 | } 267 | pid_t popen2(const char *command, int *infp, int *outfp) 268 | { 269 | int p_stdin[2], p_stdout[2]; 270 | pid_t pid; 271 | 272 | if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) 273 | return -1; 274 | 275 | pid = fork(); 276 | 277 | if (pid < 0) 278 | return pid; 279 | else if (pid == 0){ 280 | close(p_stdin[WRITE]); 281 | if (infp == NULL) 282 | close(p_stdin[READ]); 283 | else 284 | dup2(p_stdin[READ], READ); 285 | close(p_stdout[READ]); 286 | if (outfp == NULL) 287 | close(p_stdout[WRITE]); 288 | else 289 | dup2(p_stdout[WRITE], WRITE); 290 | 291 | execl("/bin/sh", "sh", "-c", command, NULL); 292 | perror("execl"); 293 | exit(1); 294 | } 295 | 296 | if (infp == NULL) 297 | close(p_stdin[WRITE]); 298 | else 299 | *infp = p_stdin[WRITE]; 300 | 301 | if (outfp == NULL) 302 | close(p_stdout[READ]); 303 | else 304 | *outfp = p_stdout[READ]; 305 | 306 | close(p_stdin[READ]); 307 | close(p_stdout[WRITE]); 308 | return pid; 309 | } 310 | int utp_pipe(char *command, ... ) 311 | { 312 | int infp; 313 | char shell_cmd[1024]; 314 | va_list vptr; 315 | va_start(vptr, command); 316 | vsnprintf(shell_cmd, sizeof(shell_cmd), command, vptr); 317 | va_end(vptr); 318 | 319 | child_pid = popen2(shell_cmd, &infp, NULL); 320 | if (child_pid < 0){ 321 | printf("the fork is failed \n"); 322 | return -1; 323 | } 324 | utp_file = infp; 325 | printf("pid is %d, UTP: executing \"%s\"\n",child_pid, shell_cmd); 326 | return 0; 327 | } 328 | 329 | /* 330 | * Check the process is dead 331 | */ 332 | #define NAME_MAX 30 333 | int is_child_dead(void) 334 | { 335 | FILE *fh; 336 | char path[NAME_MAX + 1]; 337 | sprintf(path, "/proc/%u/status", (unsigned int)child_pid); 338 | if ((fh = fopen(path, "r"))){ 339 | char buf[1024]; 340 | while (fgets(buf, sizeof(buf) -1, fh)){ 341 | if (!strncmp(buf, "State:", 6)) 342 | { 343 | char *p = buf + 6; 344 | while (*p == '\t'){ 345 | p++; 346 | continue; 347 | } 348 | if (*p == 'Z'){ 349 | printf("Process status polling: %s is in zombie.\n",path); 350 | fclose(fh); 351 | return 1; 352 | } 353 | break; 354 | } 355 | } 356 | } 357 | else{ 358 | printf("Process polling: can't open %s, maybe the process %u has been killed already\n",path,child_pid); 359 | return 1; 360 | } 361 | 362 | fclose(fh); 363 | return 0; 364 | } 365 | 366 | int utp_poll_pipe() 367 | { 368 | int ret = 0, cnt = 0xFFFF; 369 | while(ret == 0 && cnt > 0){ 370 | ret = is_child_dead(); 371 | usleep(10000); 372 | cnt--; 373 | } 374 | 375 | if(ret == 0) 376 | return 1;//failure 377 | else 378 | return 0;//Success 379 | } 380 | 381 | 382 | #else 383 | static int utp_flush(void) 384 | { 385 | int ret; 386 | if (utp_file_f) { 387 | printf("UTP: waiting for pipe to close\n"); 388 | ret = pclose(utp_file_f); 389 | } 390 | else if (utp_file >= 0) { 391 | printf("UTP: closing the file\n"); 392 | ret = close(utp_file); 393 | } 394 | utp_file_f = NULL; 395 | utp_file = -1; 396 | printf("UTP: files were flushed.\n"); 397 | return ret; 398 | } 399 | static int utp_pipe(char *command, ... ) 400 | { 401 | int r; 402 | char shell_cmd[1024]; 403 | va_list vptr; 404 | 405 | va_start(vptr, command); 406 | vsnprintf(shell_cmd, sizeof(shell_cmd), command, vptr); 407 | va_end(vptr); 408 | 409 | utp_file_f = popen(shell_cmd, "w"); 410 | utp_file = fileno(utp_file_f); 411 | 412 | return utp_file_f ? 0 : errno; 413 | } 414 | #endif 415 | /* 416 | * utp_handle_command 417 | * 418 | * handle the command from MSC driver 419 | * command can be: 420 | * ? 421 | * ! 422 | * $ 423 | * wfs/wff write firmware to SD/flash 424 | * wrs/wrf write rootfs to SD/flash 425 | * frs/frf format partition for root on SD/flash 426 | * erase erase partition on flash 427 | * read not implemented yet 428 | * write not implemented yet 429 | */ 430 | static struct utp_message *utp_handle_command(int u, char *cmd, unsigned long long payload) 431 | { 432 | struct utp_message *w = NULL; 433 | char devnode[50]; /* enough to fit /dev/mmcblk0p99 */ 434 | char sysnode[50]; /* -"- -"- mmcblk0/mmcblk0p99 */ 435 | uint32_t flags, status; 436 | char *data = NULL; 437 | int f; 438 | size_t size; 439 | 440 | printf("UTP: received command '%s'\n", cmd); 441 | 442 | /* defaults */ 443 | status = 0; 444 | flags = 0; 445 | size = 0; 446 | 447 | /* these are asynchronous commands and need to send busy */ 448 | if (utp_can_busy(cmd)){ 449 | utp_send_busy(u); 450 | } 451 | 452 | if (strcmp(cmd, "?") == 0) { 453 | /* query */ 454 | flags = UTP_FLAG_DATA; 455 | data = malloc(256); 456 | sprintf(data, 457 | "\n" 458 | " %s\n" 459 | " %s\n" 460 | " %s" 461 | " %s" 462 | " %04X" 463 | " %04X" 464 | "\n", utp_firmware_version, VERSION, utp_sn, utp_chipid, 0x66F, 0x37FF); 465 | size = (strlen(data) + 1 ) * sizeof(data[0]); 466 | } 467 | 468 | else if (cmd[0] == '!') { 469 | /* reboot the system, and the ACK has already sent out */ 470 | if (cmd[1] == '3') { 471 | sync(); 472 | kill(-1, SIGTERM); 473 | sleep(1); 474 | kill(-1, SIGKILL); 475 | 476 | reboot(LINUX_REBOOT_CMD_RESTART); 477 | return NULL; 478 | } 479 | } 480 | 481 | else if (strncmp(cmd, "$ ", 2) == 0) { 482 | status = utp_run(cmd + 2); 483 | if (status) 484 | flags = UTP_FLAG_STATUS; 485 | } 486 | else if ((strcmp(cmd,"wff") == 0) || (strcmp(cmd, "wfs") == 0)) { 487 | /* Write firmware - to flash or to SD, no matter */ 488 | utp_file = open(UTP_TARGET_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0666); 489 | } 490 | 491 | else if (strcmp(cmd, "fff") == 0) { 492 | /* perform actual flashing of the firmware to the NAND */ 493 | utp_flush(); 494 | if (utp_mk_devnode("class/mtd", "mtd1", "/dev/mtd1", S_IFCHR) >= 0 && 495 | utp_mk_devnode("class/mtd", "mtd0", "/dev/mtd0", S_IFCHR) >= 0) { 496 | utp_run("kobs-ng -v -d %s", UTP_TARGET_FILE); 497 | } 498 | } 499 | else if (strcmp(cmd, "ffs") == 0) { 500 | /* perform actual flashing of the firmware to the SD */ 501 | utp_flush(); 502 | 503 | /* partition the card */ 504 | if (!status && utp_mk_devnode("block", "mmcblk0", "/dev/mmc", S_IFBLK) >= 0) { 505 | status = utp_partition_mmc("/dev/mmc"); 506 | /* waiting for partition table to settle */ 507 | sleep(5); 508 | } 509 | 510 | /* write data to the first partition */ 511 | if (!status && utp_mk_devnode("block", "mmcblk0/mmcblk0p1", "/dev/mmc0p1", S_IFBLK) >= 0) { 512 | utp_run("dd if=/dev/zero of=/dev/mmc0p1 bs=512 count=4"); 513 | utp_run("dd if=%s of=/dev/mmc0p1 ibs=512 seek=4 conv=sync,notrunc", UTP_TARGET_FILE); 514 | } 515 | 516 | if (status) 517 | flags = UTP_FLAG_STATUS; 518 | } 519 | 520 | else if (strncmp(cmd, "mknod", 5) == 0) { 521 | int devtype = S_IFCHR; 522 | char *class, *item, *type, *node; 523 | 524 | class = strtok(cmd + 6, " \t,;"); 525 | printf("class = '%s'\n", class); 526 | item = strtok(NULL, " \t,;"); 527 | printf("item = '%s'\n", item); 528 | node = strtok(NULL, " \t,;"); 529 | printf("node = %s\n", node); 530 | type = strtok(NULL, " \t,;"); 531 | printf("type = %s\n", type); 532 | if (!node && item) 533 | sprintf(devnode, "/dev/%s", item); 534 | else 535 | strcpy(devnode, node); 536 | if (type && (strcmp(type, "block") == 0 || strcmp(type, "blk") == 0)) 537 | devtype = S_IFBLK; 538 | printf("UTP: running utp_mk_devnode(%s,%s,%s,0x%x)\n", 539 | class, item, devnode, devtype); 540 | status = utp_mk_devnode(class, item, devnode, devtype); 541 | if (status) 542 | flags = UTP_FLAG_STATUS; 543 | } 544 | 545 | else if (strncmp(cmd, "wrf", 3) == 0) { 546 | /* Write rootfs to flash */ 547 | printf("UTP: writing rootfs to flash, mtd #%c, size %lld\n", 548 | cmd[3], payload); 549 | 550 | /* ensure that device node is here */ 551 | snprintf(devnode, sizeof(devnode), "/dev/mtd%c", cmd[3]); 552 | utp_mk_devnode("class/mtd", devnode + 5, devnode, S_IFCHR); 553 | 554 | /* then start ubiformat and redirect its input */ 555 | status = utp_pipe("ubiformat %s -f - -S %lld", devnode, payload); 556 | if (status) 557 | flags = UTP_FLAG_STATUS; 558 | } 559 | 560 | else if (strncmp(cmd, "pipe", 4) == 0) { 561 | status = utp_pipe(cmd + 5); 562 | if (status) 563 | flags = UTP_FLAG_STATUS; 564 | } 565 | 566 | else if (strncmp(cmd, "pollpipe", 8) == 0) { 567 | printf("UTP: poll pipe.\n"); 568 | status = utp_poll_pipe(); 569 | if (status) 570 | flags = UTP_FLAG_STATUS; 571 | } 572 | 573 | else if (strncmp(cmd, "wrs", 3) == 0) { 574 | /* Write rootfs to the SD */ 575 | printf("UTP: writing rootfs to SD card, mmc partition #%c, size %lld\n", 576 | cmd[3], payload); 577 | 578 | /* ensure that device node is here */ 579 | snprintf(devnode, sizeof(devnode), "/dev/mmcblk0p%c", cmd[3]); 580 | snprintf(sysnode, sizeof(sysnode), "mmcblk0/mmcblk0p%d", cmd[3]); 581 | utp_mk_devnode("block", sysnode, devnode, S_IFBLK); 582 | 583 | if (payload % 1024) 584 | printf("UTP: WARNING! payload %% 1024 != 0, the rest will be skipped"); 585 | 586 | status = utp_pipe("dd of=%s bs=1K", devnode); 587 | if (status) 588 | flags = UTP_FLAG_STATUS; 589 | } 590 | 591 | 592 | else if (strcmp(cmd, "frf") == 0 || strcmp(cmd, "frs") == 0) { 593 | /* perform actual flashing of the rootfs to the NAND/SD */ 594 | status = utp_flush(); 595 | if (status) 596 | flags = UTP_FLAG_STATUS; 597 | } 598 | 599 | else if (strncmp(cmd, "untar.", 6) == 0) { 600 | status = utp_pipe("tar %cxv -C %s", cmd[6], cmd + 8); 601 | if (status) 602 | flags = UTP_FLAG_STATUS; 603 | } 604 | 605 | else if (strncmp(cmd, "read", 4) == 0) { 606 | f = open(cmd + 5, O_RDONLY); 607 | if (f < 0) { 608 | flags = UTP_FLAG_STATUS; 609 | status = errno; 610 | } else { 611 | size = lseek(f, 0, SEEK_END); /* get the file size */ 612 | lseek(f, 0, SEEK_SET); 613 | 614 | data = malloc(size); 615 | if (!data) { 616 | flags = UTP_FLAG_STATUS; 617 | status = -ENOMEM; 618 | } else { 619 | read(f, data, size); 620 | flags = UTP_FLAG_DATA; 621 | } 622 | } 623 | } 624 | 625 | else if (strcmp(cmd, "send") == 0) { 626 | utp_file = open(UTP_TARGET_FILE, O_TRUNC | O_CREAT | O_WRONLY, 0666); 627 | } 628 | 629 | else if (strncmp(cmd, "save", 4) == 0) { 630 | close(utp_file); 631 | rename(UTP_TARGET_FILE, cmd + 5); 632 | } 633 | 634 | 635 | else if (strcmp(cmd, "selftest") == 0) { 636 | status = utp_do_selftest(); 637 | if (status) 638 | flags = UTP_FLAG_STATUS; 639 | } 640 | 641 | else { 642 | printf("UTP: Unknown command received, ignored\n"); 643 | flags = UTP_FLAG_STATUS; 644 | status = -EINVAL; 645 | } 646 | 647 | w = malloc(size + sizeof(*w)); 648 | if (!w) { 649 | printf("UTP: Could not allocate %zu+%zu bytes!\n", size, sizeof(*w)); 650 | return NULL; 651 | } 652 | 653 | memset(w, 0, sizeof(*w) + size); 654 | w->flags = flags; 655 | w->size = size + sizeof(*w); 656 | if (flags & UTP_FLAG_DATA) { 657 | w->bufsize = size; 658 | memcpy(w->data, data, size); 659 | } 660 | if (flags & UTP_FLAG_STATUS) 661 | w->status = status; 662 | if (data) 663 | free(data); 664 | return w; 665 | } 666 | 667 | void feed_watchdog(void *arg) 668 | { 669 | int res; 670 | int *fd = arg; 671 | while(1) { 672 | res = ioctl(*fd, WDIOC_KEEPALIVE); 673 | if (res) 674 | printf("ioctl WDIOC_KEEPALIVE error L%d, %s\n", __LINE__, strerror(errno)); 675 | printf("%s\n", __func__); 676 | sleep(60); 677 | } 678 | } 679 | 680 | int main(int argc, char **argv) 681 | { 682 | int u = -1, wdt_fd = -1, r, need_watchdog = 0; 683 | int watchdog_timeout = 127; /* sec */ 684 | int cpu_id = 50; 685 | struct utp_message *uc, *answer; 686 | pthread_t a_thread; 687 | char * utp_devnode="/dev/utp"; 688 | if (argc > 1) 689 | utp_devnode = argv[1]; 690 | 691 | printf("%s %s [built %s %s]\n", PACKAGE, VERSION, __DATE__, __TIME__); 692 | /* set stdout unbuffered, what is the usage??? */ 693 | // setvbuf(stdout, NULL, _IONBF, 0); 694 | uc = malloc(sizeof(*uc) + 0x10000); 695 | 696 | mkdir("/tmp", 0777); 697 | 698 | setenv("FILE", UTP_TARGET_FILE, !0); 699 | 700 | printf("UTP: Waiting for %s to appear\n", utp_devnode); 701 | 702 | while (utp_mk_devnode("class/misc", "utp", utp_devnode, S_IFCHR) < 0) { 703 | putchar('.'); 704 | sleep(1); 705 | } 706 | u = open(utp_devnode, O_RDWR); 707 | r = ioctl(u, UTP_GET_CPU_ID, &cpu_id); 708 | if (r) 709 | printf("cpu id get error:L%d, %s\n", __LINE__, strerror(errno)); 710 | else{ 711 | switch (cpu_id) { 712 | case 23: 713 | case 25: 714 | case 28: 715 | case 50: 716 | need_watchdog = 0; 717 | break; 718 | case 35: 719 | case 51: 720 | case 53: 721 | need_watchdog = 1; 722 | break; 723 | default: 724 | need_watchdog = 0; 725 | } 726 | printf("cpu_id is %d\n", cpu_id); 727 | if (need_watchdog){ 728 | if (utp_mk_devnode("class/misc", "watchdog", "/dev/watchdog", S_IFCHR)){ 729 | printf("The watchdog is not configured, needed by mx35/mx51/mx53 \n"); 730 | printf("%d, %s\n", __LINE__, strerror(errno)); 731 | } else{ 732 | wdt_fd = open("/dev/watchdog", O_RDWR); 733 | /* set the MAX timeout */ 734 | r = ioctl(wdt_fd, WDIOC_SETTIMEOUT, &watchdog_timeout); 735 | if (r) 736 | printf("%d, %s\n", __LINE__, strerror(errno)); 737 | r = pthread_create(&a_thread, NULL, (void *)feed_watchdog, (void *)(&wdt_fd)); 738 | if (r != 0) { 739 | perror("Thread creation failed"); 740 | exit(EXIT_FAILURE); 741 | } 742 | } 743 | } 744 | } 745 | 746 | for(;;) { 747 | r = read(u, uc, sizeof(*uc) + 0x10000); 748 | if (uc->flags & UTP_FLAG_COMMAND) { 749 | answer = utp_handle_command(u, uc->command, uc->payload); 750 | if (answer) { 751 | printf("UTP: sending %s to kernel for command %s.\n", utp_answer_type(answer), uc->command); 752 | write(u, answer, answer->size); 753 | free(answer); 754 | } 755 | }else if (uc->flags & UTP_FLAG_DATA) { 756 | write(utp_file, uc->data, uc->bufsize); 757 | }else { 758 | printf("UTP: Unknown flag %x\n", uc->flags); 759 | } 760 | } 761 | 762 | /* should never be here */ 763 | return 0; 764 | } 765 | 766 | --------------------------------------------------------------------------------