zynq开发板的xvcServer作为烧录器

把ZYNQ变成一个网口下载器,调试其他的板子。

项目地址

https://github.com/Xilinx/XilinxVirtualCable

移植指南

https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/644579329/Xilinx+Virtual+Cable

编译驱动和app

bash 复制代码
# 拉取代码
git clone https://github.com/Xilinx/XilinxVirtualCable.git
cd .\XilinxVirtualCable\jtag\zynqMP\src\driver\
wsl
ssdk
# 编译驱动
make modules KERNEL_SRC=/home/minglie/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2
# 编译app
cd /mnt/e/ant_s9_fix_nand/XilinxVirtualCable/jtag/zynqMP/src/user
make -e MYCC="$CC"

加载驱动,启动xvcServer_ioctl

bash 复制代码
root@ant:~# ls               
xilinx_xvc_driver.ko   xvcServer_ioctl
root@ant:~# insmod xilinx_xvc_driver.ko
xilinx_xvc_driver: loading out-of-tree module taints kernel.
xilinx_xvc_driver: Starting...
xilinx_xvc_driver: Created device xilinx_xvc_driver
root@ant:~# ./xvcServer_ioctl
INFO: XVC driver character file: /dev/xilinx_xvc_driver
INFO: debug_bridge base address: 0x43C10000
INFO: debug_bridge size: 0x10000
INFO: debug_bridge device tree compatibility string: xlnx,xvc
INFO: To connect to this xvcServer instance, use url: TCP:z8:2542

设备树

bash 复制代码
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/media/xilinx-vip.h>
#include <dt-bindings/phy/phy.h>

