Hi all,<br>I modified the etherlab mini example to test the ethercat master with our ethercat slaves, which are custom devices, implementing the CanOpen dsp402 Drive profile. The configuration seems all right, and the slave is actually going to the OP state, but the working counter reports a 2/3 actual/expected value, and the domain process data are not reflecting actual values on the slave. <br>
<br>I'm using an intel PRO/100 interface, a vanilla linux 2.6.24.6 kernel (I'm planning to add rtai or xenomai after a successful test with the vanilla kernel), and the ethercat-devel-r1718 master.<br><br>I don't know where the problem may reside. Please, can anyone give a look at the following commands outputs and code? <br>
<br>Thank you very much<br>Antonio<br><br><br>Command outputs<br>===========================================<br><br>/ # insmod /lib/modules/<a href="http://2.6.24.6/ethercat/ec_mini.ko">2.6.24.6/ethercat/ec_mini.ko</a> <br>
[   56.256713] ec_mini: Starting...<br>[   56.260529] EtherCAT: Requesting master 0...<br>[   56.267747] EtherCAT: Successfully requested master 0.<br>[   56.273488] ec_mini: Registering domain...<br>[   56.278978] ec_mini: Configuring PDOs...<br>
[   56.283259] ec_mini: Registering PDO entries...<br>[   56.288097] ec_mini: Activating master...<br>[   56.292414] EtherCAT: Domain0: Logical address 0x00000000, 16 byte, expected working counter 3.<br>[   56.302129] EtherCAT:   Datagram domain0-0: Logical offset 0x00000000, 16 byte, type LRW.<br>
[   56.311247] EtherCAT: Stopping EoE processing.<br>[   56.316059] EtherCAT: Master thread exited.<br>[   56.320484] EtherCAT: Starting EtherCAT-OP thread.<br>[   56.326125] EtherCAT: Starting EoE processing.<br>[   56.331456] ec_mini: Starting cyclic sample thread.<br>
[   56.336632] ec_mini: Started.<br>/ # [   56.374385] ec_mini: 1 slave(s).<br>[   56.377739] ec_mini: AL states: 0x02.<br>[   56.381336] ec_mini: Link is up.<br>[   56.384546] ec_mini: AM_MP: State 0x02.<br>[   56.388368] ec_mini: AM_MP: online.<br>
[   62.374365] ec_mini: AM_MP: State 0x01.<br>[   78.374358] ec_mini: AM_MP: State 0x02.<br>[   81.374354] EtherCAT: Domain 0: Working counter changed to 2/3.<br>[   81.380226] ec_mini: Domain1: WC 2.<br>[   81.383874] ec_mini: Domain1: State 1.<br>
[   86.374362] ec_mini: AM_MP: State 0x04.<br>[   90.374359] ec_mini: AM_MP: State 0x08.<br>[   90.378130] ec_mini: AM_MP: operational.<br>[   90.382960] EtherCAT: Slave states: OP.<br>[   92.374355] ec_mini: AL states: 0x08.<br>
<br>/ # ethercat slaves -v                               <br>=== Slave 0 ===<br>State: OP<br>Flag: +<br>Identity:<br>  Vendor Id:       0x65547241<br>  Product code:    0x3030504d<br>  Revision number: 0x00000002<br>  Serial number:   0x00000000<br>
Ports:<br>  0: MII.<br>  1: MII.<br>  2: Not implemented.<br>  3: Not implemented.<br>DL information:<br>  FMMU bit operation: no<br>  Distributed clocks: yes (64 bit)<br>Mailboxes:<br>  Bootstrap RX: 0x0000/0, TX: 0x0000/0<br>
  Standard  RX: 0x1000/192, TX: 0x1200/192<br>  Supported protocols: EoE, CoE, FoE<br>General:<br>  Group: ET1100 SPI<br>  Image name: DRIVE<br>  Order number: MP_ECAT<br>  Device name: Motopiattello - Modo interpolato<br>
  CoE details:<br>    Enable SDO: yes<br>    Enable SDO Info: no<br>    Enable PDO Assign: no<br>    Enable PDO Configuration: no<br>    Enable Upload at startup: no<br>    Enable SDO complete access: no<br>  Flags:<br>    Enable SafeOp: no<br>
    Enable notLRW: no<br>  Current consumption: 0 mA<br><br>/ # ethercat config -v<br>Alias: 0<br>Position: 0<br>Vendor Id: 0x65547241<br>Product code: 0x3030504d<br>Attached slave: 0 (OP)<br>SM2 (Output)<br>  PDO 0x1600<br>
    PDO entry 0x607a:00, 32 bit<br>    PDO entry 0x6040:00, 16 bit<br>    PDO entry 0x6060:00,  8 bit<br>    PDO entry 0x6098:00,  8 bit<br>SM3 (Input)<br>  PDO 0x1a00<br>    PDO entry 0x6064:00, 32 bit<br>    PDO entry 0x6041:00, 16 bit<br>
    PDO entry 0x6061:00,  8 bit<br>    PDO entry 0x6098:00,  8 bit<br>SDO configuration:<br>  None.<br><br>/ # ethercat pdos -v  <br>SM0: PhysAddr 0x1000, DefaultSize  192, ControlRegister 0x26, Enable 1<br>SM1: PhysAddr 0x1200, DefaultSize  192, ControlRegister 0x22, Enable 1<br>
SM2: PhysAddr 0x1300, DefaultSize  128, ControlRegister 0x64, Enable 1<br>  RxPDO 0x1600 "RxPdo_0"<br>    PDO entry 0x607a:00, 32 bit, "IP Data Record"<br>    PDO entry 0x6040:00, 16 bit, "Control Word"<br>
    PDO entry 0x6060:00,  8 bit, "Mode of Operation"<br>    PDO entry 0x6098:00,  8 bit, "Homing Method"<br>SM3: PhysAddr 0x1500, DefaultSize  128, ControlRegister 0x20, Enable 1<br>  TxPDO 0x1a00 "TxPdo_0"<br>
    PDO entry 0x6064:00, 32 bit, "Position Actual Value"<br>    PDO entry 0x6041:00, 16 bit, "Status Word"<br>    PDO entry 0x6061:00,  8 bit, "Mode of Operation Display"<br>    PDO entry 0x6098:00,  8 bit, "Homing Method"<br>
<br>/ # ethercat xml<br><?xml version="1.0" ?><br>  <EtherCATInfo><br>    <!-- Slave 0 --><br>    <Vendor><br>      <Id>1700033089</Id><br>    </Vendor><br>    <Descriptions><br>
      <Devices><br>        <Device><br>          <Type ProductCode="#x3030504d" RevisionNo="#x00000002">MP_ECAT</Type><br>          <Name><![CDATA[Motopiattello - Modo interpolato]]></Name><br>
          <Sm Enable="1" StartAddress="4096" ControlByte="38" DefaultSize="192" /><br>          <Sm Enable="1" StartAddress="4608" ControlByte="34" DefaultSize="192" /><br>
          <Sm Enable="1" StartAddress="4864" ControlByte="100" DefaultSize="128" /><br>          <Sm Enable="1" StartAddress="5376" ControlByte="32" DefaultSize="128" /><br>
          <RxPdo Sm="2" Fixed="1" Mandatory="1"><br>            <Index>#x1600</Index><br>            <Name>RxPdo_0</Name><br>            <Entry><br>              <Index>#x607a</Index><br>
              <SubIndex>0</SubIndex><br>              <BitLen>32</BitLen><br>              <Name>IP Data Record</Name><br>              <DataType>UINT32</DataType><br>            </Entry><br>
            <Entry><br>              <Index>#x6040</Index><br>              <SubIndex>0</SubIndex><br>              <BitLen>16</BitLen><br>              <Name>Control Word</Name><br>
              <DataType>UINT16</DataType><br>            </Entry><br>            <Entry><br>              <Index>#x6060</Index><br>              <SubIndex>0</SubIndex><br>              <BitLen>8</BitLen><br>
              <Name>Mode of Operation</Name><br>              <DataType>UINT8</DataType><br>            </Entry><br>            <Entry><br>              <Index>#x6098</Index><br>
              <SubIndex>0</SubIndex><br>              <BitLen>8</BitLen><br>              <Name>Homing Method</Name><br>              <DataType>UINT8</DataType><br>            </Entry><br>
          </RxPdo><br>          <TxPdo Sm="3" Fixed="1" Mandatory="1"><br>            <Index>#x1a00</Index><br>            <Name>TxPdo_0</Name><br>            <Entry><br>
              <Index>#x6064</Index><br>              <SubIndex>0</SubIndex><br>              <BitLen>32</BitLen><br>              <Name>Position Actual Value</Name><br>              <DataType>UINT32</DataType><br>
            </Entry><br>            <Entry><br>              <Index>#x6041</Index><br>              <SubIndex>0</SubIndex><br>              <BitLen>16</BitLen><br>              <Name>Status Word</Name><br>
              <DataType>UINT16</DataType><br>            </Entry><br>            <Entry><br>              <Index>#x6061</Index><br>              <SubIndex>0</SubIndex><br>              <BitLen>8</BitLen><br>
              <Name>Mode of Operation Display</Name><br>              <DataType>UINT8</DataType><br>            </Entry><br>            <Entry><br>              <Index>#x6098</Index><br>
              <SubIndex>0</SubIndex><br>              <BitLen>8</BitLen><br>              <Name>Homing Method</Name><br>              <DataType>UINT8</DataType><br>            </Entry><br>
          </TxPdo><br>        </Device><br>     </Devices><br>  </Descriptions><br></EtherCATInfo><br><br>/ # ethercat domains -v<br>Domain0: LogBaseAddr 0x00000000, Size  16, WorkingCounter 2/3<br>
  SlaveConfig 0:0, SM2 (Output), LogAddr 0x00000000, Size 8<br>    0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 <br>  SlaveConfig 0:0, SM3 ( Input), LogAddr 0x00000008, Size 8<br>    0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 <br>
