读书人

Linux上的SPI总线驱动(二)

发布时间: 2012-11-22 00:16:41 作者: rapoo

Linux下的SPI总线驱动(二)

版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127

四.SPI内核代码分析

我们已经讲过SPI驱动的移植,SPI控制设备和SPI接口设备的注册,SPI控制设备驱动和SPI接口设备驱动的注册。在移植时候,我们配置的选项告诉我们有些文件已经编入内核,那些就是SPI驱动的比较重要的文件。其中spi.c是SPI初始化和核心代码。spi_gpio.c是IO模拟SPI接口代码。spi_s3c24xx.c是s3c24xx系列芯片的SPI控制器驱动,它向更上层的SPI核心层(spi.c)提供接口用来控制芯片的SPI控制器,是一个被其他驱动使用的驱动。spi_s3c24xx_gpio.c允许用户指定3个gpio口,分别充当spi_clk、spi_mosi和spi_miso接口,模拟标准的spi总线。spidev.c是在核心层基础之上将SPI控制器模拟成一个字符型的驱动,向文件系统提供标准的文件系统接口,用来操作对应的SPI控制器。

通过这些代码的分析,我们可以获得三个知识:其一,更清楚的理解设备和驱动的注册。其二,掌握SPI数据的传输过程。三,更清晰的明白SPI各个文件的功能

我们先看spi.c

static int __init spi_init(void)

{

int status;

buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); //定义一个缓冲区,供后期数据传输使用

if (!buf) {

status = -ENOMEM;

goto err0;

}

status = bus_register(&spi_bus_type); //SPI总线注册

if (status < 0)

goto err1;

status = class_register(&spi_master_class); //SPI类注册

if (status < 0)

goto err2;

return 0;

err2:

bus_unregister(&spi_bus_type);

err1:

kfree(buf);

buf = NULL;

err0:

return status;

}

上面的总线注册让我们联想到前面讲SPI接口设备注册和SPI接口设备驱动注册时候,在那里我们看到将它们都注册到我们现在这里注册的SPI总线上来的。

然后我们在spi.c中定义了几个函数

spi_write_then_read spi_sync spi_async spi_setup

spi_busnum_to_master spi_unregister_master spi_register_master

spi_alloc_master spi_new_device spi_add_device

spi_alloc_device spi_register_driver spi_get_device_id

其次我们看看spi_bitbang.c

这个文件里主要定义了几个函数

spi_bitbang_stop spi_bitbang_start spi_bitbang_transfer

spi_bitbang_setup spi_bitbang_cleanup spi_bitbang_setup_transfer

然后看看spi_s3c24xx.c

前面我们已经知道spi_s3c24xx.c中,我们注册了SPI控制器驱动,注册了SPI接口设备。现在,我们好好读读这个文件,看看怎么由这个SPI控制器驱动到注册这个SPI接口设备的。

static struct platform_driver s3c24xx_spi_driver = {

.remove = __exit_p(s3c24xx_spi_remove),

.driver = {

.name = "s3c2410-spi",

.owner = THIS_MODULE,

.pm = S3C24XX_SPI_PMOPS,

},

};

static int __init s3c24xx_spi_init(void)

{

return platform_driver_probe(&s3c24xx_spi_driver, s3c24xx_spi_probe);

}

这个平台设备s3c24xx_spi_driver就是我们说的SPI控制器驱动,现在我们把精力集中在s3c24xx_spi_probe上吧

static int __init s3c24xx_spi_probe(struct platform_device *pdev)

{

struct s3c2410_spi_info *pdata;

struct s3c24xx_spi *hw;

struct spi_master *master;

struct resource *res;

int err = 0;

// 分配master+ s3c24xx_spi大小的数据,把s3c24xx_spi设为spi_master的私有数据

master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi));

if (master == NULL) {

dev_err(&pdev->dev, "No memory for spi_master\n");

err = -ENOMEM;

goto err_nomem;

}

hw = spi_master_get_devdata(master); //从master中获得s3c24xx_spi

memset(hw, 0, sizeof(struct s3c24xx_spi)); //清空s3c24xx_spi

