zynq的linux驱动版终端AtShellDrv

参考

嵌入式终端AtShell.csdn

zynq中linux远程调试和寄存器读写脚本.csdn

atshell_drv

atshell_drv 是一个运行在 Linux 内核驱动里的 AtShell 风格命令路由框架。

它用于纯驱动自测,命令通过 /dev/atshell_drv 进入驱动,再由驱动内部的

AtShell 表分发到具体处理函数。

支持的入口:

  • 直接写设备:echo "1 abc" > /dev/atshell_drv
  • 脚本封装:./at_shell.sh 1 abc
  • ioctl 路由:ATSHELL_DRV_IOC_CMDLINE

目录结构

text 复制代码
atshell_drv/
  Makefile
  at_shell.sh
  module/
    atshell_drv_main.c
    atshell_app.c
    common/
      atshell_drv.c
      atshell_drv.h
  app/

主要文件:

  • module/atshell_drv_main.c:字符设备、.write.readioctl
  • module/common/atshell_drv.c:AtShell 通用注册、解析、执行逻辑
  • module/common/atshell_drv.h:AtShell 接口和 ioctl 协议定义
  • module/atshell_app.c:当前驱动自测命令注册和实现
  • at_shell.sh:对 echo "..." > /dev/atshell_drv 的简单封装

源码

module/common/atshell_drv.h

c 复制代码
#ifndef ATSHELL_DRV_H
#define ATSHELL_DRV_H

#include <linux/ioctl.h>
#include <linux/types.h>

#define ATSHELL_DRV_DEVICE_NAME "atshell_drv"

#define ATSHELL_DRV_MAX_ARGS     16U
#define ATSHELL_DRV_MAX_COMMANDS 32U
#define ATSHELL_DRV_ARGS_SIZE    256U
#define ATSHELL_DRV_RESULT_SIZE  4096U

#define ATSHELL_DRV_IOC_MAGIC 'A'
#define ATSHELL_DRV_IOC_CMDLINE \
	_IOWR(ATSHELL_DRV_IOC_MAGIC, 0x00, struct atshell_drv_ioc_req)

struct atshell_drv_ioc_req {
	__u32 args_len;
	__u32 result_size;
	__u64 args_ptr;
	__u64 result_ptr;
	__s32 ret;
	__u32 result_len;
};

#define CON_AT_METHOD_NUM    ATSHELL_DRV_MAX_COMMANDS
#define CON_METHOD_NAME_SIZE 16
#define CON_HELP_INFO_SIZE   50

typedef int (*ATServerFun)(int argc, char **argv);

typedef struct {
	char methodName[CON_METHOD_NAME_SIZE];
	char helpInfo[CON_HELP_INFO_SIZE];
	ATServerFun atFun;
	void *userData;
} AT_CMD_ENTRY_TypeDef;

typedef struct {
	char *buf;
	size_t len;
	size_t size;
} AT_OUTPUT_TypeDef;

#define AT_FUN(fun) #fun
#define AT_printf(format, ...) at_printf(format, ##__VA_ARGS__)
#define AT_EXEC(cmd) atshell_drv_exec((cmd), NULL)
#define AT_SHELL_EXPORT(cmdName, desc, fun, ...) \
	do { \
		AT_CMD_ENTRY_TypeDef fun##entrycmd = { \
			AT_FUN(cmdName), #desc, fun, ##__VA_ARGS__ \
		}; \
		at_register(fun##entrycmd); \
	} while (0)

bool at_register(AT_CMD_ENTRY_TypeDef cmd);
bool at_register_many(AT_CMD_ENTRY_TypeDef *cmdList, int cmdLen);
int at_printf(const char *format, ...);
void at_show_version(void);

void atshell_drv_reset(void);
int atshell_drv_exec(char *cmdline, AT_OUTPUT_TypeDef *out);

void at_app_init(void);

#endif

module/common/atshell_drv.c

c 复制代码
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/string.h>

#include "atshell_drv.h"

#define ATSHELL_DRV_VERSION "0.1"

static int __help(int argc, char **argv);

static AT_CMD_ENTRY_TypeDef s_ap_cmd_entry[CON_AT_METHOD_NUM];
static int s_cmd_num;
static AT_OUTPUT_TypeDef *s_active_output;