<br><br>mini.c modified code<br>===========================================<br>#include <linux/module.h><br>#include <linux/timer.h><br>#include <linux/spinlock.h><br>#include <linux/interrupt.h><br>
#include <linux/err.h><br><br>#include "../../include/ecrt.h" // EtherCAT realtime interface<br><br>/*****************************************************************************/<br><br>// Module parameters<br>
#define FREQUENCY 1<br><br>// Optional features<br>#define CONFIGURE_PDOS  1<br>#define EL3152_ALT_PDOS 0<br>#define EXTERNAL_MEMORY 1<br>#define SDO_ACCESS      0<br>#define VOE_ACCESS      0<br><br>#define PFX "ec_mini: "<br>
<br>/*****************************************************************************/<br><br>// EtherCAT<br>static ec_master_t *master = NULL;<br>static ec_master_state_t master_state = {};<br>spinlock_t master_lock = SPIN_LOCK_UNLOCKED;<br>
<br>static ec_domain_t *domain1 = NULL;<br>static ec_domain_state_t domain1_state = {};<br><br>static ec_slave_config_t *sc_am_mp = NULL;<br>static ec_slave_config_state_t sc_am_mp_state = {};<br><br>// Timer<br>static struct timer_list timer;<br>
<br>/*****************************************************************************/<br><br>// process data<br>static uint8_t *domain1_pd; // process data memory<br><br>#define AM_MP_ECAT_SLAVE_POS     0, 0<br>#define AM_MP_ECAT                 0x65547241, 0x3030504D<br>
<br>// offsets for PDO entries<br>static unsigned int off_am_mp_ip_data_record;<br>static unsigned int off_am_mp_control_word;<br>static unsigned int off_am_mp_mode_of_operation;<br>static unsigned int off_am_mp_homing_method;<br>
static unsigned int off_am_mp_position_actual_value;<br>static unsigned int off_am_mp_status_word;<br>static unsigned int off_am_mp_mode_of_operation_display;<br><br>const static ec_pdo_entry_reg_t domain1_regs[] = {<br>    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x607A, 0, &off_am_mp_ip_data_record},<br>
    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x6040, 0, &off_am_mp_control_word},<br>    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x6060, 0, &off_am_mp_mode_of_operation},<br>    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x6098, 0, &off_am_mp_homing_method},<br>
    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x6064, 0, &off_am_mp_position_actual_value},<br>    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x6041, 0, &off_am_mp_status_word},<br>    {AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT, 0x6061, 0, &off_am_mp_mode_of_operation_display},<br>
    {}<br>};<br><br>static unsigned int counter = 0;<br><br>/*****************************************************************************/<br><br>#if CONFIGURE_PDOS<br>static ec_pdo_entry_info_t am_mp_rxpdo_0_entries[] = {<br>
    {0x607A, 0, 32},     // ip data record<br>    {0x6040, 0, 16},     // control word<br>    {0x6060, 0, 8},     // mode of operation<br>    {0x6098, 0, 8}         // homing method<br>};<br><br>static ec_pdo_entry_info_t am_mp_txpdo_0_entries[] = {<br>
    {0x6064, 0, 32},     // position actual value<br>    {0x6041, 0, 16},     // status word<br>    {0x6061, 0, 8},     // mode of operation display<br>    {0x6098, 0, 8}         // homing method<br>};<br><br>static ec_pdo_info_t am_mp_pdos[] = {<br>
    {0x1600, 4, am_mp_rxpdo_0_entries},<br>    {0x1A00, 4, am_mp_txpdo_0_entries}<br>};<br><br>static ec_sync_info_t am_mp_syncs[] = {<br>    {2, EC_DIR_OUTPUT, 1, am_mp_pdos},<br>    {3, EC_DIR_INPUT, 1, am_mp_pdos + 1},<br>
    {0xff}<br>};<br>#endif<br><br>/*****************************************************************************/<br><br>#if SDO_ACCESS<br>static ec_sdo_request_t *sdo;<br>#endif<br><br>#if VOE_ACCESS<br>static ec_voe_handler_t *voe;<br>
