免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1801 | 回复: 5
打印 上一主题 下一主题

这段代码为什么不能在中断中调用 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-07-27 09:24 |只看该作者 |倒序浏览
开发CAN总线程序,以下这个函数my_spidev_rd_wr在中断中调用就会出错,但是在定时器轮询中就可以正常使用。
帮我看看为什么,谢谢!
static char pSpiReadBuf[100];
int my_spidev_rd_wr(int dev, char*buf, int count)
{
     unsigned int spi_device = m_SpiDevNum;
     unsigned int ofs, pagelen;
     int res, i;
     int j;
     struct spi_transfer_list* list;
     list = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL);
     if (!list)
          return -ENOMEM;
     for(j = 0; j < count; ++j)
     {
          pSpiReadBuf[j] = buf[j];
     }
     res = count;
     ofs = (unsigned long)pSpiReadBuf & (~PAGE_MASK);
     pagelen = PAGE_SIZE - ofs;
     list->tx[0] = list->rx[0] = virt_to_page(pSpiReadBuf) + ofs;
     list->txlen[0] = list->rxlen[0] = count;
     list->nr_transfers = 1;
     spi_access_bus(spi_device);
     spi_transfer(list);
     spi_release_bus(spi_device);
     kfree(list);
     for(j = 0; j < res; j++)
     {
          buf[j] = pSpiReadBuf[j];
     }
     return res;
}

其中的spi_access_bus,transfer,spi_release_bus是linux自带的函数。

static struct spi_local spi_dev[NR_SPI_DEVICES];                        /* state of the SPI devices */
static int spi_enabled = 0;
static struct semaphore spi_lock;                        /* protect access to SPI bus */
static int current_device = -1;                                /* currently selected SPI device */

DECLARE_COMPLETION(transfer_complete);

/* SPI controller device */
AT91PS_SPI controller = (AT91PS_SPI) AT91C_VA_BASE_SPI;

/* ......................................................................... */

/*
* Access and enable the SPI bus.
* This MUST be called before any transfers are performed.
*/
void spi_access_bus(short device)
{
        /* Ensure that requested device is valid */
        if ((device < 0) || (device >= NR_SPI_DEVICES))
                panic("at91_spi: spi_access_bus called with invalid device");

        if (spi_enabled == 0) {
                AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI;        /* Enable Peripheral clock */
                controller->SPI_CR = AT91C_SPI_SPIEN;        /* Enable SPI */
#ifdef DEBUG_SPI
                printk("SPI on\n");
#endif
        }
        MOD_INC_USE_COUNT;
        spi_enabled++;

        /* Lock the SPI bus */
        down(&spi_lock);
        current_device = device;

        /* Enable PIO */
        if (!spi_dev[device].pio_enabled) {
                switch (device) {
                        case 0: AT91_CfgPIO_SPI_CS0();
                        case 1: AT91_CfgPIO_SPI_CS1();
                        case 2: AT91_CfgPIO_SPI_CS2();
                        case 3: AT91_CfgPIO_SPI_CS3();
                }
                spi_dev[device].pio_enabled = 1;
#ifdef DEBUG_SPI
                printk("SPI CS%i enabled\n", device);
#endif
        }

        /* Configure SPI bus for device */
        controller->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | (spi_dev[device].pcs << 16);
}

/*
* Relinquish control of the SPI bus.
*/
void spi_release_bus(short device)
{
        if (device != current_device)
                panic("at91_spi: spi_release called with invalid device");

        /* Release the SPI bus */
        current_device = -1;
        up(&spi_lock);

        spi_enabled--;
        MOD_DEC_USE_COUNT;
        if (spi_enabled == 0) {
                controller->SPI_CR = AT91C_SPI_SPIDIS;        /* Disable SPI */
                AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI;        /* Disable Peripheral clock */
#ifdef DEBUG_SPI
                printk("SPI off\n");
#endif
        }
}