hw->master = spi_master_get(master);

//驱动移植的时候需要实现的重要结构,初始化为&s3c2410_spi0_platdata

hw->pdata = pdata = pdev->dev.platform_data;

hw->dev = &pdev->dev;

if (pdata == NULL) {

dev_err(&pdev->dev, "No platform data supplied\n");

err = -ENOENT;

goto err_no_pdata;

}

platform_set_drvdata(pdev, hw); //设置平台的私有数据为s3c24xx_spi

init_completion(&hw->done);

master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;

master->num_chipselect = hw->pdata->num_cs; //该总线上的设备数

master->bus_num = pdata->bus_num; //总线号

hw->bitbang.master = hw->master;

//spi_bitbang专门负责数据的传输

//此处不定义除s3c24xx_spi_chipsel外其他函数也可以,那样会在spi_bitbang.c中定义

hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;

hw->bitbang.chipselect = s3c24xx_spi_chipsel;

hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;

hw->master->setup = s3c24xx_spi_setup;

hw->master->cleanup = s3c24xx_spi_cleanup;

dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (res == NULL) {

dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");

err = -ENOENT;

goto err_no_iores;

}

hw->ioarea = request_mem_region(res->start, resource_size(res),

pdev->name);

if (hw->ioarea == NULL) {

dev_err(&pdev->dev, "Cannot reserve region\n");

err = -ENXIO;

goto err_no_iores;

}

hw->regs = ioremap(res->start, resource_size(res)); //读取寄存器基址

if (hw->regs == NULL) {

dev_err(&pdev->dev, "Cannot map IO\n");

err = -ENXIO;

goto err_no_iomap;

}

hw->irq = platform_get_irq(pdev, 0);

if (hw->irq < 0) {

dev_err(&pdev->dev, "No IRQ specified\n");

err = -ENOENT;

goto err_no_irq;

}

err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); //申请中断

if (err) {

dev_err(&pdev->dev, "Cannot claim IRQ\n");

goto err_no_irq;

}

hw->clk = clk_get(&pdev->dev, "spi");

if (IS_ERR(hw->clk)) {

dev_err(&pdev->dev, "No clock for device\n");

err = PTR_ERR(hw->clk);

goto err_no_clk;

}

if (!pdata->set_cs) {

if (pdata->pin_cs < 0) {

dev_err(&pdev->dev, "No chipselect pin\n");

goto err_register;

}

err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev)); //申请中断

if (err) {

dev_err(&pdev->dev, "Failed to get gpio for cs\n");

goto err_register;

}

hw->set_cs = s3c24xx_spi_gpiocs; //如果私有数据没定义set_cs,则调用内核中函数

gpio_direction_output(pdata->pin_cs, 1);

} else

hw->set_cs = pdata->set_cs; //如果私有数据没定义了set_cs就调用之

//初始化设置寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置

s3c24xx_spi_initialsetup(hw);

err = spi_bitbang_start(&hw->bitbang); //注册SPI接口设备

if (err) {

dev_err(&pdev->dev, "Failed to register SPI master\n");

goto err_register;

}

return 0;

err_register:

if (hw->set_cs == s3c24xx_spi_gpiocs)

gpio_free(pdata->pin_cs);

clk_disable(hw->clk);

clk_put(hw->clk);

err_no_clk:

free_irq(hw->irq, hw);

err_no_irq:

iounmap(hw->regs);

err_no_iomap:

release_resource(hw->ioarea);

kfree(hw->ioarea);

err_no_iores:

err_no_pdata:

spi_master_put(hw->master);

err_nomem:

return err;

}

总结下,在s3c24xx_spi_probe中,我们主要做了如下几个事情:其一,申请了spi_master,将s3c24xx_spi作为其私有数据,同时,s3c24xx_spi也作为了平台设备的私有数据。其二,对spi_master和s3c24xx_spi下的bitbang分别进行了初始化。其三,读取寄存器基址和中断号,申请中断。其四,初始化寄存器,包括对SPIMOSI,SPIMISO,SPICLK引脚的设置。其五,注册SPI接口设备。

