最近要開發一個項目,用到了內存映射mmap機制,於是自己在Ubuntu下先寫了一個測試的小程序,由於測試代碼是從網上摘抄更改的,出現了一個頭文件的問題,這兩天搞的自己心情相當的鬱悶,都已經開始嚴重懷疑這個世界了,當得知是頭文件出錯時,心裏更是五味雜瓶,感覺生活欺騙了自己一樣,代碼如下:
驅動代碼:
/*
* Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
*
* Copyright (C) 2012-2014 Alexander Shiyan <[email protected]>
*
* Based on max3100.c, by Christian Pellegrin <[email protected]>
* Based on max3110.c, by Feng Tang <[email protected]>
* Based on max3107.c, by Aavamobile
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <asm/pgtable_types.h>
#include <linux/mm.h>
#include "pic.h"
#define SD16A_NAME "sd16a"
#define SD16A_MAJOR 204
#define SD16A_MINOR 0
//#define DATA_LEN 259200
#define DATA_LEN 259200
uint8_t data[DATA_LEN];
static unsigned long buf_size;
static int sd16a_major = SD16A_MAJOR;
static int sd16a_minor = SD16A_MINOR;
struct sd16a_devtype {
char name[9];
int (*detect)(struct device *);
void (*power)(int);
};
uint8_t * sd16a_pic_data_buf;
struct sd16a_dev{
struct spi_device * spi;
int irq_gpio;
int sda_gpio;
spinlock_t buf_lock;
u8 * data_buf;
struct spi_master *master;
u8 irq_cnt;
};
static const struct sd16a_devtype sd16a_devtype = {
.name = "SD16A",
};
static const struct spi_device_id sd16a_id_table[] = {
{"dm,sd16a",(kernel_ulong_t)&sd16a_devtype,},
{},
};
static const struct of_device_id __maybe_unused sd16a_dt_ids[] = {
{.compatible = "dm,sd16a", .data = &sd16a_devtype, },
{},
};
static int sd16a_open(struct inode *inode, struct file *filp){
printk("open sd16a start\n");
sd16a_pic_data_buf = (uint8_t *)kmalloc(DATA_LEN, GFP_KERNEL); //內核申請內存只能按頁申請,申請該內存以便後面把它當作虛擬設備
printk("sd16a_pic_data_buf is %p\n",sd16a_pic_data_buf);
if(sd16a_pic_data_buf){
printk("sd16a mmap addr is %p\n",sd16a_pic_data_buf);
memset(sd16a_pic_data_buf,0,buf_size);
}
printk("open sd16a end\n");
return 0;
}
static size_t sd16a_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){
//u8 ret;
uint32_t i = 0;
uint32_t len = count;
printk("sd16a_read start\n");
memset(data,10,DATA_LEN - 1);
data[DATA_LEN - 1] = 0;
//pic_buf[i][0] = i;
memcpy(sd16a_pic_data_buf,data,DATA_LEN);
printk("sd16a_read success\n");
printk("sd16a_pic_data_buf is");
for(i = 0;i < len;i ++){
printk("%d",sd16a_pic_data_buf[i]);
}
return 0;
}
static long sd16a_ioctl(struct file * file, unsigned int cmd, unsigned long arg){
return 0;
}
static int sd16a_release(struct inode *inode, struct file *filp){
unsigned long size = buf_size;
//unsigned long addr = (unsigned long)sd16a_pic_data_buf;
printk("sd16a close\n");
kfree(sd16a_pic_data_buf);
//free_pages(addr,get_order(size));
return 0;
}
#if 0
static int sd16a_llseek(struct file *file, loff_t offset, int whence){
}
#endif
static int sd16a_mmap(struct file *file, struct vm_area_struct *vma) {
unsigned int size = 0;
uint8_t * kmalloc_addr = NULL;
uint32_t i = 0;
unsigned int alloc_size = 0;
unsigned int page_res_size = 0;
size = vma->vm_end - vma->vm_start;
alloc_size = size + 2 * PAGE_SIZE;
page_res_size = (size + PAGE_SIZE - 1) & PAGE_MASK;
kmalloc_addr = (uint8_t *)kmalloc(alloc_size, GFP_KERNEL);
sd16a_pic_data_buf = (uint8_t *)(((unsigned long)kmalloc_addr + PAGE_SIZE - 1) & PAGE_MASK);
for(i = 0;i < (page_res_size / PAGE_SIZE);i += PAGE_SIZE){
SetPageReserved(virt_to_page(((unsigned long)sd16a_pic_data_buf) + i));
}
if(sd16a_pic_data_buf == NULL){
pr_err("kmalloc failed\n");
return -1;
}
if(remap_pfn_range(vma,
vma->vm_start,
virt_to_phys((void *)sd16a_pic_data_buf)>>PAGE_SHIFT,
size,
vma->vm_page_prot))
{
pr_err("remap_pfn_range failed\n");
return -EAGAIN;
}
return 0;
}
static struct file_operations sd16a_ops = {
.owner = THIS_MODULE,
.read = sd16a_read,
//.write = sd16a_write,
.mmap = sd16a_mmap,
.open = sd16a_open,
.unlocked_ioctl = sd16a_ioctl,
.release = sd16a_release,
.llseek = noop_llseek,
};
static struct cdev sd16a_cdev;
static struct class * sd16a_class;
static int sd16a_spi_probe(struct spi_device *spi){
//struct sd16a_devtype * devtype;
int ret;
struct device * dev;
dev_t devid = MKDEV(sd16a_major,sd16a_minor);
dev = &spi->dev;
if(!dev){
pr_err("in file :%s function: %s, dev is null\n",__FILE__,__func__);
return -EINVAL;
}
printk("enter %s \n",__func__);
if (sd16a_major) {
devid = MKDEV(sd16a_major, sd16a_minor);
ret = register_chrdev_region(devid, 1, "dm_sd16a");
}else{
ret = alloc_chrdev_region(&devid, 0, 1, "dm_sd16a");
sd16a_major = MAJOR(devid);
}
if(ret < 0){
dev_err(dev, "dm_sd16a chrdev_region err: %d\n", ret);
goto out;
}
cdev_init(&sd16a_cdev, &sd16a_ops);
cdev_add(&sd16a_cdev, devid, 1);
sd16a_class = class_create(THIS_MODULE, "sd16a-dev");
if (IS_ERR(sd16a_class)) {
ret = PTR_ERR(sd16a_class);
goto out_unreg_chrdev;
}
device_create(sd16a_class,dev,devid,NULL,"%s",SD16A_NAME);
printk("sda16a probe success\n");
return 0;
out_unreg_chrdev:
//error:
//err_register_chrdev:
out:
return ret;
}
static int sd16a_spi_remove(struct spi_device *spi)
{
;
/*
*should remove irq and clk , all the alooced sources
*/
return 0;
}
/*
static struct spi_driver sd16a_uart_driver = {
.driver = {
.name = SD16A_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(sd16a_dt_ids),
},
.probe = sd16a_spi_probe,
.remove = sd16a_spi_remove,
.id_table = sd16a_id_table,
};*/
static int __init sd16a_uart_driver_init(void)
{
int ret;
dev_t devid = MKDEV(sd16a_major,sd16a_minor);
if (sd16a_major) {
devid = MKDEV(sd16a_major, sd16a_minor);
ret = register_chrdev_region(devid, 1, "dm_sd16a");
}else{
ret = alloc_chrdev_region(&devid, 0, 1, "dm_sd16a");
sd16a_major = MAJOR(devid);
}
cdev_init(&sd16a_cdev, &sd16a_ops);
cdev_add(&sd16a_cdev, devid, 1);
sd16a_class = class_create(THIS_MODULE, "sd16a-dev");
if (IS_ERR(sd16a_class)) {
ret = PTR_ERR(sd16a_class);
}
device_create(sd16a_class,NULL,devid,NULL,"%s",SD16A_NAME);
printk("sda16a probe success\n");
return 0;
}
module_init(sd16a_uart_driver_init);
static void __exit sd16a_uart_driver_exit(void)
{
device_destroy(sd16a_class,MKDEV(SD16A_MAJOR,SD16A_MINOR));
class_destroy(sd16a_class);
cdev_del(&sd16a_cdev);
unregister_chrdev_region(MKDEV(SD16A_MAJOR,SD16A_MINOR),1);
}
module_exit(sd16a_uart_driver_exit);
//module_spi_driver(sd16a_uart_driver);
//#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Const jxh");
MODULE_DESCRIPTION("sd16a serial driver");
測試代碼:
#include<stdio.h>
#include<string.h>
#include<linux/mman.h>
#include<fcntl.h>
#include<unistd.h>
#define LEN 262144 //259400
//#define LEN 20000 //262144
#define MS(n) (n*1000)
#define CHR_DEV_NAME "/dev/sd16a"
int main()
{
int ret;
int i = 0;
int j = 0;
char buf[LEN];
unsigned char * mmp_addr;
char * buf_p;
int fd = open(CHR_DEV_NAME,O_RDWR | O_NONBLOCK);
if(fd < 0)
{
printf("open file %s failed!\n",CHR_DEV_NAME);
return -1;
}
printf("fd is %d\n",fd);
mmp_addr = (unsigned char*)mmap(NULL,LEN,PROT_READ|PROT_WRITE,MAP_LOCKED|MAP_SHARED,fd,0);
if(mmp_addr == NULL){
printf("mmap failed\n");
return -1;
}
printf("mmp_addr is %p\n",mmp_addr);
for(i = 0;i < 10;i ++){
read(fd,buf,32);
// memcpy(mmp_addr,"hello world",sizeof("hello world"));
for(j = 0;j < 32;j ++){
printf("%d",mmp_addr[j]);
}
printf("\n");
usleep(MS(10));
}
close(fd);
munmap(mmp_addr,LEN);
return 0;
}
原本是一個很簡單的程序,但是搞了兩天才確定下來問題,真是頭大。其實引起錯誤的原因很簡單,就是頭文件引用不對,雖然編譯的時候沒有報錯,只是彈出了警告,但是執行的時候卻會出現段錯誤。
編譯時彈出的警告:
運行時的段錯誤提示:
看到段錯誤的原因是4,根據段錯誤碼如下圖
段錯誤碼:
bit2爲1,代表的是用戶訪問越界造成的,可是應用程序那麼簡單,也沒看到數組訪問錯誤的地方,於是懷疑是自己驅動mmap函數哪裏設置不對,就各種改驅動代碼,結果這樣調了一天也沒改觀,於是只能從其它地方着手找問題了,用man mmap查看了一下,發現其頭文件是在sys/mman.h,而不是linux/mman.h,由於這個測試代碼是自己從別的地方拷貝更改的,也不知道爲何別人用的linux/mman.h頭文件。於是抱着試試看的心態把頭文件目錄改成sys/mman.h,奇蹟發生了,代碼終於運行正常了,就這樣一個頭文件問題,搞了兩天,真是服了。順便吐槽一下,既然頭文件包含錯誤爲何編譯的時候只是警告,而不是錯誤呢?我也跟進去看了一下,發現linux/mman.h根本也沒有關於mmap函數的聲明,也是服了。看來以後關於使用的函數頭文件還是需要謹慎啊。
正確的執行結果如下: