2016年1月14日 星期四

Linux:驅動程式入門 (A short introduction to Linux Device Driver)

所使用的作業系統為Ubuntu 14.04 Desktop

安裝所需要的函式庫(library)
sudo apt-get install build-essential linux-headers-$(uname -r)

Editor輸入以下內容,檔名為helloKernel.c
/*
* helloKernel.c
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init helloKernel_init(void)
{
    printk(KERN_INFO "Hello kernel.\n");
    return 0;
}

static void __exit helloKernel_exit(void)
{
  printk(KERN_INFO "Goodbye kernel.\n");
}

module_init(helloKernel_init);
module_exit(helloKernel_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Holan Liao");
MODULE_DESCRIPTION("Kernel Module Sample");
MODULE_VERSION("1.0");

Makefile內容如下:
obj-m += helloKernel.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

記得將這兩個檔案(helloKernel.cMakefile)放置同一個資料夾底下:


接著下make



產生*.ko



下完以下指令就可看到有訊息輸出到Kernel log裡了。
sudo insmod helloKernel.ko
sudo rmmod helloKernel
demsg | tail



可看到有訊息輸出到Kernel log裡了。

Char Device Driver
/*
* helloCharDev.c
*/

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "helloCharDev"
#define CLASS_NAME "holan"

static int majorNum;
static char msg[256] = {0};
static short msgLen;
static int numberOpens = 0;
static struct class* helloCharClass = NULL;
static struct device* helloCharDev = NULL;

static int drvDev_open(struct inode *, struct file *);
static int drvDev_release(struct inode *, struct file *);
static ssize_t drvDev_read(struct file *, char *, size_t, loff_t *);
static ssize_t drvDev_write(struct file *, const char *, size_t, loff_t *);


static struct file_operations fops =
{
    .open = drvDev_open,
    .read = drvDev_read,
    .write = drvDev_write,
    .release = drvDev_release,
};

static int __init hellCharDev_init(void){
    printk(KERN_INFO "helloCharDev: Initializing the helloCharDev.\n");

    majorNum = register_chrdev(0, DEVICE_NAME, &fops);
    if (majorNum<0){
        printk(KERN_ALERT "helloCharDev couldn't register a major number\n");
    return majorNum;
}
    printk(KERN_INFO "helloCharDev: registered correctly with major number %d\n", majorNum);

    helloCharClass = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(helloCharClass)){
        unregister_chrdev(majorNum, DEVICE_NAME);
        printk(KERN_ALERT "couldn't register\n");
        return PTR_ERR(helloCharClass);
    }
    printk(KERN_INFO "helloCharDev: registered correctly\n");

    helloCharDev = device_create(helloCharClass, NULL, MKDEV(majorNum, 0), NULL, DEVICE_NAME);
    if (IS_ERR(helloCharDev)){
        class_destroy(helloCharClass);
        unregister_chrdev(majorNum, DEVICE_NAME);
        printk(KERN_ALERT "couldn't create the device\n");
        return PTR_ERR(helloCharDev);
    }

    printk(KERN_INFO "helloCharDev: created correctly\n");
    return 0;
}

static void __exit hellCharDev_exit(void){
    device_destroy(helloCharClass, MKDEV(majorNum, 0));
    class_unregister(helloCharClass);
    class_destroy(helloCharClass);
    unregister_chrdev(majorNum, DEVICE_NAME);
    printk(KERN_INFO "helloCharDev: Goodbye!\n");
}

static int drvDev_open(struct inode *inodep, struct file *filep){
    numberOpens++;
    printk(KERN_INFO "helloCharDev: Device has been opened %d time(s)\n", numberOpens);
    return 0;
}

static ssize_t drvDev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
    int error_count = 0;
    error_count = copy_to_user(buffer, msg, msgLen);

    if (error_count==0){
        printk(KERN_INFO "helloCharDev: Sent %d characters to the user\n", msgLen);
        return (msgLen=0);
    }
    else {
        printk(KERN_INFO "helloCharDev: couldn't send %d characters to the user\n", error_count);
        return -EFAULT;
    }
}

static ssize_t drvDev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
    sprintf(msg, "%s(%d letters)", buffer, len);
    msgLen = strlen(msg);
    printk(KERN_INFO "helloCharDev: Received %d characters from the user\n", len);
    return len;
}

static int drvDev_release(struct inode *inodep, struct file *filep){
    printk(KERN_INFO "helloCharDev: Device successfully closed\n");
    return 0;
}


module_init(hellCharDev_init);
module_exit(hellCharDev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Holan Liao");
MODULE_DESCRIPTION("Char driver sample");
MODULE_VERSION("1.0");

helloCharDev的測試程式:
/*
* helloCharDevTest.c : Test program for helloCharDev
*/

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<string.h>

#define BUF_LEN 256
static char recv[BUF_LEN];

int main(){
    int ret, fd;
    char msgToSend[BUF_LEN];
    printf("Device testing...\n");
    fd = open("/dev/helloCharDev", O_RDWR);
    if (fd < 0){
        perror("Couldn't open the device...");
        return errno;
    }

    printf("Please input a string:\n");
    scanf("%[^\n]%*c", msgToSend);
    printf("Sending msg:[%s]...\n", msgToSend);
    ret = write(fd, msgToSend, strlen(msgToSend));

    if (ret < 0){
        perror("Couldn't send msg.");
        return errno;
    }

    printf("Press ENTER to read back...\n");
    getchar();

    printf("Reading...\n");
    ret = read(fd, recv, BUF_LEN);
    if (ret < 0){
        perror("Couldn't read the msg.");
        return errno;
    }

    printf("The msg is: [%s]\n", recv);
    printf("Bye.\n");
    return 0;
}

Makefile:
obj-m += helloCharDev.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
$(CC) helloCharDevTest.c -o charDrvTest

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

結果: