insane-adding-machines / frosted

Frosted: Free POSIX OS for tiny embedded devices
GNU General Public License v2.0
215 stars 39 forks source link

Stacking drivers #7

Closed DarkVegetableMatter closed 8 years ago

DarkVegetableMatter commented 8 years ago

I have created SPI and a L3GD20 (gyro) drivers. The L3GD20 gyro is connected to the STM32F4 SPI bus.

When the user interacts with the gyro driver the SPI driver will send/receive data to/from the L3GD20. So my question is, how should the gyro driver obtain a pointer to the SPI driver's ops structure so that it can perform open/read/write operations?

danielinux commented 8 years ago

I think you should store a ptr to the spi module (or a fnode of the parent spi driver) in your dev_l3gd20 structure during initialization.

l3gd20 should depend on SPI, and thus assume that mod_devspi is present and initialized. The pointer to the module is stored in the .owner field of any fnode created for spi (e.g. /dev/spi1)

DarkVegetableMatter commented 8 years ago

Y, that was my idea too. The cleanest way to do that is to call device_open() from the gyros init call but task_filedesc_add() fails - because we are in the kernel's context and not a task's.

No filedesc means we can't use the ops functions, they all check fd

Can we give the kernel the ability to open a file? I think it should be able to anyway

DarkVegetableMatter commented 8 years ago

Ok, so now the l3gd20 driver can connect to the GPIO driver for the gyro driver and the spi driver. I have put a simple hack in the l3gd20 read function that can control the CS pin and start to send data via the SPI driver. So far so good...

1) The CS pin goes low 2) The SPI driver is asked to send 2 bytes. The 2 bytes are stored in a circular buffer, the first byte is then read for the circular buffer and sent via the SPI hardware.

At this point I think the 'task' should sleep and when the SPI TX empty IRQ occurs, the second byte is sent and the 'task' is resumed. At least that's what I think the code should do. Currently it is probably wrong but thats not the point

I don't think my scheme can work, the l3gd20 code will not be suspended, its executing in the Kernel's context. I can't see anyway to make the code in the l3gd20 read function sleep (not block) until the SPI TX empty buffer IRQ happens and sends the second byte :(

Can you take a look ?

(I think the answer is that the ops functions should not execute in the Kernel's context but in a 'Kernel thread' context that can sleep allowing the Kernel and other userland apps to continue running)

Hope that makes sense

danielinux commented 8 years ago

kernel never sleeps. If you want to defer some work, please use a tasklet. Tasklets allow you to defer operations for your drivers as well. If for example you need to wait for an interrupt, use non-blocking operations, then write your irq handler accordingly and when the desired event occurs, perform the read operation in the tasklet. If a user thread is waiting (e.g. is suspended on a read() syscall), it can be resumed via task_resume().

danielinux commented 8 years ago

perhaps your lower level (spi) driver should have the read/write functions split into two parts: a non-blocking back end that can be called by name from the kernel, and the frontend that is automatically associated to the syscall operations. This way you can "selectively" poll for events in tasklets only when needed.