关于 spi 的linux 的驱动的问题
总体说明: 这个结构 我肯定 一次是 写不完的, 框架有了, 以后 有机会了,就会 不断去填充这些内容。
一、 首先是 在 对于 linux spi 框架的 了解。
这是它的 总体的结构, 接口部分就是 spi core 。
1.5编程的内容 跟这个就很像了。
也就是 说 , 芯片原厂 实现的 就是 在 spi_sync 以下的部分。
这个接口 我之前看过 ,但是没看懂。
这个spi_locked 的目的 是什么呢?
这个控制器的 初始化的流程 我也没看懂。
先截图下来。
二、 然后是 对于具体的函数的了解。
spi_read()
spi_write()
spi_write_then_read()
这是具体的函数的实现,
drivers/spi/spi.c
int spi_write_then_read(struct spi_device *spi,
▎ ▎ const void *txbuf, unsigned n_tx,
▎ ▎ void *rxbuf, unsigned n_rx)
{
▎ static DEFINE_MUTEX(lock);
▎
▎ int status;
▎ struct spi_message message;
▎ struct spi_transfer x[2];
▎ u8 *local_buf;
▎
▎ /* Use preallocated DMA-safe buffer if we can. We can't avoid
▎ ▎* copying here, (as a pure convenience thing), but we can
▎ ▎* keep heap costs out of the hot path unless someone else is
▎ ▎* using the pre-allocated buffer or the transfer is too large.
▎ ▎*/
▎ if ((n_tx + n_rx) > SPI_BUFSIZ || !mutex_trylock(&lock)) {
▎ ▎ local_buf = kmalloc(max((unsigned)SPI_BUFSIZ, n_tx + n_rx),
▎ ▎ ▎ ▎ GFP_KERNEL | GFP_DMA);
▎ ▎ if (!local_buf)
▎ ▎ ▎ return -ENOMEM;
▎ } else {
▎ ▎ local_buf = buf;
▎ }
▎
▎ spi_message_init(&message);
▎ memset(x, 0, sizeof(x));
▎ if (n_tx) {
▎ ▎ x[0].len = n_tx;
▎ ▎ spi_message_add_tail(&x[0], &message);
▎ }
▎ if (n_rx) {
▎ ▎ x[1].len = n_rx;
▎ ▎ spi_message_add_tail(&x[1], &message);
▎ }
▎
▎ memcpy(local_buf, txbuf, n_tx);
▎ x[0].tx_buf = local_buf;
▎ x[1].rx_buf = local_buf + n_tx;
▎
▎ /* do the i/o */
▎ status = spi_sync(spi, &message);
▎ if (status == 0)
▎ ▎ memcpy(rxbuf, x[1].rx_buf, n_rx);
▎
▎ if (x[0].tx_buf == buf)
▎ ▎ mutex_unlock(&lock);
▎ else
▎ ▎ kfree(local_buf);
▎
▎ return status;
}
EXPORT_SYMBOL_GPL(spi_write_then_read);
重点关注这两个地方。
首先是 关于 tx_buf , 与 rx_buf.
local_buf+n_tx, 它这里的意思是说, 将rx_buf 的地址,放到 tx_buf 地址的 后面。
直接 重新开一个buf 不是更好吗?
疑问: 还有就是 这里有两个 spi_transfer , 一个 设置了tx_buf 的内容, 另一个 只是设置了 rx_buf 的地址,没有设置发送什么内容。这样可以吗?
网上找了一个截图:
我感觉 这里的意思是:
如果我只是 设置了 rx_buf 的地址,tx_buf 没有设置, 那么我默认就在 tx_buf 中填null ,这也是发送, 还记得 spi_transfer 中有一个 len 吗? 这个length 既是在说 发送的长度,也是在说 接收的长度。
这只是我猜的,具体的 这块的实现, 我没找到。
关于 kthread_worker , kthread_work 的理解。
先来看看这两个函数的使用
网上的截图:
在 kthread_work 中 有真正的执行的函数
kthread_worker 中 有一个 tast_struct ,我比较感兴趣。
首先是初始化 kthread_worker
注意 : kthread_worker_fn 函数 并不执行spi 的逻辑。
然后是 kthread_work 的初始化。
注意: 这里的 这个 xxx_work_fn 才是 真正的 执行 spi 的逻辑的函数。
注意: kthread_queue_work 将 work 与worker 联系起来了。
然后是 kthread_run() 函数
网上的截图:
我的理解是, threadfn 这个只是一个 进程, 我设计做具体的事情, 后面肯定还有 某些流程,将worker 里面的具体的 spi 的 执行函数, 挂到这个进程上。
然后是 kthread_worker_fn 函数
代码如下:
int kthread_worker_fn(void *worker_ptr)
{
struct kthread_worker *worker = worker_ptr;
struct kthread_work *work;
/*
* FIXME: Update the check and remove the assignment when all kthread
* worker users are created using kthread_create_worker*() functions.
*/
WARN_ON(worker->task && worker->task != current);
worker->task = current;
if (worker->flags & KTW_FREEZABLE)
set_freezable();
repeat:
set_current_state(TASK_INTERRUPTIBLE); /* mb paired w/ kthread_stop */
if (kthread_should_stop()) {
__set_current_state(TASK_RUNNING);
spin_lock_irq(&worker->lock);
worker->task = NULL;
spin_unlock_irq(&worker->lock);
return 0;
}
work = NULL;
spin_lock_irq(&worker->lock);
if (!list_empty(&worker->work_list)) {
work = list_first_entry(&worker->work_list,
struct kthread_work, node);
list_del_init(&work->node);
}
worker->current_work = work;
spin_unlock_irq(&worker->lock);
if (work) {
__set_current_state(TASK_RUNNING);
work->func(work);
} else if (!freezing(current))
schedule();
try_to_freeze();
cond_resched();
goto repeat;
}
EXPORT_SYMBOL_GPL(kthread_worker_fn);
可以看到 比较重要的是 worker->task = current 这一句。
将 worker 的 tast ,设置成了 当前进程。
但是具体的还不是很了解。
九 、 我自己 关于 spi 有设备树 与没有设备树的 配置的区别,以及 对于 spi_board_info 的理解。
三 、 然后是 关于 mcp2515 的数据手册的解析。
四、 然后是 一些 需要注意的小点。
比如 spi 时钟的 时延的理解。
关于spi bit_word 的理解。
然后是 mcp2515 的 晶振 与 can 总线速率的关系。
五、 然后是 关于 通用的 spidev 的理解。
六 、然后是关于 gpio 模拟 spi 的理解。
七、 然后是关于 移植 mcp2515 的理解。
八、 是关于 stm32 对于 mcp2515 的使用。
这部分估计我没有时间做。