用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/JohnNuwan/EVA_CORE --skill device-drivers命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
基于 SOC 职业分类
| name | device-drivers |
| description | Développement de drivers — Linux char/mem/block, I2C, SPI, GPIO, kernel modules, DT, device model |
| category | edge-ai |
| author | E.V.A |
| version | 1.0.0 |
Développement de drivers pour périphériques embarqués : drivers Linux (char, platform, I2C, SPI, GPIO), Device Tree, kernel modules, et drivers bare-metal pour MCU.
┌──────────────────────────────────────────────┐
│ Userspace │
│ open("/dev/mydev") ioctl() mmap() read() │
└──────────────────────┬───────────────────────┘
│ syscall
┌──────────────────────▼───────────────────────┐
│ VFS (Virtual File System) │
│ inode → file_operations → driver │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ Device Model (driver core) │
│ bus → device → driver → probe()/remove() │
│ Platform Bus / I2C Bus / SPI Bus / USB │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ Hardware (MMIO, IRQ, DMA) │
│ ioremap() request_irq() dma_alloc() │
└──────────────────────────────────────────────┘
// minimal.c — Module noyau minimal
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init my_init(void) {
pr_info("Module chargé\n");
return 0;
}
static void __exit my_exit(void) {
pr_info("Module déchargé\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("E.V.A");
MODULE_DESCRIPTION("Driver minimal embarqué");
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h> // copy_to_user / copy_from_user
#include <linux/slab.h> // kmalloc
#define DEVICE_NAME "mydev"
#define CLASS_NAME "myclass"
static dev_t dev_num;
static struct cdev my_cdev;
static struct class *my_class;
// Buffer interne
#define BUF_SIZE 1024
static char *dev_buf;
// open()
static int my_open(struct inode *inodep, struct file *filep) {
pr_info("mydev: ouvert\n");
return 0;
}
// release()
static int my_release(struct inode *inodep, struct file *filep) {
pr_info("mydev: fermé\n");
return 0;
}
{
ret;
(*offset >= BUF_SIZE) ;
(len > BUF_SIZE - *offset) len = BUF_SIZE - *offset;
(copy_to_user(buffer, dev_buf + *offset, len)) {
-EFAULT;
}
*offset += len;
len;
}
{
(len > BUF_SIZE - *offset) len = BUF_SIZE - *offset;
(copy_from_user(dev_buf + *offset, buffer, len)) {
-EFAULT;
}
*offset += len;
len;
}
{
(cmd) {
MY_CMD_RESET:
(dev_buf, , BUF_SIZE);
;
MY_CMD_GET_LEN:
(copy_to_user(( __user *)arg, &buf_len, (buf_len)))
-EFAULT;
;
:
-ENOTTY;
}
;
}
{
.owner = THIS_MODULE,
.open = my_open,
.release = my_release,
.read = my_read,
.write = my_write,
.unlocked_ioctl = my_ioctl,
};
__init {
(alloc_chrdev_region(&dev_num, , , DEVICE_NAME) < )
;
cdev_init(&my_cdev, &fops);
(cdev_add(&my_cdev, dev_num, ) < ) {
unregister_chrdev_region(dev_num, );
;
}
my_class = class_create(THIS_MODULE, CLASS_NAME);
device_create(my_class, , dev_num, , DEVICE_NAME);
dev_buf = kmalloc(BUF_SIZE, GFP_KERNEL);
(dev_buf, , BUF_SIZE);
pr_info(,
MAJOR(dev_num), MINOR(dev_num));
;
}
/ {
my_device: my-device@40005000 {
compatible = "eva,my-ip-core";
reg = <0x40005000 0x1000>;
interrupts = <42 2>; // IRQ 42, rising edge
clocks = <&clk 12>;
reset-gpios = <&gpio3 15 GPIO_ACTIVE_LOW>;
dmas = <&dma 0 1>; // DMA channel 0, request 1
dma-names = "rx";
status = "okay";
};
};
#include <linux/platform_device.h>
#include <linux/of.h> // Device Tree helpers
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/io.h> // ioremap / readl / writel
#include <linux/dmaengine.h>
#include <linux/gpio/consumer.h>
#include <linux/clk.h>
struct my_priv {
void __iomem *base;
int irq;
struct clk *clk;
struct gpio_desc *reset_gpio;
struct dma_chan *dma_chan;
struct device *dev;
spinlock_t lock;
};
// Probing
static int my_probe(struct platform_device *pdev) {
struct my_priv *priv;
priv = devm_kzalloc(&pdev->dev, (*priv), GFP_KERNEL);
(!priv) -ENOMEM;
priv->dev = &pdev->dev;
spin_lock_init(&priv->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, );
priv->base = devm_ioremap_resource(&pdev->dev, res);
(IS_ERR(priv->base)) PTR_ERR(priv->base);
priv->irq = platform_get_irq(pdev, );
(priv->irq < ) priv->irq;
ret = devm_request_irq(&pdev->dev, priv->irq, my_isr,
IRQF_TRIGGER_RISING, , priv);
(ret) ret;
priv->clk = devm_clk_get(&pdev->dev, );
(!IS_ERR(priv->clk)) clk_prepare_enable(priv->clk);
priv->reset_gpio = devm_gpiod_get(&pdev->dev, , GPIOD_OUT_HIGH);
platform_set_drvdata(pdev, priv);
dev_info(&pdev->dev, );
;
}
{
dev_id;
status = readl(priv->base + IRQ_STATUS);
(status & RX_READY) {
data = readl(priv->base + RX_DATA);
schedule_work(&priv->work);
}
writel(status, priv->base + IRQ_CLEAR);
IRQ_HANDLED;
}
{
{ .compatible = , },
{ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
{
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = ,
.of_match_table = my_of_match,
},
};
module_platform_driver(my_driver);
&i2c1 {
status = "okay";
clock-frequency = <100000>; // 100kHz standard
temperature@48 {
compatible = "ti,tmp102";
reg = <0x48>; // I2C address 7-bit
#address-cells = <1>;
#size-cells = <0>;
};
};
#include <linux/i2c.h>
struct tmp102_priv {
struct i2c_client *client;
int temperature;
struct mutex lock;
};
static int tmp102_read_temp(struct i2c_client *client, int *temp) {
u8 reg = 0x00; // Temperature register
u8 buf[2];
struct i2c_msg msgs[2] = {
{ .addr = client->addr, .flags = 0, .len = 1, .buf = ® },
{ .addr = client->addr, .flags = I2C_M_RD, .len = 2, .buf = buf },
};
int ret = i2c_transfer(client->adapter, msgs, 2);
if (ret != 2) return -EIO;
// TMP102 : 12-bit signed, 0.0625°C par bit
s16 raw = (buf[0] << 8) | buf[1];
*temp = (raw >> 4) * 625 / 10; // °C × 10
return 0;
}
static int tmp102_probe(struct i2c_client *client,
const struct i2c_device_id *id) {
devm_kzalloc(&client->dev, (*priv), GFP_KERNEL);
priv->client = client;
mutex_init(&priv->lock);
i2c_set_clientdata(client, priv);
u8 config[] = {, };
i2c_master_send(client, config, );
dev_info(&client->dev, );
;
}
{
{ , },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp102_id);
{
{ .compatible = },
{ }
};
MODULE_DEVICE_TABLE(of, tmp102_of_match);
{
.driver = {
.name = ,
.of_match_table = tmp102_of_match,
},
.probe = tmp102_probe,
.id_table = tmp102_id,
};
module_i2c_driver(tmp102_driver);
#include <linux/spi/spi.h>
// Device Tree
// &spi1 {
// status = "okay";
// cs-gpios = <&gpio3 10 GPIO_ACTIVE_LOW>;
// adc@0 {
// compatible = "ti,ads1256";
// reg = <0>; // Chip select 0
// spi-max-frequency = <19200000>;
// };
// };
static int ads1256_transfer(struct spi_device *spi, u8 *tx, u8 *rx, int len) {
struct spi_transfer t = {
.tx_buf = tx,
.rx_buf = rx,
.len = len,
.cs_change = 0,
.delay_usecs = 1,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
// SPI read via register
int ads1256_reg_read(struct spi_device *spi, u8 reg) {
u8 tx[3] = {0x0F | (reg << 4), 0, 0}; // RREG command
u8 rx[3];
ads1256_transfer(spi, tx, rx, 3);
return rx[2];
}
// DMA scatter-gather
struct dma_async_tx_descriptor *tx;
struct dma_slave_config cfg = {
.direction = DMA_DEV_TO_MEM,
.src_addr = (phys_addr_t)(priv->base + RX_FIFO),
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst = 4,
.dst_addr = 0,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dst_maxburst = 4,
};
dmaengine_slave_config(priv->dma_chan, &cfg);
tx = dmaengine_prep_slave_sg(priv->dma_chan, dma_sg, 1,
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
tx->callback = dma_callback;
tx->callback_param = priv;
dmaengine_submit(tx);
dma_async_issue_pending(priv->dma_chan);
// my_periph.h — Interface HAL
typedef struct {
uint32_t (*init)(void);
uint32_t (*read)(uint8_t *buf, uint32_t len);
uint32_t (*write)(const uint8_t *buf, uint32_t len);
uint32_t (*ioctl)(uint32_t cmd, void *arg);
} my_periph_driver_t;
// my_periph_stm32.c — Implémentation STM32
static uint32_t stm32_init(void) {
// Activer clock périphérique
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// Configurer GPIO
// Configurer USART
USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
return 0;
}
static const my_periph_driver_t stm32_impl = {
.init = stm32_init,
.read = stm32_usart_read,
.write = stm32_usart_write,
.ioctl = stm32_ioctl,
};
// my_periph_imx.c — Implémentation i.MX (même interface)
static const my_periph_driver_t imx_impl = {
.init = imx_uart_init,
.read = imx_uart_read,
.write = imx_uart_write,
.ioctl = imx_ioctl,
};
// Polling — simple, CPU 100%
void wait_for_rx(void) {
while (!(USART2->SR & USART_SR_RXNE));
return USART2->DR;
}
// IRQ — efficace, overlap CPU
void USART2_IRQHandler(void) {
if (USART2->SR & USART_SR_RXNE) {
uint8_t byte = USART2->DR;
// Mettre dans ring buffer
ringbuf_put(&rx_ring, byte);
}
}
// DMA — zéro CPU, discontinuité mémoire
void DMA_Config(void) {
// DMA circular buffer UART → RAM
DMA1_Stream5->CR = DMA_SxCR_CHSEL_4 | // Ch4=UART2_RX
DMA_SxCR_CIRC | // Circular
DMA_SxCR_MINC | // Memory inc
DMA_SxCR_TCIE; // Transfer complete IRQ
DMA1_Stream5->NDTR = BUF_SIZE;
DMA1_Stream5->PAR = (uint32_t)&USART2->DR;
DMA1_Stream5->M0AR = (uint32_t)uart_rx_buf;
DMA1_Stream5->CR |= DMA_SxCR_EN;
}
obj-m += mydriver.o
mydriver-objs := mydriver_main.o mydriver_io.o
KDIR ?= /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
install:
insmod mydriver.ko
rmmod mydriver # si reload
# Compilation croisée
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
KDIR ?= /path/to/kernel/build