未来我们主要研究中断函数和如何注册SPI接口设备的函数,这里,我们先看看如何注册SPI接口的函数spi_bitbang_start

int spi_bitbang_start(struct spi_bitbang *bitbang)

{

int status;

if (!bitbang->master || !bitbang->chipselect)

return -EINVAL;

//动态创建一个work_struct结构,它的处理函数是bitbang_work

INIT_WORK(&bitbang->work, bitbang_work);

spin_lock_init(&bitbang->lock);

INIT_LIST_HEAD(&bitbang->queue);

if (!bitbang->master->mode_bits)

bitbang->master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;

if (!bitbang->master->transfer) // spi的数据传输就是用这个方法

bitbang->master->transfer = spi_bitbang_transfer;

if (!bitbang->txrx_bufs) { // 在spi_s3c24xx.c中的probe中已经定义,故跳过

bitbang->use_dma = 0;

bitbang->txrx_bufs = spi_bitbang_bufs;

if (!bitbang->master->setup) {

if (!bitbang->setup_transfer)

bitbang->setup_transfer =

spi_bitbang_setup_transfer;

bitbang->master->setup = spi_bitbang_setup;

bitbang->master->cleanup = spi_bitbang_cleanup;

}

} else if (!bitbang->master->setup)

return -EINVAL;

bitbang->busy = 0;

//调用create_singlethread_workqueue创建单个工作线程

bitbang->workqueue = create_singlethread_workqueue(

dev_name(bitbang->master->dev.parent));

if (bitbang->workqueue == NULL) {

status = -EBUSY;

goto err1;

}

status = spi_register_master(bitbang->master); //注册SPI接口设备

if (status < 0)

goto err2;

return status;

err2:

destroy_workqueue(bitbang->workqueue);

err1:

return status;

}

我们跟踪spi_bitbang_start中的spi_register_master函数

int spi_register_master(struct spi_master *master)

{

static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);

struct device *dev = master->dev.parent;

int status = -ENODEV;

int dynamic = 0;

if (!dev)

return -ENODEV;

if (master->num_chipselect == 0)

return -EINVAL;

if (master->bus_num < 0) {

master->bus_num = atomic_dec_return(&dyn_bus_id);

dynamic = 1;

}

//将spi添加到内核,这也是sys/class/Spi_master下产生Spi0,Spi1的原因

dev_set_name(&master->dev, "spi%u", master->bus_num);

status = device_add(&master->dev);

if (status < 0)

goto done;

dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),

dynamic ? " (dynamic)" : "");

scan_boardinfo(master);

status = 0;

done:

return status;

}

这里主要跟踪spi_register_master中的scan_boardinfo函数

static void scan_boardinfo(struct spi_master *master)

{

struct boardinfo *bi;

mutex_lock(&board_lock);

//遍历所有挂在board_list上的 boardinfo

list_for_each_entry(bi, &board_list, list) {

//遍历每个boardinfo管理的spi_board_info

struct spi_board_info *chip = bi->board_info; //控制器

unsigned n;

for (n = bi->n_board_info; n > 0; n--, chip++) {

//如果设备的总线号与控制器的总线好相等,则创建新设备

if (chip->bus_num != master->bus_num)

continue;

(void) spi_new_device(master, chip); //创建SPI接口设备

}

}

mutex_unlock(&board_lock);

}

所以,在SPI控制器驱动注册的时候不但注册这个主机控制器的驱动,还要遍历这个SPI接口设备链表,比较接口设备总线号是否与控制器总线号一致,将接口设备总线号一致的接口设备spi_device全部注册进内核。

现在看看scan_boardinfo中创建新设备的函数spi_new_device

struct spi_device *spi_new_device(struct spi_master *master,

struct spi_board_info *chip)

{

struct spi_device *proxy;

int status;

proxy = spi_alloc_device(master); //分配spi_device

if (!proxy)

return NULL;

WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

//初始化spi_device的各个字段

proxy->chip_select = chip->chip_select;

proxy->max_speed_hz = chip->max_speed_hz;

proxy->mode = chip->mode;

proxy->irq = chip->irq;

//获得spi_device的名字,移植时在mach-smdk2440.c中的s3c2410_spi0_board中设定的

strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));

proxy->dev.platform_data = (void *) chip->platform_data;

proxy->controller_data = chip->controller_data;

proxy->controller_state = NULL;

status = spi_add_device(proxy); //主要完成将spi_device添加到内核

if (status < 0) {

spi_dev_put(proxy);

return NULL;

}

return proxy;

}

之前我们说过spi_new_device中的分配spi_device函数会调用spi->dev.bus = &spi_bus_type;设置SPI接口设备的总线类型为spi_bus_type,这样便于与SPI接口设备驱动相联系。

好了,现在我们看看spi_new_device中将spi_device添加到内核的函数spi_add_device

int spi_add_device(struct spi_device *spi)

{

static DEFINE_MUTEX(spi_add_lock);

struct device *dev = spi->master->dev.parent;

int status;

if (spi->chip_select >= spi->master->num_chipselect) {

dev_err(dev, "cs%d >= max %d\n",

spi->chip_select,

spi->master->num_chipselect);

return -EINVAL;

}

//设置是spi_device在Linux设备驱动模型中的name,也就是spi0.0 dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),

spi->chip_select);

mutex_lock(&spi_add_lock);

//如果总线上挂的设备已经有这个名字,则设置状态忙碌,并退出

if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev))

!= NULL) {

dev_err(dev, "chipselect %d already in use\n",

spi->chip_select);

status = -EBUSY;

goto done;

}

status = spi_setup(spi); // 对spi_device的时钟等进行设置

if (status < 0) {

dev_err(dev, "can't %s %s, status %d\n",

"setup", dev_name(&spi->dev), status);

goto done;

}

status = device_add(&spi->dev); //添加到内核

if (status < 0)

dev_err(dev, "can't %s %s, status %d\n",

"add", dev_name(&spi->dev), status);

else

dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));

done:

mutex_unlock(&spi_add_lock);

return status;

}

下面跟踪spi_add_device中对spi_device的时钟等进行设置函数spi_setup

status = spi->master->setup(spi);而我们知道master所指向的setup在spi_s3c24xx.c中定义为 s3c24xx_spi_setup,我们跟踪这个函数会看到ret = s3c24xx_spi_update_state(spi, NULL);

接着看看s3c24xx_spi_update_state

static int s3c24xx_spi_update_state(struct spi_device *spi,

struct spi_transfer *t)

{

struct s3c24xx_spi *hw = to_hw(spi);

struct s3c24xx_spi_devstate *cs = spi->controller_state;

unsigned int bpw;

unsigned int hz;

unsigned int div;

unsigned long clk;

//设置了每字长的位数和发送速度

bpw = t ? t->bits_per_word : spi->bits_per_word;

hz = t ? t->speed_hz : spi->max_speed_hz;

if (!bpw)

bpw = 8;

if (!hz)

hz = spi->max_speed_hz;

if (bpw != 8) {

dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);

return -EINVAL;

}

if (spi->mode != cs->mode) {

u8 spcon = SPCON_DEFAULT;

if (spi->mode & SPI_CPHA)

spcon |= S3C2410_SPCON_CPHA_FMTB;

if (spi->mode & SPI_CPOL)

spcon |= S3C2410_SPCON_CPOL_HIGH;

cs->mode = spi->mode;

cs->spcon = spcon;

}

if (cs->hz != hz) {

clk = clk_get_rate(hw->clk); //设置分频值

div = DIV_ROUND_UP(clk, hz * 2) - 1;

if (div > 255)

div = 255;

dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n",

div, hz, clk / (2 * (div + 1)));

cs->hz = hz;

cs->sppre = div;

}

return 0;

}

好了,到此为止,我们跟着spi_s3c24xx.c中s3c24xx_spi_probe已经走完了,看到了SPI接口设备注册的整个流程。

接着看看spidev.c

我们知道spidev.c里面,我们注册了SPI接口设备驱动,并且添加了字符设备,为用户提供接口。

static const struct file_operations spidev_fops = {

.owner = THIS_MODULE,

.write = spidev_write,

.read = spidev_read,

.unlocked_ioctl = spidev_ioctl,

.open = spidev_open,

.release = spidev_release,

};

我们一个个的看看这些函数,看看怎么实现数据传输的吧。

static int spidev_open(struct inode *inode, struct file *filp)

{

struct spidev_data *spidev;

int status = -ENXIO;

lock_kernel();

mutex_lock(&device_list_lock);

list_for_each_entry(spidev, &device_list, device_entry) {//从接口设备链表中获取接口设备

if (spidev->devt == inode->i_rdev) {

status = 0;

break;

}

}

if (status == 0) {

if (!spidev->buffer) { //如果没申请缓冲区就去申请缓冲区

spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);

if (!spidev->buffer) {

dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");

status = -ENOMEM;

}

}

if (status == 0) { //如果初次使用就把使用次数加一

spidev->users++;

filp->private_data = spidev; //把spidev作为文件的私有数据

nonseekable_open(inode, filp);

}

} else

pr_debug("spidev: nothing for minor %d\n", iminor(inode));

mutex_unlock(&device_list_lock);

unlock_kernel();

return status;

}

这个open函数很简单,主要就是从接口设备链表中获取spidev设备,并对其初始化。好了,我们重点看看ioctl函数

static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

{

int err = 0;

int retval = 0;

struct spidev_data *spidev;

struct spi_device *spi;

u32 tmp;

unsigned n_ioc;

struct spi_ioc_transfer *ioc;

if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) //查看这个命令的幻数字段是否为'k'

return -ENOTTY;

//如果方向是用户空间从内核读,即内核向用户空间写,检查用户空间的地址是否有效

if (_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE,

(void __user *)arg, _IOC_SIZE(cmd));

//如果方向是用户空间向内核写,即内核读用户空间,则检查用户空间的地址是否有效

if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ,

(void __user *)arg, _IOC_SIZE(cmd));

if (err)

return -EFAULT;

spidev = filp->private_data; //从文件的私有数据中获取spidev

spin_lock_irq(&spidev->spi_lock);

spi = spi_dev_get(spidev->spi); //从spidev中获取spi_device

spin_unlock_irq(&spidev->spi_lock);

if (spi == NULL)

return -ESHUTDOWN;

mutex_lock(&spidev->buf_lock);

switch (cmd) {

case SPI_IOC_RD_MODE:

//因为已经进行了地址是否有效的检查

//所以这里使用__put_user,__get_user,__copy_from_user可以节省几个时钟周期

retval = __put_user(spi->mode & SPI_MODE_MASK,

(__u8 __user *)arg);

break;

case SPI_IOC_RD_LSB_FIRST:

retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,

(__u8 __user *)arg);

break;

case SPI_IOC_RD_BITS_PER_WORD:

retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);

break;

case SPI_IOC_RD_MAX_SPEED_HZ:

retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);

break;

case SPI_IOC_WR_MODE: //设置SPI模式

retval = __get_user(tmp, (u8 __user *)arg);

if (retval == 0) {

u8 save = spi->mode; //保存老的SPI模式

if (tmp & ~SPI_MODE_MASK) {

retval = -EINVAL;

break;

}

tmp |= spi->mode & ~SPI_MODE_MASK;

spi->mode = (u8)tmp;

retval = spi_setup(spi); //设置新的SPI模式

if (retval < 0)

spi->mode = save;

else

dev_dbg(&spi->dev, "spi mode %02x\n", tmp);

}

break;

case SPI_IOC_WR_LSB_FIRST:

retval = __get_user(tmp, (__u8 __user *)arg);

if (retval == 0) {

u8 save = spi->mode;

if (tmp)

spi->mode |= SPI_LSB_FIRST;

else

spi->mode &= ~SPI_LSB_FIRST;

retval = spi_setup(spi);

if (retval < 0)

spi->mode = save;

else

dev_dbg(&spi->dev, "%csb first\n",

tmp ? 'l' : 'm');

}

break;

