看一个设备驱动的方法:
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);
-
}
结束处理所有任务后,见上面红色底纹部分解除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,
-
.len = len,
-
};
-
struct spi_message m;
-
-
spi_message_init(&m);
-
spi_message_add_tail(&t, &m);
-
return spidev_sync(spidev, &m);
-
}
-
-
static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
-
{
-
struct spidev_data *spidev;
-
ssize_t status = 0;
-
-
if (count > bufsiz)
-
return -EMSGSIZE;
-
spidev = filp->private_data;
-
mutex_lock(&spidev->buf_lock);
-
status = spidev_sync_read(spidev, count);
-
if (status > 0) {
-
unsigned long missing;
-
missing = copy_to_user(buf, spidev->buffer, status);
-
if (missing == status)