#endif<br><br>/*****************************************************************************/<br><br>void check_domain1_state(void)<br>{<br>    ec_domain_state_t ds;<br><br>    spin_lock(&master_lock);<br>    ecrt_domain_state(domain1, &ds);<br>
    spin_unlock(&master_lock);<br><br>    if (ds.working_counter != domain1_state.working_counter)<br>        printk(KERN_INFO PFX "Domain1: WC %u.\n", ds.working_counter);<br>    if (ds.wc_state != domain1_state.wc_state)<br>
        printk(KERN_INFO PFX "Domain1: State %u.\n", ds.wc_state);<br><br>    domain1_state = ds;<br>}<br><br>/*****************************************************************************/<br><br>void check_master_state(void)<br>
{<br>    ec_master_state_t ms;<br><br>    spin_lock(&master_lock);<br>    ecrt_master_state(master, &ms);<br>    spin_unlock(&master_lock);<br><br>    if (ms.slaves_responding != master_state.slaves_responding)<br>
        printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);<br>    if (ms.al_states != master_state.al_states)<br>        printk(KERN_INFO PFX "AL states: 0x%02X.\n", ms.al_states);<br>    if (ms.link_up != master_state.link_up)<br>
        printk(KERN_INFO PFX "Link is %s.\n", ms.link_up ? "up" : "down");<br><br>    master_state = ms;<br>}<br><br>/*****************************************************************************/<br>
<br>void check_slave_config_states(void)<br>{<br>    ec_slave_config_state_t s;<br><br>    spin_lock(&master_lock);<br>    ecrt_slave_config_state(sc_am_mp, &s);<br>    spin_unlock(&master_lock);<br><br>    if (s.al_state != sc_am_mp_state.al_state)<br>
        printk(KERN_INFO PFX "AM_MP: State 0x%02X.\n", s.al_state);<br>    if (s.online != sc_am_mp_state.online)<br>        printk(KERN_INFO PFX "AM_MP: %s.\n", s.online ? "online" : "offline");<br>
    if (s.operational != sc_am_mp_state.operational)<br>        printk(KERN_INFO PFX "AM_MP: %soperational.\n",<br>                s.operational ? "" : "Not ");<br><br>    sc_am_mp_state = s;<br>
}<br><br>/*****************************************************************************/<br><br>#if SDO_ACCESS<br>void read_sdo(void)<br>{<br>    switch (ecrt_sdo_request_state(sdo)) {<br>        case EC_REQUEST_UNUSED: // request was not used yet<br>
            ecrt_sdo_request_read(sdo); // trigger first read<br>            break;<br>        case EC_REQUEST_BUSY:<br>            printk(KERN_INFO PFX "Still busy...\n");<br>            break;<br>        case EC_REQUEST_SUCCESS:<br>
            printk(KERN_INFO PFX "SDO value: 0x%04X\n",<br>                    EC_READ_U16(ecrt_sdo_request_data(sdo)));<br>            ecrt_sdo_request_read(sdo); // trigger next read<br>            break;<br>        case EC_REQUEST_ERROR:<br>
            printk(KERN_INFO PFX "Failed to read SDO!\n");<br>            ecrt_sdo_request_read(sdo); // retry reading<br>            break;<br>    }<br>}<br>#endif<br><br>/*****************************************************************************/<br>
