总体说明: 这个结构 我肯定 一次是 写不完的, 框架有了, 以后 有机会了,就会 不断去填充这些内容。
一、 首先是 在 对于 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 的使用。
这部分估计我没有时间做。