- [root@FriendlyARM 2.6.36-FriendlyARM]# ./scd_open /dev/scd
Simple Char Device's Memory starts at 0x57700000
BUG: Your driver calls ioremap() on system memory. This leads
<4>to architecturally unpredictable behaviour on ARMv6+, and ioremap()
<4>will fail in the next kernel release. Please fix your driver.
------------[ cut here ]------------
WARNING: at arch/arm/mm/ioremap.c:211 __arm_ioremap_pfn_caller+0x50/0x18c()
Modules linked in: simple_char_dev [last unloaded: simple_char_dev]
[<c016d464>] (unwind_backtrace+0x0/0xe4) from [<c0184a5c>] (warn_slowpath_common+0x4c/0x64)
[<c0184a5c>] (warn_slowpath_common+0x4c/0x64) from [<c0184a8c>] (warn_slowpath_null+0x18/0x1c)
[<c0184a8c>] (warn_slowpath_null+0x18/0x1c) from [<c016f0a0>] (__arm_ioremap_pfn_caller+0x50/0x18c)
[<c016f0a0>] (__arm_ioremap_pfn_caller+0x50/0x18c) from [<c016f23c>] (__arm_ioremap_caller+0x50/0x54)
[<c016f23c>] (__arm_ioremap_caller+0x50/0x54) from [<bf006298>] (scd_open+0x38/0x64 [simple_char_dev])
[<bf006298>] (scd_open+0x38/0x64 [simple_char_dev]) from [<c01db838>] (chrdev_open+0x168/0x190)
[<c01db838>] (chrdev_open+0x168/0x190) from [<c01d6d58>] (__dentry_open.clone.12+0x164/0x27c)
[<c01d6d58>] (__dentry_open.clone.12+0x164/0x27c) from [<c01e3328>] (do_last.clone.29+0x458/0x5a8)
[<c01e3328>] (do_last.clone.29+0x458/0x5a8) from [<c01e35e0>] (do_filp_open+0x168/0x4b0)
[<c01e35e0>] (do_filp_open+0x168/0x4b0) from [<c01d7b84>] (do_sys_open+0x58/0xf0)
[<c01d7b84>] (do_sys_open+0x58/0xf0) from [<c0167e20>] (ret_fast_syscall+0x0/0x30)
one_wire_status: 4
---[ end trace 8650f19e2bfa1630 ]---
Trying to free nonexistent resource <0000000057700000-000000005777f7ff>
I can open the device scd now.
[root@FriendlyARM 2.6.36-FriendlyARM]#
代码: 全选
/*
* simple_char_dev.c
* date: Dec 21 2013
* description: 将一块内存区域抽象成一个字符设备,通过打开字符设备文件/dev/scd,
* 然后就可以读写这块内存.把这个字符设备作为module加载到内核中,内存区域的起始地
* 址和大小作为module parameters传入到module
*/
/*
* Compilation error: fatal error: linux/config.h: No such file or directory
* 从2.6.20起, config.h已经被移除了
*/
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
#include <linux/config.h>
#endif
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/io.h> /* ioremap() iounmap() */
#include <linux/ioport.h> /* request/release_mem_region() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/cdev.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
/*
* Our parameters which MUST be set at load time.
*/
unsigned long scd_physical_start;
unsigned long scd_mem_size;
/*
* module_param(name, type, perm);
* type支持的类型:
* bool
* invbool
* charp
* int
* long
* short
* uint
* ulong
* ushort
*/
module_param(scd_physical_start, ulong, S_IRUGO);
module_param(scd_mem_size, ulong, S_IRUGO);
MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");
/*
* Structure that represents simple char device
*/
struct scd_dev {
void __iomem *virtual_base;
unsigned long physical_start;
unsigned long size;
struct semaphore sem;
struct cdev cdev; /* char device structure */
};
/* global */
int scd_major;
int scd_minor = 0;
struct scd_dev *scd_device; /* allocated in scd_init */
/*
* Open and close
*/
int scd_open(struct inode *inode, struct file *filp)
{
struct scd_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scd_dev, cdev);
filp->private_data = dev; /* for other methods */
/* Upon open the device, request_mem_region and ioremap */
if (dev->physical_start) {
printk("Simple Char Device's Memory starts at 0x%08X\n", dev->physical_start);
#if 0
/*
* request_mem_region不能成功,所以先不request,直接ioremap,试试
*/
if (!request_mem_region(dev->physical_start, dev->size, NULL)) {
printk("scd: Failed to Request memory\n");
return -ENOMEM;
}
#endif
dev->virtual_base = ioremap(dev->physical_start, dev->size);
if (!dev->virtual_base) {
printk("scd: Error Mapping memory\n");
return -ENOMEM;
}
}
return 0; /* success */
}
int scd_release(struct inode *inode, struct file *filp)
{
/* When /dev/scd is closed, release_mem_region and iounmap */
struct scd_dev *dev = filp->private_data;
if (dev->physical_start && dev->size && dev->virtual_base) {
iounmap(dev->virtual_base);
release_mem_region(dev->physical_start, dev->size);
}
return 0;
}
ssize_t scd_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct scd_dev *dev = filp->private_data;
ssize_t retval = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= dev->size)
goto out;
if (*f_pos + count > dev->size)
count = dev->size - *f_pos;
/* 这里对*f_pos的理解还不确定,暂且理解为文件的offset,就是内存的offset */
if (copy_to_user(buf, dev->virtual_base + *f_pos, count)) {
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t scd_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
struct scd_dev *dev = filp->private_data;
ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (copy_from_user(dev->virtual_base + *f_pos, buf, count)) {
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
/* update the size */
if (dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
#if 0
/*
* The ioctl() implementation
*/
int scull_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err = 0, tmp;
int retval = 0;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
switch(cmd) {
case SCULL_IOCRESET:
scull_quantum = SCULL_QUANTUM;
scull_qset = SCULL_QSET;
break;
case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_quantum = arg;
break;
case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
retval = __put_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
return scull_quantum;
case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_quantum;
retval = __get_user(scull_quantum, (int __user *)arg);
if (retval == 0)
retval = __put_user(tmp, (int __user *)arg);
break;
case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_quantum;
scull_quantum = arg;
return tmp;
case SCULL_IOCSQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_qset, (int __user *)arg);
break;
case SCULL_IOCTQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_qset = arg;
break;
case SCULL_IOCGQSET:
retval = __put_user(scull_qset, (int __user *)arg);
break;
case SCULL_IOCQQSET:
return scull_qset;
case SCULL_IOCXQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_qset;
retval = __get_user(scull_qset, (int __user *)arg);
if (retval == 0)
retval = put_user(tmp, (int __user *)arg);
break;
case SCULL_IOCHQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_qset;
scull_qset = arg;
return tmp;
/*
* The following two change the buffer size for scullpipe.
* The scullpipe device uses this same ioctl method, just to
* write less code. Actually, it's the same driver, isn't it?
*/
case SCULL_P_IOCTSIZE:
scull_p_buffer = arg;
break;
case SCULL_P_IOCQSIZE:
return scull_p_buffer;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return retval;
}
#endif
/*
* The "extended" operations -- only seek
*/
loff_t scd_llseek(struct file *filp, loff_t off, int whence)
{
struct scd_dev *dev = filp->private_data;
loff_t newpos;
switch(whence) {
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = dev->size + off; /* in this case, off should be negative */
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0) return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
struct file_operations scd_fops = {
.owner = THIS_MODULE,
.llseek = scd_llseek,
.read = scd_read,
.write = scd_write,
//.ioctl = scd_ioctl,
.open = scd_open,
.release = scd_release,
};
/*
* Finally, the module stuff
*/
/*
* The cleanup function is used to handle initialization failures as well.
* Therefore, it must be careful to work correctly even if some of the items
* have not been initialized
*/
void scd_cleanup_module(void)
{
dev_t devno = MKDEV(scd_major, scd_minor);
/* Get rid of our char dev entries */
if (scd_device) {
cdev_del(&scd_device->cdev);
kfree(scd_device);
}
unregister_chrdev_region(devno, 1);
printk("scd: I'll be back.\n");
}
/*
* Set up the char_dev structure for this device.
*/
static void scd_setup_cdev(struct scd_dev *dev, dev_t devno)
{
int err;
cdev_init(&dev->cdev, &scd_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scd_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scd", err);
}
int scd_init_module(void)
{
int result;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
result = alloc_chrdev_region(&dev, scd_minor, 1, "scd");
if (result < 0) {
printk(KERN_WARNING "scd: can't get major %d\n", scd_major);
return result;
}
scd_major = MAJOR(dev);
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
scd_device = kmalloc(sizeof(struct scd_dev), GFP_KERNEL);
if (!scd_device) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(scd_device, 0, sizeof(struct scd_dev));
/* Initialize device. */
scd_device->physical_start = scd_physical_start;
scd_device->size = scd_mem_size;
init_MUTEX(&scd_device->sem);
scd_setup_cdev(scd_device, dev);
printk("scd: I've already come in the kernel,ah-ah.\n");
return 0; /* succeed */
fail:
scd_cleanup_module();
return result;
}
module_init(scd_init_module);
module_exit(scd_cleanup_module);
模块的加载过程是顺利的,但当运行测试程序时,出现了最前面的错误输出,应该是ioremap出了问题,不知道这样的问题怎样解决,或者要实现我的这个思路,还有其它更好的方法来做。
测试/dev/scd的程序,检查是否能顺利打开它(看来是不行)
代码: 全选
#include <stdio.h>
#include <unistd.h> // open close
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h> // ioctl
#include <linux/errno.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fbfd;
char *devname = argv[1];
/* Open video memory */
fbfd = open(devname, O_RDWR);
if (fbfd < 0 ) {
printf("cannot open %s\n", devname);
return 1;
} else {
printf("I can open the device scd now.\n");
}
close(fbfd);
return 0;
}