最近要开发一个项目,用到了内存映射mmap机制,于是自己在Ubuntu下先写了一个测试的小程序,由于测试代码是从网上摘抄更改的,出现了一个头文件的问题,这两天搞的自己心情相当的郁闷,都已经开始严重怀疑这个世界了,当得知是头文件出错时,心里更是五味杂瓶,感觉生活欺骗了自己一样,代码如下:
驱动代码:
/*
* Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver
*
* Copyright (C) 2012-2014 Alexander Shiyan <shc_work@mail.ru>
*
* Based on max3100.c, by Christian Pellegrin <chripell@evolware.org>
* Based on max3110.c, by Feng Tang <feng.tang@intel.com>
* 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函数的声明,也是服了。看来以后关于使用的函数头文件还是需要谨慎啊。
正确的执行结果如下: