文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>An Introduction to Embedded Linux Development

An Introduction to Embedded Linux Development

时间:2006-05-31  来源:zzzppp

An Introduction to Embedded Linux Development, Part 1

By Richard A. Sevenich on Tue, 2004-10-19 01:00. Embedded Part 1 in a series on embedded development explains the purpose of the project and outlines hardware options.

Over the last decade, a sea change has occurred in the world of embedded systems, driven by the appearance of sophisticated new products such as PDAs and cell phones and by the continual increase in the amount of resources that can be packed into a small form factor. The traditional small, narrowly focused embedded systems retain their significant presence, but these newer arrivals can capitalize on embedding a full-featured operating system. The operating system provides a wide selection of capabilities from which to choose. For example, wireless Internet connectivity and a GUI already are built in. Only a few years ago, embedded operating systems typically were found only at the high end of the embedded system spectrum.

Linux, available for many architectures, is an obvious candidate for an embedded system, and it already is being used widely in this area. Its open nature makes it particularly attractive to developers. Development tool suites have begun to appear in response to the perceived need, although one can work without such luxury and employ less integrated tools already available in Linux. New embedded systems companies using Linux have opened for business, and various older embedded systems companies have added Linux to their product line.

Assuming the technology sector of the economy continues to recover, many opportunities for software engineers and programmers should develop in this general area of embedded systems. Many of these potential developers have worked with desktop systems, however, but not with embedded systems. How high is the barrier to making the switch? It used to be that each embedded system developer needed to be somewhat of a hardware guru. However, with a resident operating system, an embedded system project can get by with perhaps one hardware guy and then add application developers as needed. In that scenario, the hardware knowledge is not a barrier, because the hardware guy becomes a shared resource--and others pick up whatever superficial hardware knowledge is efficient for their tasks. In short, in-depth hardware expertise is not a barrier. However, some familiarity with the development environment is necessary. The intent of this article series is to provide enough of the basics to get a reader started.

Choosing a System

If we're going to learn about an embedded Linux development environment, it's best to do so as a hands-on process. A careful reading of these articles without the hands-on component still has some value, though. The typical reader probably has a Linux box to use as a development work station but does not have an embedded target. Let's discuss options for incorporating a target.

Option 1: Stay within the environment of your Linux box and choose a target such as a floppy disk, a CD-ROM, a USB pen drive or something similar. This option offers some prototypical features of what we would like to learn, but it is only a small start.

Option 2: Choose a PDA known to have the capability to support Linux. The Sharp Zaurus SL-5500 comes to mind. It comes with Linux installed, has an active developer community and offers a rather full set of features. Because it is not the most recent Zaurus to hit the market, it can be found at significantly discounted prices. This target is much more prototypical than those suggested in Option 1. However, it still falls short of what we ultimately want. In particular, if we choose this option, we won't have the opportunity to learn much about the bootloader, an important topic.

Option 3: Find a relatively affordable single-board computer (SBC) known to support Linux. This gives us a truly typical target. If we're lucky, we'll find a vendor willing to work with us. It should be noted that I have worked with embedded systems but am not a hardware guru. I might need some input from such a person as this article series evolves.

We are going to go with Option 3. The two other options also offer significant value, however. In fact, the author has a beta version of a course that explores these two options, currently located here. Material based on this series of articles eventually will be added to that on-line course.

I gave a presentation on the beta version of the aforementioned course at the Real World Linux Conference 2004 in Toronto. There, I met Iain Galloway from Engineering Technologies Canada Ltd.. Our discussions led me to choose an SBC from that company to meet Option 3, above. I chose the LBOX with uClinux, which features a Motorola Coldfire MCF5272 processor, Flash memory, a serial port, a fiber port, up to three 10/100 Ethernet ports and more. It's ready to go without first needing to build it into something else. That is, you can power it up with any supply in the 5-12 volt range. The LBOX also has a full expansion port, allowing easy experimentation.

It should be noted that I have no financial connection to Engineering Technologies Canada. The LBOX simply looked like it was a suitable product for which I could receive appropriate help for any glitches encountered along the way. Further, Engineering Technologies Canada is willing to work with students at my institution. In particular, the company is willing to donate some new add-on hardware in exchange for open-source code that would enhance its value--a good deal for both parties.

Part 2 of this series will describe the complete hardware setup comprising our development environment with LBOX as the target and then show how to bring it to life.

An Introduction to Embedded Linux Development, Part 2

By Richard Sevenich on Fri, 2004-11-19 02:00. Embedded Part 2 in a series on embedded development explains how to establish serial communication between an LBox and a workstation, compile tool chains and write and run a simple program.

In Part 1 of this series, we indicated that we would use, as our SBC, the LBox with uClinux from Engineering Technologies Canada Ltd.. Recall that it features the Motorola Coldfire MCF5272 processor, Flash memory, a serial port, a fiber port and up to three 10/100 Ethernet ports. It's ready to go without needing to be built into something else--simply power it up with any suitable power supply in the 5-12 volt range. Although we are using a specific SBC for this project, the activities we undertake here correspond to similar activities on any typical SBC. That said, significant specific differences exist at the more detailed level from one SBC to another.

I purchased about 12 of these systems for our computer science department. If not purchased in quantity, the basic board goes for about $250. Then, you add whatever else you need.

Following along with this series while using an actual LBox SBC would be optimal. Nevertheless, I have organized this series of articles so a reader can glean useful information without purchasing the board. Yet another option would be to use some other SBC and parallel our activities.

To avoid putting forth too much nitty gritty detail here, I refer you to information posted in the FAQ section of the Engineering Technologies Web site.

The goals and subsequent sections for the current article are:

  • Power up the LBox.

  • Establish serial communication between LBox and workstation, including what to do if your workstation has no serial port.

  • Connect via Ethernet.

  • Install the cross compiling tool chains.

  • Carry out NFS mounting.

  • Write a program for the LBox and run it.

The last two sections are quite general and apply to most embedded Linux systems.

Power Up the LBox

My particular setup consists of:

  • the LBox SBC (from Engtech)

  • a power supply (from Engtech)

  • a serial header-to-DB9 cable (from Engtech)

  • a CD with all needed software (from Engtech)

  • my laptop (the workstation) with Libranet 2.81, updated to the 2.4.27 kernel

  • a Belkin F5U409 USB-to-DB9 adapter because my laptop has no external RS-232 DB9 port but does have USB ports

I configured the laptop to use the widely available, tried and true Minicom terminal emulator for the serial connection. It comes with most Linux distributions, and for connecting to SBCs with serial ports, Minicom is a common choice. The Belkin F5U409 uses the mct_u232 driver, which is available with the kernel source. It didn't work properly for me with the 2.4.24 kernel, however, hence the update to 2.4.27.

Before applying power, I connected an Ethernet cable and the serial cable. The Ethernet ports provided on the LBox, when populated, have the expected RJ45 female sockets. The serial port header allows connection of the serial header-to-DB9 cable, which I connected to my laptop via the Belkin F5U409. At this point, everything seemed ready, so I applied power by plugging in the power adapter.

Establish Serial Communication Between LBox and Workstation

I used Minicom on my laptop to establish the serial connection to the LBox. The details can be found in this FAQ. Once Minicom was configured properly, I reset the LBox using the reset button, located near the board edge, kitty corner from the serial port header. Then, the Minicom window on my laptop spewed out the LBox startup messages. These could be useful subsequently, so I pasted them to an editor on the laptop for subsequent printout.

When the startup messages were finished, I was presented with the command prompt. I then investigated the system to determine what's available. For example, examining /bin showed both Busybox and Tinylogin were present. That suggested a small project to update Busybox to the recent 1.0 version, which has incorporated the Tinylogin functionality. Other things worth noting:

  • the result from uname -a was


    uClinux lbox 2.4.20-uc0 #176 Mon Aug 16 11:25:42 ADT 2004 m68knommu unknown
  • the result from df was


    Filesystem 1k-blocks Used Available Use% Mounted on
    rootfs 1113 1113 0 100% /
    /dev/root 1113 1113 0 100% /
    /dev/ram1 115 7 108 6% /var
    /dev/mtdblock3 3008 336 2672 11% /etc/config
  • from ls /bin, one could see that a version of Vi was present, and so on.

Connect via Ethernet

If connected to the Internet when booted, the LBox dhcpcd daemon should negotiate an IP address. If this doesn't occur--check by entering ifconfig in the Minicom window--we either can assign it manually or try again from the LBox command line by entering dhcpcd -p -a eth0 &.

The LBox supports telnet, so I now could telnet into the LBox from a shell on the laptop--if I configured the laptop for telnet. As shipped, the LBox does not support SSH. However, it does have an HTTP daemon, so I could access it with a browser. A Web page already was set up on the LBox (see Figure 1) that allows one to turn on, turn off or toggle an LED on the LBox board; it also provides links to the source code. This nicely demonstrated that you could interact with the LBox through a Web interface.

Figure 1. LBox Web Page

Install the Cross Compilation Tool Chains

For any SBC, the vendor either supplies the tool chains or tell users from where to download them. Explicit directions should be provided and followed for installing the chains. These directions vary from vendor to vendor.

In our case, the tool chains were available on the CD provided with the LBox. Other software packages are included on the CD, including the uClinux source hierarchy, but we'll deal with those later and keep to the task at hand. The CD also provides an INSTALL file containing directions for installing the tool chains. These are quite simple. In particular, there is a shell script to execute that carries out the installation.

Carry Out NFS Mounting

This subsection is not particularly LBox specific and is of general interest. Compared to a workstation, the LBox has limited resources--no hard drive. It is convenient for the developer to create a workspace on the workstation hard drive and then (NFS) mount that workspace as part of the LBox hierarchy. Once mounted, this workspace can be accessed from both the workstation and the LBox. Because the workspace exists on the workstation, it is easy to create periodic archival backups, for example, on CD-ROM. And because the workspace also is mounted on the LBox, we can test code developed in the workspace by running it on the LBox without using the limited file storage capabilities of the LBox.

There are two methods we can use to create the workspace. The first presumes that both the LBox and workstation are connected to the Internet; we then can mount a workstation directory on the LBox. The second method presumes that the LBox and workstation are not connected to the Internet but are connected directly to each other with an Ethernet cable. We discuss the second method here. The first is discussed (along with telnet connection) in this FAQ.

Connecting from the LBOX directly to the workstation by way of an Ethernet cable isolates us from the wider Internet, which might be of interest for security reasons or when we don't have access to the Internet, for example, while on the road in a remote setting. That's what we describe here.

Our assumptions here are that the NFS server is up and running on the workstation, the LBox is connected directly to the workstation with an Ethernet cable and the LBox is connected to the workstation with a serial cable. In my case, some firewall rules got in the way, so I turned the firewall off--the system was isolated from the Internet anyway.

We proceeded as follows:

  • Reset the LBox

  • Start Minicom on the workstation so the Minicom window emulates a terminal for the LBox

  • Configure eth0 for the LBox (in the Minicom window) with these commands:


    ifconfig eth0 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255
  • Configure eth0 for the workstation using a workstation shell, as follows:


    ifconfig eth0 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255
  • Test each by pinging the other--from a workstation shell try ping 192.168.1.1 and from the Minicom window try ping 192.168.1.2

  • Assuming all worked fine, now establish the file on the workstation you want to mount on the LBox; that is, on the workstation create a new directory for your LBox stuff; let's say it's /home/fredsmith/lbox_stuff

  • Create a file /etc/exports on your workstation containing the line /home/fredsmith/lbox_stuff *(rw,insecure). Take care not to include spaces in the parenthetical options list.

  • So that the new /etc/exports is scanned, restart the NFS server daemon on your workstation. For my Debian-based workstation, I would enter /etc/init.d/nfs-kernel-server restart. Some years ago, I did run into Red Hat-based systems on which I needed to stop and then start the daemon because restart wasn't quite equivalent.

  • Use the Minicom window to make a mount point: mkdir /var/nfs.

  • Now try the NFS mount command from the Minicom window as follows:


    mount -t nfs -o nolock 192.168.1.2:/home/fredsmith/lbox_stuff /var/nfs
  • Investigate whether the mount was successful by examining the contents of /var/nfs in the Minicom window.

Write a Program for the LBox and Run It

With the tool chains available and with the NFS mount still active, I can work in the NFS mounted directory, write a standalone program and cross compile it. I then can run it with Minicom while it resides in the NFS mounted directory.

Let's try that. Start by working on the workstation as follows:

  • Using a shell window on the workstation, enter a simple program in the /home/fredsmith/lbox_stuff directory:


    /* bye.c
    */
    int main() {
    printf("Lbox says goodbye.\n");
    exit(0);
    }
  • Compile the program with the cross compiler:


    m68k-elf-gcc -m5200 -msep-data -Wl,-elf2flt -o bye bye.c -lc
  • Make sure the LBox can execute the result by entering chmod 755 bye.

Next, we work on the LBox by way of the Minicom window.

  • Navigate in the NFS mounted directory to wherever the executable is. For example, if it's at the top of the mounted hierarchy, enter cd /var/nfs.

  • Enter ls -l to assure that you are in the directory containing bye, and check that it's executable.

  • Execute the program by entering ./bye, and you should see as output: Lbox says goodbye.

In the next installment of this series, we will recompile an LBox kernel and install it on the LBox, along with a root filesystem. This gives you the option to tailor the kernel and root filesystem for your specific needs.


An Introduction to Embedded Linux Development, Part 3

By Richard Sevenich and Ben Anderson on Thu, 2004-12-30 02:00. Embedded Part 3 explores different scenarios for updating and/or replacing the root filesystem, the kernel image or even the bootloader on our embedded development system.

At the end of the second article in this series, which outlines a step-by-step process for embedded Linux development, we had the LBox up and running, ready to use for application development. On some occasions, however, the developer needs to modify the underlying system. Any Linux system can be considered to consist of three parts: the root filesystem, the kernel image and the bootloader (the BIOS plays this role in the desktop system). Typically, we leave the bootloader alone--at least we hope to--and modify the root filesystem, the kernel image or both.

With this need for modification in mind, Part 3 of this article series consists of four sections:

  • Memory organization and filesystem layout

  • Replacing the kernel and root filesystem

  • Replacing the JFFS2 filesystem

  • Replacing the bootloader

Some of these activities can be dangerous and may result in a non-functional system. Recovery from that state will be discussed in the fourth and final installment of this introductory sequence.

We continue to work with the SBC (single-board computer) we used in Parts 1 and 2 of this series, the LBox from Engineering Technologies Canada Ltd. Nevertheless, much of the material has broader applications and should be useful for a variety of target platforms.

Memory Organization and Filesystem Layout

To replace the root filesystem and/or kernel image intelligently, it is useful both to understand the boot-up process in a general way and to have a grasp of memory organization. Useful details can be obtained from the SBC vendor; if Linux is resident, the operating system itself can be a source of information. In particular, when connected to the LBox we found these Linux commands to be informative: dmesg, df, cat /proc/mtd, cat /proc/meminfo and cat /proc/mounts.

The memory and filesystem layout differ in their details, depending on the SBC. Using the aforementioned commands and information from the vendor, the following information emerges. Our LBox memory consists of 4MB of Flash memory and 8MB of SDRAM. At the start of the boot process, the RAM is not yet deployed and the Flash organization is as follows:

Bootloader (colilo)

  • device is /dev/mtd0

  • space provided = 0x0000E000

  • location: 0x00000000 -> 0x0000E000

Config

  • device is /dev/mtd1

  • space provided = 0x00002000

  • location: 0x0000E000 -> 0x00010000

Image (root filesystem and kernel image, both compressed)

  • device is /dev/mtd2

  • space provided = 0x00100000

  • location: 0x00010000 -> 0x00110000

JFSS2 (Journaling filesystem to be mounted during boot)

  • device is /dev/mtd3

  • space provided = 0x002F0000

  • location: 0x00110000 -> 0x00400000

At boot, the typical SBC with Linux resident has a bootloader that finds the compressed kernel image and root filesystem in Flash, decompresses them into RAM and transfers control to the decompressed kernel in RAM. The kernel then completes the initialization of the system.

The LBox provides a specific example of the foregoing. When it powers up, the bootloader, in Flash, begins executing. It decompresses the root filesystem and the kernel image and moves them into the 8MB of SDRAM. In addition, part of the SDRAM is used for the root filesystem, which is set up as a ROM filesystem. Then, a ramdisk, /dev/ram1, is mounted on /var. Finally, the JFFS2 filesystem, /dev/mtdblock3, is mounted on /etc/config. As a result, the root filesystem, originally a totally ROM filesystem, has been modified by the mount events to include read/write areas--a ramdisk and a JFFS2 area. Things to notice:

  • The ROM filesystem intrinsically is read-only and its contents will be reconstituted at the next boot sequence.

  • The ramdisk is volatile and its contents will be lost at power down.

  • The JFFS2 filesystem is on Flash and its contents will persist through power-down events, so that it emulates a hard drive.

