参考
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、.read、ioctlmodule/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