<br>#if VOE_ACCESS<br>void read_voe(void)<br>{<br>    switch (ecrt_voe_handler_execute(voe)) {<br>        case EC_REQUEST_UNUSED:<br>            ecrt_voe_handler_read(voe); // trigger first read<br>            break;<br>        case EC_REQUEST_BUSY:<br>
            printk(KERN_INFO PFX "VoE read still busy...\n");<br>            break;<br>        case EC_REQUEST_SUCCESS:<br>            printk(KERN_INFO PFX "VoE received.\n");<br>            // get data via ecrt_voe_handler_data(voe)<br>
            ecrt_voe_handler_read(voe); // trigger next read<br>            break;<br>        case EC_REQUEST_ERROR:<br>            printk(KERN_INFO PFX "Failed to read VoE data!\n");<br>            ecrt_voe_handler_read(voe); // retry reading<br>
            break;<br>    }<br>}<br>#endif<br><br>/*****************************************************************************/<br><br>void cyclic_task(unsigned long data)<br>{<br>    // receive process data<br>    spin_lock(&master_lock);<br>
    ecrt_master_receive(master);<br>    ecrt_domain_process(domain1);<br>    spin_unlock(&master_lock);<br><br>    // check process data state (optional)<br>    check_domain1_state();<br><br>    if (counter) {<br>        counter--;<br>
    } else { // do this at 1 Hz<br>        counter = FREQUENCY;<br><br>        // TODO: calculate new process data<br><br>        // check for master state (optional)<br>        check_master_state();<br><br>        // check for islave configuration state(s) (optional)<br>
        check_slave_config_states();<br>        <br>#if SDO_ACCESS<br>        // read process data SDO<br>        read_sdo();<br>#endif<br><br>#if VOE_ACCESS<br>        read_voe();<br>#endif<br>    }<br><br>    // write process data<br>
    EC_WRITE_U32((uint32_t *)(domain1_pd + off_am_mp_control_word), 0x0001);<br><br>    // send process data<br>    spin_lock(&master_lock);<br>    ecrt_domain_queue(domain1);<br>    ecrt_master_send(master);<br>    spin_unlock(&master_lock);<br>
<br>    // restart timer<br>    timer.expires += HZ / FREQUENCY;<br>    add_timer(&timer);<br>}<br><br>/*****************************************************************************/<br><br>int request_lock(void *data)<br>
{<br>    spin_lock(&master_lock);<br>    return 0; // access allowed<br>}<br><br>/*****************************************************************************/<br><br>void release_lock(void *data)<br>{<br>    spin_unlock(&master_lock);<br>
}<br><br>/*****************************************************************************/<br><br>int __init init_mini_module(void)<br>{<br>    int ret = -1;<br>#if CONFIGURE_PDOS<br>    ec_slave_config_t *sc;<br>#endif<br>
#if EXTERNAL_MEMORY<br>    unsigned int size;<br>#endif<br>    <br>    printk(KERN_INFO PFX "Starting...\n");<br><br>    master = ecrt_request_master(0);<br>    if (!master) {<br>        ret = -EBUSY; <br>        printk(KERN_ERR PFX "Requesting master 0 failed.\n");<br>
        goto out_return;<br>    }<br><br>    ecrt_master_callbacks(master, request_lock, release_lock, NULL);<br><br>    printk(KERN_INFO PFX "Registering domain...\n");<br>    if (!(domain1 = ecrt_master_create_domain(master))) {<br>
        printk(KERN_ERR PFX "Domain creation failed!\n");<br>        goto out_release_master;<br>    }<br><br>    if (!(sc_am_mp = ecrt_master_slave_config(<br>                    master, AM_MP_ECAT_SLAVE_POS, AM_MP_ECAT))) {<br>
        printk(KERN_ERR PFX "Failed to get slave configuration.\n");<br>        goto out_release_master;<br>    }<br><br>#if CONFIGURE_PDOS<br>    printk(KERN_INFO PFX "Configuring PDOs...\n");<br>    if (ecrt_slave_config_pdos(sc_am_mp, EC_END, am_mp_syncs)) {<br>
        printk(KERN_ERR PFX "Failed to configure PDOs.\n");<br>        goto out_release_master;<br>    }<br>#endif<br><br>#if SDO_ACCESS<br>    printk(KERN_INFO PFX "Creating SDO requests...\n");<br>    // status word...<br>
    if (!(sdo = ecrt_slave_config_create_sdo_request(sc_am_mp, 0x6041, 0, 2))) {<br>        printk(KERN_ERR PFX "Failed to create SDO request.\n");<br>        goto out_release_master;<br>    }<br>    ecrt_sdo_request_timeout(sdo, 500); // ms<br>