The filesystem organization of a pristine system, after boot, looks like this:

  • root file system (romfs) in RAM, read-only

    /dev/root mounted on /

    size = 1113 kbytes

  • ramdisk (ext2 file system) in RAM, read/write

    /dev/ram1 mounted on /var

    size = 115 kbytes

  • emulated hard drive (JFFS2 filesystem) in Flash, read/write

    /dev/mtdblock3 mounted on /etc/config

    size = 3008 kbytes

Replacing the Kernel and Root Filesystem

Various reasons exist for changing the kernel and/or root filesystem. Compiling in new hardware support, updating existing driver modules or adding new capabilities to the root filesystem are examples.

As you may have noticed above, before boot the kernel image and root filesystem, both compressed, are on the same partition, /dev/mtd2. Before shipping, the vendor placed the kernel image and the romfs for the root filesystem into the same compressed file, image.gz, which then was flashed into the /dev/mtd2 partition in Flash memory. We retain this approach, so that updating either the kernel image or the root filesystem involves reflashing both.

Let's look at how these software components are put on the LBox. To update software, the LBox makes use of a uClinux utility called netflash. In order to use this utility, a kernel must be running on the LBox. Further, the workstation must have the tftp (trivial FTP) daemon running, because netflash relies on that. It is outside the scope of this article to cover the installation of tftp, but most distributions have some sort of package management tool that allows one to install tftp, which is part of the larger netkit package. As of this writing, the current version is netkit-tftp-0.17, which can be found here. In recent years, security concerns have led us to avoid FTP and its variants in favor of sftp. You also may need to change firewall rules or turn off the firewall on the workstation. Consequently, we strongly recommend prudent paranoia; that is, tftp should be used when you are networked to the LBox only, not to the Internet itself. (See the section called "NFS Mounting" in Part 2 of this series for more details.)

The netflash utility allows us to transfer a file from the host computer by way of the network connection and save it in the flash memory on the target device. The command is entered on the LBox. For example, to retrieve a synopsis of its parameters and options, enter netflash -h to get a help screen.

One option we use is the -b option, which prevents automatic rebooting after the flashing operation is complete; otherwise, the LBox reboots when netflash completes. The -b option therefore provides a layer of protection should we suddenly realize the software component we just flashed was the wrong one or one that doesn't work. We also note that the netflash utility complains if you try to load a file larger than the partition you are trying to flash. The failure messages are not always informative, however. For example, it merely may provide a usage synopsis when it fails for some underlying cause, even though the syntax was okay.

So, from where do we get the image.gz file? This is accomplished on the workstation, where we use the tool chains that come with LBox. Recall that these use uClinux for kernel and root filesystem configuration. The uClinux directory hierarchy descends from the uClinux-dist directory node we installed on the workstation in Part 2 of this series. We assume that is our working directory when giving relative pathnames below. As necessary, we also use absolute pathnames.

To change the kernel and/or root filesystem, we must run make menuconfig or make xconfig as root from the uClinux-dist directory. This command presents a high-level GUI from which we can choose "Target Platform Selection" to open a new lower-level GUI. The lower-level GUI then allows us to configure the kernel, the vendor settings or both. The GUI at this level is somewhat non-intuitive. In particular, after making a selection, the Next button does not become active. Instead, after making our selection, we must close the lower-level GUI, returning to the original high-level GUI. We then choose the Save and Exit button, whereupon we are presented with a new GUI for making kernel and/or vendor setting modifications.

We suggest that for the first time through, no changes be made. Then, after the make xconfig step has been completed, enter make dep and make all. These result in writing the new image.gz file to both the images subdirectory and the tftpboot directory. The latter is accessed by the LBox using netflash.

When we are sure that all has been done correctly, we can enter the following command on the LBox--for example, by way of minicom--to update the kernel image:

netflash -knrb /dev/mtd2 tftp_server /tftpboot/image.gz

where tftp_server is replaced with the IP address of your workstation.

Recall that the /dev/mtd2 device corresponds to the joint kernel/root filesystem partition. Also, as noted above, we use the -b option to prevent automatic reboot after completing the update of the flash memory.

