Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Beispiel: Grundgeruest einer Modbus-Kommunikation
- *
- * Aufbau einer Modbus-Kommunikation ohne weitere Funktion
- *
- */
- // Linked Header
- #include <rtai_mbx.h>
- #include <rtai_sched.h>
- #include <uint128.h>
- #include <sys/rtai_modbus.h>
- #include <time.h>
- // Register Values
- // Digital Output Register
- // Define Name Maskierte Bits
- #define dOut_Magazin1 (1<<0)|(1<<1) //Bit dOut.00 und dOut.01
- #define dOut_Magazin2 (1<<2)|(1<<3) //Bit dOut.02 und dOut.03
- #define dOut_Auswerfer (1<<4) //Bit dOut.04
- #define dOut_Magazin3 (1<<5)|(1<<6) //Bit dOut.05 und dOut.06
- #define dOut_Band (1<<7) //Bit dOut.07
- //#define BandAn (1<<3) //Bit Out 00
- //#define BandAus (1<<3) //Bit Out 01
- //#define MagazStopp (1<<3)|(1<<4) //Bit Out 00
- //#define MagzinPull (1<<3)|(1<<4) //Bit Out 10
- //#define MagzinPush (1<<3)|(1<<4) //Bit Out 01
- // Digital Input Register
- // Define Name Maskierte Bits
- #define dIn_Magazin1_On 0 //Bit dIn.00 Magazin 1 eingefahren
- #define dIn_Magazin1_Off 1 //Bit dIn.01 Magazin 1 ausgefahren
- #define dIn_Magazin1_Empty 2 //Bit dIn.02 Magazin 1 leer (ohne Inhalt)
- #define dIn_Magazin2_On 3 //Bit dIn.03 Magazin 2 eingefahren
- #define dIn_Magazin2_Off 4 //Bit dIn.04 Magazin 2 ausgefahren
- #define dIn_Magazin2_Empty 5 //Bit dIn.05 Magazin 2 leer (ohne Inhalt)
- #define dIn_Magazin2_Critical 15 //Bit dIn.15 Magazin 2 kritischer Bereich betreten
- #define dIn_Magazin3_On 7 //Bit dIn.07 Magazin 3 eingefahren
- #define dIn_Magazin3_Off 8 //Bit dIn.08 Magazin 3 ausgefahren
- #define dIn_Magazin3_Empty 9 //Bit dIn.09 Magazin 3 leer (ohne Inhalt)
- #define dIn_Magazin3_Critical 6 //Bit dIn.06 Magazin 3 kritischer Bereich betreten
- #define dIn_Auswerfer_In 10 //Bit dIn.10 Auswerfer eingefahren
- #define dIn_Auswerfer_Out 11 //Bit dIn.11 Auswerfer ausgefahren
- #define dIn_Rutsche_Voll 14 //Bit dIn.14 Magazin 3 kritischer Bereich betreten
- #define dIn_Altitude_W_Reg 12 //Bit dIn.14 Höhensensor hat Werkstück registriert
- #define dIn_Altitude_M_OK 13 //Bit dIn.13 Höhensensor Messung ist in Ordnung
- /*
- #define dIn_Magazin1_On (1<<00) //Bit dIn.00 Magazin 1 eingefahren
- #define dIn_Magazin1_Off (1<<01) //Bit dIn.01 Magazin 1 ausgefahren
- #define dIn_Magazin1_Empty (1<<02) //Bit dIn.02 Magazin 1 leer (ohne Inhalt)
- #define dIn_Magazin2_On (1<<03) //Bit dIn.03 Magazin 2 eingefahren
- #define dIn_Magazin2_Off (1<<04) //Bit dIn.04 Magazin 2 ausgefahren
- #define dIn_Magazin2_Empty (1<<05) //Bit dIn.05 Magazin 2 leer (ohne Inhalt)
- #define dIn_Magazin2_Critical (1<<15) //Bit dIn.15 Magazin 2 kritischer Bereich betreten
- #define dIn_Magazin3_On (1<<07) //Bit dIn.07 Magazin 3 eingefahren
- #define dIn_Magazin3_Off (1<<08) //Bit dIn.08 Magazin 3 ausgefahren
- #define dIn_Magazin3_Empty (1<<09) //Bit dIn.09 Magazin 3 leer (ohne Inhalt)
- #define dIn_Magazin3_Critical (1<<06) //Bit dIn.06 Magazin 3 kritischer Bereich betreten
- #define dIn_Auswerfer_In (1<<10) //Bit dIn.10 Auswerfer eingefahren
- #define dIn_Auswerfer_Out (1<<11) //Bit dIn.11 Auswerfer ausgefahren
- #define dIn_Rutsche_Voll (1<<14) //Bit dIn.14 Magazin 3 kritischer Bereich betreten
- #define dIn_Altitude_W_Reg (1<<12) //Bit dIn.14 Höhensensor hat Werkstück registriert
- #define dIn_Altitude_M_OK (1<<13) //Bit dIn.13 Höhensensor Messung ist in Ordnung
- */
- // Analog Input Register
- // Define Name Maskierte Bits
- //efine aIn_Hoehensenormesswert //Bit aIn.00 bis 16
- /// Hilfsvariablen Global
- int fd_node;
- short valAnalog = 0;
- short valDigital = 0;
- int Mag3Senzor = 0;
- int Mag2Senzor = 0 ;
- int BadPart = 0 ;
- unsigned short registers; // theorisch muss sie nicht global sein
- //Threads bzw. Tasks
- static RT_TASK task;
- static RT_TASK tskM1;
- static RT_TASK tskM2;
- static RT_TASK tskM3;
- static RT_TASK ReadStatus;
- static RT_TASK tskRemoveBadPart;
- #define STACKSIZE 10000
- // Union zur Bitmanipulation
- // Output Register
- union {
- short Value;
- struct {
- unsigned char bit0 :1;
- unsigned char bit1 :1;
- unsigned char bit2 :1;
- unsigned char bit3 :1;
- unsigned char bit4 :1;
- unsigned char bit5 :1;
- unsigned char bit6 :1;
- unsigned char bit7 :1;
- } bits;
- } var;
- // Output Register
- union {
- int Value;
- struct {
- unsigned char bit0 :1;
- unsigned char bit1 :1;
- unsigned char bit2 :1;
- unsigned char bit3 :1;
- unsigned char bit4 :1;
- unsigned char bit5 :1;
- unsigned char bit6 :1;
- unsigned char bit7 :1;
- unsigned char bit8 :1;
- unsigned char bit9 :1;
- unsigned char bit10 :1;
- unsigned char bit11 :1;
- unsigned char bit12 :1;
- unsigned char bit13 :1;
- unsigned char bit14 :1;
- unsigned char bit15 :1;
- unsigned char bit16 :1;
- } bits;
- } read;
- //Funktionen
- // Deklarationen
- long long decimalToBinary(long n);
- int setMask(int Register);
- int getMask(int Register);
- void RemovePusherOn (unsigned int);
- void RemovePusherOff (unsigned int);
- // Funktionen
- // Hilfsfunktion
- // Set Registers with mask
- // Tested: OK
- void setBit(char *var, unsigned int pos){
- unsigned int mask=1;
- mask = mask << (pos);
- *var = *var|mask;
- }
- // Get Registers with mask
- // Tested: OK
- unsigned int getBit(unsigned int var, int pos){
- unsigned int mask = 1;
- unsigned int old = var;
- mask = mask << (pos);
- mask = old|mask;
- var = var&mask; // nur neu bits hinzufügen alte sollen bleiben, was im mom nicht passiter
- var = var >> (pos);
- return (unsigned short)var; // rück gabe wert 16stellig. und dmait als == vergleichswert ungeeignet für bolsche algebra
- }
- // Control WE über Union
- // Tested: OK
- // 3 Input Values
- // Actor: 'M' = Magazin n, 'B' = Band, 'P' = Pusher/Greifer
- // Number: 1 for (M1, B and P), 2 for M2 , 3 for M3
- // Mode: Desired Operation Mode
- //For M1, M2, M3
- // Mode: 00 Pressure off (and stay)
- // Mode: 10 Pull in (Pull the piston IN; back again)
- // Mode: 01 Push out (Push the piston OUT)
- //For B,P
- // Mode 0 Turn off
- // Mode 1 Turn on
- /*
- void controlActUnion(char actor, int number, int mode) {
- int OldValue;
- OldValue = var.Value;
- if (actor == 'M') {
- if (number == 1) {
- if (mode == 0) {
- var.bits.bit0 = 0;
- var.bits.bit1 = 0;
- }
- if (mode == 1) {
- var.bits.bit0 = 1;
- var.bits.bit1 = 0;
- }
- if (mode == 2) {
- var.bits.bit0 = 0;
- var.bits.bit1 = 1;
- }
- //rt_sleep(300 * nano2count(1000000));
- //var.bits.bit0=0;
- //var.bits.bit1=0;
- }
- if (number == 2) {
- if (mode == 0) {
- var.bits.bit2 = 0;
- var.bits.bit3 = 0;
- }
- if (mode == 1) {
- var.bits.bit2 = 1;
- var.bits.bit3 = 0;
- }
- if (mode == 2) {
- var.bits.bit2 = 0;
- var.bits.bit3 = 1;
- }
- //rt_sleep(300 * nano2count(1000000));
- //var.bits.bit2=0;
- //var.bits.bit3=0;
- }
- if (number == 3) {
- if (mode == 0) {
- var.bits.bit5 = 0;
- var.bits.bit6 = 0;
- }
- if (mode == 1) {
- var.bits.bit5 = 1;
- var.bits.bit6 = 0;
- }
- if (mode == 2) {
- var.bits.bit5 = 0;
- var.bits.bit6 = 1;
- }
- //rt_sleep(300 * nano2count(1000000));
- //var.bits.bit5=0;
- //var.bits.bit6=0;
- }
- }
- if (actor == 'P') {
- if (mode == 1 || mode == 0) {
- var.bits.bit4 = mode;
- }
- }
- if (actor == 'B') {
- if (mode == 1 || mode == 0) {
- var.bits.bit7 = mode;
- }
- }
- rt_modbus_set(fd_node, DIGITAL_OUT, 0, var.Value);
- }
- */
- void controlAct(char actor, int number, int mode) {
- int OldValue;
- OldValue = var.Value;
- if (actor == 'M') {
- if (number == 1) {
- if (mode == 0) {
- clearBit(®isters, dIn_Magazin1_On );
- clearBit(®isters, dIn_Magazin1_Off);
- }
- if (mode == 1) {
- setBit(®isters, dIn_Magazin1_On);
- clearBit(®isters, dIn_Magazin1_Off);
- }
- if (mode == 2) {
- clearBit(®isters, dIn_Magazin1_On);
- setBit(®isters, dIn_Magazin1_Off);
- }
- //rt_sleep(300 * nano2count(1000000));
- //var.bits.bit0=0;
- //var.bits.bit1=0;
- }
- if (number == 2) {
- if (mode == 0) {
- clearBit(®isters, 2);
- clearBit(®isters, 3);
- }
- if (mode == 1) {
- setBit(®isters, 2);
- clearBit(®isters, 3);
- }
- if (mode == 2) {
- clearBit(®isters, 2);
- setBit(®isters, 3);
- }
- //rt_sleep(300 * nano2count(1000000));
- //var.bits.bit2=0;
- //var.bits.bit3=0;
- }
- if (number == 3) {
- if (mode == 0) {
- clearBit(®isters, 5);
- clearBit(®isters, 6);
- }
- if (mode == 1) {
- setBit(®isters, 5);
- clearBit(®isters, 6);
- }
- if (mode == 2) {
- clearBit(®isters, 5);
- setBit(®isters, 6);
- }
- //rt_sleep(300 * nano2count(1000000));
- //var.bits.bit5=0;
- //var.bits.bit6=0;
- }
- }
- if (actor == 'P') {
- if (mode == 1){
- setBit(®isters, 4);
- }
- if (mode == 0){
- clearBit(®isters, 4);
- }
- }
- if (actor == 'B') {
- if (mode == 1) {
- setBit(®isters, 7);
- }
- if (mode == 0){
- clearBit(®isters, 7);
- }
- }
- rt_modbus_set(fd_node, DIGITAL_OUT, 0, registers);
- }
- // Control WE über unsigned short
- // Tested: Untested
- // Turn Magazin Pressure off (Magzin Mode Change to 00)
- void controlActToZero(char actor, int number, int mode) {
- controlAct(actor, number, mode);
- //rt_sleep(3000 * nano2count(1000000));
- controlAct(actor, number, 0);
- }
- // Coverts long Value into binary
- // Tested: OK
- // Copied from:
- long long decimalToBinary(long n) {
- int remainder;
- long long binary = 0, i = 1;
- while (n != 0) {
- remainder = n % 2;
- n = n / 2;
- binary = binary + (remainder * i);
- i = i * 10;
- }
- return binary;
- }
- // Get Temp Register and
- // Tested: OK
- // Copied from:
- void func(int DIG_IN, int setMask) {
- // int fd_node;
- short valDigital = 0;
- if ((fd_node = rt_modbus_connect("modbus-node")) == -1) {
- rt_printk("control: task exited\n");
- return;
- }
- int tempRegister = rt_modbus_get(fd_node, DIGITAL_IN, 0, &valDigital);
- rt_printk("Register: %d", tempRegister);
- }
- // Slap Pusher with Delay
- // Tested: OK
- void RemovePusherOn (unsigned int secs) {
- unsigned int retTime = time(0) + secs; // Get finishing time.
- while (time(0) < retTime);
- controlAct('P', 0, 1);
- // Loop until it arrives.
- }
- // Slap Pusher with Delay back
- // Tested: OK
- void RemovePusherOff (unsigned int secs) {
- unsigned int retTime = time(0) + secs; // Get finishing time.
- while (time(0) < retTime);
- controlAct('P', 0, 0);
- // Loop until it arrives.
- }
- // Threads
- // Threads
- // Threads
- // Threads
- // Threads
- // Task 1: Magazin 1
- // Tested: OK
- // Comments:
- static void mag1(long x) {
- int status = 0;
- while (1) {
- //if (getBit(®ister, 0)==1 && getBit(®isters, 1)==0 && getBit(®isters, 2)==0
- if (read.bits.bit0 == 1 && read.bits.bit1 == 0 && read.bits.bit2 == 0) // here checks if the magazine have something inside
- {
- controlAct('M', 1, 2);
- rt_sleep(200 * nano2count(1000000));
- controlAct('M', 1, 0);
- status = 1;
- rt_sleep(200 * nano2count(1000000));
- }
- else {
- if (read.bits.bit0 == 0 && read.bits.bit1 == 1 && read.bits.bit2 == 0 && status == 1)
- {
- controlAct('M', 1, 1);
- rt_sleep(200 * nano2count(1000000));
- controlAct('M', 1, 0);
- rt_sleep(200 * nano2count(1000000));
- status = 0;
- }
- rt_sleep(10 * nano2count(1000000));
- }
- }
- }
- // Task 2: Magazin 2
- // Tested: OK
- // Comments:
- static void mag2(long x) {
- int status=0;
- while (1) {
- if (Mag2Senzor == 1)
- {rt_sleep(2000 * nano2count(1000000));
- rt_printk("\nMag2 on pause\n");
- }
- else {
- if (read.bits.bit3 == 1 && read.bits.bit4 == 0 && read.bits.bit5 == 0) // here checks if the magazine have something inside
- {
- controlAct('M', 2, 2);
- rt_sleep(200 * nano2count(1000000));
- controlAct('M', 2, 0);
- rt_sleep(200 * nano2count(1000000));
- status=1;}
- // push mag out
- //stop presure
- if (status==1){
- controlAct('M', 2, 1);
- // get the piston back
- rt_sleep(200 * nano2count(1000000)); // wait for piston to be full in
- controlAct('M', 2, 0);
- // stop the air presure
- rt_sleep(200 * nano2count(1000000));
- status =0;
- }
- rt_sleep(10 * nano2count(1000000));
- }
- }
- }
- // Task 3: Magazin 3
- // Tested: OK
- // Comments:
- static void mag3(long x) {
- int status = 0;
- while (1) {
- if (Mag3Senzor == 1)
- {rt_sleep(2000 * nano2count(1000000));
- rt_printk("\nMag3 on pause\n");}
- else{
- if (read.bits.bit7 == 1 && read.bits.bit8 == 0 && read.bits.bit9 == 0) // here checks if the magazine have something inside
- {
- controlAct('M', 3, 2);
- rt_sleep(200 * nano2count(1000000));
- controlAct('M', 3, 0);
- //stop presure
- rt_sleep(200 * nano2count(1000000));
- status = 1;
- }
- if (status == 1) {
- controlAct('M', 3, 1); // get the piston back
- rt_sleep(200 * nano2count(1000000)); // wait for piston to be full in
- controlAct('M', 3, 0); // stop the air presure
- rt_sleep(200 * nano2count(1000000));
- status = 0;
- }
- rt_sleep(10 * nano2count(1000000));
- }
- }
- }
- // Task 4: Sensors
- // Tested: OK
- // Comments:
- static void ReadAllSensors(long x) {
- int PusherSenzor =0;
- while (1) {
- rt_modbus_get(fd_node, ANALOG_IN, 0, &valAnalog);
- rt_modbus_get(fd_node, DIGITAL_IN, 0, &valDigital);
- read.Value = valDigital;
- Mag3Senzor = read.bits.bit6;
- Mag2Senzor = read.bits.bit15;
- if (valAnalog > 20000) {
- PusherSenzor++;
- if (PusherSenzor > 5)
- BadPart = 1;
- rt_printk("Push senzor: %d", PusherSenzor);
- }
- else
- PusherSenzor = 0;
- rt_sleep(30 * nano2count(1000000));
- }
- }
- // Task 5: Slap Bad Parts to Slide
- // Tested: OK
- // Comments:
- static void RemoveBadPart(long x)
- {
- int tm =0,start = 1300,end = 1000;
- rt_printk("Remove bad part: task started\n");
- while (1) {
- if ( BadPart == 1 )
- {
- rt_printk("\nRemove bad part: Removal started\n");
- // rt_sleep(1300 * nano2count(1000000));
- // controlAct('P', 0, 1);
- RemovePusherOn(1);
- // rt_sleep(1000 * nano2count(1000000));
- RemovePusherOff(1);
- // controlAct('P', 0, 0);
- BadPart = 0;
- }
- rt_sleep(30 * nano2count(1000000));
- }
- }
- // Task 6: Check for critical area of M2/M3 with delay
- // Tested: no
- static void CheckCriticalArea(long x) {
- while(getBit(®isters, dIn_Magazin2_Critical)==1){
- }
- // sleep
- }
- // Task 0: Control
- // Tested: OK
- // Comments:
- static void control(long x) {
- // int fd_node;
- int ready = 0;
- int s1 =0;
- float valAnalogeVoltage = 0.0;
- int* valDigitalOut = 0x1000;
- int val = 0;
- rt_printk("control: task started\n");
- /* Verbindung zum Modbus-Knoten herstellen */
- if ((fd_node = rt_modbus_connect("modbus-node")) == -1) {
- rt_printk("control: task exited\n");
- return;
- }
- rt_printk("control: MODBUS communication opened\n");
- while (1) {
- /* Ablauf des Control Tasks */
- rt_printk("\nHallo!\n");
- /* Evtl. Ausgang zum Aufraemen */
- if (ready)
- goto fertig;
- /* Fair zu Linux sein und Zeit abgeben */
- rt_sleep(1000 * nano2count(1000000));
- // Bit mani set bit
- // tmp = (1<<3) | (1<<2); // set bit 3 and 2
- // ANALOG_IN
- // rt_modbus_get(fd_node, ANALOG_IN, 0, &valAnalog);
- rt_printk("AnalogValue %d\n", valAnalog);
- //valAnalogeVoltage = valAnalog*333.33; // in µV
- //rt_printk("AnalogValue %f µV\n", valAnalogeVoltage);
- // DIGITAL_IN
- // rt_modbus_get(fd_node, DIGITAL_IN, 0, &valDigital);
- rt_printk("DigitalValue: Dez: %lu Hex: %08x ", valDigital, valDigital);
- unsigned long long zahl = decimalToBinary(valDigital);
- rt_printk("\nBin: %lu ", zahl);
- // read.Value = valDigital;
- rt_printk("Laser status: %d", read.bits.bit15);
- rt_printk("\n");
- rt_sleep(1000 * nano2count(1000000));
- rt_printk("Laser ID: %d", Mag3Senzor);
- // Steuerung in Control Funktion
- if (val==0){
- int i;
- for (i=1; i<=15; i++)
- rt_modbus_set(fd_node,IB_IL_AO,i,30000);
- controlAct('B', 2, 1);
- rt_task_resume(&ReadStatus);
- rt_printk("\nRead Task started !\n");
- rt_task_resume(&tskM1);
- rt_printk("\nM1 task started!\n");
- rt_task_resume(&tskRemoveBadPart);
- rt_printk("\nremove bad parts task started\n");
- rt_sleep(2000 * nano2count(1000000));
- rt_task_resume(&tskM2);
- rt_printk("\nM2 task started!\n");
- rt_sleep(3000 * nano2count(1000000));
- rt_task_resume(&tskM3);
- rt_printk("\nM3 task started\n");
- val++;}
- }
- //
- // if (rt_task_suspend(&tskM3)== 0)
- // {
- // rt_printk("\nTask 3 suspended\n");
- // s1 =1;
- // }
- // else
- // {
- // rt_printk("\nTask 3 suspend FAILURE\n");
- // }
- // }
- //
- //
- // else
- // if (read.bits.bit6 == 0 && s1 ==1)
- // {
- //
- //
- // rt_printk("Task 3 resumed");
- // rt_printk("\n");
- // rt_task_resume(&tskM3);
- // s1 =0;
- // }
- // rt_printk("BIT 1: %d", read.bits.bit4);
- // rt_printk("\n");
- // rt_printk("BIT 2: %d", read.bits.bit5);
- // rt_printk("\n");
- /* Aufraeumen */
- fertig: rt_modbus_disconnect(fd_node);
- rt_printk("control: task exited\n");
- }
- /* Funktion die beim Entfernen des Moduls aufgerufen wird */
- static void __exit
- example_exit(void) {
- /* Task loeschen */
- rt_task_delete(&task);
- /* Timer stoppen */
- stop_rt_timer();
- printk("rtai_example unloaded\n");
- }
- /* Funktion die beim Laden des Moduls aufgerufen wird */
- static int __init
- example_init(void) {
- /* variables Timing basierend auf dem CPU-Takt */
- rt_set_oneshot_mode();
- /* Timer starten */
- start_rt_timer(0);
- /* Modbuskommunikation initialisieren */
- modbus_init();
- /* Taskinitialisierung
- *
- * &task = Adresse des zu initialisierenden TCB
- * control = zum Task gehörende Funktion
- * 0 = Uebergabeparameter an die zum Task gehoerende Funktion (long)
- * 1024 = Stacksize
- * 0 = Priorität des Tasks (0 = groesste)
- * 0 = uses_fpu (Fliesskommaeinheit nicht benutzen); im Praktikum sollte es 0 bleiben
- * NULL = &signal_handler; Referenz zu einer Fkt., die bei jeder Aktivierung
- * des Tasks aufgerufen wird, ansonsten NULL
- *
- * Achtung: Nach der Initialisierung ist der Task "suspended"!
- *
- */
- if (rt_task_init(&task, control, 0, STACKSIZE, 0, 0, NULL)) {
- printk("cannot initialize control task\n");
- goto fail;
- }
- if (rt_task_init(&tskM1, mag1, 0,STACKSIZE , 0, 0, NULL)) {
- printk("cannot initialize M1 task\n");
- goto fail;
- }
- if (rt_task_init(&tskM2, mag2, 0, STACKSIZE, 0, 0, NULL)) {
- printk("cannot initialize M2 task\n");
- goto fail;
- }
- if (rt_task_init(&tskM3, mag3, 0, STACKSIZE, 0, 0, NULL)) {
- printk("cannot initialize M3 task\n");
- goto fail;
- }
- if (rt_task_init(&ReadStatus, ReadAllSensors, 0, STACKSIZE, 0, 0, NULL)) {
- printk("cannot initialize Read All Sensors task\n");
- goto fail;
- }
- if (rt_task_init(&tskRemoveBadPart, RemoveBadPart, 0, STACKSIZE, 0, 0, NULL)) {
- printk("cannot initialize RemoveBadPart task\n");
- goto fail;
- }
- /* suspend -> ready bzw. running */
- rt_task_resume(&task);
- printk("rtai_example loaded\n");
- return (0);
- fail:
- /* Aufraeumen */
- rt_task_delete(&tskM1);
- rt_task_delete(&tskM2);
- rt_task_delete(&tskM3);
- stop_rt_timer();
- return (1);
- }
- /* Modulein- und ausstieg festlegen */
- module_exit(example_exit)
- module_init(example_init)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement