在Linux驱动程序调用定期spi_write [英] Calling spi_write periodically in a linux driver
问题描述
我写的液晶显示器的驱动程序。根据应用笔记,我需要写一个虚拟SPI写命令定期最大限度地发挥其对比度。要做到这一点,我成立了一个计时器,并尝试写入从计时器处理对比度最大化的2个字节的空命令。
不过,不顺心的事,因为spi_write功能使一个完整的内核崩溃并出现以下错误:
BUG:调度,而原子:交换技术/ 1/0 / 0x00000102
根据以下职位:
<一href=\"http://stackoverflow.com/questions/3537252/how-to-solve-bug-scheduling-while-atomic-swapper-0x00000103-0-cpu0-in-ts\">How解决&QUOT; BUG:调度,而原子:交换技术/ 0x00000103 / 0,CPU#0&QUOT;?在TSC2007驱动程序?
计划,而原子表示你已经试过睡觉
的地方,你不应该 - 像内的自旋锁保护
临界区或中断处理程序。
块引用>也许调用spi_write触发某种睡眠行为。这将是有意义的禁止在这里睡觉,是因为基于堆栈跟踪,我看到code为软IRQ状态:
[&LT; 404ec600&GT;来自(schedule_timeout)&LT; 404eac3c&GT;(wait_for_common +量0x114 / 0x15c)
从(wait_for_common)并[d; 404eac3c&GT]并[d 4031c7a4]的计算值(spi_sync + 0x70 /均为0x88)
并[d 4031c7a4]的计算值从(spi_sync)并[d 3f08a6b0]的计算值(plt_lcd_send_toggle_comin_cmd + 0x7c /的0x84 [plt_lcd_spi])
并[d 3f08a6b0]的计算值(plt_lcd_send_toggle_comin_cmd [plt_lcd_spi])从并[d 3f08a6c4]的计算值(plt_lcd_timer_handler +位于0xC / 0x2c [plt_lcd_spi])
并[d 3f08a6c4]的计算值从(plt_lcd_timer_handler [plt_lcd_spi])并[d 40058818]的计算值(call_timer_fn.isra.26 + 0x20的/的0x30)
并[d 40058818]的计算值从(call_timer_fn.isra.26)并[d 40058f30]的计算值(run_timer_softirq +为0x1EC / 0x21c)
并[d 40058f30]的计算值从(run_timer_softirq)并[d 40023414]的计算值(__do_softirq + 0xe0的/ 0x1c8)
并[d 40023414]的计算值从(__do_softirq)并[d 400236f0]的计算值(irq_exit +将0x58 / 0xac)
并[d 400236f0]的计算值从(irq_exit)并[d 4004ee4c]的计算值(__handle_domain_irq + 0x80的/ 0XA0)
从(__handle_domain_irq)并[d; 4004ee4c&GT]并[d 400085ac]的计算值(gic_handle_irq + 0x38 / 0x5c)
并[d 400085ac]的计算值从(gic_handle_irq)并[d 40011740]的计算值(__irq_svc + 0X40 / 0x74)我的问题是:什么是实施这种周期性行为的正确方法,其中一个SPI事务需要周期性地发生
以下是定时器处理程序的总结(尽管有一些手动修改,使名称更通用的 - 我可能会插入一些拼写错误,在这个过程中)
静态无效lcd_timer_handler(unsigned long类型数据)
{
//私法是包含私人信息的私人结构
//司机:定时器结构,定时器超时,上下文哑命令
lcd_priv * const的私法=(memlcd_priv *)的数据; 无符号的字符哑[2];
假[0] = get_dummy_command_ code(私法);
虚设[1] = 0; //命令必须由一个0被终止。 //这是导致失败的呼叫。
// priv-&GT; SPI是一个结构spi_device *
spi_write(priv-&GT; SPI,((常量无效*)虚拟),2); //重新手臂上的计时器
mod_timer(安培; priv-&GT;定时器的jiffies + priv-&GT; timer_timeout);
}谢谢!
编辑:这里是我想出了实施从下面的答案建议后。工作得很好,但使用delayed_work涉及让通过一些跳铁圈。
typedef结构lcd_priv {
/ *私人的东西:* /
/ * ... * / / *工作队列的东西:* /
结构workqueue_struct * WQ;
结构delayed_work periodic_work;
} lcd_priv;
无效lcd_periodic_work(结构work_struct * work_struct_ptr)
{
/ *
*旧文档是指一个数据指针,但是API
*不再支持它。开发商请把work_struct
*里面有什么会被数据指出,并使用container_of()
*恢复此主结构。
*请参阅http://lwn.net/Articles/211279/获取更多信息。
* / 结构delayed_work *延迟= container_of(work_struct_ptr,结构delayed_work,工作);
lcd_priv *私法= container_of(延迟,lcd_priv,periodic_work); / *(prepare SPI缓冲区priv-&GT; spi_buf)* /
/ * ... * / / *这可能是进入休眠状态的任何活动:* /
spi_write(priv-&GT; SPI,((常量无效*)及priv-&GT; spi_buf [0]),2); queue_delayed_work(priv-&GT; WQ,&安培; priv-&GT; periodic_work,TOGGLE_FREQUENCY);
}静态无效lcd_start_workqueue(lcd_priv * const的私法){
priv-&GT; WQ = create_singlethread_workqueue(lcd_periodic_st_wq); INIT_DELAYED_WORK(安培; priv-&GT; periodic_work,lcd_periodic_work);
queue_delayed_work(priv-&GT; WQ,&安培; priv-&GT; periodic_work,TOGGLE_FREQUENCY);
}静态无效lcd_stop_workqueue(lcd_priv * const的私法){
destroy_workqueue(priv-&GT; WQ);
}
解决方案如果看看
spi_write
源$ C $ C,它会调用spi_sync
,如果看的第一线spi_sync
- >的mutex_lock
,所以spi_write
无法运行内部中断,
它无法通过的.config
固定或的sysfs
。
我的问题是:什么是实现这种周期行为,其中> SPI处理需要定期发生的正确方法
块引用>答取决于你的硬件,你要多长时间通过SPI发送数据,
什么延迟你接受等。您可以使用
spi_write
的工作队列里面的回调,见
<一href=\"https://www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s08.html\" rel=\"nofollow\">https://www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s08.html工作队列专为这类事情(运行的东西,不能在中断上下文中运行),
也可以使用
spi_async
来安排通过SPI写。spy_async
可中断处理程序中被调用。你也搬东西到用户空间如果等待时间没有关系,并通过
spidev
接口写入SPI。I am writing a driver for an LCD display. According to the application note, I need to write a dummy SPI write to the command periodically to maximize its contrast. To accomplish this, I set up a timer and attempt to write the contrast-maximizing 2-byte dummy command from the timer handler.
However, something goes wrong because the spi_write function causes a complete kernel crash with the following error:
BUG: scheduling while atomic: swapper/1/0/0x00000102
Based on the following post: How to solve "BUG: scheduling while atomic: swapper /0x00000103/0, CPU#0"? in TSC2007 Driver?
"Scheduling while atomic" indicates that you've tried to sleep somewhere that you shouldn't - like within a spinlock-protected critical section or an interrupt handler.
Maybe the call to spi_write triggers some sort of sleep behavior. It would make sense to disallow sleeping here, because based on the stack trace, I see that the code is in a soft IRQ state:
[<404ec600>] (schedule_timeout) from [<404eac3c>] (wait_for_common+0x114/0x15c) [<404eac3c>] (wait_for_common) from [<4031c7a4>] (spi_sync+0x70/0x88) [<4031c7a4>] (spi_sync) from [<3f08a6b0>] (plt_lcd_send_toggle_comin_cmd+0x7c/0x84 [plt_lcd_spi]) [<3f08a6b0>] (plt_lcd_send_toggle_comin_cmd [plt_lcd_spi]) from [<3f08a6c4>] (plt_lcd_timer_handler+0xc/0x2c [plt_lcd_spi]) [<3f08a6c4>] (plt_lcd_timer_handler [plt_lcd_spi]) from [<40058818>] (call_timer_fn.isra.26+0x20/0x30) [<40058818>] (call_timer_fn.isra.26) from [<40058f30>] (run_timer_softirq+0x1ec/0x21c) [<40058f30>] (run_timer_softirq) from [<40023414>] (__do_softirq+0xe0/0x1c8) [<40023414>] (__do_softirq) from [<400236f0>] (irq_exit+0x58/0xac) [<400236f0>] (irq_exit) from [<4004ee4c>] (__handle_domain_irq+0x80/0xa0) [<4004ee4c>] (__handle_domain_irq) from [<400085ac>] (gic_handle_irq+0x38/0x5c) [<400085ac>] (gic_handle_irq) from [<40011740>] (__irq_svc+0x40/0x74)
My question is: what is the right way to implement such periodic behavior, where an SPI transaction needs to occur periodically?
The following is a summary of the timer handler (albeit with some manual modifications to make the names more generic -- I might have inserted some typos in the process)
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); }
Thanks!
EDIT: Here is what I came up with after implementing the recommendations from the answer below. Works nicely, but using delayed_work involved having to jump through a few hoops.
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); }
解决方案If look at
spi_write
source code, it callsspi_sync
, and if look at first lines ofspi_sync
->mutex_lock
, sospi_write
can not be run inside interrupt, and it can not be fixed via.config
orsysfs
.My question is: what is the right way to implement such periodic behavior, where > an SPI transaction needs to occur periodically?
Answer depend on your hardware, how often you want send data via SPI, what latency you accept etc.
you can use
spi_write
inside workqueue callback, see https://www.safaribooksonline.com/library/view/understanding-the-linux/0596005652/ch04s08.htmlworkqueue specially designed for such kind of things (running something that can not be run in interrupt context),
also you can use
spi_async
to schedule write via spi.spy_async
can be called inside interrupt handler.also you move things to userspace if latency not matter, and write to SPI via
spidev
interface.这篇关于在Linux驱动程序调用定期spi_write的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!