/*
* Perform a data transfer over the SPI bus
*/
int spi_transfer(struct spi_transfer_list* list)
{
        struct spi_local *device = (struct spi_local *) &spi_dev[current_device];

        if (!list)
                panic("at91_spi: spi_transfer called with NULL transfer list");
        if (current_device == -1)
                panic("at91_spi: spi_transfer called without acquiring bus");

#ifdef DEBUG_SPI
        printk("SPI transfer start [%i]\n", list->nr_transfers);
#endif

        /* Store transfer list */
        device->xfers = list;
        list->curr = 0;

        /* Assume there must be at least one transfer */
        device->tx = pci_map_single(NULL, list->tx[0], list->txlen[0], PCI_DMA_TODEVICE);
        device->rx = pci_map_single(NULL, list->rx[0], list->rxlen[0], PCI_DMA_FROMDEVICE);

        /* Program PDC registers */
        controller->SPI_TPR = device->tx;
        controller->SPI_RPR = device->rx;
        controller->SPI_TCR = list->txlen[0];
        controller->SPI_RCR = list->rxlen[0];

        /* Is there a second transfer? */
        if (list->nr_transfers > 1) {
                device->txnext = pci_map_single(NULL, list->tx[1], list->txlen[1], PCI_DMA_TODEVICE);
                device->rxnext = pci_map_single(NULL, list->rx[1], list->rxlen[1], PCI_DMA_FROMDEVICE);

                /* Program Next PDC registers */
                controller->SPI_TNPR = device->txnext;
                controller->SPI_RNPR = device->rxnext;
                controller->SPI_TNCR = list->txlen[1];
                controller->SPI_RNCR = list->rxlen[1];
        }
        else {
                device->txnext = 0;
                device->rxnext = 0;
                controller->SPI_TNCR = 0;
                controller->SPI_RNCR = 0;
        }

        // TODO: If we are doing consecutive transfers (at high speed, or
        //   small buffers), then it might be worth modifying the 'Delay between
        //   Consecutive Transfers' in the CSR registers.
        //   This is an issue if we cannot chain the next buffer fast enough
        //   in the interrupt handler.

        /* Enable transmitter and receiver */
        controller->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

        controller->SPI_IER = AT91C_SPI_SPENDRX;        /* enable buffer complete interrupt */
        wait_for_completion(&transfer_complete);

#ifdef DEBUG_SPI
        printk("SPI transfer end\n");
#endif

        return 0;
}

/* ......................................................................... */

/*
* Handle interrupts from the SPI controller.
*/
void spi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
        unsigned int status;
        struct spi_local *device = (struct spi_local *) &spi_dev[current_device];
        struct spi_transfer_list *list = device->xfers;

#ifdef DEBUG_SPI
        printk("SPI interrupt %i\n", current_device);
#endif

        if (!list)
                panic("at91_spi: spi_interrupt with a NULL transfer list");

               status = controller->SPI_SR & controller->SPI_IMR;        /* read status */

        pci_unmap_single(NULL, device->tx, list->txlen[list->curr], PCI_DMA_TODEVICE);
        pci_unmap_single(NULL, device->rx, list->rxlen[list->curr], PCI_DMA_FROMDEVICE);

        device->tx = device->txnext;        /* move next transfer to current transfer */
        device->rx = device->rxnext;

        list->curr = list->curr + 1;
        if (list->curr == list->nr_transfers) {                /* all transfers complete */
                controller->SPI_IDR = AT91C_SPI_SPENDRX;        /* disable interrupt */

                /* Disable transmitter and receiver */
                controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;

                device->xfers = NULL;
                complete(&transfer_complete);
        }
        else if (list->curr+1 == list->nr_transfers) {        /* no more next transfers */
                device->txnext = 0;
                device->rxnext = 0;
                controller->SPI_TNCR = 0;
                controller->SPI_RNCR = 0;
        }
        else {
                int i = (list->curr)+1;

                device->txnext = pci_map_single(NULL, list->tx, list->txlen, PCI_DMA_TODEVICE);
                device->rxnext = pci_map_single(NULL, list->rx, list->rxlen, PCI_DMA_FROMDEVICE);
                controller->SPI_TNPR = device->txnext;
                controller->SPI_RNPR = device->rxnext;
                controller->SPI_TNCR = list->txlen;
                controller->SPI_RNCR = list->rxlen;
        }
}

[ 本帖最后由 c_kun 于 2009-7-27 14:00 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2009-07-27 13:58 |只看该作者

回复 #1 c_kun 的帖子

下面哪段代码?
.....


不要在中断处理程序中使用kmalloc之类所有可以引起睡眠挂起的函数调用,
因为中断处理程序没有进程上下文!

论坛徽章:
0
3 [报告]
发表于 2009-07-27 14:03 |只看该作者
my_spidev_rd_wr是在调用
spi_transfer(list);
时发生的错误。

论坛徽章:
0
4 [报告]
发表于 2009-07-27 22:44 |只看该作者
spi可能会阻塞,所以不能再中断中调用

论坛徽章:
0
5 [报告]
发表于 2009-07-27 23:07 |只看该作者

回复 #1 c_kun 的帖子

1.中断程序中kmalloc使用GFP_ATMOIC标志,并检查返回值!
2.看了spi_transfer使用的是异步传输,故需要将其放在中断下半部tasklet或work_struct中实现
仅供参考!

论坛徽章:
0
6 [报告]
发表于 2009-07-28 08:32 |只看该作者
spi_transfer这个函数是armlinux自带的,不是我写的。
我只是调用了一下。
即使有阻塞,但我是用的是DMA异步传输啊,会有影响吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP