goldfish_pipe为在平台总线上的一个misc类型设备
下来说一下goldfish_pipe的工作原理
首先该硬件设备要完成的功能是一个双向管道
从用户层的角度来看设备实现的功能如下
1 打开管道
2 写入数据
3 读取数据
设备支持的寄存器和功能如下
PIPE_REG_COMMAND 用于通知硬件用户要执行的操作类型
PIPE_REG_STATUS 返回用户发起操作的执行结果
PIPE_REG_CHANNEL 通道标识低32位和PIPE_REG_CHANNEL_HIGH寄存器配合使用,用于设置和读取通道的唯一标识,用户每次打开管道设备会创建一个通道,通过将该标示写入寄存器,在设备中断的时候通过该寄存器读取通道标示,找到要操作的通道
PIPE_REG_CHANNEL_HIGH 通道高32位
PIPE_REG_SIZE 读取或者写入数据过程中告知设备用户buffer的大小
PIPE_REG_ADDRESS 读取或者写入的用户buffer物理地址低32位
PIPE_REG_ADDRESS_HIGH 读取或者写入的用户buffer物理地址高32位
用户去读写数据的操作分为两种
1 批量操作
在驱动注册的时候会将参数物理地址通过PIPE_REG_PARAMS_ADDR_LOW|PIPE_REG_PARAMS_ADDR_HEIGH寄存器写到设备中,然后设备就知道该物理地址了,对于批量读写使用access_with_param函数进行
/* A value that will not be set by qemu emulator */
#define INITIAL_BATCH_RESULT (0xdeadbeaf)
static int access_with_param(struct goldfish_pipe_dev *dev, const int cmd,
unsigned long address, unsigned long avail,
struct goldfish_pipe *pipe, int *status)
{
struct access_params *aps = dev->aps;
if (aps == NULL)
return -1;
aps->result = INITIAL_BATCH_RESULT;
aps->channel = (unsigned long)pipe;
aps->size = avail;
aps->address = address;
aps->cmd = cmd;
writel(cmd, dev->base + PIPE_REG_ACCESS_PARAMS);
/*
* If the aps->result has not changed, that means
* that the batch command failed
*/
if (aps->result == INITIAL_BATCH_RESULT)
return -1;
*status = aps->result;
return 0;
}
其实就是通过参数设置要buffer的信息, 然后通过参数的result获取写结果。
2 非批量操作
非批量操作和批量操作的区别在于批量操作通过一个命令将所有信息写入应将,而非批量操作则每次写入一个信息 ,代码如下
gf_write64((u64)(unsigned long)pipe,
dev->base + PIPE_REG_CHANNEL,
dev->base + PIPE_REG_CHANNEL_HIGH);
writel(avail, dev->base + PIPE_REG_SIZE);
gf_write64(xaddr, dev->base + PIPE_REG_ADDRESS,
dev->base + PIPE_REG_ADDRESS_HIGH);
writel(is_write ? CMD_WRITE_BUFFER : CMD_READ_BUFFER,
dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
只有批量操作失败后才会专用非批量操作。
数据读写阻塞的通知操作:
1 在等待队列上等待
CMD_WAKE_ON_WRITE 或者 CMD_WAKE_ON_READ命令通知设备该管道需要接收数据可写/可读通知,然后挂起
2 通过中断通知, 当通道数据满足后发起中断,具体哪种数据满足(可读,可写,或者管道关闭)通过PIPE_REG_WAKES寄存器读取
另外需要注意的是大多数命令操作都需要传递channel id。
具体代码分析如下
我们今天就分析下它是如何工作的
static struct platform_driver goldfish_pipe = {
.probe = goldfish_pipe_probe,
.remove = goldfish_pipe_remove,
.driver = {
.name = "goldfish_pipe",
.owner = THIS_MODULE,
.of_match_table = goldfish_pipe_of_match,
.acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
}
};
module_platform_driver(goldfish_pipe);
首先做为platform_bus上的驱动注册到平台总线
我们知道平台总线在添加设备的或者驱动的过程会进行设备驱动匹配,然后调用驱动的probe函数,不知道的可以参考文章platform driver注册过程
我们直接从goldfish_pipe_probe函数进行分析
static int goldfish_pipe_probe(struct platform_device *pdev)
{
DPRINT("%s: call. platform_device=0x%lx\n", __FUNCTION__, pdev);
int err;
struct resource *r;
struct goldfish_pipe_dev *dev = pipe_dev;
/* not thread safe, but this should not happen */
WARN_ON(dev->base != NULL);
spin_lock_init(&dev->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL || resource_size(r) < PAGE_SIZE) {
dev_err(&pdev->dev, "can't allocate i/o page\n");
return -EINVAL;
}
dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
if (dev->base == NULL) {
dev