在linux驱动程序中定期调用spi_write
我正在为LCD显示器编写驱动程序。 根据应用笔记,我需要定期向命令写一个伪SPI写,以最大化其对比度。 为此,我设置了一个定时器,并尝试从定时器处理程序中编写对比度最大化的2字节伪命令。
但是,出现问题是因为spi_write函数导致完整的内核崩溃并出现以下错误:
BUG: scheduling while atomic: swapper/1/0/0x00000102
基于以下post: 如何解决“BUG:调度而primefaces:swapper / 0x00000103 / 0,CPU#0”? 在TSC2007驱动程序?
“primefaces调度”表示你试图在某个你不应该睡觉的地方睡觉 – 就像在受自旋锁保护的关键部分或中断处理程序中一样。
也许对spi_write的调用会触发某种睡眠行为。 禁止在这里睡觉是有意义的,因为基于堆栈跟踪,我看到代码处于软IRQ状态:
[] (schedule_timeout) from [] (wait_for_common+0x114/0x15c) [] (wait_for_common) from [] (spi_sync+0x70/0x88) [] (spi_sync) from [] (plt_lcd_send_toggle_comin_cmd+0x7c/0x84 [plt_lcd_spi]) [] (plt_lcd_send_toggle_comin_cmd [plt_lcd_spi]) from [] (plt_lcd_timer_handler+0xc/0x2c [plt_lcd_spi]) [] (plt_lcd_timer_handler [plt_lcd_spi]) from [] (call_timer_fn.isra.26+0x20/0x30) [] (call_timer_fn.isra.26) from [] (run_timer_softirq+0x1ec/0x21c) [] (run_timer_softirq) from [] (__do_softirq+0xe0/0x1c8) [] (__do_softirq) from [] (irq_exit+0x58/0xac) [] (irq_exit) from [] (__handle_domain_irq+0x80/0xa0) [] (__handle_domain_irq) from [] (gic_handle_irq+0x38/0x5c) [] (gic_handle_irq) from [] (__irq_svc+0x40/0x74)
我的问题是:实现这种周期性行为的正确方法是什么,SPI事务需要定期发生?
以下是计时器处理程序的摘要(虽然有一些手动修改,使名称更通用 – 我可能在过程中插入了一些拼写错误)
static void lcd_timer_handler(unsigned long data) { // priv is a private structure that contains private info for the // driver: timer structure, timer timeout, context for the dummy command lcd_priv * const priv = (memlcd_priv *) data; unsigned char dummy[2]; dummy[0] = get_dummy_command_code(priv); dummy[1] = 0; // command must be terminated by a 0. // This is the call that causes the failure. // priv->spi is a struct spi_device * spi_write(priv->spi, ((const void *) dummy), 2); // Re-arm the timer mod_timer(&priv->timer, jiffies + priv->timer_timeout); }
谢谢!
编辑:这是我在实施以下答案中的建议后想出的。 很好地工作,但使用delayed_work涉及必须跳过一些箍。
typedef struct lcd_priv { /* private stuff: */ /* ... */ /* workqueue stuff: */ struct workqueue_struct * wq; struct delayed_work periodic_work; } lcd_priv; void lcd_periodic_work(struct work_struct * work_struct_ptr) { /* * Old documentation refers to a "data" pointer, but the API * no longer supports it. The developer is invited to put the work_struct * inside what would have been pointed to by "data" and to use container_of() * to recover this master struct. * See http://lwn.net/Articles/211279/ for more info. */ struct delayed_work * delayed = container_of(work_struct_ptr, struct delayed_work, work); lcd_priv * priv = container_of(delayed, lcd_priv, periodic_work); /* (prepare spi buffer in priv->spi_buf) */ /* ... */ /* This could be any activity that goes to sleep: */ spi_write(priv->spi, ((const void *) &priv->spi_buf[0]), 2); queue_delayed_work(priv->wq, &priv->periodic_work, TOGGLE_FREQUENCY); } static void lcd_start_workqueue(lcd_priv * const priv) { priv->wq = create_singlethread_workqueue("lcd_periodic_st_wq"); INIT_DELAYED_WORK(&priv->periodic_work, lcd_periodic_work); queue_delayed_work(priv->wq, &priv->periodic_work, TOGGLE_FREQUENCY); } static void lcd_stop_workqueue(lcd_priv * const priv) { destroy_workqueue(priv->wq); }
如果查看spi_write
源代码,它会调用spi_sync
,如果查看第一行spi_sync
– > mutex_lock
,那么spi_write
不能在中断内运行,也不能通过.config
或sysfs
修复。
我的问题是:实现这种周期性行为的正确方法是什么,其中> SPI事务需要定期发生?
答案取决于您的硬件,您希望通过SPI发送数据的频率,您接受的延迟等。
你可以在spi_write
回调中使用spi_write
,参见https://www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s08.html
专门为这类事物设计的workqueue(运行在中断环境中无法运行的东西),
您也可以使用spi_async
来安排通过spi进行写入。 spy_async
可以在中断处理程序中调用。
如果延迟无关紧要,你也可以将事物移动到用户空间,并通过spidev
接口写入SPI。