static void output_write(AT_OUTPUT_TypeDef *out, const char *buf, size_t len)
{
	size_t copy_len;

	if (!out || !out->buf || out->len >= out->size || !buf || len == 0)
		return;

	copy_len = min(len, out->size - out->len - 1);
	if (copy_len == 0)
		return;

	memcpy(out->buf + out->len, buf, copy_len);
	out->len += copy_len;
	out->buf[out->len] = '\0';
}

int at_printf(const char *format, ...)
{
	char log_buf[256];
	va_list args;
	int n;

	va_start(args, format);
	n = vscnprintf(log_buf, sizeof(log_buf), format, args);
	va_end(args);

	if (n <= 0)
		return n;

	if (s_active_output)
		output_write(s_active_output, log_buf, n);
	else
		pr_info("%s", log_buf);

	return n;
}

static int split_args(char *line, char **argv, int max_args)
{
	int argc = 0;
	char *token;

	while ((token = strsep(&line, " \t\r\n")) != NULL) {
		if (*token == '\0')
			continue;
		argv[argc++] = token;
		if (argc >= max_args)
			break;
	}

	return argc;
}

static int parse_u32(const char *s, u32 *out)
{
	if (!s || !*s)
		return -EINVAL;

	return kstrtou32(s, 0, out);
}

static void show_help(void)
{
	int i;

	at_printf("AtShell commands:\n");
	for (i = 0; i < s_cmd_num; i++)
		at_printf("%2d.%-20s - %s\n",
			  i,
			  s_ap_cmd_entry[i].methodName,
			  s_ap_cmd_entry[i].helpInfo);
}

static int __help(int argc, char **argv)
{
	show_help();
	return 0;
}

void atshell_drv_reset(void)
{
	memset(s_ap_cmd_entry, 0, sizeof(s_ap_cmd_entry));
	s_cmd_num = 0;
	s_active_output = NULL;

	snprintf(s_ap_cmd_entry[0].methodName,
		 sizeof(s_ap_cmd_entry[0].methodName), "%s", "help");
	snprintf(s_ap_cmd_entry[0].helpInfo,
		 sizeof(s_ap_cmd_entry[0].helpInfo), "%s", "list cmd");
	s_ap_cmd_entry[0].atFun = __help;
	s_cmd_num = 1;
}

bool at_register(AT_CMD_ENTRY_TypeDef cmd)
{
	if (s_cmd_num >= CON_AT_METHOD_NUM)
		return false;
	if (!cmd.methodName[0] || !cmd.atFun)
		return false;

	s_ap_cmd_entry[s_cmd_num] = cmd;
	s_cmd_num++;
	return true;
}

bool at_register_many(AT_CMD_ENTRY_TypeDef *cmdList, int cmdLen)
{
	int i;

	if (!cmdList || cmdLen < 0)
		return false;
	if (s_cmd_num + cmdLen > CON_AT_METHOD_NUM)
		return false;

	for (i = 0; i < cmdLen; i++) {
		if (!at_register(cmdList[i]))
			return false;
	}

	return true;
}

void at_show_version(void)
{
	at_printf(" \\ | /\n");
	at_printf("- ATSHELL_DRV - %s\n", ATSHELL_DRV_VERSION);
	at_printf(" / | \\\n");
}

static int exec_cmd_entry(int cmd_index, int argc, char **argv,
			  AT_OUTPUT_TypeDef *out)
{
	int ret;

	s_active_output = out;

	if (cmd_index < 0 || cmd_index >= s_cmd_num ||
	    !s_ap_cmd_entry[cmd_index].atFun) {
		at_printf("unknown command id: %d\n", cmd_index);
		show_help();
		s_active_output = NULL;
		return -EINVAL;
	}

	ret = s_ap_cmd_entry[cmd_index].atFun(argc, argv);
	s_active_output = NULL;
	return ret;
}

int atshell_drv_exec(char *cmdline, AT_OUTPUT_TypeDef *out)
{
	char *argv[ATSHELL_DRV_MAX_ARGS];
	u32 cmd_id;
	int argc;
	int ret;
	int i;

	if (!cmdline)
		cmdline = "";

	argc = split_args(cmdline, argv, ARRAY_SIZE(argv));
	if (argc == 0)
		return exec_cmd_entry(0, 0, NULL, out);

	for (i = 0; i < s_cmd_num; i++) {
		if (strcmp(s_ap_cmd_entry[i].methodName, argv[0]) == 0)
			return exec_cmd_entry(i, argc - 1, argv + 1, out);
	}

	ret = parse_u32(argv[0], &cmd_id);
	if (!ret)
		return exec_cmd_entry(cmd_id, argc - 1, argv + 1, out);

	s_active_output = out;
	at_printf("%s: command not found.\n", argv[0]);
	s_active_output = NULL;
	return -EINVAL;
}

module/atshell_app.c

c 复制代码
#include "common/atshell_drv.h"

extern int at_app_read(int argc, char **argv);
extern int at_app_write(int argc, char **argv);

void at_app_init(void)
{
	AT_SHELL_EXPORT(read, read reg, at_app_read);
	AT_SHELL_EXPORT(write, write reg, at_app_write);
}

static void print_args(const char *name, int argc, char **argv)
{
	int i;

	AT_printf("%s argc=%d\n", name, argc);
	for (i = 0; i < argc; i++)
		AT_printf("%s argv[%d]=%s\n", name, i, argv[i]);
}

int at_app_read(int argc, char **argv)
{
	print_args("read", argc, argv);
	return 0;
}

int at_app_write(int argc, char **argv)
{
	print_args("write", argc, argv);
	return 0;
}

module/atshell_drv_main.c

c 复制代码
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>

#include "common/atshell_drv.h"

#define DRIVER_NAME "atshell_drv"
#define DEVICE_NAME ATSHELL_DRV_DEVICE_NAME
#define CLASS_NAME  "atshell_drv"

struct atshell_drv_dev {
	struct mutex lock;
	dev_t devt;
	struct cdev cdev;
	struct class *class;
	struct device *device;
	char result[ATSHELL_DRV_RESULT_SIZE];
	size_t result_len;
};

static struct atshell_drv_dev g_dev;

static int copy_args_from_user(const struct atshell_drv_ioc_req *req,
			       char *args, size_t args_size)
{
	size_t len;

	args[0] = '\0';
	if (!req->args_ptr || req->args_len == 0)
		return 0;

	len = min_t(size_t, req->args_len, args_size - 1);
	if (copy_from_user(args, u64_to_user_ptr(req->args_ptr), len))
		return -EFAULT;

	args[len] = '\0';
	return 0;
}

static int copy_result_to_user(struct atshell_drv_ioc_req *req,
			       const AT_OUTPUT_TypeDef *res)
{
	size_t len;

	req->result_len = res->len;
	if (!req->result_ptr || req->result_size == 0)
		return 0;

	len = min_t(size_t, res->len, req->result_size - 1);
	if (copy_to_user(u64_to_user_ptr(req->result_ptr), res->buf, len))
		return -EFAULT;
	if (put_user('\0', (char __user *)u64_to_user_ptr(req->result_ptr) + len))
		return -EFAULT;

	return 0;
}

static long atshell_drv_ioctl(struct file *file, unsigned int cmd,
			      unsigned long arg)
{
	struct atshell_drv_ioc_req req;
	AT_OUTPUT_TypeDef res;
	char *args;
	int ret;

	if (cmd != ATSHELL_DRV_IOC_CMDLINE)
		return -ENOTTY;

	if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
		return -EFAULT;

	args = kzalloc(ATSHELL_DRV_ARGS_SIZE, GFP_KERNEL);
	if (!args)
		return -ENOMEM;

	res.size = ATSHELL_DRV_RESULT_SIZE;
	res.buf = kzalloc(res.size, GFP_KERNEL);
	if (!res.buf) {
		kfree(args);
		return -ENOMEM;
	}
	res.len = 0;

	ret = copy_args_from_user(&req, args, ATSHELL_DRV_ARGS_SIZE);
	if (ret)
		goto out;

	mutex_lock(&g_dev.lock);
	ret = atshell_drv_exec(args, &res);
	mutex_unlock(&g_dev.lock);

out:
	req.ret = ret;
	if (copy_result_to_user(&req, &res))
		ret = -EFAULT;
	if (copy_to_user((void __user *)arg, &req, sizeof(req)))
		ret = -EFAULT;

	kfree(res.buf);
	kfree(args);
	return ret;
}

static ssize_t atshell_drv_read(struct file *file, char __user *buf,
				size_t count, loff_t *ppos)
{
	ssize_t ret;

	mutex_lock(&g_dev.lock);
	ret = simple_read_from_buffer(buf, count, ppos,
				      g_dev.result, g_dev.result_len);
	mutex_unlock(&g_dev.lock);

	return ret;
}

static ssize_t atshell_drv_write(struct file *file, const char __user *buf,
				 size_t count, loff_t *ppos)
{
	char *args;
	size_t len;
	int ret;

	if (count == 0)
		return 0;
	if (count >= ATSHELL_DRV_ARGS_SIZE)
		return -E2BIG;

	args = kzalloc(ATSHELL_DRV_ARGS_SIZE, GFP_KERNEL);
	if (!args)
		return -ENOMEM;

	len = min_t(size_t, count, ATSHELL_DRV_ARGS_SIZE - 1);
	if (copy_from_user(args, buf, len)) {
		kfree(args);
		return -EFAULT;
	}
	args[len] = '\0';

	mutex_lock(&g_dev.lock);
	ret = atshell_drv_exec(args, NULL);
	mutex_unlock(&g_dev.lock);

	kfree(args);
	return ret ? ret : count;
}

static int atshell_drv_open(struct inode *inode, struct file *file)
{
	return 0;
}

static const struct file_operations atshell_drv_fops = {
	.owner = THIS_MODULE,
	.open = atshell_drv_open,
	.read = atshell_drv_read,
	.write = atshell_drv_write,
	.unlocked_ioctl = atshell_drv_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = atshell_drv_ioctl,
#endif
};

static int __init atshell_drv_init(void)
{
	int ret;

	memset(&g_dev, 0, sizeof(g_dev));
	mutex_init(&g_dev.lock);
	atshell_drv_reset();
	at_app_init();
	g_dev.result_len = scnprintf(g_dev.result, sizeof(g_dev.result),
				     "use ioctl or write command to /dev/%s\n",
				     DEVICE_NAME);

	ret = alloc_chrdev_region(&g_dev.devt, 0, 1, DEVICE_NAME);
	if (ret)
		return ret;

	cdev_init(&g_dev.cdev, &atshell_drv_fops);
	g_dev.cdev.owner = THIS_MODULE;
	ret = cdev_add(&g_dev.cdev, g_dev.devt, 1);
	if (ret)
		goto err_unregister;

	g_dev.class = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(g_dev.class)) {
		ret = PTR_ERR(g_dev.class);
		goto err_cdev;
	}

	g_dev.device = device_create(g_dev.class, NULL, g_dev.devt, NULL,
				     DEVICE_NAME);
	if (IS_ERR(g_dev.device)) {
		ret = PTR_ERR(g_dev.device);
		goto err_class;
	}

	pr_info("%s loaded: /dev/%s major=%u minor=%u\n",
		DRIVER_NAME, DEVICE_NAME, MAJOR(g_dev.devt), MINOR(g_dev.devt));
	return 0;

err_class:
	class_destroy(g_dev.class);
err_cdev:
	cdev_del(&g_dev.cdev);
err_unregister:
	unregister_chrdev_region(g_dev.devt, 1);
	return ret;
}

static void __exit atshell_drv_exit(void)
{
	if (g_dev.device)
		device_destroy(g_dev.class, g_dev.devt);
	if (g_dev.class)
		class_destroy(g_dev.class);
	cdev_del(&g_dev.cdev);
	unregister_chrdev_region(g_dev.devt, 1);

	pr_info("%s unloaded\n", DRIVER_NAME);
}

