天天看点

Embedded Linux S3C2440 - Kernel Module

文章目录

  • ​​Summary​​
  • ​​Device driver interface​​
  • ​​Configure Kernel to support Module and Kernel Debugging​​
  • ​​Create the Driver Module and add to the source tree​​
  • ​​Configure and compile Busybox​​
  • ​​Generate initramfs​​
  • ​​Compile Linux Kernel and Module Foo​​
  • ​​Download new zImage to S3C2440 board​​
  • ​​Load the Foo module​​
  • ​​Create user application to test the foo.ko module​​
  • ​​Reference​​

Summary

Embedded Linux device driver is part of Linux kernel, application will use Linux device driver to interact with Linux Kernel. You can compile the driver into the kernel or you can compile the driver into module, load and use the module.

Application programs have a main() function, where Linux drivers have macro module_init to insert the driver’s initialization routine into kernel’s list of global initialization routines, when kernel initialized, the driver will be initialized. Macro module_exit will unregister driver when driver exits.

Applications linked against the C library, the drivers modules do not link to standard C libraries, so they cannot call standard C functions.

Each module defines a version symbol called __module_kernel_version, and for device management, the kernel uses a pair of major and minor numbers to identify a device. Same device driver use the same major number, different instances of device will use different minor number.

Device driver interface

Device driver interface is in the file_operation() data structure, defined in the file /home/iot/mini2440/linux-3.8.7/include/linux/fs.h

struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        int (*readdir) (struct file *, void *, filldir_t);
        unsigned int (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*aio_fsync) (struct kiocb *, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        int (*show_fdinfo)(struct seq_file *m, struct file *f);
};      

struct file is also defined in <linux/fs.h>.

Configure Kernel to support Module and Kernel Debugging

[root@localhost linux-3.8.7]# pwd
/home/iot/mini2440/linux-3.8.7
[root@localhost linux-3.8.7]# make menuconfig      

Select and enter “Enable loadable module support”, as below,

[*]      

Select below items:

--- Enable loadable module support
[*]   Module unloading
[*]     Forced module unloading
[*]   Module versioning support
[*]   Source checksum for all modules
[ ]      

Exit to the main menu, and enter into “Kernel hacking”,

[*] Compile the kernel with debug info
[*] KGDB: kernel debugger  --->      

Enter into “KGDB: kernel debugger”,

--- KGDB: kernel debugger
<*>   KGDB: use kgdb over the serial console (NEW)
[*]   KGDB: internal test      

Exit and save.

Create the Driver Module and add to the source tree

[root@localhost char]# pwd
/home/iot/mini2440/linux-3.8.7/drivers/char
[root@localhost char]# vim foo.c      

The source code of foo.c is as below,

#ifndef
#define
#endif

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>/* printk() */
#include <linux/slab.h>/* kmalloc() */
#include <linux/fs.h>/* file system */
#include <linux/errno.h>
#include <linux/types.h>/* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>/* O_ACCMODE */
#include <linux/ioctl.h>
#include <linux/uaccess.h>/* COPY_TO_USER */
#include <asm/system.h>/* cli(), *_flags */

#define
#define
#define
#define

char drv_buf[MAX_BUF_LEN];
int WRT_LEN = 0;

/* reverse data in buffer */
void do_write(void)
{
        int i;
        int len = WRT_LEN;
        char temp;
        for(i = 0; i<(len>>1); i++, len--)
        {
                temp = drv_buf[len-1];
                drv_buf[len-1] = drv_buf[i];
                drv_buf[i] = temp;
        }

}

ssize_t foo_write(struct file *filp, const char *buffer, size_t count, loff_t *f_pos)
{
        if(count > MAX_BUF_LEN)
                count = MAX_BUF_LEN;

        copy_from_user(drv_buf, buffer, count);
        WRT_LEN = count;
        printk("user write data to driver.\n");
        do_write();
        return count;
}

/********************************************************************/
ssize_t foo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
        if(count >MAX_BUF_LEN)
                count = MAX_BUF_LEN;
        copy_to_user(buffer, drv_buf, count);
        printk("user read data from driver.\n");
        return count;
}

/**********************************************************************/
long foo_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
        switch(cmd)
        {
                case 1:
                        printk("running ioctl command 1\n");
                        break;
                case 2:
                        printk("running ioctl command 2\n");
                        break;
                default:
                        printk("Error ioctl command.\n");
                        break;
        }

        return 0;
}

/***********************************************************************/
int foo_open(struct inode *inode, struct file *filp)
{
        sprintf(drv_buf, "device open success!.\n");
        printk("device open success!.\n");
        return 0;
}

/************************************************************************/
int foo_release(struct inode *inode, struct file *filp)
{
        printk("Device release.\n");
        return 0;
}

/***********************************************************************/
static struct file_operations foo_fops =
{
        .write  = foo_write,
        .read   = foo_read,
        .unlocked_ioctl = foo_ioctl,
        .open   = foo_open,
        .release=foo_release,
};


