1. 程式人生 > >嵌入式系統開發踩坑記[一]

嵌入式系統開發踩坑記[一]

    目前在拿著一本<嵌入式系統原理與應用>的書肝裝置驅動開發,不同於STM32\51,系統級的開發更多開始呼叫函式而非自己擼所有,因此踩坑在所難免,寫部落格來記錄一下.

1. 裝置驅動程式框架問題

簡單的字元裝置驅動程式框架

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#define DEVICE_NAME "Dev_frame0"

int DEVICE_MAJOR=50;
int num=0;

static int device_open(struct inode * inode, struct file *filp)
{
	printk("user open device.\n");
	return 0;
}
static ssize_t device_read(struct file * filp, char * buffer, size_t count, loff_t * f_pos)
{
	printk("user read data to device.\n");
	return count;
}
static ssize_t device_write(struct file * filp, const char * buffer, size_t count, loff_t * f_pos)
{
	printk("user write data to device.\n");
	return count;
}
static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	printk("user ioctl running.\n");
	return cmd;
}
static int device_release(struct inode * inode, struct file *filp)
{
	printk("device release\n");
	return 0;
}

struct file_operations device_fops = {
	.owner=THIS_MODULE,
	.open=device_open,
	.read=device_read,
	.write=device_write,
	.unlocked_ioctl=device_ioctl,
	.release=device_release,
};

static int device_init(void)
{
	int ret;
	ret = register_chrdev( DEVICE_MAJOR,DEVICE_NAME,&device_fops );
	
	if(ret<0)
	{
		printk("register_chrdev failure!\n");
		
		return ret;
	}
	else
	{
		printk("register chrdev ok!\n");
	}
	return 0;
}
static void device_exit(void)
{
	unregister_chrdev(DEVICE_MAJOR,DEVICE_NAME);
	printk("unregister chrdev ok!\n");
}

module_init(device_init);
module_exit(device_exit);
module_param(num,int,S_IRUGO);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author Name");
MODULE_DESCRIPTION("device frame with parament");
MODULE_ALIAS("device frame");
MODULE_VERSION("V1.0");

標頭檔案解釋如下:

<linux/module.h> 包含了模組所需的符號和函式定義,如MODULE_AUTHOR();等

<linux/fs.h> 包含檔案系統相關的函式與標頭檔案,包含了file_operations結構體

<linux/cdev> cdev結構的標頭檔案

BUG1:編譯時出現下面的報錯

/home/ubuntu/Documents/raspberry_pi/driver_frame/Dev_frame.c:39:8: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
  .read=device_read,
        ^
/home/ubuntu/Documents/raspberry_pi/driver_frame/Dev_frame.c:39:8: note: (near initialization for ‘device_fops.read’)
/home/ubuntu/Documents/raspberry_pi/driver_frame/Dev_frame.c:40:9: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
  .write=device_write,
         ^
/home/ubuntu/Documents/raspberry_pi/driver_frame/Dev_frame.c:40:9: note: (near initialization for ‘device_fops.write’)
/home/ubuntu/Documents/raspberry_pi/driver_frame/Dev_frame.c:41:18: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
  .unlocked_ioctl=device_ioctl,

這種情況是由於Dev_frame.c中使用的read/write/ioctl函式不符合標頭檔案中定義的形式

file_operations在fs.h中的部分定義如下

struct  {
    struct module *owner;
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    int (*open) (struct inode *, struct file *);
    int (*release) (struct inode *, struct file *);
}

而需要注意read與write函式引數型別並不相同,並且引數型別size_t與返回值型別ssize_t也是不同的

更重要的是老版核心中的ioctl函式已被更新的unlock_ioctl()函式所取代,其引數也是不同的

編寫帶引數的模組時還需注意,用module_param();宣告的模組引數需要在宣告以前先定義好

BUG2:新核心make與舊核心也有區別

insmod: ERROR: could not insert module Dev_frame.ko: Invalid module format

make模組以後terminal出現如上ERROR,提示是無效的模組格式.百度了一下,這種差別還是由於新舊核心改變了模組的編譯原則,即便用舊有的方式編譯通過,模組依舊是不能使用的.

由於沒有時間深入研究裡面的原因,在這僅僅貼上適用於新核心的makefile

# Makefile2.6
ifneq ($(KERNELRELEASE),)
#kbuild syntax. dependency relationshsip of files and target modules are listed here.
obj-m := Dev_frame.o
else
PWD  := $(shell pwd)
KVER ?= $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
        $(MAKE) -C $(KDIR) M=$(PWD) 
clean:
        rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif         

針對此驅動檔案的測試程式如下

#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#define DEVICE_NAME "Dev_frame0"

int main()
{
        int fd,num;
        fd = open(DEVICE_NAME, O_RDWR, S_IRUSR|S_IWUSR);
        if(fd==-1)
        {
                printf("%s device open failure.\n",DEVICE_NAME);
                return -1;
        }
        printf("Reading data.\n");
        read(fd,&num,sizeof(int));
        printf("Writing data.\n");
        write(fd, &num, sizeof(int));
        printf("Control data input.\n");
        ioctl(fd,5);
        printf("Device closed.\n");
        close(fd);
        return 0;
}      

BUG3:在書中,示例程式裡僅包含了三個標頭檔案<sys/stat.h> <stdio.h> <fcntl.h>

然後make的時候就出現瞭如下報錯

test.c: In function ‘main’:
test.c:17:2: warning: implicit declaration of function ‘read’ [-Wimplicit-function-declaration]
  read(fd,&num,sizeof(int));
  ^
test.c:19:2: warning: implicit declaration of function ‘write’ [-Wimplicit-function-declaration]
  write(fd, &num, sizeof(int));
  ^
test.c:21:2: warning: implicit declaration of function ‘ioctl’ [-Wimplicit-function-declaration]
  ioctl(fd,5);
  ^
test.c:23:2: warning: implicit declaration of function ‘close’ [-Wimplicit-function-declaration]
  close(fd);

這明顯是沒有包含標頭檔案

查了一下,主要是缺少下面兩個標頭檔案

<unistd.h>

對於類 Unix 系統,unistd.h 中所定義的介面通常都是大量針對系統呼叫的封裝(英語:wrapper functions),如 fork、pipe 以及各種I/O原語(read、write、close 等等)。

<sys/ioctl.h>

使用ioctl時必備

然而在程式呼叫ioctl()的時候,使用哪個ioctl()函式是有一個順序的,這裡也暫時沒有深入研究

BUG4:執行測試檔案,輸出裝置檔案開啟失敗

    正如我們都知道的,程式與裝置驅動的資訊傳遞是通過裝置節點進行的,因此測試程式的DEVICE_NAME應該為裝置節點的名稱/dev/Dev_frame(之前已經sudo mknod /dev/Dev_frame),而不是模組裡的DEVICE_NAME=Dev_frame0!因此通過這個我們可以認識到裝置節點最好與裝置名稱同名以方便記憶\使用,並且建立裝置檔案後要對其屬性進行修改(sudo chmod 666 /dev/filename)