case SPI_IOC_WR_BITS_PER_WORD: //写入几比特代表一个字

retval = __get_user(tmp, (__u8 __user *)arg);

if (retval == 0) {

u8 save = spi->bits_per_word;

spi->bits_per_word = tmp;

retval = spi_setup(spi);

if (retval < 0)

spi->bits_per_word = save;

else

dev_dbg(&spi->dev, "%d bits per word\n", tmp);

}

break;

case SPI_IOC_WR_MAX_SPEED_HZ: //写传输速度

retval = __get_user(tmp, (__u32 __user *)arg);

if (retval == 0) {

u32 save = spi->max_speed_hz;

spi->max_speed_hz = tmp;

retval = spi_setup(spi);

if (retval < 0)

spi->max_speed_hz = save;

else

dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);

}

break;

default:

if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))

|| _IOC_DIR(cmd) != _IOC_WRITE) {

retval = -ENOTTY;

break;

}

tmp = _IOC_SIZE(cmd); //得到用户空间数据的大小

//如果这些数据不能分成spi_ioc_transfer的整数倍,则不能进行传输

//spi_io_transfer是对spi_transfer的映射

if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {

retval = -EINVAL;

break;

}

n_ioc = tmp / sizeof(struct spi_ioc_transfer); //计算出能分多少个spi_ioc_transfer

if (n_ioc == 0)

break;

ioc = kmalloc(tmp, GFP_KERNEL); //在内核中分配装载这些数据的内存空间

if (!ioc) {

retval = -ENOMEM;

break;

}

if (__copy_from_user(ioc, (void __user *)arg, tmp)) { //把用户空间的数据拷贝过来

kfree(ioc);

retval = -EFAULT;

break;

}

retval = spidev_message(spidev, ioc, n_ioc); //进行数据传输

kfree(ioc);

break;

}

mutex_unlock(&spidev->buf_lock);

spi_dev_put(spi);

return retval;

}

在这个ioctl函数中,我们可以设置工作模式,可以设置bit/word,可以设置波特率,当然当其他命令进来会执行数据传输函数spidev_message,那么我们就跟踪看看这个函数如何实现数据传输的吧

static int spidev_message(struct spidev_data *spidev,

struct spi_ioc_transfer *u_xfers, unsigned n_xfers)

{

struct spi_message msg;

struct spi_transfer *k_xfers;

struct spi_transfer *k_tmp;

struct spi_ioc_transfer *u_tmp;

unsigned n, total;

u8 *buf;

int status = -EFAULT;

spi_message_init(&msg); //初始化spi_message的tranfers链表头

//分配n个spi_transfer的内存空间,一个spi_message由多个数据段spi_transfer组成

k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);

if (k_xfers == NULL)

return -ENOMEM;

buf = spidev->buffer; //获得缓冲区

total = 0;

//这个for循环的主要任务是将所有的spi_transfer组装成一个spi_message

for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;

n;

n--, k_tmp++, u_tmp++) {

k_tmp->len = u_tmp->len;

total += k_tmp->len; //统计要传输数据的总量

if (total > bufsiz) {

status = -EMSGSIZE;

goto done;

}

//spi_transfer是一个读写的buffer对,如果是要接收则把buffer给接收的rx_buf

if (u_tmp->rx_buf) {

k_tmp->rx_buf = buf;

if (!access_ok(VERIFY_WRITE, (u8 __user *)

(uintptr_t) u_tmp->rx_buf,

u_tmp->len))

goto done;

}

//如果要传输,这个buffer给tx_buf使用,从用户空间拷过来要传输的数据

if (u_tmp->tx_buf) {

k_tmp->tx_buf = buf;

if (copy_from_user(buf, (const u8 __user *)

(uintptr_t) u_tmp->tx_buf,

u_tmp->len))

goto done;

}

buf += k_tmp->len; //指向下一段内存

//最后一个transfer传输完毕是否会影响片选

k_tmp->cs_change = !!u_tmp->cs_change;

//每字长的字节数

k_tmp->bits_per_word = u_tmp->bits_per_word;

//一段数据传输完需要一定的时间等待

k_tmp->delay_usecs = u_tmp->delay_usecs;

//初始化传输速度

k_tmp->speed_hz = u_tmp->speed_hz;

//将spi_transfer通过它的transfer_list字段挂到spi_message的transfer队列上

spi_message_add_tail(k_tmp, &msg);

}

