Monday, March 17, 2014

Using Netlink to post events from kernelspace to userspace

I have been working on an older version of Linux on which I wanted to use netlink as a way to post events from kernelspace to userspace. I wanted to avoid using Netlink Protocol Library Suite (libnl) so that I could delve deeper into netlink and have better control of what I am doing. I was already successful in using Generic Netlink Sockets to perform two way request-response communication between kernelspace and userspace. This time I referred ACPI daemon 1.0.10's source and used genlmsg_multicast() to post events from the kernel side. The implementation of netlink has evolved over time and the following code ought to work only on kernels versioned around 2.6.35 (I tested my code on Ubuntu 10.10)

Source Code Organization:


kern/genl_event.c:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
#include <net/genetlink.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include "genl_event.h"

//Code based on http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html

static struct genl_family genl_exmpl_event_family = {
 .id = GENL_ID_GENERATE,         //Genetlink should generate an id
 .hdrsize = 0,
 .name = GENL_EXMPL_EVENT_FAMILYNAME,        //The name of this family, used by userspace application
 .version = GENL_EXMPL_EVENT_VERSION,          //Version number  
 .maxattr = GENL_EXMPL_EVENT_A_MAX,
};

static struct genl_multicast_group genl_exmpl_event_group = {
 .name = GENL_EXMPL_EVENT_GROUPNAME,
};

uint8_t genl_exmpl_event_init(void) {
 int rc;

    //Step 01: Register the new family
 rc = genl_register_family(&genl_exmpl_event_family);
 if (rc != 0) {
  goto failure;
 }

 //Step 02: Register group for the new family
 //Referred: http://lxr.free-electrons.com/source/drivers/acpi/event.c?v=2.6.33#L254
 rc = genl_register_mc_group(&genl_exmpl_event_family, &genl_exmpl_event_group);
 if (rc != 0) {
  printk("Generic Netlink register group: %i",rc);
  genl_unregister_family(&genl_exmpl_event_family);
  goto failure;
 }

 return 0; 
failure:
 return -1;
}

void genl_exmpl_event_deinit(void) {
 int ret;

    //Unregister the family
 ret = genl_unregister_family(&genl_exmpl_event_family);
 if(ret !=0) {
  printk("Generic Netlink unregister family %i\n",ret);
 }
}

int genl_exmpl_event_send(u8 *msg, int len) {
 //Referred: http://lxr.free-electrons.com/source/drivers/acpi/event.c?v=2.6.33#L183
 struct sk_buff *skb;
 int rc;
 void *msg_head;

 //Send a message back ti userspace
    //Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE
 skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 if (skb == NULL) {
  printk("Could not allocate skb\n");
  return 0;
 }

 //Create the message headers
    /* arguments of genlmsg_put: 
       struct sk_buff *, 
       int (sending) pid, 
       int sequence number, 
       struct genl_family *, 
       int flags, 
       u8 command index (why do we need this?)
    */
 msg_head = genlmsg_put(skb, 0, 0, &genl_exmpl_event_family, 0, GENL_EXMPL_EVENT_C_DO);
 if (msg_head == NULL) {
  rc = -ENOMEM;
  printk("genlmsg_put() returned error\n");
  return 0;
 }

 //Add a GENL_EXMPL_EVENT_A_MSG attribute (actual value to be sent)
 rc = nla_put(skb, GENL_EXMPL_EVENT_A_MSG, len, msg);
 if (rc != 0) {
  printk("nla_put() returned error\n");
  return 0;
 }
 
 //Finalize the message
 genlmsg_end(skb, msg_head);

    //Send the message
 rc = genlmsg_multicast(skb, 0,genl_exmpl_event_group.id,GFP_ATOMIC);
 if (rc != 0) {
  //printk("genlmsg_multicast() returned error (Group ID: %d)\n", genl_exmpl_event_group.id);
  return 0;
 } else {
  printk("genlmsg_multicast() event message sent (Group ID: %d): %s\n", genl_exmpl_event_group.id, msg);
 }
 return 0;
}

kern/genl_event.h:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* attributes (variables):
 * the index in this enum is used as a reference for the type,
 * userspace application has to indicate the corresponding type
 * the policy is used for security considerations 
 */