If you have trouble using the netflash utility, here are some things to check:

  • Make sure the file you are flashing actually exists in /tftpboot. Then, confirm that the /tftpboot directory permissions are 777 and the image.gz file permissions are 666.

  • Check that the tftp daemon is running. On the workstation, enter the command netstat -a | grep tftp.

  • Ensure that the network interface between the LBox and host computer is working properly.

  • Ensure that no firewall rules are preventing the file transfer by tftp.

Replacing the JFFS2 Filesystem

Of course, we can modify the JFFS2 filesystem while the LBox is running--after all, it acts as our hard drive. If we want to make a dramatic overhaul, however, we could reorganize and reflash the whole thing. Doing exactly that is the subject of this section. We again would use netflash to flash the whole /dev/mtd3 partition with a JFFS2 image. Doing so leaves the JFFS2 flash partition metadata out of sync with filesystem information--for example, inode data--that the kernel brought into memory earlier. Therefore, rebooting after the netflash operation seems okay is recommended.

A few considerations must be made when getting the user filesystem ready to netflash to the LBox. First, we need to construct a directory on the workstation that contains what ultimately will be the contents of the JFFS2 directory on the LBox. This directory already exists on the CD provided by the vendor, and we copied it over to the workstation earlier. Its name is etc_config/. We suggest that it be left as is for the trial run that follows.

Next, we use the following command to create an output file, calling it config.jffs2:

mkfs.jffs2 -b --pad=0x002f0000 --root=etc_config/ --output=config.jffs2

The --root option specifies that the etc_config/ directory is used to provide the intended contents for the JFFS2 filesystem. The --output option identifies the name, config.jffs2, for the file that will be created containing the desired JFFS2 filesystem. The --pad option pads the result out to the same size as the /dev/mtd3 partition on the flash memory. The -b option is needed to create the filesystem as big-endian, appropriate for the embedded Motorola architecture.

After the filesystem is ready, we move it into the /tftpboot directory on the host computer. We then run the following command from the LBox, by way of minicom, to flash the filesystem:

netflash -knrb /dev/mtd3 tftp_server /tftpboot/config.jffs2

where tftp_server is replaced by the IP address of your workstation.

Notice that the device is /dev/mtd3 and that this corresponds to the memory location for the JFFS2 filesystem. After rebooting the LBox you should see the new filesystem residing at /etc/config on the LBox.

Replacing the Bootloader

The bootloader is the software component least likely to require an update. However, there are times when you may need to do this. Perhaps you need to compile in new features, or maybe a newer version of the bootloader is needed. As with the kernel/root filesystem update, updating the bootloader also can be risky. Now, with the new bootloader binary in the tftp directory, we can update the LBox by executing the following command from the LBox.

netflash -bknr /dev/mtd0 tftp_server /tftpboot/colilo.bin

where tftp_server again is replaced by the IP address of your workstation.

In this case, we put the binary file into the bootloader partition, which is /dev/mtd0. We again used the -b option to give us one more chance to recover should we suddenly realize that the colilo.bin file has a problem. That is, we still could repeat the above steps with the correct file. If everything goes well, we can reboot the LBox and ultimately be presented with the command prompt.

Preview of the Next Article

Several of the netflash/reboot iterations discussed in the current article can leave the LBox in a non-functional state if the transferred files are not correct. In particular, if the Linux image is bad, netflash will not be available to access the workstation's tftp server; if the colilo binary is bad, we can't even start the boot process. To recover, we can use a Background Debug Mode (BDM) Coldfire cable to access the BDM of the Motorola Coldfire microcontroller. The next and final installment of this series will explain how to conduct such a recovery operation.

Resources

"An Introduction to Embedded Linux Development, Part 2"

"An Introduction to Embedded Linux Development, Part 1"

"Power Trio: Three Diverse Embedded Linux SBCs"

"Flash Filesystems for Embedded Linux Systems"

Richard Sevenich is a Professor of Computer Science at Eastern Washington University in Cheney, WA.

Ben Anderson is an upper division Computer Science major at Eastern Washington University. His current interests are hardware and embedded systems, particularly those which use Linux.


相关阅读 更多 +
排行榜 更多 +
单挑幸存者安卓版

单挑幸存者安卓版

飞行射击 下载
决战战地指挥官

决战战地指挥官

飞行射击 下载
鸡仔幸存者最新版

鸡仔幸存者最新版

飞行射击 下载