/***************************************************************************/

static int __init foo_init(void)
{

        int result;

        result = register_chrdev(FOO_MAJOR, "scull", &foo_fops);
        if(result<0)
                return result;

        printk("%s initialized.\n", DEVICE_NAME);
        return 0;
}

static void __exit foo_exit(void)
{
        unregister_chrdev(FOO_MAJOR, "foo");
}


module_init(foo_init);
module_exit(foo_exit);


MODULE_LICENSE("GPL");      

Edit the Makefile under the character driver directory,

[root@localhost char]# pwd
/home/iot/mini2440/linux-3.8.7/drivers/char
[root@localhost char]# vim Makefile      

Add below line,

obj-m                           +=      

Configure and compile Busybox

We need to include lsmod (list module), insmod (install module), mknod (create device file) and rmmod (uninstall module) into Busybox.

Start Busybox configuration editor.

[root@localhost busybox-1.19.4]# pwd
/home/iot/mini2440/busybox-1.19.4
[root@localhost busybox-1.19.4]# make menuconfig      

Enter “Linux Module Utilities”, and select below items,

[*]   insmod
[*]   rmmod
[*]   lsmod
[*]   modprobe
[*]      

Exit to main and enter “Coreutils”, select mknod,

[*] mknod      

Save and exit.

Compile and install Busybox,

[root@localhost busybox-1.19.4]# make clean
[root@localhost busybox-1.19.4]# make
[root@localhost busybox-1.19.4]# make install      

Generate initramfs

Generate new rootfs, and include the newly compiled Busybox binary into rootfs,

[root@localhost rootfilesystem]# pwd
/home/iot/mini2440/rootfilesystem
[root@localhost rootfilesystem]# ./create_rootfs_bash.sh
------Create rootfs --------
/home/iot/mini2440/rootfilesystem/rootfs /home/iot/mini2440/rootfilesystem
--------Create root,dev....----------
---------Copy from busybox, rootfs-base, libs -----------
---------make node dev/console dev/null-----------------
mknod: ‘/dev/ptmx’: File exists
14092 blocks
/home/iot/mini2440/rootfilesystem
[root@localhost rootfilesystem]#
[root@localhost rootfilesystem]# ll initramfs.cpio      

Compile Linux Kernel and Module Foo

After generate initramfs.cpio, now can build the kernel and newly defined module Foo.

[root@localhost linux-3.8.7]# pwd
/home/iot/mini2440/linux-3.8.7
[root@localhost linux-3.8.7]# make clean
[root@localhost linux-3.8.7]# make
[root@localhost linux-3.8.7]# ll ./arch/arm/boot/zImage      

Compile the module,

[root@localhost linux-3.8.7]# make modules
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
make[1]: 'include/generated/mach-types.h' is up to date.
  CALL    scripts/checksyscalls.sh
  Building modules, stage 2.
  MODPOST 2 modules
[root@localhost linux-3.8.7]# ls -l drivers/char/foo.ko
-rw-r--r--. 1 root root 69081 May  8 13:26 drivers/char/foo.ko
[root@localhost linux-3.8.7]#      

Copy foo.ko to TFTP /var/lib/tftpboot directory, will transfer to S3C2440 board later.

[root@localhost linux-3.8.7]# cp /home/iot/mini2440/linux-3.8.7/drivers/char/foo.ko /var/lib/tftpboot/      

Download new zImage to S3C2440 board

From minicom:

##### FriendlyARM BIOS 2.0 for 2440 #####
[x] format NAND FLASH for Linux
[v] Download vivi 
[k] Download linux kernel 
[y] Download root_yaffs image 
[a] Absolute User Application
[n] Download Nboot for WinCE 
[l] Download WinCE boot-logo
[w] Download WinCE NK.bin 
[d] Download & Run 
[z] Download zImage into RAM 
[g] Boot linux from RAM 
[f] Format the nand flash 
[b] Boot the system 
[s] Set the boot parameters 
[u] Backup NAND Flash to HOST through USB(upload) 
[r] Restore NAND Flash from HOST through USB 
[q] Goto shell of vivi 
[i] Version: 1026-2K
Enter your selection: k
USB host is connected. Waiting a download.

Now, Downloading [ADDRESS:30000000h,TOTAL:5496682]
RECEIVED FILE SIZE: 5496682 (18KB/S, 286S)
Downloaded file at 0x30000000, size = 5496672 bytes
Found block size = 0x00540000
Erasing...    ... done
Writing...    ... done      

From host,

[root@localhost mini2440]# ./download_image.sh
csum = 0x8e86
send_file: addr = 0x33f80000, len =      

Key in “b” to boot up S3C2440 board, and use telnet to login,

[root@localhost ~]# telnet 192.168.0.11
Trying 192.168.0.11...
Connected to 192.168.0.11.
Escape character is '^]'.