enum {
 GENL_EXMPL_EVENT_A_UNSPEC,
 GENL_EXMPL_EVENT_A_MSG,
    __GENL_EXMPL_EVENT_A_MAX,
};
#define GENL_EXMPL_EVENT_A_MAX (__GENL_EXMPL_EVENT_A_MAX - 1)

/* commands: enumeration of all commands (functions), 
 * used by userspace application to identify command to be executed
 */
enum {
 GENL_EXMPL_EVENT_C_UNSPEC,
 GENL_EXMPL_EVENT_C_DO,
 __GENL_EXMPL_EVENT_C_MAX,
};
#define GENL_EXMPL_EVENT_C_MAX (__GENL_EXMPL_EVENT_C_MAX - 1)

#define GENL_EXMPL_EVENT_VERSION 1
#define GENL_EXMPL_EVENT_FAMILYNAME "CONTROL_EXMPL"
#define GENL_EXMPL_EVENT_GROUPNAME "EXMPL_GRP"

uint8_t genl_exmpl_event_init(void);
void genl_exmpl_event_deinit(void);
int genl_exmpl_event_send(u8 *msg, int len);

kern/main.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/genetlink.h>
#include <linux/ioport.h>

#include <linux/wait.h>
#include <linux/kthread.h>
#include <asm/io.h>
#include <linux/sched.h>
#include <linux/delay.h>

#include "genl_event.h"

struct task_struct *thr_ts1;

static char nl_string[50] = "Hello";


int thread_func(void *data) {
 while (!kthread_should_stop ()) {
  sprintf(nl_string, "Hello from kernel at jiffies %ld",jiffies);
  genl_exmpl_event_send(nl_string,50);
  set_current_state (TASK_INTERRUPTIBLE);
        schedule_timeout (cputime_to_jiffies(secs_to_cputime (5))); //Do not schedule this thread again for the specified number of seconds
 }
 return 0;
}

static int __init gnKernel_init(void) {
 printk("Generic Netlink Example Module inserted.\n");

 genl_exmpl_event_init();
 
 thr_ts1 = kthread_run(thread_func, NULL, "kthread1");

 return 0;
}

static void __exit gnKernel_exit(void) {
 kthread_stop(thr_ts1);
 genl_exmpl_event_deinit();

 printk("Generic Netlink Example Module unloaded.\n");
}

module_init(gnKernel_init);
module_exit(gnKernel_exit);
MODULE_LICENSE("GPL");

     

user/main.c:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <signal.h>

#include <linux/genetlink.h>



/* Code based on
 * http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html
 * and
 * http://sourcecodebrowser.com/acpid/1.0.10/acpid_2src_2drivers_2netlink_8c_source.html
 * and
 * http://sourcecodebrowser.com/acpid/1.0.10/acpid_2include_2acpid_2driver_2netlink_8h.html
 */



/* Generic macros for dealing with netlink sockets. Might be duplicated
 * elsewhere. It is recommended that commercial grade applications use
 * libnl or libnetlink and use the interfaces provided by the library
 */
#define NLMSG_TAIL(nlh) ((struct nlattr *) (((void *) (nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
#define NLA_OK(rta,len) ((len) >= (int)sizeof(struct nlattr) && \
                (rta)->nla_len >= sizeof(struct nlattr) && \
                (rta)->nla_len <= (len))
#define NLA_NEXT(rta,attrlen)      ((attrlen) -= NLA_ALIGN((rta)->nla_len), \
                               (struct nlattr*)(((char*)(rta)) + NLA_ALIGN((rta)->nla_len)))
#define NLA_LENGTH(len)     (NLA_ALIGN(sizeof(struct nlattr)) + (len))
#define NLA_SPACE(len)      NLA_ALIGN(NLA_LENGTH(len))
#define NLA_DATA(rta)   ((void*)(((char*)(rta)) + NLA_LENGTH(0)))
#define NLA_PAYLOAD(rta) ((int)((rta)->nla_len) - NLA_LENGTH(0))
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
#define GENL_MAX_FAM_OPS   256
#define GENL_MAX_FAM_GRPS   256


//Variables used for netlink
int nl_fd;  //netlink socket's file descriptor
struct sockaddr_nl nl_address;  //netlink socket address
int nl_family_id; //The family ID resolved by the netlink controller for this userspace program
int nl_group_id;
int nl_rxtx_length; //Number of bytes sent or received via send() or recv()
struct nlattr *nl_na;   //pointer to netlink attributes structure within the payload 
struct {    //memory for netlink request and response messages - headers are included
    struct nlmsghdr n;
    struct genlmsghdr g;
    char buf[256];
} nl_request_msg, nl_response_msg;
unsigned long nl_sequence_number = 0;
static const char *nl_family_name = "CONTROL_EXMPL";
static const char *nl_group_name = "EXMPL_GRP";
#define GENL_EXMPL_EVENT_VERSION 1



//Implementation of common netlink related methods: open, close, send, receive
int netlink_open(int * fd, unsigned long * seq_init, int protocol, int groups) {
    struct sockaddr_nl nladdr;  //netlink socket address
    socklen_t len;
    
    *fd = socket(AF_NETLINK, SOCK_RAW, protocol);
    if (*fd < 0) {
        return -1;
    }
    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_groups = groups;


    if (bind(*fd, (struct sockaddr *)&nladdr, sizeof(nladdr)) < 0) {
        goto fail;
    }
    len = sizeof(nladdr);
    if (getsockname(*fd, (struct sockaddr *)&nladdr, &len) < 0) {
        goto fail;
    }

    *seq_init = time(NULL);

    return 0;
fail:
    close(*fd);
    return -1;
}

int netlink_send(int fd, unsigned long * seq_num, struct nlmsghdr *n, pid_t peer, int groups) {
    struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK, .nl_pid = peer, .nl_groups = groups };
    n->nlmsg_seq = ++(*seq_num);
    return sendto(fd, n, n->nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(nladdr));
}

int netlink_recv(int fd, struct nlmsghdr *n, pid_t *peer) {
    struct sockaddr_nl nladdr;
    socklen_t len = sizeof(nladdr);
    int ret = recvfrom(fd, n, n->nlmsg_len, 0, (struct sockaddr *)&nladdr, &len);
    *peer = nladdr.nl_pid;
    return ret;
}


int netlink_wait(int fd, unsigned long *seq_num, struct nlmsghdr *n, pid_t peer) {
    int len = n->nlmsg_len;

    for (;;) {
        pid_t sender;
        struct nlmsghdr * h;
        n->nlmsg_len = len;
        int ret = netlink_recv(fd, n, &sender);
        if (ret < 0)  {
            fprintf(stderr, "%s() | Error\n", __func__);
            continue;
        }
        if (sender != peer) {
            fprintf(stderr, "%s() | Error: Source PID mismatch\n", __func__);
            continue;
        }
        for (h = n; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
            if (h->nlmsg_pid != getpid()) {
                fprintf(stderr, "%s() | Error: Destination PID mismatch\n", __func__);
                continue;
            }

            if (h->nlmsg_type == NLMSG_ERROR) {
                fprintf(stderr, "%s() | Error: Message receive error\n", __func__);
                return -1;
            }
            memcpy(n, h, h->nlmsg_len);
            return 0;
        }
    }
}

void netlink_close(int fd) {
    close(fd);
}

//Implementation of common netlink message parsing methods
int netlink_attr_attach(struct nlmsghdr *n, int max, int type, const void *data, int alen) {
    int len = NLA_LENGTH(alen);
    struct nlattr *nla;

    if (NLMSG_ALIGN(n->nlmsg_len) + NLA_ALIGN(len) > max) {
        return -1;
    }

    nla = NLMSG_TAIL(n);
    nla->nla_type = type;
    nla->nla_len = len;
    memcpy(NLA_DATA(nla), data, alen);
    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLA_ALIGN(len);
    return 0;
}

int netlink_attr_parse(struct nlattr *table[], int max, struct nlattr *src, int len) {
    memset(table, 0, sizeof(struct nlattr *) * (max + 1));
    while (NLA_OK(src, len)) {
        if (src->nla_type <= max)
        table[src->nla_type] = src;
        src = NLA_NEXT(src, len);
    }
    return 0;
}

static int netlink_verify_group(struct nlattr * attr, int * group, const char * expected_group_name) {
    const char *name;
    if (attr == NULL) {
        return -1;
    }

    struct nlattr *attrs[CTRL_ATTR_MCAST_GRP_MAX + 1];
    netlink_attr_parse(attrs, CTRL_ATTR_MCAST_GRP_MAX, NLA_DATA(attr), attr->nla_len - NLA_HDRLEN);

    name = NLA_DATA(attrs[CTRL_ATTR_MCAST_GRP_NAME]);
    if (strcmp(name, expected_group_name)) {
        return -1;
    }

    *group = *(__u32 *) (NLA_DATA(attrs[CTRL_ATTR_MCAST_GRP_ID]));
    return 0;
}

int netlink_wait_for_event(int fd, char * received_msg, int max) {
    char buffer[256];
    int i;
    int ret;
    struct nlmsghdr *n = (struct nlmsghdr *) &buffer;
    struct nlmsghdr * h;
    pid_t sender;

    n->nlmsg_len = 256;

    
    ret = netlink_recv(fd, n, &sender);

    if (ret < 0) {
        return -1;
    }

    i = 0;
    for (h = n; NLMSG_OK(h, ret) && i < max; h = NLMSG_NEXT(h, ret), ++i) {
        if (h->nlmsg_type == NLMSG_ERROR) {
            return -1;
        }
        printf("Event received from Kernel: %s\n",(char *)NLA_DATA(((struct nlattr *) GENLMSG_DATA(h))));
    }

    return i;
}

int main(void) {
    int return_code;
//Step 1: Open & Bind the socket. Note that protocol = NETLINK_GENERIC
    return_code = netlink_open(&nl_fd, &nl_sequence_number, NETLINK_GENERIC,0);
    if (return_code < 0) {
        fprintf(stderr, "Error: Socket could not be created\n");
  return -1;
    }
    printf("%s() | Socket Opened\n", __func__);

//Step 2. Resolve the family ID corresponding to the string "EXMPL_EVENT"
    {
        char buffer[256];
        struct nlmsghdr *nlmsg = (struct nlmsghdr *)&buffer;
        struct nlattr *attrs[CTRL_ATTR_MAX + 1];

        nlmsg->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
        nlmsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
        nlmsg->nlmsg_type = GENL_ID_CTRL;
        nlmsg->nlmsg_pid = getpid();

        struct genlmsghdr *ghdr = NLMSG_DATA(nlmsg);
        ghdr->cmd = CTRL_CMD_GETFAMILY;

        netlink_attr_attach(nlmsg, 128, CTRL_ATTR_FAMILY_NAME, nl_family_name, strlen(nl_family_name) + 1);

        if (netlink_send(nl_fd, &nl_sequence_number, nlmsg, 0, 0) < 0) {
            fprintf(stderr, "%s() | Error: sending family ID request message\n", __func__);
            netlink_close(nl_fd);
            return -1;
        }
        printf("%s() | Family Request Sent\n", __func__);

        nlmsg->nlmsg_len = 256;
        //Wait for the response message
        if (netlink_wait(nl_fd, &nl_sequence_number, nlmsg, 0) < 0) {
            fprintf(stderr, "Error: receiving family ID request message\n", __func__);
            netlink_close(nl_fd);
            return -1;
        }

        //Validate response message
        if (nlmsg->nlmsg_type != GENL_ID_CTRL) {
            fprintf(stderr, "%s() | Error: family ID request - invalid message\n", __func__);
            netlink_close(nl_fd);
            return -1;
        }
        ghdr = NLMSG_DATA(nlmsg);
        if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
            fprintf(stderr, "%s() | Error: family ID request - invalid message\n", __func__);
            netlink_close(nl_fd);
            return -1;
        }
        if (nlmsg->nlmsg_len < NLMSG_LENGTH(GENL_HDRLEN)){
            fprintf(stderr, "%s() | Error: family ID request - invalid message\n", __func__);
            netlink_close(nl_fd);
            return -1;
        }


        netlink_attr_parse(attrs, CTRL_ATTR_MAX, NLMSG_DATA(nlmsg) + GENL_HDRLEN, NLMSG_PAYLOAD(nlmsg, GENL_HDRLEN));

        if (attrs[CTRL_ATTR_FAMILY_ID]) {
            nl_family_id = *(__u32 *) (NLA_DATA(attrs[CTRL_ATTR_FAMILY_ID]));
            printf("%s() | Family ID resolved for \"%s\": %d\n", __func__, nl_family_name, nl_family_id);
        }

        if (attrs[CTRL_ATTR_MCAST_GROUPS]) {
            int i;
            struct nlattr *attrs2[GENL_MAX_FAM_GRPS + 1];
            netlink_attr_parse(attrs2, GENL_MAX_FAM_GRPS, NLA_DATA(attrs[CTRL_ATTR_MCAST_GROUPS]), attrs[CTRL_ATTR_MCAST_GROUPS]->nla_len - NLA_HDRLEN);

            for (i = 0; i < GENL_MAX_FAM_GRPS; i++) {
                if (netlink_verify_group(attrs2[i], &nl_group_id, nl_group_name) == 0) {
                    printf("%s() | Group ID resolved for \"%s\": %d\n", __func__, nl_group_name, nl_group_id);
                }
            }
        }

//Step 3. Close and Reopen Socket for specified group
        netlink_close(nl_fd);
        if (netlink_open(&nl_fd, &nl_sequence_number, NETLINK_GENERIC, nl_group_id ? (1 << (nl_group_id - 1)) : 0)) {
            fprintf(stderr, "Error: Socket could not be re-created\n");
            return -1;
        }

        netlink_wait_for_event(nl_fd, NULL, 10);
    }
    netlink_close(nl_fd);
    printf("%s() | Socket Closed\n",__func__);
    return 0;
}