/ {
	model = "z7 Board ant";
	compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";
	chosen {
		bootargs = "console=ttyPS0,115200 earlycon=cdns,mmio,0xe0000000,115200n8 keep_bootcon earlyprintk root=/dev/mmcblk0p2 rw rootwait";
		stdout-path = "serial0:115200n8";
	};

	leds {
		compatible = "gpio-leds";
		gpio-led1 {
			label = "led2";
			gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
		gpio-led5 {
			label = "ps_led0";
			gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
		gpio-led6 {
			label = "ps_led1";
			gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
			linux,default-trigger = "timer";
		};
	};



};


&debug_bridge_0 {
        compatible = "xlnx,xvc";
};

驱动源码

bash 复制代码
minglie@DESKTOP-NTI9KM5:/mnt/e/ant_s9_fix_nand/XilinxVirtualCable/jtag/zynqMP/src/driver$ ll
total 32
drwxrwxrwx 1 minglie minglie 4096 Jul 23 23:32 ./
drwxrwxrwx 1 minglie minglie 4096 Jul 23 22:05 ../
-rwxrwxrwx 1 minglie minglie  535 Jul 23 22:05 Makefile*
-rwxrwxrwx 1 minglie minglie 3625 Jul 23 22:05 README.md*
-rwxrwxrwx 1 minglie minglie 5659 Jul 23 22:05 xvc_driver.c*
-rwxrwxrwx 1 minglie minglie 1422 Jul 23 22:05 xvc_driver.h*
-rwxrwxrwx 1 minglie minglie 8086 Jul 23 22:05 xvc_driver_base.c*
-rwxrwxrwx 1 minglie minglie   59 Jul 23 22:05 xvc_fragment.dts*
-rwxrwxrwx 1 minglie minglie 1409 Jul 23 22:05 xvc_ioctl.h*
-rwxrwxrwx 1 minglie minglie   27 Jul 23 22:05 xvc_user_config.h*
-rwxrwxrwx 1 minglie minglie 2947 Jul 23 22:05 xvc_user_config_example.h*

Makefile

c 复制代码
DRIVER := xilinx_xvc_driver
export CROSS_COMPILE ?= aarch64-linux-gnu-
export ARCH ?= arm64

ccflags-y := -DLOG_PREFIX=\"$(DRIVER):\ \"
$(DRIVER)-y := \
	xvc_driver_base.o \
	xvc_driver.o

obj-m += $(DRIVER).o

SRC := $(shell pwd)

modules:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules

modules_install:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

.PHONY: clean
clean:
	rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
	rm -f Module.markers Module.symvers modules.order
	rm -rf .tmp_versions Modules.symvers

xvc_driver.h

c 复制代码
/*
 * Xilinx XVC Driver
 * Copyright (C) 2019 Xilinx Corporation
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef _XVC_DRIVER_H
#define _XVC_DRIVER_H

#include "xvc_ioctl.h"
#include "xvc_user_config.h"

#ifndef _XVC_USER_CONFIG_H
#define XVC_DRIVER_NAME "xilinx_xvc_driver"
#define DEBUG_BRIDGE_COMPAT_STRING "xlnx,xvc"
// debug bridge configuration
struct db_config {
	const char* name;
	unsigned long base_addr;
	unsigned long size;
};
#endif

#ifndef LOG_PREFIX
#define LOG_PREFIX XVC_DRIVER_NAME ": "
#endif

long xil_xvc_ioctl(unsigned char* db_ptr, const char __user* arg);
long xil_xvc_readprops(const struct db_config* db_config, const char __user* arg);

#endif /* _XVC_DRIVER_H */

xvc_driver.c

c 复制代码
/*
 * Xilinx XVC Driver
 * Copyright (C) 2019 Xilinx Corporation
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#include "xvc_driver.h"

#define LENGTH_REG_OFFSET   (0)
#define TMS_REG_OFFSET      (4)
#define TDI_REG_OFFSET      (8)
#define TDO_REG_OFFSET      (12)
#define CONTROL_REG_OFFSET  (16)

static int xil_xvc_shift_bits(unsigned char* db_ptr, u32 tms_bits, u32 tdi_bits, u32 *tdo_bits) {
	int status = 0;
	u32 control_reg_data;
	u32 write_reg_data;
	int count = 100;

	// Set tms bits
	iowrite32(tms_bits, db_ptr + TMS_REG_OFFSET);

	// Set tdi bits and shift data out
	iowrite32(tdi_bits, db_ptr + TDI_REG_OFFSET);

	// Read control register
	control_reg_data = ioread32(db_ptr + CONTROL_REG_OFFSET);

	// Enable shift operation in control register
	write_reg_data = control_reg_data | 0x01;

	// Write control register
	iowrite32(write_reg_data, db_ptr + CONTROL_REG_OFFSET);

	while (count) {
		// Read control reg to check shift operation completion
		control_reg_data = ioread32(db_ptr + CONTROL_REG_OFFSET);
		if ((control_reg_data & 0x01) == 0)	{
			break;
		}
		count--;
	}
	if (count == 0)	{
		printk(KERN_ERR LOG_PREFIX "XVC transaction timed out (%0X)\n", control_reg_data);
		return -ETIMEDOUT;
	}

	// Read tdo bits back out
	*tdo_bits = ioread32(db_ptr + TDO_REG_OFFSET);

	return status;
}

long xil_xvc_ioctl(unsigned char* db_ptr, const char __user *arg) {
	struct xil_xvc_ioc xvc_obj;
	u32 operation_code;
	u32 num_bits;
	int num_bytes;
	char *tms_buf_temp = NULL;
	char *tdi_buf_temp = NULL;
	char *tdo_buf_temp = NULL;
	int current_bit;
	u32 bypass_status;
	long status = 0;

	if ((status = copy_from_user((void *)&xvc_obj, arg, sizeof(struct xil_xvc_ioc)))) {
		goto cleanup;
	}

	operation_code = xvc_obj.opcode;

	// Invalid operation type, no operation performed
	if (operation_code != 0x01 && operation_code != 0x02) {
		return 0;
	}

	num_bits = xvc_obj.length;
	num_bytes = (num_bits + 7) / 8;

	// Allocate and copy data into temporary buffers
	tms_buf_temp = (char*) kmalloc(num_bytes, GFP_KERNEL);
	if (tms_buf_temp == NULL) {
		status = -ENOMEM;
		goto cleanup;
	}
	if ((status = copy_from_user((void *)tms_buf_temp, xvc_obj.tms_buf, num_bytes))) {
		goto cleanup;
	}

	tdi_buf_temp = (char*) kmalloc(num_bytes, GFP_KERNEL);
	if (tdi_buf_temp == NULL) {
		status = -ENOMEM;
		goto cleanup;
	}
	if ((status = copy_from_user((void *)tdi_buf_temp, xvc_obj.tdi_buf, num_bytes))) {
		goto cleanup;
	}

	// Allocate TDO buffer
	tdo_buf_temp = (char*) kmalloc(num_bytes, GFP_KERNEL);
	if (tdo_buf_temp == NULL) {
		status = -ENOMEM;
		goto cleanup;
	}

	if (operation_code == 0x2) {
		bypass_status = 0x2;
	} else {
		bypass_status = 0x0;
	}

	iowrite32(bypass_status, db_ptr + CONTROL_REG_OFFSET);

	// Set length register to 32 initially if more than one word-transaction is to be done
	if (num_bits >= 32) {
		iowrite32(0x20, db_ptr + LENGTH_REG_OFFSET);
	}

	current_bit = 0;
	while (current_bit < num_bits) {
		int shift_num_bytes;
		int shift_num_bits = 32;

		u32 tms_store = 0;
		u32 tdi_store = 0;
		u32 tdo_store = 0;

		if (num_bits - current_bit < shift_num_bits) {
			shift_num_bits = num_bits - current_bit;
			// do LENGTH_REG_OFFSET here
			// Set number of bits to shift out
			iowrite32(shift_num_bits, db_ptr + LENGTH_REG_OFFSET);
		}

		// Copy only the remaining number of bytes out of user-space
		shift_num_bytes = (shift_num_bits + 7) / 8;

		memcpy(&tms_store, tms_buf_temp + (current_bit / 8), shift_num_bytes);
		memcpy(&tdi_store, tdi_buf_temp + (current_bit / 8), shift_num_bytes);

		// Shift data out and copy to output buffer
		status = xil_xvc_shift_bits(db_ptr, tms_store, tdi_store, &tdo_store);
		if (status) {
			goto cleanup;
		}

		memcpy(tdo_buf_temp + (current_bit / 8), &tdo_store, shift_num_bytes);

		current_bit += shift_num_bits;
	}

	if (copy_to_user((void *)xvc_obj.tdo_buf, tdo_buf_temp, num_bytes)) {
		status = -EFAULT;
		goto cleanup;
	}

cleanup:
	if (tms_buf_temp) kfree(tms_buf_temp);
	if (tdi_buf_temp) kfree(tdi_buf_temp);
	if (tdo_buf_temp) kfree(tdo_buf_temp);
	return status;
}

long xil_xvc_readprops(const struct db_config* db_config, const char __user* arg) {
	struct xil_xvc_properties xvc_props_obj;

	if (!db_config) {
		return -EINVAL;
	}

	xvc_props_obj.debug_bridge_base_addr = db_config->base_addr;
	xvc_props_obj.debug_bridge_size = db_config->size;
	strcpy(xvc_props_obj.debug_bridge_compat_string, DEBUG_BRIDGE_COMPAT_STRING);

	if (copy_to_user((void *)arg, &xvc_props_obj, sizeof(xvc_props_obj))) {
		return -ENOMEM;
	}

	return 0;
}

xvc_driver_base.c

c 复制代码
/*
 * Xilinx XVC Driver
 * Copyright (C) 2019 Xilinx Corporation
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/io.h>
#include <linux/mod_devicetable.h>

#include "xvc_driver.h"
#include "xvc_user_config.h"

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Max Heimer <maxh@xilinx.com>");
MODULE_DESCRIPTION("XVC Debug Register Access");
MODULE_VERSION("0.1.0");

static dev_t xvc_ioc_dev_region;
static struct class* xvc_dev_class = NULL;
static struct cdev xvc_char_ioc_dev;

#ifndef _XVC_USER_CONFIG_H
#define CONFIG_COUNT 1
#define GET_DB_BY_RES 1
static struct resource *db_res = NULL;
#endif /* _XVC_USER_CONFIG_H */

static void __iomem * db_ptrs[CONFIG_COUNT];

static void xil_xvc_cleanup(void) {
	printk(KERN_INFO LOG_PREFIX "Cleaning up resources...\n");

	if (!IS_ERR(xvc_dev_class)) {
		class_destroy(xvc_dev_class);
		xvc_dev_class = NULL;
		if (xvc_char_ioc_dev.owner != NULL) {
			cdev_del(&xvc_char_ioc_dev);
		}
		unregister_chrdev_region(xvc_ioc_dev_region, CONFIG_COUNT);
	}
}

long char_ctrl_ioctl(struct file *file_p, unsigned int cmd, unsigned long arg) {
	long status = 0;
	unsigned long irqflags = 0;
	int char_index = iminor(file_p->f_path.dentry->d_inode) - MINOR(xvc_ioc_dev_region);

	spin_lock_irqsave(&file_p->f_path.dentry->d_inode->i_lock, irqflags);

	switch (cmd) {
		case XDMA_IOCXVC:
			status = xil_xvc_ioctl((unsigned char*)(db_ptrs[char_index]), (void __user *)arg);
			break;
		case XDMA_RDXVC_PROPS:
			{
#ifndef GET_DB_BY_RES
				struct db_config config_info = db_configs[char_index];
#else
				struct db_config config_info = {
					.name = NULL,
					.base_addr = db_res ? db_res->start : 0,
					.size = db_res ? resource_size(db_res) : 0,
				};
#endif
				status = xil_xvc_readprops(&config_info, (void __user*)arg);
				break;
			}
		default:
			status = -ENOIOCTLCMD;
			break;
	}

#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 1, 0)
	mmiowb();
#endif
	spin_unlock_irqrestore(&file_p->f_path.dentry->d_inode->i_lock, irqflags);

	return status;
}

static struct file_operations xil_xvc_ioc_ops = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = char_ctrl_ioctl
};

int probe(struct platform_device* pdev) {
	int status;
	int i;
	unsigned int use_index = CONFIG_COUNT > 1;
	dev_t ioc_device_number;
	char ioc_device_name[32];
	struct device* xvc_ioc_device = NULL;

	if (!xvc_dev_class) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
		xvc_dev_class = class_create(XVC_DRIVER_NAME);
#else
		xvc_dev_class = class_create(THIS_MODULE, XVC_DRIVER_NAME);
#endif
		if (IS_ERR(xvc_dev_class)) {
			xil_xvc_cleanup();
			dev_err(&pdev->dev, "unable to create class\n");
			return PTR_ERR(xvc_dev_class);
		}

		cdev_init(&xvc_char_ioc_dev, &xil_xvc_ioc_ops);
		xvc_char_ioc_dev.owner = THIS_MODULE;
		status = cdev_add(&xvc_char_ioc_dev, xvc_ioc_dev_region, CONFIG_COUNT);
		if (status != 0) {
			xil_xvc_cleanup();
			dev_err(&pdev->dev, "unable to add char device\n");
			return status;
		}
	}

	for (i = 0; i < CONFIG_COUNT; ++i) {
		if (db_ptrs[i] == NULL) {
#ifndef GET_DB_BY_RES
			const char *name = db_configs[i].name;
			unsigned long db_addr = db_configs[i].base_addr;
			unsigned long db_size = db_configs[i].size;
#else
			const char *name = NULL;
			unsigned long db_addr = 0;
			unsigned long db_size = 0;
#endif

			if (name && name[0]) {
				sprintf(ioc_device_name, "%s_%s", XVC_DRIVER_NAME, name);
			} else if (use_index) {
				sprintf(ioc_device_name, "%s_%d", XVC_DRIVER_NAME, i);
			} else {
				sprintf(ioc_device_name, "%s", XVC_DRIVER_NAME);
			}

			ioc_device_number = MKDEV(MAJOR(xvc_ioc_dev_region), MINOR(xvc_ioc_dev_region) + i);

			xvc_ioc_device = device_create(xvc_dev_class, NULL, ioc_device_number, NULL, ioc_device_name);
			if (IS_ERR(xvc_ioc_device)) {
				printk(KERN_WARNING LOG_PREFIX "Failed to create device %s", ioc_device_name);
				xil_xvc_cleanup();
				dev_err(&pdev->dev, "unable to create the device\n");
				return status;
			} else {
				printk(KERN_INFO LOG_PREFIX "Created device %s", ioc_device_name);
			}

#ifndef GET_DB_BY_RES
			db_ptrs[i] = ioremap(db_addr, db_size);
#else
			db_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
			if (db_res) {
				db_addr = db_res->start;
				db_size = resource_size(db_res);
			}
			db_ptrs[i] = devm_ioremap_resource(&pdev->dev, db_res);
#endif
			if (!db_ptrs[i] || IS_ERR(db_ptrs[i])) {
				printk(KERN_ERR LOG_PREFIX "Failed to remap debug bridge memory at offset 0x%lX, size %lu", db_addr, db_size);
				return -ENOMEM;
			} else {
				printk(KERN_INFO LOG_PREFIX "Mapped debug bridge at offset 0x%lX, size 0x%lX", db_addr, db_size);
			}
		}
	}

	return 0;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
static int remove(struct platform_device* pdev)
#else
static void remove(struct platform_device* pdev)
#endif
{
	int i;
	dev_t ioc_device_number;
	if (pdev) {
		for (i = 0; i < CONFIG_COUNT; ++i) {
			if (db_ptrs[i]) {
#ifndef GET_DB_BY_RES
				unsigned long db_addr = db_configs[i].base_addr;
				unsigned long db_size = db_configs[i].size;
#else
				unsigned long db_addr = 0;
				unsigned long db_size = 0;
				if (db_res) {
					db_addr = db_res->start;
					db_size = resource_size(db_res);
				}
#endif

				printk(KERN_INFO LOG_PREFIX "Unmapping debug bridge at offset 0x%lX, size %lu", db_addr, db_size);
#ifndef GET_DB_BY_RES
				iounmap(db_ptrs[i]);
#else
				// devm_ioremap_resource is managed by the kernel and undone on driver detach.
#endif
				db_ptrs[i] = NULL;

				ioc_device_number = MKDEV(MAJOR(xvc_ioc_dev_region), MINOR(xvc_ioc_dev_region) + i);
				device_destroy(xvc_dev_class, ioc_device_number);
				printk(KERN_INFO LOG_PREFIX "Destroyed device number %u (user config %i)", ioc_device_number, i);
			}
		}
	}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
	return 0;
#endif
}

static const struct of_device_id xvc_of_ids[] = {
	{ .compatible = DEBUG_BRIDGE_COMPAT_STRING, },
	{}
};

static struct platform_driver xil_xvc_plat_driver = {
	.driver = {
		.name = XVC_DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table = xvc_of_ids,
	},
	.probe = probe,
	.remove = remove,
};

// --------------------------
// --------------------------
// Driver initialization code
// --------------------------
// --------------------------

static int __init xil_xvc_init(void) {
	int err = 0;

	printk(KERN_INFO LOG_PREFIX "Starting...\n");

	// Register the character packet device major and minor numbers
	err = alloc_chrdev_region(&xvc_ioc_dev_region, 0, CONFIG_COUNT, XVC_DRIVER_NAME);
	if (err != 0) {
		xil_xvc_cleanup();
		printk(KERN_ERR LOG_PREFIX "unable to get char device region\n");
		return err;
	}

	memset(db_ptrs, 0, sizeof(*db_ptrs));

	return platform_driver_register(&xil_xvc_plat_driver);
}

static void __exit xil_xvc_exit(void) {
	platform_driver_unregister(&xil_xvc_plat_driver);
	xil_xvc_cleanup();
}

module_init(xil_xvc_init);
module_exit(xil_xvc_exit);

xvc_ioctl.h

c 复制代码
/*
 * Xilinx XVC Driver
 * Copyright (C) 2019 Xilinx Corporation
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef _XVC_IOCTL_H
#define _XVC_IOCTL_H

#include <linux/ioctl.h>

#define XIL_XVC_MAGIC 0x58564344  // "XVCD"

struct xil_xvc_ioc {
	unsigned opcode;
	unsigned length;
	unsigned char* tms_buf;
	unsigned char* tdi_buf;
	unsigned char* tdo_buf;
};

struct xil_xvc_properties {
    unsigned long debug_bridge_base_addr;
    unsigned long debug_bridge_size;
    char debug_bridge_compat_string[64];
};

#define XDMA_IOCXVC      _IOWR(XIL_XVC_MAGIC, 1, struct xil_xvc_ioc)
#define XDMA_RDXVC_PROPS _IOR(XIL_XVC_MAGIC, 2, struct xil_xvc_properties)

#endif /* _XVC_IOCTL_H */

xvc_user_config.h

c 复制代码
 //空

xvc_user_config_example.h

c 复制代码
/*
 * Xilinx XVC Driver
 * Copyright (C) 2019 Xilinx Corporation
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef _XVC_USER_CONFIG_H
#define _XVC_USER_CONFIG_H

// debug bridge configuration
struct db_config {
    const char* name;
    unsigned long base_addr;
    unsigned long size;
};

/*
 * Modify the macros and structure below with customizations
 * for the driver, if desired.
 *
 * XVC_DRIVER_NAME            - name of the driver and character files
 * DEBUG_BRIDGE_COMPAT_STRING - debug_bridge ".compatible" entry in device tree
 *
 * name      - alias to append to character file name
 * base_addr - debug_bridge base address from device tree
 * size      - debug_bridge size from device tree
 */
#define XVC_DRIVER_NAME "xilinx_xvc_driver"
#define DEBUG_BRIDGE_COMPAT_STRING "xlnx,xvc"

static const struct db_config db_configs[] = {
    /////////////////////////////////////////////////////////
    //  The single debug tree entry below with an empty 
    //  name modifier will create a character file called:
    //
    //   /dev/xilinx_xvc_driver
    //
    /////////////////////////////////////////////////////////
    // {
    //     .name = "",
    //     .base_addr = 0x80010000,
    //     .size = 0x10000,
    // },
    /////////////////////////////////////////////////////////
    //  For two debug trees in the same driver, you can 
    //  uncomment and modify the entries below.  If names are
    //  empty, only the index will be appended to the
    //  character file:
    //   /dev/xilinx_xvc_driver_0
    //   /dev/xilinx_xvc_driver_1
    //
    //  In this example, since the names are not empty, the
    //  names "tree0" and "tree1" are appended to the
    //  character files as follows:
    //
    //   /dev/xilinx_xvc_driver_tree0
    //   /dev/xilinx_xvc_driver_tree1
    //
    /////////////////////////////////////////////////////////
    //
    {
        .name = "tree0",
        .base_addr = 0x90020000,
        .size = 0x10000,
    },
    {
        .name = "tree1",
        .base_addr = 0x90030000,
        .size = 0x10000,
    },
};

#define CONFIG_COUNT (sizeof(db_configs) / sizeof(*db_configs))

#endif /* _XVC_USER_CONFIG_H */

app源码

bash 复制代码
minglie@DESKTOP-NTI9KM5:/mnt/e/ant_s9_fix_nand/XilinxVirtualCable/jtag/zynqMP/src/user$ ll
total 24
drwxrwxrwx 1 minglie minglie  4096 Jul 23 23:29 ./
drwxrwxrwx 1 minglie minglie  4096 Jul 23 22:05 ../
-rwxrwxrwx 1 minglie minglie   667 Jul 23 22:05 Makefile*
-rwxrwxrwx 1 minglie minglie  6408 Jul 23 22:05 verify_xilinx_xvc_driver.c*
-rwxrwxrwx 1 minglie minglie 13048 Jul 23 22:05 xvcServer.c*

Makefile

bash 复制代码
export CROSS_COMPILE ?= aarch64-linux-gnu-
export ARCH ?= arm64
MYCC := $(CROSS_COMPILE)gcc

DRIVER := xilinx_xvc_driver
XVCSERVER := xvcServer
TEST := verify_$(DRIVER)
IOCTL_HDR_DIR := ../driver

.PHONY: ioctl
ioctl: $(XVCSERVER)_ioctl

.PHONY: mmap
mmap: $(XVCSERVER)_mmap

.PHONY: test
test: $(TEST)

.PHONY: all
all: ioctl mmap test

$(XVCSERVER)_ioctl: $(XVCSERVER).c
	$(MYCC) -Wall -I$(IOCTL_HDR_DIR) -DUSE_IOCTL -o $@ $^

$(XVCSERVER)_mmap: $(XVCSERVER).c
	$(MYCC) -Wall -o $@ $^

$(TEST): $(TEST).c
	$(MYCC) -I$(IOCTL_HDR_DIR) -o $@ $^

.PHONY: clean
clean:
	rm -f $(XVCSERVER)_mmap
	rm -f $(XVCSERVER)_ioctl
	rm -f $(TEST)

verify_xilinx_xvc_driver.c

c 复制代码
/*
 * Copyright (c) 2019 Xilinx, Inc.  All rights reserved.
 *
 *                 XILINX CONFIDENTIAL PROPERTY
 * This   document  contains  proprietary information  which   is
 * protected by  copyright. All rights  are reserved. No  part of
 * this  document may be photocopied, reproduced or translated to
 * another  program  language  without  prior written  consent of
 * XILINX Inc., San Jose, CA. 95124
 *
 * Xilinx, Inc.
 * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
 * COURTESY TO YOU.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
 * ONE POSSIBLE   IMPLEMENTATION OF THIS FEATURE, APPLICATION OR
 * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
 * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE
 * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
 * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
 * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO
 * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
 * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <getopt.h>

#include "xvc_ioctl.h"

static const char *char_path = "/dev/xilinx_xvc_driver";

#define BYTE_ALIGN(a) ((a + 7) / 8)
#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)

static unsigned findMaxBytes(unsigned *nbits_list)
{
    unsigned max_bits = 0;
    while(*nbits_list)
    {
        max_bits = MAX(max_bits, *nbits_list);
        ++nbits_list;
    }

    return BYTE_ALIGN(max_bits);
}

static void rotatePattern(unsigned char *pattern)
{
    unsigned char c;
    unsigned pat_nbytes = strlen(pattern);

    c = pattern[0];
    memmove(pattern, pattern + 1, pat_nbytes - 1);

    pattern[pat_nbytes - 1] = c;
}

static int testXVC(int fd, struct xil_xvc_ioc *xvc_ioc, unsigned cmd_nbits, unsigned char *pattern)
{
    unsigned char *tms_buf = xvc_ioc->tms_buf;
    unsigned char *tdi_buf = xvc_ioc->tdi_buf;
    unsigned char *tdo_buf = xvc_ioc->tdo_buf;
    struct timeval stop, start;
    long delta_us;
    double mbps;
    unsigned cmd_nbytes;
    unsigned pat_nbytes;
    unsigned fill_nbytes;
    unsigned vb;

    cmd_nbytes = BYTE_ALIGN(cmd_nbits);
    pat_nbytes = strlen(pattern);

    // setup tms_buf
    memset(tms_buf, 0xff, cmd_nbytes);

    // copy pattern into tdi_buf
    fill_nbytes = 0;
    while (fill_nbytes < cmd_nbytes)
    {
        unsigned nbytes = MIN(pat_nbytes, cmd_nbytes);
        memcpy(tdi_buf + fill_nbytes, pattern, nbytes);
        fill_nbytes += nbytes;
    }

    // reset tdo_buf
    memset(tdo_buf, 0, cmd_nbytes);

    // set up ioctl codes
    xvc_ioc->opcode = 0x02; // 0x01 for normal, 0x02 for bypass
    xvc_ioc->length = cmd_nbits;

    // start timer
    gettimeofday(&start, NULL);

    // run the test
    int ret = ioctl(fd, XDMA_IOCXVC, xvc_ioc);
    if (ret < 0)
    {
        printf("Could not run the command test bitlen %d\n", cmd_nbits);
        printf("Error: %s\n", strerror(errno));
        return -1;
    }

    // stop timer
    gettimeofday(&stop, NULL);

    if (stop.tv_usec < start.tv_usec)
        delta_us = 1000000 - start.tv_usec + stop.tv_usec;
    else
        delta_us = stop.tv_usec - start.tv_usec;
    delta_us += 1000000 * (stop.tv_sec - start.tv_sec);

    mbps = (double) cmd_nbits / (double)(delta_us);

    // verify tdo
    while (vb < cmd_nbits)
    {
        unsigned nbits = MIN(cmd_nbits - vb, 8);
        unsigned mask = 0xFF;
        unsigned index = vb / 8;

        mask >>= (8 - nbits);
        if ((tdi_buf[index] & mask) != (tdo_buf[index] & mask))
        {
            printf("Test Length: %d, pattern %s FAILURE\n", cmd_nbits, pattern);
            printf("\tByte %d did not match (0x%02X != 0x%02X mask 0x%02X), pattern %s\n", 
                index, tdi_buf[index] & mask, tdo_buf[index] & mask, mask, pattern);
            return -1;
        }
        vb += nbits;
    }

    printf("Test Length: %d, %ld us, %.2f Mbps SUCCESS\n", cmd_nbits, delta_us, mbps);

    return 0;
}

int main(int argc, char **argv)
{
    int fd;
    int c;
    struct xil_xvc_ioc xvc_ioc;
    unsigned max_nbytes;
    unsigned char pattern[] = "abcdefgHIJKLMOP";

    // unsigned test_lens[] = {1, 4, 6, 12, 24, 32, 64, 89, 144, 233, 
    //  377, 610, 987, 1597, 2584, 4096, 0x2000, 0x800000, 0};
    unsigned test_lens[] = {32, 0};
    unsigned test_index = 0;

    static struct option longopts[] = {
        { "device",  required_argument, NULL, 'd' },
        { NULL,      0,                 NULL, 0 }
    };


    while ((c = getopt_long(argc, argv, "d:", longopts, NULL)) != -1) {
        switch (c) {
            case 'd':
                char_path = optarg;
                break;
            case '?':
                fprintf(stderr, "usage: %s [-d,--device <device>]\n", *argv);
                return 1;
        }
    }

    // try opening the driver
    fd = open(char_path, O_RDWR | O_SYNC);
    if (fd <= 0)
    {
        printf("Could not open driver at %s\n", char_path);
        printf("Error: %s\n", strerror(errno));
        return 0;
    }

    max_nbytes = findMaxBytes(test_lens);

    // set up buffers with the maximum size to be tested
    xvc_ioc.tms_buf = (unsigned char *) malloc(max_nbytes);
    xvc_ioc.tdi_buf = (unsigned char *) malloc(max_nbytes);
    xvc_ioc.tdo_buf = (unsigned char *) malloc(max_nbytes);
    if (!xvc_ioc.tms_buf || !xvc_ioc.tdi_buf || !xvc_ioc.tdo_buf)
    {
        printf("Could not allocate %d bytes for buffers\n", max_nbytes);
        printf("Error: %s\n", strerror(errno));
        return 0;
    }

    // run tests
    while (test_lens[test_index])
    {
        if (testXVC(fd, &xvc_ioc, test_lens[test_index], pattern) < 0)
            return 0;
        ++test_index;
        rotatePattern(pattern);
    }

    // if we get this far we must be good
    printf("XVC Driver Verified Successfully!\n");

    return 0;
}

xvcServer.c

c 复制代码
/* This work, "xvcServer.c", is a derivative of "xvcd.c" (https://github.com/tmbinc/xvcd)
* by tmbinc, used under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/).
* "xvcServer.c" is licensed under CC0 1.0 Universal (http://creativecommons.org/publicdomain/zero/1.0/)
* by Avnet and is used by Xilinx for XAPP1251.
*
*  Description : XAPP1251 Xilinx Virtual Cable Server for Linux
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <pthread.h>
#include <getopt.h>
#include <ctype.h>

#define USE_IOCTL

#ifndef USE_IOCTL
#define MAP_SIZE    0x10000
static const char *UIO_PATH = "/dev/uio0";

typedef struct {
    uint32_t length_offset;
    uint32_t tms_offset;
    uint32_t tdi_offset;
    uint32_t tdo_offset;
    uint32_t ctrl_offset;
} jtag_t;
#else /* USE_IOCTL */
#include <sys/ioctl.h>
#include "xvc_ioctl.h"
static const char *CHAR_DEV_PATH = "/dev/xilinx_xvc_driver";
#endif /* !USE_IOCTL */

static int verbose = 0;

static int XVC_PORT = 2542;

static int sread(int fd, void *target, int len) {
    unsigned char *t = target;
    while (len) {
        int r = read(fd, t, len);
        if (r <= 0)
            return r;
        t += r;
        len -= r;
    }
    return 1;
}

#ifdef USE_IOCTL
static void hexdump(FILE *fp, const void *buff, unsigned int size)
{
        unsigned int i;
        const uint8_t *b = (uint8_t *)buff;
        char ascii[17];
        char str[2] = { 0x0, };

        if (size == 0)
                return;

        for (i = 0; i < size; i++) {
                if ((i & 0x0f) == 0x00) {
                        fprintf(fp, " %08x:", i);
                        memset(ascii, 0, sizeof(ascii));
                }
                fprintf(fp, " %02x", b[i]);
                str[0] = isprint(b[i]) ? b[i] : '.';
                str[1] = '\0';
                strncat(ascii, str, sizeof(ascii) - 1);

                if ((i & 0x0f) == 0x0f)
                        fprintf(fp, " | %s\n", ascii);
        }
        /* print trailing up to a 16 byte boundary. */
        for (; i < ((size + 0xf) & ~0xf); i++) {
                fprintf(fp, "   ");
                str[0] = ' ';
                str[1] = '\0';
                strncat(ascii, str, sizeof(ascii) - 1);

                if ((i & 0x0f) == 0x0f)
                        fprintf(fp, " | %s\n", ascii);
        }
        fprintf(fp, "\n");
}
#endif

#ifndef USE_IOCTL
int handle_data(int fd, volatile jtag_t* ptr) {
#else /* USE_IOCTL */
int handle_data(int fd, int fd_ioctl) {
#endif /* !USE_IOCTL */
    char xvcInfo[32];
    unsigned int bufferSize = 2048;

    sprintf(xvcInfo, "xvcServer_v1.0:%u\n", bufferSize);

    do {
        char cmd[16];
        unsigned char buffer[bufferSize], result[bufferSize / 2];
        memset(cmd, 0, 16);

        if (sread(fd, cmd, 2) != 1)
            return 1;

        if (memcmp(cmd, "ge", 2) == 0) {
            if (sread(fd, cmd, 6) != 1)
                return 1;
            memcpy(result, xvcInfo, strlen(xvcInfo));
            if (write(fd, result, strlen(xvcInfo)) != strlen(xvcInfo)) {
                perror("write");
                return 1;
            }
            if (verbose) {
                printf("%u : Received command: 'getinfo'\n", (int)time(NULL));
                printf("\t Replied with %s\n", xvcInfo);
            }
            break;
        } else if (memcmp(cmd, "se", 2) == 0) {
            if (sread(fd, cmd, 9) != 1)
                return 1;
            memcpy(result, cmd + 5, 4);
            if (write(fd, result, 4) != 4) {
                perror("write");
                return 1;
            }
            if (verbose) {
                printf("%u : Received command: 'settck'\n", (int)time(NULL));
                printf("\t Replied with '%.*s'\n\n", 4, cmd + 5);
            }
            break;
        } else if (memcmp(cmd, "sh", 2) == 0) {
            if (sread(fd, cmd, 4) != 1)
                return 1;
            if (verbose) {
                printf("%u : Received command: 'shift'\n", (int)time(NULL));
            }
        } else {
            fprintf(stderr, "invalid cmd '%s'\n", cmd);
            return 1;
        }

        int len;
        if (sread(fd, &len, 4) != 1) {
            fprintf(stderr, "reading length failed\n");
            return 1;
        }

        int nr_bytes = (len + 7) / 8;
        if (nr_bytes * 2 > sizeof(buffer)) {
            fprintf(stderr, "buffer size exceeded\n");
            return 1;
        }

        if (sread(fd, buffer, nr_bytes * 2) != 1) {
            fprintf(stderr, "reading data failed\n");
            return 1;
        }
        memset(result, 0, nr_bytes);

        if (verbose) {
            printf("\tNumber of Bits  : %d\n", len);
            printf("\tNumber of Bytes : %d \n", nr_bytes);
            printf("\n");
        }

#ifndef USE_IOCTL
        int bytesLeft = nr_bytes;
        int bitsLeft = len;
        int byteIndex = 0;
        int tdi = 0;
        int tms = 0;
        int tdo = 0;

        while (bytesLeft > 0) {
            int shift_num_bytes;
            int shift_num_bits = 32;
            tms = 0;
            tdi = 0;
            tdo = 0;

            if (bytesLeft < 4) {
                shift_num_bits = bitsLeft;
            }
            shift_num_bytes = (shift_num_bits + 7) / 8;

            memcpy(&tms, &buffer[byteIndex], shift_num_bytes);
            memcpy(&tdi, &buffer[byteIndex + nr_bytes], shift_num_bytes);

            ptr->length_offset = shift_num_bits;
            ptr->tms_offset = tms;
            ptr->tdi_offset = tdi;
            ptr->ctrl_offset = 0x01;

            /* Switch this to interrupt in next revision */
            while (ptr->ctrl_offset) {}

            tdo = ptr->tdo_offset;
            memcpy(&result[byteIndex], &tdo, shift_num_bytes);

            bytesLeft -= shift_num_bytes;
            bitsLeft -= shift_num_bits;
            byteIndex += shift_num_bytes;

            if (verbose) {
                printf("LEN : 0x%08x\n", shift_num_bits);
                printf("TMS : 0x%08x\n", tms);
                printf("TDI : 0x%08x\n", tdi);
                printf("TDO : 0x%08x\n", tdo);
            }
        }
#else /* USE_IOCTL */
        struct xil_xvc_ioc xvc_ioc;

        xvc_ioc.opcode = 0x01;
        xvc_ioc.length = len;
        xvc_ioc.tms_buf = buffer;
        xvc_ioc.tdi_buf = buffer + nr_bytes;
        xvc_ioc.tdo_buf = result;

        if (ioctl(fd_ioctl, XDMA_IOCXVC, &xvc_ioc) < 0) {
            int errsv = errno;
            fprintf(stderr, "xvc ioctl error: %s\n", strerror(errsv));
            return errsv;
        }

        if (verbose > 1) {
            fprintf(stderr, "TMS (%d bytes/%d bits):\n", nr_bytes, len);
            hexdump(stderr, buffer, nr_bytes);
            fprintf(stderr, "TDI (%d bytes/%d bits):\n", nr_bytes, len);
            hexdump(stderr, buffer + nr_bytes, nr_bytes);
            fprintf(stderr, "TDO (%d bytes/%d bits):\n", nr_bytes, len);
            hexdump(stderr, result, nr_bytes);
        }
#endif /* !USE_IOCTL */
        if (write(fd, result, nr_bytes) != nr_bytes) {
            perror("write");
            return 1;
        }
    } while (1);

    /* Note: Need to fix JTAG state updates, until then no exit is allowed */
    return 0;
}

#ifdef USE_IOCTL
void display_driver_properties(int fd_ioctl) {
    int ret = 0;
    struct xil_xvc_properties props;

    ret = ioctl(fd_ioctl, XDMA_RDXVC_PROPS, &props);
    if (ret < 0) {
        perror("failed to read XVC driver properties");
        return;
    }

    printf("INFO: XVC driver character file: %s\n", CHAR_DEV_PATH);
    printf("INFO: debug_bridge base address: 0x%lX\n", props.debug_bridge_base_addr);
    printf("INFO: debug_bridge size: 0x%lX\n", props.debug_bridge_size);
    printf("INFO: debug_bridge device tree compatibility string: %s\n\n", props.debug_bridge_compat_string);
}
#endif /* USE_IOCTL */

int main(int argc, char **argv) {
    int i;
    int s;
    int c;
    struct sockaddr_in address;
    char hostname[256];
    /* options descriptor */
    static struct option longopts[] = {
        { "help",    no_argument,       NULL, '?' },
        { "verbose", no_argument,       NULL, 'v' },
        { "port",    required_argument, NULL, 'p' },
        { "device",  required_argument, NULL, 'd' },
        { NULL,      0,                 NULL, 0 }
    };

    opterr = 0;

    while ((c = getopt_long(argc, argv, "vp:d:", longopts, NULL)) != -1) {
        switch (c) {
            case 'v':
                verbose++;
                break;
            case 'p':
                XVC_PORT = atoi(optarg);
                break;
            case 'd':
#ifdef USE_IOCTL
                CHAR_DEV_PATH = optarg;
#else
                UIO_PATH = optarg;
#endif
                break;
            case '?':
                fprintf(stderr, "usage: %s [-v,--verbose] [-p,--port <port>] "
                    "[-d,--device <device>]\n", *argv);
                return 1;
        }
    }

#ifndef USE_IOCTL
    int fd_uio;
    volatile jtag_t* ptr = NULL;

    fd_uio = open(UIO_PATH, O_RDWR);
    if (fd_uio < 1) {
        fprintf(stderr, "Failed to open uio: %s\n", UIO_PATH);
        return -1;
    }

    ptr = (volatile jtag_t*) mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd_uio, 0);
    if (ptr == MAP_FAILED) {
        fprintf(stderr, "MMAP Failed\n");
        return -1;
    }
    close(fd_uio);
#else /* USE_IOCTL */
    int fd_ioctl;

    fd_ioctl = open(CHAR_DEV_PATH, O_RDWR | O_SYNC);
    if (fd_ioctl < 1) {
        fprintf(stderr, "Failed to open xvc ioctl device driver: %s\n", CHAR_DEV_PATH);
        return -1;
    }

    display_driver_properties(fd_ioctl);
#endif /* !USE_IOCTL */

    s = socket(AF_INET, SOCK_STREAM, 0);

    if (s < 0) {
        perror("socket");
        return 1;
    }

    i = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof i);

    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(XVC_PORT);
    address.sin_family = AF_INET;

    if (bind(s, (struct sockaddr*) &address, sizeof(address)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(s, 1) < 0) {
        perror("listen");
        return 1;
    }

    if (gethostname(hostname, sizeof(hostname)) != 0) {
        perror("hostname lookup");
        close(s);
        return 1;
    }

    printf("INFO: To connect to this xvcServer instance, use url: TCP:%s:%u\n\n", hostname, XVC_PORT);

    fd_set conn;
    int maxfd = 0;

    FD_ZERO(&conn);
    FD_SET(s, &conn);

    maxfd = s;

    while (1) {
        fd_set read = conn, except = conn;
        int fd;

        if (select(maxfd + 1, &read, 0, &except, 0) < 0) {
            perror("select");
            break;
        }

        for (fd = 0; fd <= maxfd; ++fd) {
            if (FD_ISSET(fd, &read)) {
                if (fd == s) {
                    int newfd;
                    socklen_t nsize = sizeof(address);

                    newfd = accept(s, (struct sockaddr*) &address, &nsize);

                    printf("connection accepted - fd %d\n", newfd);
                    if (newfd < 0) {
                        perror("accept");
                    } else {
                        printf("setting TCP_NODELAY to 1\n");
                        int flag = 1;
                        int optResult = setsockopt(newfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
                        if (optResult < 0)
                            perror("TCP_NODELAY error");
                        if (newfd > maxfd) {
                            maxfd = newfd;
                        }
                        FD_SET(newfd, &conn);
                    }
#ifndef USE_IOCTL
                } else if (handle_data(fd, ptr)) {
#else /* USE_IOCTL */
                } else if (handle_data(fd, fd_ioctl)) {
#endif /* !USE_IOCTL */
                    printf("connection closed - fd %d\n", fd);
                    close(fd);
                    FD_CLR(fd, &conn);
                }
            } else if (FD_ISSET(fd, &except)) {
                printf("connection aborted - fd %d\n", fd);
                close(fd);
                FD_CLR(fd, &conn);
                if (fd == s)
                    break;
            }
        }
    }

#ifndef USE_IOCTL
    munmap((void *) ptr, MAP_SIZE);
#else /* USE_IOCTL */
    close(fd_ioctl);
#endif /* !USE_IOCTL */

    return 0;
}
相关推荐
微露清风2 小时前
快速排序算法学习记录
学习·算法·排序算法
AAA@峥3 小时前
系统化学习 MySQL:数据类型、库表管理、增删改查全解析
数据库·学习·mysql
GeekArch15 小时前
第28讲:避坑——AI堆栈分配错误、栈溢出BUG
c语言·人工智能·stm32·mcu·学习·bug
weixin_4280053016 小时前
C#调用 AI学习从0开始-第3阶段RAG向量数据库-文档切分与入库第15天
人工智能·学习·c#·向量数据库·rag·qdrant·文档切分与入库
dankokoko17 小时前
一.函数与极限2
学习
知识分享小能手17 小时前
统计学学习教程,从入门到精通,分类数据分析 — 知识点详解(14)
学习·分类·数据分析
菜鸟‍18 小时前
【论文学习】MICCAI 2025 || ARSeg:面向不完整文本提示的鲁棒医学图像指代表达分割
图像处理·学习
三克的油18 小时前
c++算法学习-4
学习
知识分享小能手19 小时前
统计学学习教程,从入门到精通,分类数据分析 — 知识点详解(13)
学习·分类·数据分析