| /* |
| * iMX utp decode program |
| * |
| * Copyright 2010-2013 Freescale Semiconductor, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <syscall.h> |
| #include <stdint.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <string.h> |
| #include <malloc.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| #include <pthread.h> |
| #include <linux/reboot.h> |
| #include <sys/reboot.h> |
| /* mxc SoC will enable watchdog at USB recovery mode |
| * so, the user must service watchdog |
| */ |
| #include <linux/watchdog.h> |
| |
| #define UTP_DEVNODE "/dev/utp" |
| #define UTP_TARGET_FILE "/tmp/file.utp" |
| |
| #define UTP_FLAG_COMMAND 0x00000001 |
| #define UTP_FLAG_DATA 0x00000002 |
| #define UTP_FLAG_STATUS 0x00000004 //indicate an error happens |
| #define UTP_FLAG_REPORT_BUSY 0x10000000 |
| |
| |
| #pragma pack(1) |
| |
| #define PACKAGE "uuc" |
| #define VERSION "0.4" |
| |
| char *utp_firmware_version = "2.6.31"; |
| char *utp_sn = "000000000000"; |
| char *utp_chipid = "370000A5"; |
| /* for utp ioctl */ |
| #define UTP_IOCTL_BASE 'U' |
| #define UTP_GET_CPU_ID _IOR(UTP_IOCTL_BASE, 0, int) |
| |
| #define NEED_TO_GET_CHILD_PID 1 |
| /* |
| * this structure should be in sync with the same in |
| * $KERNEL/drivers/usb/gadget/fsl_updater.c |
| */ |
| struct utp_message { |
| uint32_t flags; |
| size_t size; |
| union { |
| struct { |
| uint64_t payload; |
| char command[1]; |
| }; |
| struct { |
| size_t bufsize; |
| uint8_t data[1]; |
| }; |
| uint32_t status; |
| }; |
| }; |
| #pragma pack() |
| |
| static int utp_file = -1; |
| static FILE *utp_file_f = NULL; |
| |
| static inline char *utp_answer_type(struct utp_message *u) |
| { |
| if (!u) |
| return "UNKNOWN"; |
| if (u->flags & UTP_FLAG_STATUS) |
| return "Non-success"; |
| if (u->flags & UTP_FLAG_DATA) |
| return "Data"; |
| if (u->flags & UTP_FLAG_REPORT_BUSY) |
| return "Busy"; |
| if (u->flags & UTP_FLAG_COMMAND) |
| return "Command ?!"; |
| return "Success"; |
| } |
| |
| /* |
| * utp_mk_devnode |
| * |
| * Parse the /sys entry to find major and minor for device |
| * If/when found, create device node with type 'type' |
| * |
| * Example: utp_mk_devnode("block", "sda", "/dev/scsi-disk-0", S_IFBLK) |
| */ |
| static int utp_mk_devnode(char *class, char *name, char *node, int type) |
| { |
| char sys[256]; |
| char devnode[20]; /* major:minor */ |
| int major, minor; |
| char *colon; |
| int len, f, rc = -EINVAL; |
| |
| if (access(node, F_OK) == 0) { |
| printf("UTP: file/device node %s already exists\n", node); |
| return 0; |
| } |
| |
| snprintf(sys, sizeof(sys), "/sys/%s/%s/dev", class, name); |
| f = open(sys, O_RDONLY); |
| if (f >= 0) { |
| memset(devnode, 0, sizeof(devnode)); |
| len = read(f, devnode, sizeof(devnode)); |
| if (len >= 0) { |
| sscanf(devnode, "%d:%d", &major, &minor); |
| printf("%s: creating node '%s' with %d+%d\n", __func__, node, major, minor); |
| unlink(node); |
| rc = mknod(node, type | 0666, makedev(major, minor)); |
| } |
| close(f); |
| } |
| return rc; |
| } |
| |
| /* |
| * utp_run |
| * |
| * Start the subshell and execute the command passed |
| */ |
| static utp_run(char *command, ... ) |
| { |
| int r; |
| char cmd[1024]; |
| va_list vptr; |
| |
| va_start(vptr, command); |
| vsnprintf(cmd, sizeof(cmd), command, vptr); |
| va_end(vptr); |
| |
| printf("UTP: executing \"%s\"\n", cmd); |
| return system(cmd); |
| } |
| |
| /* |
| * utp_send_busy |
| * |
| * report the busy state to the kernel state machine |
| */ |
| static void utp_send_busy(int u) |
| { |
| struct utp_message w; |
| |
| w.flags = UTP_FLAG_REPORT_BUSY; |
| w.size = sizeof(w); |
| write(u, &w, w.size); |
| } |
| |
| /* |
| * utp_partition_mmc |
| * |
| * chat with fdisk to create bootable partition of type 0x53 and extended one |
| */ |
| static int utp_partition_mmc(char *disk) |
| { |
| char fc[50]; |
| int i; |
| char shell_cmd[256]; |
| int fdisk; |
| FILE *fdisk_f; |
| |
| sprintf(shell_cmd, "fdisk %s", disk); |
| fdisk_f = popen(shell_cmd, "w"); |
| if (fdisk_f < 0) |
| return errno; |
| |
| fdisk = fileno(fdisk_f); |
| for (i = 4; i >= 1 ; i--) { |
| sprintf(fc, "d\n%d\n", i); |
| write(fdisk, fc, strlen(fc)); |
| } |
| |
| sprintf(fc, "n\np\n1\n1\n+16M\n"); |
| write(fdisk, fc, strlen(fc)); |
| |
| sprintf(fc, "n\np\n2\n\n\n"); |
| write(fdisk, fc, strlen(fc)); |
| |
| sprintf(fc, "t\n1\n0x%X\n\n", 0x53); |
| write(fdisk, fc, strlen(fc)); |
| |
| write(fdisk, "w\nq\n", 2); |
| |
| pclose(fdisk_f); |
| |
| return 0; |
| } |
| |
| /* |
| * utp_do_selftest |
| * |
| * perform some diagnostics |
| * |
| * TBW |
| */ |
| static int utp_do_selftest(void) |
| { |
| return 0; |
| } |
| /* |
| * Put the command which needs to send busy first |
| * And the host will send poll for getting its return value |
| * later, we call these kinds of commands as Asynchronous Commands. |
| */ |
| static int utp_can_busy(char *command) |
| { |
| char *async[] ={ |
| "$ ", "frf", "pollpipe", NULL, |
| }; |
| char **ptr; |
| |
| ptr = async; |
| while (*ptr) { |
| if (strncmp(command, *ptr, strlen(*ptr)) == 0) |
| return 1; |
| ptr++; |
| } |
| return 0; |
| } |
| #ifdef NEED_TO_GET_CHILD_PID |
| /* for pipe */ |
| #define READ 0 |
| #define WRITE 1 |
| static pid_t child_pid = -1; |
| static int utp_flush(void) |
| { |
| int pstat; |
| int ret = 0; |
| pid_t pid; |
| if (utp_file >= 0) { |
| fflush(NULL); |
| ret = close(utp_file); |
| do{ |
| pid = waitpid(child_pid, &pstat, 0); /* wait for child finished */ |
| }while (pid == -1 && errno == EINTR); |
| printf("UTP: closing the file\n"); |
| } |
| utp_file = -1; |
| return ret; |
| } |
| pid_t popen2(const char *command, int *infp, int *outfp) |
| { |
| int p_stdin[2], p_stdout[2]; |
| pid_t pid; |
| |
| if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0) |
| return -1; |
| |
| pid = fork(); |
| |
| if (pid < 0) |
| return pid; |
| else if (pid == 0){ |
| close(p_stdin[WRITE]); |
| if (infp == NULL) |
| close(p_stdin[READ]); |
| else |
| dup2(p_stdin[READ], READ); |
| close(p_stdout[READ]); |
| if (outfp == NULL) |
| close(p_stdout[WRITE]); |
| else |
| dup2(p_stdout[WRITE], WRITE); |
| |
| execl("/bin/sh", "sh", "-c", command, NULL); |
| perror("execl"); |
| exit(1); |
| } |
| |
| if (infp == NULL) |
| close(p_stdin[WRITE]); |
| else |
| *infp = p_stdin[WRITE]; |
| |
| if (outfp == NULL) |
| close(p_stdout[READ]); |
| else |
| *outfp = p_stdout[READ]; |
| |
| close(p_stdin[READ]); |
| close(p_stdout[WRITE]); |
| return pid; |
| } |
| int utp_pipe(char *command, ... ) |
| { |
| int r, infp; |
| char shell_cmd[1024]; |
| va_list vptr; |
| va_start(vptr, command); |
| vsnprintf(shell_cmd, sizeof(shell_cmd), command, vptr); |
| va_end(vptr); |
| |
| child_pid = popen2(shell_cmd, &infp, NULL); |
| if (child_pid < 0){ |
| printf("the fork is failed \n"); |
| return -1; |
| } |
| utp_file = infp; |
| printf("pid is %d, UTP: executing \"%s\"\n",child_pid, shell_cmd); |
| return 0; |
| } |
| int utp_poll_pipe() |
| { |
| int ret = 0, cnt = 0xFFFF; |
| while(ret == 0 && cnt > 0){ |
| ret = is_child_dead(); |
| usleep(10000); |
| cnt--; |
| } |
| |
| if(ret == 0) |
| return 1;//failure |
| else |
| return 0;//Success |
| } |
| |
| |
| #else |
| static int utp_flush(void) |
| { |
| int ret; |
| if (utp_file_f) { |
| printf("UTP: waiting for pipe to close\n"); |
| ret = pclose(utp_file_f); |
| } |
| else if (utp_file >= 0) { |
| printf("UTP: closing the file\n"); |
| ret = close(utp_file); |
| } |
| utp_file_f = NULL; |
| utp_file = -1; |
| printf("UTP: files were flushed.\n"); |
| return ret; |
| } |
| static int utp_pipe(char *command, ... ) |
| { |
| int r; |
| char shell_cmd[1024]; |
| va_list vptr; |
| |
| va_start(vptr, command); |
| vsnprintf(shell_cmd, sizeof(shell_cmd), command, vptr); |
| va_end(vptr); |
| |
| utp_file_f = popen(shell_cmd, "w"); |
| utp_file = fileno(utp_file_f); |
| |
| return utp_file_f ? 0 : errno; |
| } |
| #endif |
| /* |
| * utp_handle_command |
| * |
| * handle the command from MSC driver |
| * command can be: |
| * ? |
| * !<type> |
| * $ <shell_command> |
| * wfs/wff <X> write firmware to SD/flash |
| * wrs/wrf <X> write rootfs to SD/flash |
| * frs/frf <X> format partition for root on SD/flash |
| * erase <X> erase partition on flash |
| * read not implemented yet |
| * write not implemented yet |
| */ |
| static struct utp_message *utp_handle_command(int u, char *cmd, unsigned long long payload) |
| { |
| struct utp_message *w = NULL; |
| char devnode[50]; /* enough to fit /dev/mmcblk0p99 */ |
| char sysnode[50]; /* -"- -"- mmcblk0/mmcblk0p99 */ |
| uint32_t flags, status; |
| char *data = NULL; |
| int f; |
| size_t size; |
| |
| printf("UTP: received command '%s'\n", cmd); |
| |
| /* defaults */ |
| status = 0; |
| flags = 0; |
| size = 0; |
| |
| /* these are asynchronous commands and need to send busy */ |
| if (utp_can_busy(cmd)){ |
| utp_send_busy(u); |
| } |
| |
| if (strcmp(cmd, "?") == 0) { |
| /* query */ |
| flags = UTP_FLAG_DATA; |
| data = malloc(256); |
| sprintf(data, |
| "<DEVICE>\n" |
| " <FW>%s</FW>\n" |
| " <DCE>%s</DCE>\n" |
| " <SN>%s</SN>" |
| " <CID>%s</CID>" |
| " <VID>%04X</VID>" |
| " <PID>%04X</PID>" |
| "</DEVICE>\n", utp_firmware_version, VERSION, utp_sn, utp_chipid, 0x66F, 0x37FF); |
| size = (strlen(data) + 1 ) * sizeof(data[0]); |
| } |
| |
| else if (cmd[0] == '!') { |
| /* reboot the system, and the ACK has already sent out */ |
| if (cmd[1] == '3') { |
| sync(); |
| kill(-1, SIGTERM); |
| sleep(1); |
| kill(-1, SIGKILL); |
| |
| reboot(LINUX_REBOOT_CMD_RESTART); |
| return NULL; |
| } |
| } |
| |
| else if (strncmp(cmd, "$ ", 2) == 0) { |
| status = utp_run(cmd + 2); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| else if ((strcmp(cmd,"wff") == 0) || (strcmp(cmd, "wfs") == 0)) { |
| /* Write firmware - to flash or to SD, no matter */ |
| utp_file = open(UTP_TARGET_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0666); |
| } |
| |
| else if (strcmp(cmd, "fff") == 0) { |
| /* perform actual flashing of the firmware to the NAND */ |
| utp_flush(); |
| if (utp_mk_devnode("class/mtd", "mtd1", "/dev/mtd1", S_IFCHR) >= 0 && |
| utp_mk_devnode("class/mtd", "mtd0", "/dev/mtd0", S_IFCHR) >= 0) { |
| utp_run("kobs-ng -v -d %s", UTP_TARGET_FILE); |
| } |
| } |
| else if (strcmp(cmd, "ffs") == 0) { |
| /* perform actual flashing of the firmware to the SD */ |
| utp_flush(); |
| |
| /* partition the card */ |
| if (!status && utp_mk_devnode("block", "mmcblk0", "/dev/mmc", S_IFBLK) >= 0) { |
| status = utp_partition_mmc("/dev/mmc"); |
| /* waiting for partition table to settle */ |
| sleep(5); |
| } |
| |
| /* write data to the first partition */ |
| if (!status && utp_mk_devnode("block", "mmcblk0/mmcblk0p1", "/dev/mmc0p1", S_IFBLK) >= 0) { |
| utp_run("dd if=/dev/zero of=/dev/mmc0p1 bs=512 count=4"); |
| utp_run("dd if=%s of=/dev/mmc0p1 ibs=512 seek=4 conv=sync,notrunc", UTP_TARGET_FILE); |
| } |
| |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "mknod", 5) == 0) { |
| int devtype = S_IFCHR; |
| char *class, *item, *type, *node; |
| |
| class = strtok(cmd + 6, " \t,;"); |
| printf("class = '%s'\n", class); |
| item = strtok(NULL, " \t,;"); |
| printf("item = '%s'\n", item); |
| node = strtok(NULL, " \t,;"); |
| printf("node = %s\n", node); |
| type = strtok(NULL, " \t,;"); |
| printf("type = %s\n", type); |
| if (!node && item) |
| sprintf(devnode, "/dev/%s", item); |
| else |
| strcpy(devnode, node); |
| if (type && (strcmp(type, "block") == 0 || strcmp(type, "blk") == 0)) |
| devtype = S_IFBLK; |
| printf("UTP: running utp_mk_devnode(%s,%s,%s,0x%x)\n", |
| class, item, devnode, devtype); |
| status = utp_mk_devnode(class, item, devnode, devtype); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "wrf", 3) == 0) { |
| /* Write rootfs to flash */ |
| printf("UTP: writing rootfs to flash, mtd #%c, size %lld\n", |
| cmd[3], payload); |
| |
| /* ensure that device node is here */ |
| snprintf(devnode, sizeof(devnode), "/dev/mtd%c", cmd[3]); |
| utp_mk_devnode("class/mtd", devnode + 5, devnode, S_IFCHR); |
| |
| /* then start ubiformat and redirect its input */ |
| status = utp_pipe("ubiformat %s -f - -S %lld", devnode, payload); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "pipe", 4) == 0) { |
| status = utp_pipe(cmd + 5); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "pollpipe", 8) == 0) { |
| printf("UTP: poll pipe.\n"); |
| status = utp_poll_pipe(); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "wrs", 3) == 0) { |
| /* Write rootfs to the SD */ |
| printf("UTP: writing rootfs to SD card, mmc partition #%c, size %lld\n", |
| cmd[3], payload); |
| |
| /* ensure that device node is here */ |
| snprintf(devnode, sizeof(devnode), "/dev/mmcblk0p%c", cmd[3]); |
| snprintf(sysnode, sizeof(sysnode), "mmcblk0/mmcblk0p%d", cmd[3]); |
| utp_mk_devnode("block", sysnode, devnode, S_IFBLK); |
| |
| if (payload % 1024) |
| printf("UTP: WARNING! payload %% 1024 != 0, the rest will be skipped"); |
| |
| status = utp_pipe("dd of=%s bs=1K", devnode); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| |
| else if (strcmp(cmd, "frf") == 0 || strcmp(cmd, "frs") == 0) { |
| /* perform actual flashing of the rootfs to the NAND/SD */ |
| status = utp_flush(); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "untar.", 6) == 0) { |
| status = utp_pipe("tar %cxv -C %s", cmd[6], cmd + 8); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else if (strncmp(cmd, "read", 4) == 0) { |
| f = open(cmd + 5, O_RDONLY); |
| if (f < 0) { |
| flags = UTP_FLAG_STATUS; |
| status = errno; |
| } else { |
| size = lseek(f, 0, SEEK_END); /* get the file size */ |
| lseek(f, 0, SEEK_SET); |
| |
| data = malloc(size); |
| if (!data) { |
| flags = UTP_FLAG_STATUS; |
| status = -ENOMEM; |
| } else { |
| read(f, data, size); |
| flags = UTP_FLAG_DATA; |
| } |
| } |
| } |
| |
| else if (strcmp(cmd, "send") == 0) { |
| utp_file = open(UTP_TARGET_FILE, O_TRUNC | O_CREAT | O_WRONLY, 0666); |
| } |
| |
| else if (strncmp(cmd, "save", 4) == 0) { |
| close(utp_file); |
| rename(UTP_TARGET_FILE, cmd + 5); |
| } |
| |
| |
| else if (strcmp(cmd, "selftest") == 0) { |
| status = utp_do_selftest(); |
| if (status) |
| flags = UTP_FLAG_STATUS; |
| } |
| |
| else { |
| printf("UTP: Unknown command received, ignored\n"); |
| flags = UTP_FLAG_STATUS; |
| status = -EINVAL; |
| } |
| |
| w = malloc(size + sizeof(*w)); |
| if (!w) { |
| printf("UTP: Could not allocate %d+%d bytes!\n", size, sizeof(*w)); |
| return NULL; |
| } |
| |
| memset(w, 0, sizeof(*w) + size); |
| w->flags = flags; |
| w->size = size + sizeof(*w); |
| if (flags & UTP_FLAG_DATA) { |
| w->bufsize = size; |
| memcpy(w->data, data, size); |
| } |
| if (flags & UTP_FLAG_STATUS) |
| w->status = status; |
| if (data) |
| free(data); |
| return w; |
| } |
| |
| /* |
| * Check the process is dead |
| */ |
| #define NAME_MAX 30 |
| int is_child_dead(void) |
| { |
| FILE *fh; |
| char path[NAME_MAX + 1]; |
| sprintf(path, "/proc/%u/status", (unsigned int)child_pid); |
| if ((fh = fopen(path, "r"))){ |
| char buf[1024]; |
| while (fgets(buf, sizeof(buf) -1, fh)){ |
| if (!strncmp(buf, "State:", 6)) |
| { |
| char *p = buf + 6; |
| while (*p == '\t'){ |
| p++; |
| continue; |
| } |
| if (*p == 'Z'){ |
| printf("Process status polling: %s is in zombie.\n",path); |
| fclose(fh); |
| return 1; |
| } |
| break; |
| } |
| } |
| } |
| else{ |
| printf("Process polling: can't open %s, maybe the process %u has been killed already\n",path,child_pid); |
| return 1; |
| } |
| |
| fclose(fh); |
| return 0; |
| } |
| |
| |
| void feed_watchdog(void *arg) |
| { |
| int res; |
| int *fd = arg; |
| while(1) { |
| res = ioctl(*fd, WDIOC_KEEPALIVE); |
| if (res) |
| printf("ioctl WDIOC_KEEPALIVE error L%d, %s\n", __LINE__, strerror(errno)); |
| printf("%s\n", __func__); |
| sleep(60); |
| } |
| } |
| |
| int main(void) |
| { |
| int u = -1, wdt_fd = -1, r, need_watchdog = 0; |
| int watchdog_timeout = 127; /* sec */ |
| int cpu_id = 50; |
| struct utp_message *uc, *answer; |
| char env[256]; |
| pthread_t a_thread; |
| void *thread_result; |
| |
| printf("%s %s [built %s %s]\n", PACKAGE, VERSION, __DATE__, __TIME__); |
| /* set stdout unbuffered, what is the usage??? */ |
| // setvbuf(stdout, NULL, _IONBF, 0); |
| uc = malloc(sizeof(*uc) + 0x10000); |
| |
| mkdir("/tmp", 0777); |
| |
| setenv("FILE", UTP_TARGET_FILE, !0); |
| |
| printf("UTP: Waiting for device to appear\n"); |
| while (utp_mk_devnode("class/misc", "utp", UTP_DEVNODE, S_IFCHR) < 0) { |
| putchar('.'); |
| sleep(1); |
| } |
| u = open(UTP_DEVNODE, O_RDWR); |
| r = ioctl(u, UTP_GET_CPU_ID, &cpu_id); |
| if (r) |
| printf("cpu id get error:L%d, %s\n", __LINE__, strerror(errno)); |
| else{ |
| switch (cpu_id) { |
| case 23: |
| case 25: |
| case 28: |
| case 50: |
| need_watchdog = 0; |
| break; |
| case 35: |
| case 51: |
| case 53: |
| need_watchdog = 1; |
| break; |
| default: |
| need_watchdog = 0; |
| } |
| printf("cpu_id is %d\n", cpu_id); |
| if (need_watchdog){ |
| if (utp_mk_devnode("class/misc", "watchdog", "/dev/watchdog", S_IFCHR)){ |
| printf("The watchdog is not configured, needed by mx35/mx51/mx53 \n"); |
| printf("%d, %s\n", __LINE__, strerror(errno)); |
| } else{ |
| wdt_fd = open("/dev/watchdog", O_RDWR); |
| /* set the MAX timeout */ |
| r = ioctl(wdt_fd, WDIOC_SETTIMEOUT, &watchdog_timeout); |
| if (r) |
| printf("%d, %s\n", __LINE__, strerror(errno)); |
| r = pthread_create(&a_thread, NULL, (void *)feed_watchdog, (void *)(&wdt_fd)); |
| if (r != 0) { |
| perror("Thread creation failed"); |
| exit(EXIT_FAILURE); |
| } |
| } |
| } |
| } |
| |
| for(;;) { |
| r = read(u, uc, sizeof(*uc) + 0x10000); |
| if (uc->flags & UTP_FLAG_COMMAND) { |
| answer = utp_handle_command(u, uc->command, uc->payload); |
| if (answer) { |
| printf("UTP: sending %s to kernel for command %s.\n", utp_answer_type(answer), uc->command); |
| write(u, answer, answer->size); |
| free(answer); |
| } |
| }else if (uc->flags & UTP_FLAG_DATA) { |
| write(utp_file, uc->data, uc->bufsize); |
| }else { |
| printf("UTP: Unknown flag %x\n", uc->flags); |
| } |
| } |
| |
| /* should never be here */ |
| return 0; |
| } |
| |