#endif<br><br>#if VOE_ACCESS<br>    // TODO...<br>#endif<br><br>    printk(KERN_INFO PFX "Registering PDO entries...\n");<br>    if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {<br>        printk(KERN_ERR PFX "PDO entry registration failed!\n");<br>
        goto out_release_master;<br>    }<br><br>#if EXTERNAL_MEMORY<br>    if ((size = ecrt_domain_size(domain1))) {<br>        if (!(domain1_pd = (uint8_t *) kmalloc(size, GFP_KERNEL))) {<br>            printk(KERN_ERR PFX "Failed to allocate %u bytes of process data"<br>
                    " memory!\n", size);<br>            goto out_release_master;<br>        }<br>        ecrt_domain_external_memory(domain1, domain1_pd);<br>    }<br>#endif<br><br>    printk(KERN_INFO PFX "Activating master...\n");<br>
    if (ecrt_master_activate(master)) {<br>        printk(KERN_ERR PFX "Failed to activate master!\n");<br>#if EXTERNAL_MEMORY<br>        goto out_free_process_data;<br>#else<br>        goto out_release_master;<br>
#endif<br>    }<br><br>#if !EXTERNAL_MEMORY<br>    // Get internal process data for domain<br>    domain1_pd = ecrt_domain_data(domain1);<br>#endif<br><br>    printk(KERN_INFO PFX "Starting cyclic sample thread.\n");<br>
    init_timer(&timer);<br>    timer.function = cyclic_task;<br>    timer.expires = jiffies + 10;<br>    add_timer(&timer);<br><br>    printk(KERN_INFO PFX "Started.\n");<br>    return 0;<br><br>#if EXTERNAL_MEMORY<br>
out_free_process_data:<br>    kfree(domain1_pd);<br>#endif<br>out_release_master:<br>    printk(KERN_ERR PFX "Releasing master...\n");<br>    ecrt_release_master(master);<br>out_return:<br>    printk(KERN_ERR PFX "Failed to load. Aborting.\n");<br>
    return ret;<br>}<br><br>/*****************************************************************************/<br><br>void __exit cleanup_mini_module(void)<br>{<br>    printk(KERN_INFO PFX "Stopping...\n");<br><br>
    del_timer_sync(&timer);<br><br>#if EXTERNAL_MEMORY<br>    kfree(domain1_pd);<br>#endif<br><br>    printk(KERN_INFO PFX "Releasing master...\n");<br>    ecrt_release_master(master);<br><br>    printk(KERN_INFO PFX "Unloading.\n");<br>
}<br><br>/*****************************************************************************/<br><br>MODULE_LICENSE("GPL");<br>MODULE_DESCRIPTION("EtherCAT minimal test environment");<br><br>module_init(init_mini_module);<br>
module_exit(cleanup_mini_module);<br><br>/*****************************************************************************/<br>