status = spidev_sync(spidev, &msg); //调用底层的传输函数

if (status < 0)

goto done;

buf = spidev->buffer;

//把传输数据拷贝到用户空间打印出来,可以查看是否传输成功

for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {

if (u_tmp->rx_buf) {

if (__copy_to_user((u8 __user *)

(uintptr_t) u_tmp->rx_buf, buf,

u_tmp->len)) {

status = -EFAULT;

goto done;

}

}

buf += u_tmp->len;

}

status = total;

done:

kfree(k_xfers);

return status;

}

其实在上面这个spidev_message函数中,我们由spi_ioc_transfer映射到spi_transfer,同时初始化spi_message,将所有的spi_transfer组装成一个spi_message,最后调用调用底层的传输函数spidev_sync进行传输spi_message。好了,我们现在有必要看看这个底层传输函数

static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message)

{

DECLARE_COMPLETION_ONSTACK(done); //声明并初始化一个完成量

int status;

message->complete = spidev_complete; //指定spi_message使用的唤醒完成量函数

message->context = &done;

spin_lock_irq(&spidev->spi_lock);

if (spidev->spi == NULL)

status = -ESHUTDOWN;

else

status = spi_async(spidev->spi, message); //调用spi核心中的函数进行数据传输

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;

}

在spidev_sync函数中,我们调用核心层传输函数,并且设置完成量,等待完成量被唤醒。spidev_sync的入口参数是spidev_data和spi_message,该函数中,我们最终调用SPI核心层数据传输函数spi_async,入口参数是spi_device和spi_message,继续看

int spi_async(struct spi_device *spi, struct spi_message *message)

{

struct spi_master *master = spi->master; //由spi_device获取spi_master

if ((master->flags & SPI_MASTER_HALF_DUPLEX)

|| (spi->mode & SPI_3WIRE)) {

struct spi_transfer *xfer;

unsigned flags = master->flags;

list_for_each_entry(xfer, &message->transfers, transfer_list) {

if (xfer->rx_buf && xfer->tx_buf)

return -EINVAL;

if ((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)

return -EINVAL;

if ((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)

return -EINVAL;

}

}

message->spi = spi;

message->status = -EINPROGRESS;

return master->transfer(spi, message); //真正的传输函数调用

}

核心层只是个包装,其真正的传输函数还是需要其他层来实现的。spi_async中我们最终调用master->transfer(spi, message);而该函数在spi_bitbang.c的spi_bitbang_start函数中曾经执行bitbang->master->transfer = spi_bitbang_transfer,所以我们看看spi_bitbang_transfer

int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)

{

struct spi_bitbang *bitbang;

unsigned long flags;

int status = 0;

m->actual_length = 0;

m->status = -EINPROGRESS;

//在spi_alloc_master函数中调用spi_master_set_devdata把struct s3c24xx_spi存放起来

//struct spi_bitbang正是struct s3c24xx_spi结构所包含的第一个结构

bitbang = spi_master_get_devdata(spi->master);

spin_lock_irqsave(&bitbang->lock, flags);

if (!spi->max_speed_hz)

status = -ENETDOWN;

else {

//把message加入到bitbang的等待队列中

list_add_tail(&m->queue, &bitbang->queue);

//把bitbang-work加入bitbang->workqueue中,调度运行

queue_work(bitbang->workqueue, &bitbang->work);

}

spin_unlock_irqrestore(&bitbang->lock, flags);

return status;

}

根据spi_bitbang_transfer我们知道,最终我们把bitbang-work加入bitbang->workqueue中,等待内核调用,而bitbang-work在spi_bitbang_start函数中定义为bitbang_work,我们现在看看bitbang_work,也就是我们把这个数据传输交给了等待队列,等待内核调用bitbang_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)) { //对bitqueue中的每一个spi_message进行处理

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);v//从链表中获取