mini2440 login: root
[root@mini2440 /root]#
[root@mini2440 /root]# ifconfig
eth0      Link encap:Ethernet  HWaddr 08:90:90:90:90:90
          inet addr:192.168.0.11  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:60 errors:0 dropped:0 overruns:0 frame:0
          TX packets:31 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:5921 (5.7 KiB)  TX bytes:2279 (2.2 KiB)
          Interrupt:51 Base address:0xc300

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)      

Load the Foo module

Transfer the foo.ko from host to S3C2440 board and load it into the kernel.

Start the TFTP service on the host,

[root@localhost ~]# systemctl start tftp.socket
[root@localhost ~]# systemctl start tftp.service      

Use TFTP on the S3C2440 board to transfer foo.ko from host,

[root@mini2440 /sbin]# pwd
/sbin
[root@mini2440 /sbin]# tftp -g -r foo.ko 192.168.0.1
foo.ko               100% |********************************************************************************************************************************************| 69081   0:00:00 ETA
[root@mini2440 /sbin]# ls foo.ko
foo.ko
[root@mini2440 /sbin]# ls -l foo.ko
-rw-r--r--    1 root     root         69081 Jan  1 00:25 foo.ko
[root@mini2440 /sbin]# chmod +x foo.ko
[root@mini2440 /sbin]# ls -l foo.ko
-rwxr-xr-x    1 root     root         69081 Jan  1 00:25 foo.ko
[root@mini2440 /sbin]#      

We will do Insert the foo.ko module, list all the modules in the system, and uninstall module foo.ko test as below,

[root@mini2440 /sbin]# insmod foo.ko
[root@mini2440 /sbin]# lsmod
    Not tainted
foo 2834 0 - Live 0xbf000000
[root@mini2440 /sbin]# rmmod foo.ko
[root@mini2440 /sbin]# lsmod
    Not tainted
[root@mini2440 /sbin]# insmod foo.ko
[root@mini2440 /sbin]# lsmod
    Not tainted
foo 2834 0 - Live 0xbf004000
[root@mini2440 /sbin]#      

Create user application to test the foo.ko module

Open the application source code,

[root@localhost moduletest]# pwd
/home/iot/mini2440/myapp/moduletest
[root@localhost moduletest]# vim footest.c      

The application write string “Hello, there” into kernel module, Kernel module will use void do_write(void) to reverse the string, then application will read out and print the new string “ereht, olleH”.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#define

int main()
{
        int fd;
        unsigned char buf[100] = "Hello, there";

        fd = open(DEV_NAME,O_RDWR);
        if(fd==-1)
        {
                printf("Cannot open device %s.\n",DEV_NAME);
                return -1;
        }

        write(fd, buf, strlen(buf));
        printf("write %s.\n",buf);
        read(fd, buf, sizeof(buf));
        printf("read %s.\n",buf);

        return 0;
}      

The Makefile is as below,

BROUTPUT = /home/iot/mini2440/buildroot-2013.02/output
INCPATH = -I$(BROUTPUT)/staging/usr/include -I$(BROUTPUT)/staging/include
CC = arm-linux-gcc
LDFLAGS = -L$(BROUTPUT)/target/lib

OBS = footest.o

footest: $(OBS)
        $(CC) $(LDFLAGS) $(OBS) -o footest
$(OBS):footest.c
        $(CC) $(LDFLAGS)      

Compile footest.c

[root@localhost moduletest]# make
arm-linux-gcc  -L/home/iot/mini2440/buildroot-2013.02/output/target/lib -c footest.c
arm-linux-gcc  -L/home/iot/mini2440/buildroot-2013.02/output/target/lib footest.o  -o footest
[root@localhost moduletest]# ll      

Reduce the size of footest, strip away the debugging symbols, we found the size almost was reduced to half after strip.

[root@localhost moduletest]# arm-linux-strip footest
[root@localhost moduletest]# ll footest
-rwxr-xr-x. 1 root root 3424 May  8 15:50 footest
[root@localhost moduletest]#
[root@localhost moduletest]# cp ./footest /var/lib/tftpboot/
[root@localhost moduletest]#      

Now test the application footest,

[root@mini2440 /sbin]# tftp -g -r footest 192.168.0.1
footest              100% |********************************************************************************************************************************************|  6122   0:00:00 ETA
[root@mini2440 /sbin]# chmod 777 footest
[root@mini2440 /sbin]# lsmod
    Not tainted
foo 2834 0 - Live 0xbf004000
[root@mini2440 /sbin]# mknod /dev/foo c 229 0
[root@mini2440 /sbin]# ls -l /dev/foo
crw-r--r--    1 root     root      229,   0 Jan  1 01:50 /dev/foo
[root@mini2440 /sbin]# ./footest
write Hello, there.
read ereho, tlleHsuccess!.
.
[root@mini2440 /sbin]# ./footest
write Hello, there.
read ereho, tlleHsuccess!.
.
[root@mini2440 /sbin]# ./footest
write Hello, there.
read ereho, tlleHsuccess!.
.      

Reference

继续阅读