Makefile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
EXTRA_CFLAGS += -I./kern

obj-m += nl_kern.o
nl_kern-objs := kern/main.o kern/genl_event.o

all: kernel-module-uninstall kernel-clean-ring-buffer kernel-build kernel-clean-temporary kernel-module-install user-build
 @tput setaf 3
 @echo "    done: all"
 @tput sgr0
clean: kernel-module-uninstall kernel-clean user-clean
 @tput setaf 3
 @echo "    done: clean"
 @tput sgr0
 
 
 
kernel-build:
 @tput setaf 1
 @echo "    kernel-build"
 @tput sgr0
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
kernel-clean:
 @tput setaf 1
 @echo "    kernel-clean"
 @tput sgr0
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
kernel-clean-temporary:
 @tput setaf 1
 @echo "    kernel-clean-temporary"
 @tput sgr0
 -rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions
 -rm -rf kern/*.o kern/*~ kern/core kern/.depend kern/.*.cmd kern/*.mod.c kern/.tmp_versions
 -rm -rf Module.symvers modules.order
kernel-module-install:
 @tput setaf 1
 @echo "    kernel-module-install"
 @tput sgr0
 -sudo insmod nl_kern.ko
kernel-module-uninstall:
 @tput setaf 1
 @echo "    kernel-module-uninstall"
 @tput sgr0
 -sudo rmmod nl_kern
kernel-clean-ring-buffer:
 @tput setaf 1
 @echo "    kernel-clean-ring-buffer"
 @tput sgr0
 sudo dmesg -c > /dev/null

 
 
user-build:
 @tput setaf 1
 @echo "    user-build"
 @tput sgr0
 gcc user/main.c -o nl_user.out
user-clean:
 @tput setaf 1
 @echo "    user-clean"
 @tput sgr0
 rm -rf *.out

Execution:
The makefile will load the kernel module automatically, so just execute nl_user.out to receive the events. The kernel module posts events every 5 seconds.

Running the code

Sunday, March 2, 2014

Cross compiling programs (userspace) for embedded Linux ARM boards like OLinuXino


This post describes how to use a Ubuntu Desktop/Laptop to compile C programs that can run on embedded ARM boards like iMX233-OLinuXino-MAXI
  1. On your Ubuntu x86 laptop (I used Ubuntu 12.04 x86), visit http://www.mentor.com/embedded-software/codesourcery and click on "Download Lite Edition". Sourcery CodeBench Lite Edition is a free, unsupported version of Sourcery CodeBench, available for select processors.
  2. On the next page, click on "Download the GNU/Linux Release" under the section titled "ARM Processors" 
  3. You will need to enter you contact details on the next page before you can download. The download link will be emailed to you. Click on the link in your mail. It will be something like:
    Download your Lite edition copy of Sourcery CodeBench now: http://go.mentor.com/3kcqy
  4. On the page that opens up, there will be a link to the recommended latest version of the compiler as well as links to the older releases. Click on the link to the recommended release. It will look something like:
    Recommended Release
    This is a fully-validated release.
    Download Sourcery CodeBench Lite 2013.11-33
  5. On the next page, you will be asked to select the package. The package depends on the operating system on which we want to run the compiler. In our case its Ubuntu, so we must select: "IA32 GNU/Linux Installer":
    Package needs to be selected depending on your host computer's operating system
  6. The download will begin. The will be named something like:
    arm-2013.11-33-arm-none-linux-gnueabi.bin
  7. Make sure you have around 1GB of free space on your hard drive before you bein installation of the compiler.
  8. Execute the compiler by issuing the command: ./arm-2013.11-33-arm-none-linux-gnueabi.bin
    (You will need to change the name of the file to that of the one you actually downloaded)
    (Also, you might need to change the permission on the file: sudo chmod +x arm-2013.11-33-arm-none-linux-gnueabi.bin)
  9. The installation wizard will be launched. Just follow the steps and complete the installation.
    Installation Wizard
  10. After completing the installation, reboot the system and issue the command:
    arm-none-linux-gnueabi-gcc -v
    Confirming if the compiler is working properly post installation using the version checking command.
  11. Create a hello world program. Execute the command gedit hello.c and type the following lines of code in it:
    1
    2
    3
    4
    #include
    main() {
        printf("Hello World\n");
    }
    

  12. Compile the program using the command arm-none-linux-gnueabi-gcc hello.c
  13. We need to now transfer the compiled executable a.out to our OLinuXino. We can do this using the scp command if the OLinuXino board is attached to the same local area network as your host PC. We would also remove the SD card from the OLinuXino board and copy the executable to it but doing that repeatedly while developing a program would be too cumbersome and time consuming. So if you were operating out of your home folder on your development PC, issue the following command to copy the file to the OLinuXino which in my case had the IP address 10.10.10.22:
    scp a.out root@10.10.10.22:~
    Using SCP to transfer files to OLinuXino.
    When prompted for password, enter root
  14. Now using PuTTY (works on Windows as well as Linux), connect to OLinuXino over serial port or network. Use login as root and password as root. You can run the program by issuing the command to the board: ./a.out
    Hello World running on OLinuXino.
NOTES:
  1. In case the hello world program doesn't execute, you may need to set the permission to allow the compiled binary file. Issue the command to the board using PuTTY: chmod +x a.out
  2. If you connect your OLinuXino ARM board to your WiFi router, the router will assign it an IP address using DHCP. Now if you want to access the console of your OLinuXino ARM Board, you can either do it over serial port or network. In case of network you will need to figure out the IP address of your ARM board. To do this use the router's web management GUI to figure out what IP address has been assigned to the ARM board. If your router doesn't show you a list of devices and the IP addresses assigned to them, you can use Advanced IP Scanner (works on Windows) or Angry IP Scanner (Java based, works on Windows as well as Linux) to ping the local IP address range and figure out the IP address of OLinuXino. Use username as root and password as root to login.
    Using Angry IP Scanner to figure out the IP address of OLinuXino.
    IP address is required for the SCP/SSH clients (PuTTY, WinSCP, scp command)
    to connect to OLinuXino on the local network.
  3. scp equivalent for Windows is WinSCP. You can use this to transfer files to your OLinuXino ARM board. This is useful if you are running Ubuntu using VMWare or VirtualBox on your Windows Host PC. This way yopu can share a folder between Ubuntu and and use WinSCP - which has a nice GUI - to transfer files to your ARM Board. Use username as root and password as root to login.