转自
http://www.cnblogs.com/sankye/p/3955630.html
一.spidev.c文件
看一个设备驱动的方法:
module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数)
设备与设备驱动匹配时候调用的probe方法spidev_probe
设备驱动的操作函数集file_operations--->spidev_fops
@@open方法spidev_open
进行检查, 重点是以后三条语句,其他的见下面代码注释:
- spidev->users++;
- filp->private_data = spidev;
- nonseekable_open(inode, filp);
@@read方法spidev_read
spidev = filp->private_data;=========>>status = spidev_sync_read(spidev, count);===========>>
spidev_sync(spidev, &m);==========>>status = spi_async(spidev->spi, message);===========>>
wait_for_completion(&done);========>>到了这一步是重点,在spi_async()方法中,使用以下语句将要做的事情加到workqueue中
list_add_tail(&m->queue, &bitbang->queue);
queue_work(bitbang->workqueue, &bitbang->work);
此后所有的处理程序便转移到在之前初始化的work方法中看以下代码:
点击(此处)折叠或打开
- static void bitbang_work(struct work_struct *work)
- {
- struct spi_bitbang *bitbang =
- container_of(work, struct spi_bitbang, work);
- unsigned long flags;
- int do_setup = -1;
- int (*setup_transfer)(struct spi_device *,
- struct spi_transfer *);
- setup_transfer = bitbang->setup_transfer;
- spin_lock_irqsave(&bitbang->lock, flags);
- bitbang->busy = 1;
- while (!list_empty(&bitbang->queue)) {
- struct spi_message *m;
- struct spi_device *spi;
- unsigned nsecs;
- struct spi_transfer *t = NULL;
- unsigned tmp;
- unsigned cs_change;
- int status;
- m = container_of(bitbang->queue.next, struct spi_message,
- queue);
- list_del_init(&m->queue);
- spin_unlock_irqrestore(&bitbang->lock, flags);
- /* FIXME this is made-up ... the correct value is known to
- * word-at-a-time bitbang code, and presumably chipselect()
- * should enforce these requirements too?
- */
- nsecs = 100;
- spi = m->spi;
- tmp = 0;
- cs_change = 1;
- status = 0;
- list_for_each_entry (t, &m->transfers, transfer_list) {
- /* override speed or wordsize? */
- if (t->speed_hz || t->bits_per_word)
- do_setup = 1;
- /* init (-1) or override (1) transfer params */
- if (do_setup != 0) {
- if (!setup_transfer) {
- status = -ENOPROTOOPT;
- break;
- }
- status = setup_transfer(spi, t);
- if (status < 0)
- break;
- }
- /* set up default clock polarity, and activate chip;
- * this implicitly updates clock and spi modes as
- * previously recorded for this device via setup().
- * (and also deselects any other chip that might be
- * selected ...)
- */
- if (cs_change) {
- bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;
- if (!t->tx_buf && !t->rx_buf && t->len) {
- status = -EINVAL;
- break;
- }
- /* transfer data. the lower level code handles any
- * new dma mappings it needs. our caller always gave
- * us dma-safe buffers.
- */
- if (t->len) {
- /* REVISIT dma API still needs a designated
- * DMA_ADDR_INVALID; ~0 might be better.
- */
- if (!m->is_dma_mapped)
- t->rx_dma = t->tx_dma = 0;
- status = bitbang->txrx_bufs(spi, t);
- }
- if (status > 0)
- m->actual_length += status;
- if (status != t->len) {
- /* always report some kind of error */
- if (status >= 0)
- status = -EREMOTEIO;
- break;
- }
- status = 0;
- /* protocol tweaks before next transfer */
- if (t->delay_usecs)
- udelay(t->delay_usecs);
- if (!cs_change)
- continue;
- if (t->transfer_list.next == &m->transfers)
- break;
- /* sometimes a short mid-message deselect of the chip
- * may be needed to terminate a mode or command
- */
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- m->status = status;
- m->complete(m->context);
- /* restore speed and wordsize if it was overridden */
- if (do_setup == 1)
- setup_transfer(spi, NULL);
- do_setup = 0;
- /* normally deactivate chipselect ... unless no error and
- * cs_change has hinted that the next message will probably
- * be for this chip too.
- */
- if (!(status == 0 && cs_change)) {
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- spin_lock_irqsave(&bitbang->lock, flags);
- }
- bitbang->busy = 0;
- spin_unlock_irqrestore(&bitbang->lock, flags);
- }
结束处理所有任务后,见上面红 {MOD}底纹部分解除wait_for_completion(&done);
最后missing = copy_to_user(buf, spidev->buffer, status);将数据发送到用户空间
@@write方法spidev_write
与上面open方式基本相同
@@ioctl方法spidev_ioctl
具体的详解见下面章节(三,四)
下面是spidev.c添加注释部分
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
-
#define SPIDEV_MAJOR 153 //spidev主设备号
-
#define N_SPI_MINORS 32 /* ... up to 256 */
-
static DECLARE_BITMAP(minors, N_SPI_MINORS);
-
#define SPI_MODE_MASK (SPI_CPHA|SPI_CPOL|SPI_CS_HIGH|SPI_LSB_FIRST|SPI_3WIRE|SPI_LOOP|SPI_NO_CS|SPI_READY)
-
-
struct spidev_data {
-
dev_t devt;
-
spinlock_t spi_lock;
-
struct spi_device *spi;
-
struct list_head device_entry;
-
struct mutex buf_lock;
-
unsigned users;
-
u8 *buffer;
-
};
-
-
static LIST_HEAD(device_list);
-
static DEFINE_MUTEX(device_list_lock);
-
static unsigned bufsiz = 4096;
-
module_param(bufsiz, uint, S_IRUGO);
-
MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
-
-
static void spidev_complete(void *arg)
-
{
-
complete(arg);
-
}
-
-
static ssize_t spidev_sync(struct spidev_data
*spidev, struct spi_message *message)
-
{
-
DECLARE_COMPLETION_ONSTACK(done);
-
int status;
-
-
message->complete = spidev_complete;
-
message->context = &done;
-
-
spin_lock_irq(&spidev->spi_lock);
-
if (spidev->spi == NULL)
-
status = -ESHUTDOWN;
-
else
-
status = spi_async(spidev->spi, message);
-
spin_unlock_irq(&spidev->spi_lock);
-
-
if (status == 0) {
-
wait_for_completion(&done);
-
status = message->status;
-
if (status == 0)
-
status = message->actual_length;
-
}
-
return status;
-
}
-
-
static inline ssize_t
spidev_sync_write(struct spidev_data *spidev, size_t len)
-
{
-
struct spi_transfer t = {
-
.tx_buf = spidev->buffer,
-
.len = len,
-
};
-
struct spi_message m;
-
-
spi_message_init(&m);
-
spi_message_add_tail(&t, &m);
-
return spidev_sync(spidev, &m);
-
}
-
-
static inline ssize_t
spidev_sync_read(struct spidev_data *spidev, size_t len)
-
{
-
struct spi_transfer t = {
-
.rx_buf = spidev->buffer,
-