module_init(atshell_drv_init);
module_exit(atshell_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ming");
MODULE_DESCRIPTION("AtShell command driver");

app/atshell_ctl.c

c 复制代码
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define ATSHELL_DRV_DEVICE_NAME "atshell_drv"
#define ATSHELL_DRV_ARGS_SIZE   256U
#define ATSHELL_DRV_RESULT_SIZE 4096U

#define ATSHELL_DRV_IOC_MAGIC 'A'
#define ATSHELL_DRV_IOC_CMDLINE \
	_IOWR(ATSHELL_DRV_IOC_MAGIC, 0x00, struct atshell_drv_ioc_req)

struct atshell_drv_ioc_req {
	uint32_t args_len;
	uint32_t result_size;
	uint64_t args_ptr;
	uint64_t result_ptr;
	int32_t ret;
	uint32_t result_len;
};

static int build_cmdline(int argc, char **argv, char *cmd, size_t cmd_size)
{
	size_t len = 0;
	int i;

	if (argc <= 1) {
		snprintf(cmd, cmd_size, "0");
		return 0;
	}

	cmd[0] = '\0';
	for (i = 1; i < argc; i++) {
		int n = snprintf(cmd + len, cmd_size - len,
				 "%s%s", i == 1 ? "" : " ", argv[i]);

		if (n < 0 || (size_t)n >= cmd_size - len)
			return -1;
		len += (size_t)n;
	}

	return 0;
}

int main(int argc, char **argv)
{
	char cmd[ATSHELL_DRV_ARGS_SIZE];
	char result[ATSHELL_DRV_RESULT_SIZE];
	struct atshell_drv_ioc_req req;
	int fd;
	int rc;
	int saved_errno = 0;

	if (build_cmdline(argc, argv, cmd, sizeof(cmd)) != 0) {
		fprintf(stderr, "command too long\n");
		return 1;
	}

	memset(result, 0, sizeof(result));
	memset(&req, 0, sizeof(req));
	req.args_len = (uint32_t)strlen(cmd);
	req.result_size = sizeof(result);
	req.args_ptr = (uint64_t)(uintptr_t)cmd;
	req.result_ptr = (uint64_t)(uintptr_t)result;

	fd = open("/dev/" ATSHELL_DRV_DEVICE_NAME, O_RDWR);
	if (fd < 0) {
		perror("open /dev/" ATSHELL_DRV_DEVICE_NAME);
		return 1;
	}

	rc = ioctl(fd, ATSHELL_DRV_IOC_CMDLINE, &req);
	if (rc < 0)
		saved_errno = errno;

	if (req.result_len > 0)
		printf("%s", result);

	close(fd);

	if (rc < 0) {
		errno = saved_errno;
		perror("ioctl ATSHELL_DRV_IOC_CMDLINE");
		return 1;
	}

	return req.ret == 0 ? 0 : 1;
}

Makefile

c 复制代码
ifneq ($(KERNELRELEASE),)

obj-m += atshell_drv.o
atshell_drv-objs := atshell_drv_main.o atshell_app.o common/atshell_drv.o
ccflags-y += -I$(src) -I$(src)/common

else

KERN_DIR ?= /home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
KCFLAGS ?= -march=armv7-a
BUILD_DIR ?= build
MODULE_BUILD_DIR := $(BUILD_DIR)/module
APP_BUILD_DIR := $(BUILD_DIR)/app
MODULE_SRC_DIR := module
APP_SRC_DIR := app
APP := $(APP_BUILD_DIR)/atshell_ctl
SCRIPT := $(APP_BUILD_DIR)/at_shell.sh
APP_CC ?= $(CROSS_COMPILE)gcc
APP_CFLAGS ?= -Wall -Wextra -O2

.PHONY: all module modules app clean help

all: module app

module modules: $(MODULE_BUILD_DIR)/Makefile \
	$(MODULE_BUILD_DIR)/atshell_drv_main.c \
	$(MODULE_BUILD_DIR)/atshell_app.c \
	$(MODULE_BUILD_DIR)/common/atshell_drv.c \
	$(MODULE_BUILD_DIR)/common/atshell_drv.h
	$(MAKE) -C $(KERN_DIR) M=$(CURDIR)/$(MODULE_BUILD_DIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) KCFLAGS="$(KCFLAGS)" modules

app: $(APP) $(SCRIPT)

$(APP): $(APP_SRC_DIR)/atshell_ctl.c | $(APP_BUILD_DIR)
	$(APP_CC) $(APP_CFLAGS) $< -o $@

$(SCRIPT): at_shell.sh | $(APP_BUILD_DIR)
	cp $< $@
	chmod +x $@

$(MODULE_BUILD_DIR) $(MODULE_BUILD_DIR)/common $(APP_BUILD_DIR):
	mkdir -p $@

$(MODULE_BUILD_DIR)/Makefile: Makefile | $(MODULE_BUILD_DIR)
	cp $< $@

$(MODULE_BUILD_DIR)/atshell_drv_main.c: $(MODULE_SRC_DIR)/atshell_drv_main.c | $(MODULE_BUILD_DIR)
	cp $< $@

$(MODULE_BUILD_DIR)/atshell_app.c: $(MODULE_SRC_DIR)/atshell_app.c | $(MODULE_BUILD_DIR)
	cp $< $@

$(MODULE_BUILD_DIR)/common/atshell_drv.c: $(MODULE_SRC_DIR)/common/atshell_drv.c | $(MODULE_BUILD_DIR)/common
	cp $< $@

$(MODULE_BUILD_DIR)/common/atshell_drv.h: $(MODULE_SRC_DIR)/common/atshell_drv.h | $(MODULE_BUILD_DIR)/common
	cp $< $@

clean:
	rm -rf $(BUILD_DIR)

help:
	@echo "make              build build/module/atshell_drv.ko"
	@echo "make module       build build/module/atshell_drv.ko"
	@echo "make app          build build/app/atshell_ctl and build/app/at_shell.sh"
	@echo "source layout: module/ include/ app/"
	@echo "make clean        remove build/"
	@echo "make KERN_DIR=/path/to/linux-xlnx"
	@echo "make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-"

endif

at_shell.sh

简化驱动调用

bash 复制代码
#!/bin/sh

DEV="${ATSHELL_DRV_DEV:-/dev/atshell_drv}"

if [ "$#" -eq 0 ]; then
	cmd="0"
else
	cmd="$*"
fi

if [ ! -e "$DEV" ]; then
	echo "at_shell.sh: $DEV not found" >&2
	exit 1
fi

if [ ! -w "$DEV" ]; then
	echo "at_shell.sh: $DEV not writable" >&2
	exit 1
fi

printf '%s\n' "$cmd" > "$DEV"

rw.sh

寄存器读写脚本,填充PL寄存器

查询PL状态,触发PL吐流

bash 复制代码
#!/bin/bash
set -euo pipefail

# ===== Config =====
BASE=0x43c00000           # AXI-Lite base address
DEVMEM=${DEVMEM:-devmem}  # override if needed: DEVMEM="busybox devmem"

# ===== Helpers =====
parse_u32_any() {
  local s="$1"
  if [[ -z "$s" ]]; then echo "ERR: empty value" >&2; return 3; fi

  # 0b... binary (allow underscores)
  if [[ "$s" =~ ^0[bB][01_]+$ ]]; then
    local bits="${s:2}"
    bits="${bits//_/}"
    [[ "$bits" =~ ^[01]+$ ]] || { echo "ERR: invalid binary: $s" >&2; return 3; }
    echo $((2#$bits))
    return 0
  fi

  # hex or decimal
  if [[ "$s" =~ ^0[xX][0-9a-fA-F]+$ ]]; then
    echo $((s)); return 0
  fi
  if [[ "$s" =~ ^[0-9]+$ ]]; then
    echo $((10#$s)); return 0
  fi

  echo "ERR: bad value: $s" >&2
  return 3
}

rd32_dec() {
  local idx="$1"
  local addr=$((BASE + 4 * idx))
  local hex="$($DEVMEM "$addr" 32)"
  local dec=$((hex))
  printf "R [%u] @0x%08X : %u (0x%08X)\n" "$idx" "$addr" "$dec" "$dec"
}

rd32_value() {
  local idx="$1"
  local addr=$((BASE + 4 * idx))
  local hex="$($DEVMEM "$addr" 32)"

  echo $((hex))
}

wr32_dec() {
  local idx="$1" val_dec="$2"
  local addr=$((BASE + 4 * idx))
  local val_hex
  printf -v val_hex "0x%08X" "$val_dec"
  $DEVMEM "$addr" 32 "$val_hex" >/dev/null
}

do_write() {
  local idx_str="$1" val_str="$2"
  [[ "$idx_str" =~ ^[0-9]+$ ]] || { echo "ERR: bad index: $idx_str" >&2; return 2; }
  local idx=$((10#$idx_str))
  local val_dec; val_dec="$(parse_u32_any "$val_str")" || return $?

  wr32_dec "$idx" "$val_dec"
  local rb_dec; rb_dec="$($DEVMEM $((BASE + 4 * idx)) 32)"
  rb_dec=$((rb_dec))

  printf "W [%u] @0x%08X <= %u (0x%08X); Rb=%u (0x%08X)%s\n" \
    "$idx" $((BASE + 4 * idx)) "$val_dec" "$val_dec" "$rb_dec" "$rb_dec" \
    "$( [[ $rb_dec -eq $val_dec ]] && echo "" || echo "  [WARN: mismatch]" )"
}

usage() {
  cat >&2 <<EOF
Usage:
  # single write
  $0 w <idx(dec)> <value(dec|0xhex|0bbin)>

  # single read
  $0 r <idx(dec)>

  # read reg 1..128
  $0 2

  # init reg 1..128, value = index + x
  $0 3 <x(dec|0xhex|0bbin)>

  # batch from file
  $0 -f cmds.txt

Examples (cmds.txt lines):
  w   1   111
  w  32   0b110
  w  32   0x1110
  r  32
  r  30
EOF
  exit 2
}

read_1_to_128() {
  local i
  local value

  for i in $(seq 1 128); do
    value="$(rd32_value "$i")"
    if (( i % 16 == 1 )); then
      printf "%u" "$value"
    else
      printf " %u" "$value"
    fi
    if (( i % 16 == 0 )); then
      printf "\n"
    fi
  done
}

init_1_to_128() {
  local x_str="$1"
  local x_dec
  local i

  x_dec="$(parse_u32_any "$x_str")" || return $?

  for i in $(seq 1 128); do
    wr32_dec "$i" $((i + x_dec))
  done

  printf "init [1..128] <= index + %u (0x%08X)\n" "$x_dec" "$x_dec"
}

# ===== Dispatch =====
[[ $# -eq 0 ]] && usage

if [[ "$1" == "-f" && $# -eq 2 ]]; then
  while IFS= read -r line || [[ -n "$line" ]]; do
    line="${line%%#*}"
    [[ -z "${line//[[:space:]]/}" ]] && continue
    read -r cmd idx val <<<"$line"
    case "${cmd,,}" in
      w) do_write "$idx" "$val" ;;
      r) rd32_dec "$idx" ;;
      *) echo "ERR: unknown cmd: $cmd" >&2 ;;
    esac
  done < "$2"
  exit 0
fi

if [[ $# -eq 1 && "$1" == "2" ]]; then
  read_1_to_128
  exit 0
fi

if [[ $# -eq 2 && "$1" == "3" ]]; then
  init_1_to_128 "$2"
  exit 0
fi

if [[ $# -eq 3 && "${1,,}" == "w" ]]; then
  do_write "$2" "$3"
  exit 0
fi

if [[ $# -eq 2 && "${1,,}" == "r" ]]; then
  rd32_dec "$2"
  exit 0
fi

usage

HELP.md

bash 复制代码
scp build/module/atshell_drv.ko  root@192.168.3.211:/lib/modules/5.4.0-xilinx-v2020.2
scp at_shell.sh   root@192.168.3.211:/lib/modules/5.4.0-xilinx-v2020.2

rmmod atshell_drv
insmod atshell_drv.ko
dmesg | tail

.vscode/c_cpp_properties.json

代码高亮跳转配置

json 复制代码
{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/include",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/include/linux",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/arch/arm/include",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/arch/arm/include/generated",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/include/generated",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/include/asm",
                "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/include/asm-generic",
               "D:/home/wpf/workspace/kernel-driver/linux-xlnx-xlnx_rebase_v5.4_2020.2/include/uapi",
               "D:/Xilinx/Vitis/2020.2/include/multimediaIps"
            ],
            "defines": [
                "__KERNEL__",
                "__ARM_ARCH_7A__"  ,
                "KBUILD_MODNAME=\"pl_driver\""
            ],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c11",
            "cppStandard": "c++17",
            "intelliSenseMode": "clang-x64"
        }
    ],
    "version": 4
}

测试

bash 复制代码
root@ant:/lib/modules/5.4.0-xilinx-v2020.2# ./at_shell.sh
AtShell commands:
 0.help                 - list cmd
 1.read                 - read reg
 2.write                - write reg
root@ant:/lib/modules/5.4.0-xilinx-v2020.2# ./at_shell.sh  1 555
read argc=1
read argv[0]=555
相关推荐
欧神附体1233 小时前
在虚拟机搭建本地仓库和网络仓库
linux·运维·服务器
是个西兰花4 小时前
Linux:深入解析Linux线程原理与实现
linux·运维·c++·线程·互斥锁
运维大师4 小时前
【Linux运维极简教程】07-磁盘管理与LVM
linux·运维·服务器
乖巧的妹子4 小时前
刷题总结知识
linux·服务器·windows
拳里剑气5 小时前
Linux:基础IO
linux·运维·服务器·io
小小龙学IT5 小时前
Day 2-3:Linux 文件与目录操作
linux
三8446 小时前
基本Linux命令总结
linux·运维·服务器
hehelm7 小时前
AI 大模型接入 SDK —项目概述
linux·服务器·网络·数据库·c++
mounter6257 小时前
跨越鸿沟:从内核驱动开发到超大规模云端生产环境的思考
linux·驱动开发·linux kernel·kernel