Embedded systems 7/7
J.-M Friedt
FEMTO-ST/time & frequency department [email protected]
slides at jmfriedt.free.fr
October 25, 2021
1 / 17
Linux kernel: a toolbox
I standard interface to access peripherals from userspace (timer, gpio) I access to scheduler to add tasks (tasklet)
I consistent access to resources: semaphore and mutex
Handling events (e.g. interrupts) and propagate a signal to userspace applications
2 / 17
Kernel API
Core functions provided by the kernel to handle userspace applications: scheduler, timer, communication with userspace
I tasks (tasklet): software interrupts
1char my_tasklet_data[]="my_tasklet_function was called";
void my_tasklet_function( unsigned long data ) { printk( "%s\n", (char *)data ); return; } DECLARE_TASKLET( my_tasklet, my_tasklet_function,
(unsigned long) &my_tasklet_data );
int init_module( void )
{ tasklet_schedule( &my_tasklet ); return 0; } void cleanup_module( void )
{ tasklet_kill( &my_tasklet ); return; }
→ ISR must be short and create a tasklet scheduled when time allows
1https://developer.ibm.com/technologies/linux/tutorials/l-tasklets/
3 / 17
Hardware access
Solution 1: ioremap to identify virtual address and set registers
Solution 2: use existing drivers provided by the kernel (make linux-menuconfig in Buildroot)
CONFIG_GPIO_XILINX:
Say yes here to support the Xilinx FPGA GPIO device Symbol: GPIO_XILINX [=m]
Type : tristate
Prompt: Xilinx GPIO support Location:
-> Device Drivers
-> GPIO Support (GPIOLIB [=y]) -> Memory mapped GPIO drivers Defined at drivers/gpio/Kconfig:573
Depends on: GPIOLIB [=y] && HAS_IOMEM [=y] && OF_GPIO [=y]
or for other platforms:
CONFIG_GPIO_SUNXI:
This option enables support for GPIOs on the Allwinner SOCs (sun4i/sun5i/sun7i). The GPIOs must be defined in [gpio_para]
section of sysconfig.fex file (gpio_used/gpio_num/gpio_pin_x variables) Symbol: GPIO_SUNXI [=y]
Type : tristate
Prompt: GPIO Support for sunxi platform
Depends on: GPIOLIB [=y] && (ARCH_SUN4I [=n] || ARCH_SUN5I [=y] || ARCH_SUN7I [=n]) Location:
-> Device Drivers
-> GPIO Support (GPIOLIB [=y])
WARNING: using kernel features requires a GPL license MODULE_LICENSE("GPL");
4 / 17
Hardware access
Solution 1: ioremap to identify virtual address and set registers
Solution 2: use existing drivers provided by the kernel (make linux-menuconfig in Buildroot) Using GPIO: define pins in devicetree
2gpio-leds {
compatible = "gpio-leds";
led-8-yellow { label = "led8";
gpios = <&gpio0 0 0>;
default-state = "off";
linux,default-trigger = "mmc0";
};
led-9-red { label = "led9";
gpios = <&gpio0 7 0>;
default-state = "off";
linux,default-trigger = "heartbeat";
};
};
or (pin)+906
2redpitaya/board/redpitaya/zynq-red pitaya.dtsin the BR2 EXTERNAL since the Redpitaya is not officially supported by Buildroot
5 / 17
Kernel module: using gpiolib & interrupt handling
Test if GPIO is available (22 = EINVAL, 16 = EBUSY)
3static irqreturn_t irq_handler(int irq, void *dev_id) {...
return IRQ_HANDLED;
}
gpio =906+10; // MIO10 err=gpio_is_valid(gpio);
err=gpio_request_one(gpio, GPIOF_IN, "jmf_irq");
if (err!=-22)
{irq = gpio_to_irq(gpio);
irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
err = request_irq(irq, irq_handler, IRQF_SHARED, "GPIO jmf", &dummy);
dummy=0;
}
3/usr/include/asm-generic/errno-base.h
6 / 17
Signals: event handler in userspace
Userspace equivalent to software interrupt: signals
#include <signal.h>
void my_handler (int sig) {
printf ("I got SIGINT, number %d\n", sig);}
int main ( void ) {
signal (SIGINT, my_handler);
while (1) {}
}
leads to, every time CTRL-C is hit (kill with kill -9 PID)
jmfriedt@(none):~$ ./sigint I got SIGINT, number 2 I got SIGINT, number 2
7 / 17
Interrupt signal distribution
Sharing interrupt information through signals
1. a unique hardware interrupt handling module (kernel) 2. multiple userspace programs might react to an interrupt 3. each program registers with the module
4. the module reacts quickly to the interrupt signal ...
5. ... and informs the programs identified through their PID when time allows.
8 / 17
Interrupt signal distribution
Alternate solution (thread)
1. a program wants to be notified of an interrupt as it is running
2. creates a thread blocked when reading a communication interface with the kernel in /sys/class or /dev
3. when the interrupt is triggered, the module unblocks the read syscall handling function ...
4 54. ... and the thread notifies the program of the event.
4http://www.makelinux.net/ldd3/chp-6-sect-2
5http://www.makelinux.net/ldd3/chp-5-sect-3for kernel semaphores
9 / 17
Shared data protection
Multiple tasks use the same data ⇒ consistency issues Three methods to protect shared data handling:
I semaphore (counter)
I mutex (binary with the task removed from the scheduler) I spin-lock (binary with active testing of the lock)
Producer-consumer:
1. one task produces data 2. a user requests these data
3. reading is blocked as long as the data have not been produced I Requesting a semaphore blocks the execution if already null I Raising the semaphore unblocks the waiting process/task I The counter can rise multiple time to unblock multiple processes
#include <linux/semaphore.h>
struct semaphore mysem;
sema_init(&mysem, 0); // init the semaphore as empty down (&mysem); // blocks consumer
...
up (&mysem); // producer unblocks 10 / 17
Shared data protection: mutex
Blocks-unblocks parts of the software using a common resource:
only one part of a software using a mutex can be executed at any given time
#include <linux/mutex.h>
struct mutex mymutex;
mutex_init(&mymutex);
mutex_lock(&mymutex); // blocks ...
mutex_unlock(&mymutex); // unblocks
As many mutex as shared resources
11 / 17
Shared data protection: spinlock
A mutex removes the task from the scheduler ⇒ slow
For fast operations (interrupts), actively probe the lock: spinlock
#include <linux/spinlock.h>
static DEFINE_SPINLOCK(myspin);
spin_lock_init(&myspin);
spin_lock(&myspin);
...
spin_unlock(&myspin);
MODULE_LICENSE("GPL");
deadlock issue: two processes calling simultaneously blocking conditions (e.g. SMP architecture with two cores locking mutex depending on each other).
12 / 17
Demonstration with GPIO
I The two GPIO available are connected to LEDs ⇒ output only
I Other MIO are set for communication (I2C, SPI in ps7 init.c)⇒ reconfigure as GPIO I MIO10 is pin 3 of E2 connector
I 3 bits L0 SEL, L1 SEL and L2 SEL select the GPIO function
> devmem 0xF800012C # GPIO clock active 0x00500444
> devmem 0xF8000728 # A = SPI 0x000016A0
> devmem 0xF8000728 32 0x00001600
13 / 17
Accessing the GPIO using gpiolib: interrupt handling
#i n c l u d e<l i n u x / m o d u l e . h> /∗Needed by a l l m o d u l e s ∗/
#i n c l u d e<l i n u x / k e r n e l . h> /∗Needed f o r KERN INFO∗/
#i n c l u d e<l i n u x / i n i t . h> /∗Needed f o r t h e m a c r o s ∗/
#i n c l u d e<l i n u x / i n t e r r u p t . h>
#i n c l u d e<l i n u x / i r q . h>
#i n c l u d e<l i n u x / g p i o . h>
s t a t i c i n t dummy , i r q , d e v i d , j m f g p i o =906+10;
s t a t i c i r q r e t u r n t i r q h a n d l e r (i n t i r q , v o i d ∗d e v i d ) {dummy++; p r i n t k ( KERN INFO " p l i p % d ", dummy ) ;
r e t u r n IRQ HANDLED ; }
i n t h e l l o s t a r t ( ) {i n t e r r ;
e r r= g p i o i s v a l i d ( j m f g p i o ) ;
e r r=g p i o r e q u e s t o n e ( j m f g p i o , GPIOF IN , " j m f _ i r q ") ; i f ( e r r !=−22)
{i r q = g p i o t o i r q ( j m f g p i o ) ;
i r q s e t i r q t y p e ( i r q , IRQ TYPE EDGE BOTH ) ;
e r r = r e q u e s t i r q ( i r q , i r q h a n d l e r , IRQF SHARED , " G P I O jmf ", &d e v i d ) ; dummy=0;
} r e t u r n 0 ; }
v o i d h e l l o e n d ( ) {f r e e i r q ( i r q ,& d e v i d ) ;
g p i o f r e e ( j m f g p i o ) ; }
m o d u l e i n i t ( h e l l o s t a r t ) ; m o d u l e e x i t ( h e l l o e n d ) ;
MODULE LICENSE (" GPL ") ; // n e e d e d t o u s e L i n u x k e r n e l f u n c t i o n s
14 / 17
Accessing the GPIO using gpiolib: sending a signal
#i n c l u d e<l i n u x / s c h e d / s i g n a l . h> // k i l l p i d
#i f d e f ARMEL // on R e d p i t a y a
#i n c l u d e<l i n u x / g p i o . h>
s t a t i c i r q r e t u r n t i r q h a n d l e r (i n t i r q , v o i d ∗d e v i d )
#e l s e // on PC
s t r u c t t i m e r l i s t e x p t i m e r ;
s t a t i c v o i d i r q h a n d l e r (s t r u c t t i m e r l i s t ∗t )
#e n d i f
{s t r u c t p i d ∗mypid ; i f ( p i d != 0)
{mypid= f i n d v p i d ( p i d ) ; i f ( mypid == NULL )
p r i n f o (" C a n n o t f i n d PID f r o m u s e r p r o g r a m \ r \ n ") ;
e l s e // d o s e n d s i g i n f o ( SIGUSR1 , SEND SIG FORCED , t a s k , PIDTYPE MAX ) ; k i l l p i d ( mypid , SIGUSR1 , 1 ) ;
}
#i f d e f ARMEL
r e t u r n IRQ HANDLED ;
#e l s e
m o d t i m e r ( t , j i f f i e s + HZ) ;
#e n d i f }
s t a t i c s s i z e t d e v w r i t e (s t r u c t f i l e ∗f i l ,c o n s t c h a r ∗b u f f , s i z e t l e n , l o f f t ∗o f f ) {i n t m y l e n ;
c h a r b u f [ 1 5 ] ;
i f ( l e n>14) m y l e n =14; e l s e m y l e n=l e n ;
i f ( c o p y f r o m u s e r ( b u f , b u f f , m y l e n ) == 0 ) s s c a n f ( b u f , " % d ", &p i d ) ; p r i n t k (" PID % d r e g i s t e r e d ", p i d ) ;
r e t u r n l e n ; }
15 / 17
Accessing the GPIO using gpiolib: creating a task to handle the interrupt
s t a t i c s t r u c t w o r k s t r u c t i r q w o r k ;
s t a t i c i r q r e t u r n t i r q h a n d l e r (i n t i r q , v o i d ∗d e v i d )
{s c h e d u l e w o r k (& i r q w o r k ) ; // l o n g t a s k h a n d l e d when t i m e a l l o w s r e t u r n IRQ HANDLED ;
}
v o i d d o s o m e t h i n g (s t r u c t w o r k s t r u c t ∗d a t a ) {s t r u c t p i d ∗mypid ;
i f ( p i d != 0)
{mypid= f i n d v p i d ( p i d ) ; i f ( mypid == NULL )
p r i n f o (" C a n n o t f i n d PID f r o m u s e r p r o g r a m \ r \ n ") ;
e l s e // d o s e n d s i g i n f o ( SIGUSR1 , SEND SIG FORCED , t a s k , PIDTYPE MAX ) ; k i l l p i d ( mypid , SIGUSR1 , 1 ) ;
}
#i f d e f ARMEL
r e t u r n IRQ HANDLED ;
#e l s e
m o d t i m e r ( t , j i f f i e s + HZ) ;
#e n d i f }
i n t h e l l o s t a r t ( ) // i n i t m o d u l e ( v o i d ) {. . .
INIT WORK(& i r q w o r k , d o s o m e t h i n g ) ; . . .
e r r = r e q u e s t i r q ( i r q , i r q h a n d l e r , IRQF SHARED , " G P I O jmf ", & i r q i d ) ; }
v o i d h e l l o e n d ( ) // c l e a n u p m o d u l e ( v o i d ) {. . .
f r e e i r q ( i r q ,& i r q i d ) ; }
16 / 17
Accessing the GPIO using gpiolib: userspace
#i n c l u d e<s t d i o . h>
#i n c l u d e<s t d l i b . h>
#i n c l u d e<s i g n a l . h>
#i n c l u d e<s t r i n g . h>
#i n c l u d e<s y s / t y p e s . h>
#i n c l u d e<s y s / s t a t . h>
#i n c l u d e<u n i s t d . h>
#i n c l u d e<f c n t l . h>
v o i d s i g n a l h a n d l e r (i n t s i g n u m ) {p r i n t f (" S i g n a l r e c e i v e d \ r \ n ") ;}
i n t main ( ) {FILE∗f ;
s i g n a l ( SIGUSR1 , s i g n a l h a n d l e r ) ; p r i n t f (" My PID is % d .\ n ", g e t p i d ( ) ) ;
f=f o p e n (" / dev / jmf "," w ") ; // t u n e t o c o m m u n i c a t i o n i n t e r f a c e w i t h m o d u l e f p r i n t f ( f ," % d \ n ", g e t p i d ( ) ) ; f f l u s h ( f ) ;
f c l o s e ( f ) ; w h i l e ( 1 ) {};
r e t u r n 0 ; }
Demonstrate read() blocking from a separate thread, unblocked by interrupt trigger
#i n c l u d e<p t h r e a d . h> // c o m p i l e w i t h−l p t h r e a d
v o i d ∗f u n c t i o n (v o i d ∗dummy ) {[ . . . ]} // b l o c k i n g r e a d and r e a c t t o i n t e r r u p t i n t main ( )
{p t h r e a d t m y t h r e a d ;
p t h r e a d c r e a t e (& m y t h r e a d , NULL , f u n c t i o n , NULL ) ; // l a s t NULL = dummy param p t h r e a d j o i n ( m y t h r e a d , NULL ) ;
}
17 / 17