rpmsg源码驱动分析
Posted Vanau
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rpmsg源码驱动分析相关的知识,希望对你有一定的参考价值。
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) STMicroelectronics 2019 - All Rights Reserved
* Author: Jean-Philippe Romain <jean-philippe.romain@st.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/rpmsg.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/miscdevice.h>
#include <linux/eventfd.h>
#include <linux/of_platform.h>
#include <linux/list.h>
#define RPMSG_SDB_DRIVER_VERSION "1.0"
/*
* Static global variables
*/
static const char rpmsg_sdb_driver_name[] = "stm32-rpmsg-sdb";
static int LastBufferId;
struct rpmsg_sdb_ioctl_set_efd
int bufferId, eventfd;
;
struct rpmsg_sdb_ioctl_get_data_size
int bufferId;
uint32_t size;
;
/* ioctl numbers */
/* _IOW means userland is writing and kernel is reading */
/* _IOR means userland is reading and kernel is writing */
/* _IOWR means userland and kernel can both read and write */
#define RPMSG_SDB_IOCTL_SET_EFD _IOW('R', 0x00, struct rpmsg_sdb_ioctl_set_efd *)
#define RPMSG_SDB_IOCTL_GET_DATA_SIZE _IOWR('R', 0x01, struct rpmsg_sdb_ioctl_get_data_size *)
struct sdb_buf_t
int index; /* index of buffer */
size_t size; /* buffer size */
size_t writing_size; /* size of data written by copro */
dma_addr_t paddr; /* physical address*/
void *vaddr; /* virtual address */
void *uaddr; /* mapped address for userland */
struct eventfd_ctx *efd_ctx; /* eventfd context */
struct list_head buflist; /* reference in the buffers list */
;
struct rpmsg_sdb_t
struct mutex mutex; /* mutex to protect the ioctls */
struct miscdevice mdev; /* misc device ref */
struct rpmsg_device *rpdev; /* handle rpmsg device */
struct list_head buffer_list; /* buffer instances list */
;
/*
其中:struct rpmsg_device
struct device dev;
struct rpmsg_device_id id;
char *driver_override;
u32 src;
u32 dst;
struct rpmsg_endpoint *ept;
bool announce;
const struct rpmsg_device_ops *ops;
;
*/
struct device *rpmsg_sdb_dev;
static int rpmsg_sdb_format_txbuf_string(struct sdb_buf_t *buffer, char *bufinfo_str)
return sprintf(bufinfo_str, "B%dA%08xL%08x", buffer->index, buffer->paddr, buffer->size);
// sprintf 如果成功,则返回写入的字符总数,但不包括字符串追加在字符串末尾的空字符。
/*解码收到的字符串*/
static long rpmsg_sdb_decode_rxbuf_string(char *rxbuf_str, int *buffer_id, size_t *size)
int ret = 0;
char *sub_str;
long bsize;
long bufid;
const char delimiter[1] = 'L';
//pr_err("%s: rxbuf_str:%s\\n", __func__, rxbuf_str);
/* Get first part containing the buffer id */
/*
char *strsep(char **s, const char *delim); s为要分解的字符串,delim为分隔符字符串。
返回值:从s开头开始的一个个子串,当没有分割的子串时返回NULL。
*/
sub_str = strsep(&rxbuf_str, delimiter);
//pr_err("%s: sub_str:%s\\n", __func__, sub_str);
/* Save Buffer id and size: template BxLyyyyyyyy*/
/*
long int kstrtol (const char* str, int base, char** endptr);
str 为要转换的字符串,base 为字符串 str 所采用的进制,endstr 为第一个不能转换的字符的指针,
这样,当传过来为 BxLyyyyyyyy ,则
*sub_str=Bx
*bufid = x -> buffer_id
*bsize = yyyyyyyy -> size
*/
ret = kstrtol(&sub_str[1], 10, &bufid);
if (ret < 0)
pr_err("%s: extract of buffer id failed(%d)", __func__, ret);
goto out;
ret = kstrtol(&rxbuf_str[2], 16, &bsize);
if (ret < 0)
pr_err("%s: extract of buffer size failed(%d)", __func__, ret);
goto out;
*size = (size_t)bsize;
*buffer_id = (int)bufid;
out:
return ret;
/* 发送buffer 里面的数据信息 */
static int rpmsg_sdb_send_buf_info(struct rpmsg_sdb_t *rpmsg_sdb, struct sdb_buf_t *buffer)
int count = 0, ret = 0;
const unsigned char *tbuf;
char mybuf[21];
int msg_size;
struct rpmsg_device *_rpdev;
_rpdev = rpmsg_sdb->rpdev;
/*
rpmsg_get_buffer_size返回rpmsg设备下所建立端点的可发送消息的数据大小
*/
msg_size = rpmsg_get_buffer_size(_rpdev->ept);
if (msg_size < 0)
return msg_size;
/*
根据struct sdb_buf_t *buffer中的数据成员,建立 mybuf->"B%dA%08xL%08x"结构
*/
count = rpmsg_sdb_format_txbuf_string(buffer, mybuf);
tbuf = &mybuf[0];
do
/* send a message to our remote processor */
ret = rpmsg_send(_rpdev->ept, (void *)tbuf,
count > msg_size ? msg_size : count);
if (ret)
dev_err(&_rpdev->dev, "rpmsg_send failed: %d\\n", ret);
return ret;
if (count > msg_size)
count -= msg_size;
tbuf += msg_size;
else
count = 0;
while (count > 0);
return count;
static int rpmsg_sdb_mmap(struct file *file, struct vm_area_struct *vma)
/* struct vm_area_struct 结构体 defines a memory VMM memory area
NumPages -> 页
align -> get_order(size)从size中提取order
prot -> vma->vm_page_prot 此vma的访问权限
*/
unsigned long vsize = vma->vm_end - vma->vm_start;
unsigned long size = PAGE_ALIGN(vsize); //对齐页的上边界
unsigned long NumPages = size >> PAGE_SHIFT;
unsigned long align = get_order(size);
pgprot_t prot = vma->vm_page_prot;
struct rpmsg_sdb_t *_rpmsg_sdb;
struct sdb_buf_t *_buffer;
if (align > CONFIG_CMA_ALIGNMENT)
align = CONFIG_CMA_ALIGNMENT;
if (rpmsg_sdb_dev == NULL)
return -ENOMEM;
rpmsg_sdb_dev->coherent_dma_mask = DMA_BIT_MASK(32);
rpmsg_sdb_dev->dma_mask = &rpmsg_sdb_dev->coherent_dma_mask;
_rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t,
mdev);
/* Field the last buffer entry which is the last one created */
if (!list_empty(&_rpmsg_sdb->buffer_list))
//获得list中的前一个元素地址
_buffer = list_last_entry(&_rpmsg_sdb->buffer_list,
struct sdb_buf_t, buflist);
_buffer->uaddr = NULL;
_buffer->size = NumPages * PAGE_SIZE;
_buffer->writing_size = -1;
//dma_alloc_writecombine返回分配的内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
_buffer->vaddr = dma_alloc_writecombine(rpmsg_sdb_dev,
_buffer->size,
&_buffer->paddr,
GFP_KERNEL);
if (!_buffer->vaddr)
pr_err("%s: Memory allocation issue\\n", __func__);
return -ENOMEM;
pr_debug("%s - dma_alloc_writecombine done - paddr[%d]:%x - vaddr[%d]:%p\\n", __func__, _buffer->index, _buffer->paddr, _buffer->index, _buffer->vaddr);
/* Get address for userland */
// 当用户调用mmap时,驱动中的file_operations->mmap->remap_pfn_range负责为一段物理地址建立新的页表
if (remap_pfn_range(vma, vma->vm_start,
(_buffer->paddr >> PAGE_SHIFT) + vma->vm_pgoff,
size, prot))
return -EAGAIN;
_buffer->uaddr = (void *)vma->vm_start;
/* Send information to remote proc */
rpmsg_sdb_send_buf_info(_rpmsg_sdb, _buffer);
else
dev_err(rpmsg_sdb_dev, "No existing buffer entry exist in the list !!!");
return -EINVAL;
/* Increment for number of requested buffer */
LastBufferId++;
return 0;
/**
* rpmsg_sdb_open - Open Session
*
* @inode: inode struct
* @file: file struct
*
* Return:
* 0 - Success
* Non-zero - Failure
*/
static int rpmsg_sdb_open(struct inode *inode, struct file *file)
struct rpmsg_sdb_t *_rpmsg_sdb;
_rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t,
mdev);
/* Initialize the buffer list*/
INIT_LIST_HEAD(&_rpmsg_sdb->buffer_list);
mutex_init(&_rpmsg_sdb->mutex);
return 0;
/**
* rpmsg_sdb_close - Close Session
*
* @inode: inode struct
* @file: file struct
*
* Return:
* 0 - Success
* Non-zero - Failure
*/
static int rpmsg_sdb_close(struct inode *inode, struct file *file)
struct rpmsg_sdb_t *_rpmsg_sdb;
struct sdb_buf_t *pos, *next;
_rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t,
mdev);
list_for_each_entry_safe(pos, next, &_rpmsg_sdb->buffer_list, buflist)
/* Free the CMA allocation */
dma_free_writecombine(rpmsg_sdb_dev, pos->size, pos->vaddr,
pos->paddr);
/* Remove the buffer from the list */
list_del(&pos->buflist);
/* Free the buffer */
kfree(pos);
/* Reset LastBufferId */
LastBufferId = 0;
return 0;
/**
* rpmsg_sdb_ioctl - IOCTL
*
* @session: ibmvmc_file_session struct
* @cmd: cmd field
* @arg: Argument field
*
* Return:
* 0 - Success
* Non-zero - Failure
*/
static long rpmsg_sdb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
int idx = 0;
struct rpmsg_sdb_t *_rpmsg_sdb;
struct sdb_buf_t *buffer, *lastbuffer;
struct list_head *pos;
struct sdb_buf_t *datastructureptr = NULL;
struct rpmsg_sdb_ioctl_set_efd q_set_efd;
struct rpmsg_sdb_ioctl_get_data_size q_get_dat_size;
/*
(void __user *)arg 指的是arg值是一个用户空间的地址,不能直接进行拷贝等,要使用例如copy_from_user,copy_to_user等函数。
__user 仅仅表示该变量是用户空间的变量
*/
void __user *argp = (void __user *)arg;
/*
container_of(ptr, type, member)
ptr:结构体变量中某个成员的地址
type:结构体类型
member:该结构体中的成员变量名
作用:已知member这个结构体成员的地址ptr,且其在类型为type的结构体中,获取这个结构体变量的首地址。
可知:file->private_data保存的是mdev的地址
*/
_rpmsg_sdb = container_of(file->private_data, struct rpmsg_sdb_t,
mdev);
switch (cmd)
case RPMSG_SDB_IOCTL_SET_EFD:
mutex_lock(&_rpmsg_sdb->mutex);
/* Get index from the last buffer in the list */
if (!list_empty(&_rpmsg_sdb->buffer_list))
//list_last_entry->list_entry同container_of,只不过其获取的是前一个结构体变量的地址
lastbuffer = list_last_entry(&_rpmsg_sdb->buffer_list, struct sdb_buf_t, buflist);
idx = lastbuffer->index;
/* increment this index for the next buffer creation*/
idx++;
/*
从用户空间拷贝数据到内核空间,失败则返回还没有被拷贝的字节数,成功返回0.
*/
if (copy_from_user(&q_set_efd, (struct rpmsg_sdb_ioctl_set_efd *)argp,
sizeof(struct rpmsg_sdb_ioctl_set_efd)))
pr_warn("rpmsg_sdb: RPMSG_SDB_IOCTL_GET_DATA_SIZE: copy to user failed.\\n");
mutex_unlock(&_rpmsg_sdb->mutex);
return -EFAULT;
/* create a new buffer which will be added in the buffer list */
// kmalloc() 申请的内存位于物理内存映射区域,而且在物理上也是连续的
buffer = kmalloc(sizeof(struct sdb_buf_t), GFP_KERNEL);
buffer->index = idx;
buffer->efd_ctx = eventfd_ctx_fdget(q_set_efd.eventfd);
list_add_tail(&buffer->buflist, &_rpmsg_sdb->buffer_list);
mutex_unlock(&_rpmsg_sdb->mutex);
break;
case RPMSG_SDB_IOCTL_GET_DATA_SIZE:
if (copy_from_user(&q_get_dat_size, (struct rpmsg_sdb_ioctl_get_data_size *)argp,
sizeof(struct rpmsg_sdb_ioctl_get_data_size)))
pr_warn("rpmsg_sdb: RPMSG_SDB_IOCTL_GET_DATA_SIZE: copy from user failed.\\n");
return -EFAULT;
/* Get the index of the requested buffer and then look-up in the buffer list*/
idx = q_get_dat_size.bufferId;
list_for_each(pos, &_rpmsg_sdb->buffer_list)
datastructureptr = list_entry(pos, struct sdb_buf_t, buflist);
if (datastructureptr->index == idx)
/* Get the writing size*/
q_get_dat_size.size = datastructureptr->writing_size;
break;
if (copy_to_user((struct rpmsg_sdb_ioctl_get_data_size *)argp, &q_get_dat_size,
sizeof(struct rpmsg_sdb_ioctl_get_data_size)))
pr_warn("rpmsg_sdb: RPMSG_SDB_IOCTL_GET_DATA_SIZE: copy to user failed.\\n");
return -EFAULT;
/* Reset the writing size*/
datastructureptr->writing_size = -1;
break;
default:
return -EINVAL;
return 0;
static const struct file_operations rpmsg_sdb_fops =
.owner = THIS_MODULE,
.unlocked_ioctl = rpmsg_sdb_ioctl,
.mmap = rpmsg_sdb_mmap,
.open = rpmsg_sdb_open,
.release = rpmsg_sdb_close,
;
static int rpmsg_sdb_drv_cb(struct rpmsg_device *rpdev, void *data, int len,
void *priv, u32 src)
int ret = 0;
int buffer_id = 0;
size_t buffer_size;
char rpmsg_RxBuf[len+1];
struct list_head *pos;
struct sdb_buf_t *datastructureptr = NULL;
struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpdev->dev);
if (len == 0)
dev_err(rpmsg_sdb_dev, "(%s) Empty lenght requested\\n", __func__);
return -EINVAL;
//dev_err(rpmsg_sdb_dev, "(%s) lenght: %d\\n", __func__,len);
memcpy(rpmsg_RxBuf, data, len);
rpmsg_RxBuf[len] = 0;
ret = rpmsg_sdb_decode_rxbuf_string(rpmsg_RxBuf, &buffer_id, &buffer_size);
if (ret < 0)
goto out;
if (buffer_id > LastBufferId)
ret = -EINVAL;
goto out;
/* Signal to User space application */
//遍历列表,以不断变化的pos作为循环变量
list_for_each(pos, &drv->buffer_list)
//list_entry获取链表数据
datastructureptr = list_entry(pos, struct sdb_buf_t, buflist);
if (datastructureptr->index == buffer_id)
datastructureptr->writing_size = buffer_size;
if (datastructureptr->writing_size > datastructureptr->size)
dev_err(rpmsg_sdb_dev, "(%s) Writing size is bigger than buffer size\\n", __func__);
ret = -EINVAL;
goto out;
eventfd_signal(datastructureptr->efd_ctx, 1);
break;
/* TODO: quid if nothing find during the loop ? */
out:
return ret;
static int rpmsg_sdb_drv_probe(struct rpmsg_device *rpdev)
int ret = 0;
struct device *dev = &rpdev->dev;
struct rpmsg_sdb_t *rpmsg_sdb;
/*
devm_kzalloc() 是跟设备有关的内核内存分配函数,当设备驱动卸载时,内存会被自动释放。
kzalloc()则需要手动释放(使用kfree()))
注意:在驱动的探针函数中调用devm_kzalloc()
*/
rpmsg_sdb = devm_kzalloc(dev, sizeof(*rpmsg_sdb), GFP_KERNEL);
if (!rpmsg_sdb)
return -ENOMEM;
mutex_init(&rpmsg_sdb->mutex);
rpmsg_sdb->rpdev = rpdev;
rpmsg_sdb->mdev.name = "rpmsg-sdb";
rpmsg_sdb->mdev.minor = MISC_DYNAMIC_MINOR;
rpmsg_sdb->mdev.fops = &rpmsg_sdb_fops;
/* dev_set_drvdata函数用来设置device 的私有驱动数据
static inline void dev_set_drvdata(struct device *dev, void *data)
dev->driver_data = data;
*/
dev_set_drvdata(&rpdev->dev, rpmsg_sdb);
/* Register misc device */
ret = misc_register(&rpmsg_sdb->mdev);
if (ret)
dev_err(dev, "Failed to register device\\n");
goto err_out;
rpmsg_sdb_dev = rpmsg_sdb->mdev.this_device;
//dev_info()用于启动过程、或者模块加载过程等“通知类的”信息
dev_info(dev, "%s probed\\n", rpmsg_sdb_driver_name);
err_out:
return ret;
static void rpmsg_sdb_drv_remove(struct rpmsg_device *rpmsgdev)
struct rpmsg_sdb_t *drv = dev_get_drvdata(&rpmsgdev->dev);
misc_deregister高通平台开发系列讲解(SMD篇)图解SMD驱动流程