list_del_init(&m->queue); //从链表中删除spi_message

spin_unlock_irqrestore(&bitbang->lock, flags);

nsecs = 100;

spi = m->spi;

tmp = 0;

cs_change = 1;

status = 0;

list_for_each_entry (t, &m->transfers, transfer_list) { //获取spi_transfer

if (t->speed_hz || t->bits_per_word)

do_setup = 1;

if (do_setup != 0) {

if (!setup_transfer) {

status = -ENOPROTOOPT;

break;

}

status = setup_transfer(spi, t); //传输前期准备

if (status < 0)

break;

}

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;

}

if (t->len) {

if (!m->is_dma_mapped)

t->rx_dma = t->tx_dma = 0;

status = bitbang->txrx_bufs(spi, t); //调用bitbang->txrx_bufs进行数据传输

}

if (status > 0)

m->actual_length += status;

if (status != t->len) {

if (status >= 0)

status = -EREMOTEIO;

break;

}

status = 0;

if (t->delay_usecs)

udelay(t->delay_usecs);

if (!cs_change)

continue;

if (t->transfer_list.next == &m->transfers) //链表遍历完毕,退出循环

break;

ndelay(nsecs);

bitbang->chipselect(spi, BITBANG_CS_INACTIVE);

ndelay(nsecs);

}

m->status = status; //所有spi_message传输完毕

m->complete(m->context); //传输完成,唤醒刚才的那个完成变量,调用完成函数

if (do_setup == 1)

setup_transfer(spi, NULL);

do_setup = 0;

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);

}

在bitbang_work中,我们主要知道要调用一个bitbang->txrx_bufs的传输函数,在spi_s3c24xx.c中定义为s3c24xx_spi_txrx。另外我们还需要关注一下传输完毕后调用了m->complete(m->context),也就是调用完成函数spidev_complete,这里面只有一条代码complete(arg);所以当数据传输完毕调用完成函数就可以返回到spidev_sync中的wait_for_completion(&done);好了,我们只剩下跟踪s3c24xx_spi_txrx了

static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)

{

struct s3c24xx_spi *hw = to_hw(spi);

dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",

t->tx_buf, t->rx_buf, t->len);

hw->tx = t->tx_buf; //发送指针

hw->rx = t->rx_buf; //接收指针

hw->len = t->len; //需要发送/接收的数目

hw->count = 0; //存放实际spi传输的数据数目

init_completion(&hw->done); //初始化了完成量

//只需发送第一个字节(如果发送为空,则发送0xff),中断中就会自动发送完其他字节(并//接受数据) 直到所有数据发送完毕且所有数据接收完毕才返回

writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);

wait_for_completion(&hw->done); //等待完成量被唤醒

return hw->count;

}

static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)

{

//如果还有数据没接收完且要发送的数据经已发送完毕,发送空数据0xFF

return hw->tx ? hw->tx[count] : 0;

}

下面来分析下中断函数s3c24xx_spi_irq

static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)

{

struct s3c24xx_spi *hw = dev;

unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); //读取spi的状态寄存器

unsigned int count = hw->count;

if (spsta & S3C2410_SPSTA_DCOL) { //检测冲突

dev_dbg(hw->dev, "data-collision\n");

complete(&hw->done); //唤醒完成量

goto irq_done;

}

if (!(spsta & S3C2410_SPSTA_READY)) { //检测设备忙

dev_dbg(hw->dev, "spi not ready for tx?\n");

complete(&hw->done); //唤醒完成量

goto irq_done;

}

hw->count++;

if (hw->rx)

hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); //接收数据

if (count < hw->len) //如果count小于需要发送或接收数据的数目,发送其他数据

writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);

else

complete(&hw->done); //发送接收完毕,通知s3c24xx_spi_txrx函数

irq_done:

return IRQ_HANDLED;

}

注意SPI总线数据传输时候,写和读数据是分开两个阶段来进行的,写数据的时候不读数据,读数据的时候发送空数据0xff,这样由主控产生时钟信号。

读书人网 >UNIXLINUX

热点推荐