提交 0418b15f 编写于 作者: H hypox64

sys_call finished

上级 3e64bfa4
......@@ -204,7 +204,12 @@ VmFlags: rd ex
1.每个处理器核上运行的进程使用的是虚地址,每次取指令或数据都需要将程序虚地址转换成物理地址,这需要借助于页表信息来转换.
### 5.2.2 进程页表
由于ubuntu下无法安装kernel-debuginfo,下面的内容将在CentOS7中实现
由于ubuntu下无法安装kernel-debuginfo,下面的内容将在CentOS 7中实现
* 安装crash
```bash
yum install crash
debuginfo-install kernel
```
* 屏显 5-19 show-virt-addr 的输出
```bash
[hypo@localhost code]$ ./show-virt-addr
......
#ifndef _LINUX_CDEV_H
#define _LINUX_CDEV_H
#include <linux/kobject.h>
#include <linux/kdev_t.h>
#include <linux/list.h>
struct file_operations;
struct inode;
struct module;
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);
void cd_forget(struct inode *);
#endif
/*
* linux/fs/char_dev.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/kobject.h>
#include <linux/kobj_map.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/backing-dev.h>
#include <linux/tty.h>
#include "internal.h"
static struct kobj_map *cdev_map;
static DEFINE_MUTEX(chrdevs_lock);
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
/* index in the above */
static inline int major_to_index(unsigned major)
{
return major % CHRDEV_MAJOR_HASH_SIZE;
}
#ifdef CONFIG_PROC_FS
void chrdev_show(struct seq_file *f, off_t offset)
{
struct char_device_struct *cd;
if (offset < CHRDEV_MAJOR_HASH_SIZE) {
mutex_lock(&chrdevs_lock);
for (cd = chrdevs[offset]; cd; cd = cd->next)
seq_printf(f, "%3d %s\n", cd->major, cd->name);
mutex_unlock(&chrdevs_lock);
}
}
#endif /* CONFIG_PROC_FS */
/*
* Register a single major with a specified minor range.
*
* If major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If major > 0 this function will attempt to reserve the passed range of
* minors and will return zero on success.
*
* Returns a -ve errno on failure.
*/
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock);
/* temporary */
if (major == 0) {
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
if (i == 0) {
ret = -EBUSY;
goto out;
}
major = i;
}
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
i = major_to_index(major);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor;
int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor + minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
if (new_min < old_min && new_max > old_max) {
ret = -EBUSY;
goto out;
}
}
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
static struct char_device_struct *
__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
{
struct char_device_struct *cd = NULL, **cp;
int i = major_to_index(major);
mutex_lock(&chrdevs_lock);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major == major &&
(*cp)->baseminor == baseminor &&
(*cp)->minorct == minorct)
break;
if (*cp) {
cd = *cp;
*cp = cd->next;
}
mutex_unlock(&chrdevs_lock);
return cd;
}
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
if (IS_ERR(cd))
goto fail;
}
return 0;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
return PTR_ERR(cd);
}
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(0, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}
/**
* __register_chrdev() - create and register a cdev occupying a range of minors
* @major: major device number or 0 for dynamic allocation
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: name of this range of devices
* @fops: file operations associated with this devices
*
* If @major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If @major > 0 this function will attempt to reserve a device with the given
* major number and will return zero on success.
*
* Returns a -ve errno on failure.
*
* The name of this device has nothing to do with the name of the device in
* /dev. It only helps to keep track of the different owners of devices. If
* your module name has only one type of devices it's ok to use e.g. the name
* of the module here.
*/
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc();
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
/**
* unregister_chrdev_region() - unregister a range of device numbers
* @from: the first in the range of numbers to unregister
* @count: the number of device numbers to unregister
*
* This function will unregister a range of @count device numbers,
* starting with @from. The caller should normally be the one who
* allocated those numbers in the first place...
*/
void unregister_chrdev_region(dev_t from, unsigned count)
{
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
}
/**
* __unregister_chrdev - unregister and destroy a cdev
* @major: major device number
* @baseminor: first of the range of minor numbers
* @count: the number of minor numbers this cdev is occupying
* @name: name of this range of devices
*
* Unregister and destroy the cdev occupying the region described by
* @major, @baseminor and @count. This function undoes what
* __register_chrdev() did.
*/
void __unregister_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name)
{
struct char_device_struct *cd;
cd = __unregister_chrdev_region(major, baseminor, count);
if (cd && cd->cdev)
cdev_del(cd->cdev);
kfree(cd);
}
static DEFINE_SPINLOCK(cdev_lock);
static struct kobject *cdev_get(struct cdev *p)
{
struct module *owner = p->owner;
struct kobject *kobj;
if (owner && !try_module_get(owner))
return NULL;
kobj = kobject_get(&p->kobj);
if (!kobj)
module_put(owner);
return kobj;
}
void cdev_put(struct cdev *p)
{
if (p) {
struct module *owner = p->owner;
kobject_put(&p->kobj);
module_put(owner);
}
}
/*
* Called every time a character special file is opened
*/
static int chrdev_open(struct inode *inode, struct file *filp)
{
const struct file_operations *fops;
struct cdev *p;
struct cdev *new = NULL;
int ret = 0;
spin_lock(&cdev_lock);
p = inode->i_cdev;
if (!p) {
struct kobject *kobj;
int idx;
spin_unlock(&cdev_lock);
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
if (!kobj)
return -ENXIO;
new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev;
if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list);
new = NULL;
} else if (!cdev_get(p))
ret = -ENXIO;
} else if (!cdev_get(p))
ret = -ENXIO;
spin_unlock(&cdev_lock);
cdev_put(new);
if (ret)
return ret;
ret = -ENXIO;
fops = fops_get(p->ops);
if (!fops)
goto out_cdev_put;
replace_fops(filp, fops);
if (filp->f_op->open) {
ret = filp->f_op->open(inode, filp);
if (ret)
goto out_cdev_put;
}
return 0;
out_cdev_put:
cdev_put(p);
return ret;
}
void cd_forget(struct inode *inode)
{
spin_lock(&cdev_lock);
list_del_init(&inode->i_devices);
inode->i_cdev = NULL;
spin_unlock(&cdev_lock);
}
static void cdev_purge(struct cdev *cdev)
{
spin_lock(&cdev_lock);
while (!list_empty(&cdev->list)) {
struct inode *inode;
inode = container_of(cdev->list.next, struct inode, i_devices);
list_del_init(&inode->i_devices);
inode->i_cdev = NULL;
}
spin_unlock(&cdev_lock);
}
/*
* Dummy default file-operations: the only thing this does
* is contain the open that then fills in the correct operations
* depending on the special file...
*/
const struct file_operations def_chr_fops = {
.open = chrdev_open,
.llseek = noop_llseek,
};
static struct kobject *exact_match(dev_t dev, int *part, void *data)
{
struct cdev *p = data;
return &p->kobj;
}
static int exact_lock(dev_t dev, void *data)
{
struct cdev *p = data;
return cdev_get(p) ? 0 : -1;
}
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}
static void cdev_unmap(dev_t dev, unsigned count)
{
kobj_unmap(cdev_map, dev, count);
}
/**
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
*/
void cdev_del(struct cdev *p)
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}
static void cdev_default_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);
struct kobject *parent = kobj->parent;
cdev_purge(p);
kobject_put(parent);
}
static void cdev_dynamic_release(struct kobject *kobj)
{
struct cdev *p = container_of(kobj, struct cdev, kobj);
struct kobject *parent = kobj->parent;
cdev_purge(p);
kfree(p);
kobject_put(parent);
}
static struct kobj_type ktype_cdev_default = {
.release = cdev_default_release,
};
static struct kobj_type ktype_cdev_dynamic = {
.release = cdev_dynamic_release,
};
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
static struct kobject *base_probe(dev_t dev, int *part, void *data)
{
if (request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0)
/* Make old-style 2.4 aliases work */
request_module("char-major-%d", MAJOR(dev));
return NULL;
}
void __init chrdev_init(void)
{
cdev_map = kobj_map_init(base_probe, &chrdevs_lock);
}
/* Let modules do char dev stuff */
EXPORT_SYMBOL(register_chrdev_region);
EXPORT_SYMBOL(unregister_chrdev_region);
EXPORT_SYMBOL(alloc_chrdev_region);
EXPORT_SYMBOL(cdev_init);
EXPORT_SYMBOL(cdev_alloc);
EXPORT_SYMBOL(cdev_del);
EXPORT_SYMBOL(cdev_add);
EXPORT_SYMBOL(__register_chrdev);
EXPORT_SYMBOL(__unregister_chrdev);
/*
* linux/drivers/base/map.c
*
* (C) Copyright Al Viro 2002,2003
* Released under GPL v2.
*
* NOTE: data structure needs to be changed. It works, but for large dev_t
* it will be too slow. It is isolated, though, so these changes will be
* local to that file.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/kdev_t.h>
#include <linux/kobject.h>
#include <linux/kobj_map.h>
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
unsigned index = MAJOR(dev);
unsigned i;
struct probe *p;
if (n > 255)
n = 255;
p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
}
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) {
struct probe **s = &domain->probes[index % 255];
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s;
*s = p;
}
mutex_unlock(domain->lock);
return 0;
}
void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
unsigned index = MAJOR(dev);
unsigned i;
struct probe *found = NULL;
if (n > 255)
n = 255;
mutex_lock(domain->lock);
for (i = 0; i < n; i++, index++) {
struct probe **s;
for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) {
struct probe *p = *s;
if (p->dev == dev && p->range == range) {
*s = p->next;
if (!found)
found = p;
break;
}
}
}
mutex_unlock(domain->lock);
kfree(found);
}
struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
{
struct kobject *kobj;
struct probe *p;
unsigned long best = ~0UL;
retry:
mutex_lock(domain->lock);
for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) {
struct kobject *(*probe)(dev_t, int *, void *);
struct module *owner;
void *data;
if (p->dev > dev || p->dev + p->range - 1 < dev)
continue;
if (p->range - 1 >= best)
break;
if (!try_module_get(p->owner))
continue;
owner = p->owner;
data = p->data;
probe = p->get;
best = p->range - 1;
*index = dev - p->dev;
if (p->lock && p->lock(dev, data) < 0) {
module_put(owner);
continue;
}
mutex_unlock(domain->lock);
kobj = probe(dev, index, data);
/* Currently ->owner protects _only_ ->probe() itself. */
module_put(owner);
if (kobj)
return kobj;
goto retry;
}
mutex_unlock(domain->lock);
return NULL;
}
struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct mutex *lock)
{
struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL);
struct probe *base = kzalloc(sizeof(*base), GFP_KERNEL);
int i;
if ((p == NULL) || (base == NULL)) {
kfree(p);
kfree(base);
return NULL;
}
base->dev = 1;
base->range = ~0;
base->get = base_probe;
for (i = 0; i < 255; i++)
p->probes[i] = base;
p->lock = lock;
return p;
}
/*
* linux/drivers/char/raw.c
*
* Front-end raw character devices. These can be bound to any block
* devices to provide genuine Unix raw character device semantics.
*
* We reserve minor number 0 for a control interface. ioctl()s on this
* device are used to bind the other minor numbers to block devices.
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/backing-dev.h>
#include <linux/module.h>
#include <linux/raw.h>
#include <linux/capability.h>
#include <linux/uio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/gfp.h>
#include <linux/compat.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
struct raw_device_data {
struct block_device *binding;
int inuse;
};
static struct class *raw_class;
static struct raw_device_data *raw_devices;
static DEFINE_MUTEX(raw_mutex);
static const struct file_operations raw_ctl_fops; /* forward declaration */
static int max_raw_minors = MAX_RAW_MINORS;
module_param(max_raw_minors, int, 0);
MODULE_PARM_DESC(max_raw_minors, "Maximum number of raw devices (1-65536)");
/*
* Open/close code for raw IO.
*
* We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to
* point at the blockdev's address_space and set the file handle to use
* O_DIRECT.
*
* Set the device's soft blocksize to the minimum possible. This gives the
* finest possible alignment and has no adverse impact on performance.
*/
static int raw_open(struct inode *inode, struct file *filp)
{
const int minor = iminor(inode);
struct block_device *bdev;
int err;
if (minor == 0) { /* It is the control device */
filp->f_op = &raw_ctl_fops;
return 0;
}
mutex_lock(&raw_mutex);
/*
* All we need to do on open is check that the device is bound.
*/
bdev = raw_devices[minor].binding;
err = -ENODEV;
if (!bdev)
goto out;
igrab(bdev->bd_inode);
err = blkdev_get(bdev, filp->f_mode | FMODE_EXCL, raw_open);
if (err)
goto out;
err = set_blocksize(bdev, bdev_logical_block_size(bdev));
if (err)
goto out1;
filp->f_flags |= O_DIRECT;
filp->f_mapping = bdev->bd_inode->i_mapping;
if (++raw_devices[minor].inuse == 1)
file_inode(filp)->i_mapping =
bdev->bd_inode->i_mapping;
filp->private_data = bdev;
mutex_unlock(&raw_mutex);
return 0;
out1:
blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
out:
mutex_unlock(&raw_mutex);
return err;
}
/*
* When the final fd which refers to this character-special node is closed, we
* make its ->mapping point back at its own i_data.
*/
static int raw_release(struct inode *inode, struct file *filp)
{
const int minor= iminor(inode);
struct block_device *bdev;
mutex_lock(&raw_mutex);
bdev = raw_devices[minor].binding;
if (--raw_devices[minor].inuse == 0)
/* Here inode->i_mapping == bdev->bd_inode->i_mapping */
inode->i_mapping = &inode->i_data;
mutex_unlock(&raw_mutex);
blkdev_put(bdev, filp->f_mode | FMODE_EXCL);
return 0;
}
/*
* Forward ioctls to the underlying block device.
*/
static long
raw_ioctl(struct file *filp, unsigned int command, unsigned long arg)
{
struct block_device *bdev = filp->private_data;
return blkdev_ioctl(bdev, 0, command, arg);
}
static int bind_set(int number, u64 major, u64 minor)
{
dev_t dev = MKDEV(major, minor);
struct raw_device_data *rawdev;
int err = 0;
if (number <= 0 || number >= max_raw_minors)
return -EINVAL;
if (MAJOR(dev) != major || MINOR(dev) != minor)
return -EINVAL;
rawdev = &raw_devices[number];
/*
* This is like making block devices, so demand the
* same capability
*/
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/*
* For now, we don't need to check that the underlying
* block device is present or not: we can do that when
* the raw device is opened. Just check that the
* major/minor numbers make sense.
*/
if (MAJOR(dev) == 0 && dev != 0)
return -EINVAL;
mutex_lock(&raw_mutex);
if (rawdev->inuse) {
mutex_unlock(&raw_mutex);
return -EBUSY;
}
if (rawdev->binding) {
bdput(rawdev->binding);
module_put(THIS_MODULE);
}
if (!dev) {
/* unbind */
rawdev->binding = NULL;
device_destroy(raw_class, MKDEV(RAW_MAJOR, number));
} else {
rawdev->binding = bdget(dev);
if (rawdev->binding == NULL) {
err = -ENOMEM;
} else {
dev_t raw = MKDEV(RAW_MAJOR, number);
__module_get(THIS_MODULE);
device_destroy(raw_class, raw);
device_create(raw_class, NULL, raw, NULL,
"raw%d", number);
}
}
mutex_unlock(&raw_mutex);
return err;
}
static int bind_get(int number, dev_t *dev)
{
struct raw_device_data *rawdev;
struct block_device *bdev;
if (number <= 0 || number >= max_raw_minors)
return -EINVAL;
rawdev = &raw_devices[number];
mutex_lock(&raw_mutex);
bdev = rawdev->binding;
*dev = bdev ? bdev->bd_dev : 0;
mutex_unlock(&raw_mutex);
return 0;
}
/*
* Deal with ioctls against the raw-device control interface, to bind
* and unbind other raw devices.
*/
static long raw_ctl_ioctl(struct file *filp, unsigned int command,
unsigned long arg)
{
struct raw_config_request rq;
dev_t dev;
int err;
switch (command) {
case RAW_SETBIND:
if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
return -EFAULT;
return bind_set(rq.raw_minor, rq.block_major, rq.block_minor);
case RAW_GETBIND:
if (copy_from_user(&rq, (void __user *) arg, sizeof(rq)))
return -EFAULT;
err = bind_get(rq.raw_minor, &dev);
if (err)
return err;
rq.block_major = MAJOR(dev);
rq.block_minor = MINOR(dev);
if (copy_to_user((void __user *)arg, &rq, sizeof(rq)))
return -EFAULT;
return 0;
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
struct raw32_config_request {
compat_int_t raw_minor;
compat_u64 block_major;
compat_u64 block_minor;
};
static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct raw32_config_request __user *user_req = compat_ptr(arg);
struct raw32_config_request rq;
dev_t dev;
int err = 0;
switch (cmd) {
case RAW_SETBIND:
if (copy_from_user(&rq, user_req, sizeof(rq)))
return -EFAULT;
return bind_set(rq.raw_minor, rq.block_major, rq.block_minor);
case RAW_GETBIND:
if (copy_from_user(&rq, user_req, sizeof(rq)))
return -EFAULT;
err = bind_get(rq.raw_minor, &dev);
if (err)
return err;
rq.block_major = MAJOR(dev);
rq.block_minor = MINOR(dev);
if (copy_to_user(user_req, &rq, sizeof(rq)))
return -EFAULT;
return 0;
}
return -EINVAL;
}
#endif
static const struct file_operations raw_fops = {
.read_iter = blkdev_read_iter,
.write_iter = blkdev_write_iter,
.fsync = blkdev_fsync,
.open = raw_open,
.release = raw_release,
.unlocked_ioctl = raw_ioctl,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
static const struct file_operations raw_ctl_fops = {
.unlocked_ioctl = raw_ctl_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = raw_ctl_compat_ioctl,
#endif
.open = raw_open,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
static struct cdev raw_cdev;
static char *raw_devnode(struct device *dev, umode_t *mode)
{
return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev));
}
static int __init raw_init(void)
{
dev_t dev = MKDEV(RAW_MAJOR, 0);
int ret;
if (max_raw_minors < 1 || max_raw_minors > 65536) {
printk(KERN_WARNING "raw: invalid max_raw_minors (must be"
" between 1 and 65536), using %d\n", MAX_RAW_MINORS);
max_raw_minors = MAX_RAW_MINORS;
}
raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors);
if (!raw_devices) {
printk(KERN_ERR "Not enough memory for raw device structures\n");
ret = -ENOMEM;
goto error;
}
ret = register_chrdev_region(dev, max_raw_minors, "raw");
if (ret)
goto error;
cdev_init(&raw_cdev, &raw_fops);
ret = cdev_add(&raw_cdev, dev, max_raw_minors);
if (ret) {
goto error_region;
}
raw_class = class_create(THIS_MODULE, "raw");
if (IS_ERR(raw_class)) {
printk(KERN_ERR "Error creating raw class.\n");
cdev_del(&raw_cdev);
ret = PTR_ERR(raw_class);
goto error_region;
}
raw_class->devnode = raw_devnode;
device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
return 0;
error_region:
unregister_chrdev_region(dev, max_raw_minors);
error:
vfree(raw_devices);
return ret;
}
static void __exit raw_exit(void)
{
device_destroy(raw_class, MKDEV(RAW_MAJOR, 0));
class_destroy(raw_class);
cdev_del(&raw_cdev);
unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), max_raw_minors);
}
module_init(raw_init);
module_exit(raw_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
char buf_in[1024];
int main()
{
int fd;
fd=open("/dev/memchar",O_RDWR|O_CREAT,0666);
if(fd==-1)
{
perror("open file mytest");
exit(1);
}
read(fd,buf_in,1024);
printf("retrieved message is : %s !\n",buf_in);
close(fd);
return 0;
}
\ No newline at end of file
#include <linux/module.h> //1)1~22 行的头文件、全局变量、常量和数据结构;2)23~163 驱动程序主体部分;3)164~224 行是模块相关和设备初始化代码。
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#define MEMCHAR_SIZE 0x1000 /*设备所使用的内存大小 4KB*/
#define MEM_CLEAR 0x1 /*对所占内存清零*/
#define MEMCHAR_MAJOR 199 /* memchar 的主设备号*/
static int memchar_major = MEMCHAR_MAJOR;
struct memchar_dev /*memchar 设备结构体*/
{
struct cdev cdev; /*cdev 结构体*/
unsigned char mem[MEMCHAR_SIZE]; /*设备所用内存*/
};
struct memchar_dev *memchar_devp; /* memchar 设备结构体指针*/
/*--------------------------------------------------------------------------memchar设备文件的open函数*/
int memchar_open(struct inode *inode, struct file *filp)
{
filp->private_data = memchar_devp; /*将设备结构体指针赋值给文件私有数据指针*/
return 0;
}
/*------------------------------------------------------------------------------ 文件释放函数*/
int memchar_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*------------------------------------------------------------------------------ ioctl 设备控制函数 */
static long memchar_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct memchar_dev *dev = filp->private_data; /*获得设备结构体指针*/
switch (cmd)
{
case MEM_CLEAR:
memset(dev->mem, 0, MEMCHAR_SIZE);
printk(KERN_INFO "memchar is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
/*------------------------------------------------------------------------------ 读函数*/
static ssize_t memchar_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct memchar_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= MEMCHAR_SIZE)
return count ? - ENXIO: 0;
if (count > MEMCHAR_SIZE - p)
count = MEMCHAR_SIZE - p;
/*内核空间→用户空间*/
if (copy_to_user(buf, (void*)(dev->mem + p), count))
{
ret = - EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes(s) from %ld\n", count, p);
}
return ret;
}
/*------------------------------------------------------------------------------ 写函数*/
static ssize_t memchar_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct memchar_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= MEMCHAR_SIZE)
return count ? - ENXIO: 0;
if (count > MEMCHAR_SIZE - p)
count = MEMCHAR_SIZE - p;
/*用户空间→内核空间*/
if (copy_from_user(dev->mem + p, buf, count))
ret = - EFAULT;
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "written %d bytes(s) from %ld\n", count, p);
}
return ret;
}
/*------------------------------------------------------------------------------ seek 文件定位函数 */
static loff_t memchar_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig)
{
case 0: /*相对文件开始位置偏移*/
if (offset < 0)
{
ret = - EINVAL;
break;
}
if ((unsigned int)offset > MEMCHAR_SIZE)
{
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: /*相对文件当前位置偏移*/
if ((filp->f_pos + offset) > MEMCHAR_SIZE)
{
ret = - EINVAL;
break;
}
if ((filp->f_pos + offset) < 0)
{
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret;
}
/*------------------------------------------------------------------------------ 文件操作函数列表结构体*/
static const struct file_operations memchar_fops =
{
.owner = THIS_MODULE,
.llseek = memchar_llseek,
.read = memchar_read,
.write = memchar_write,
.unlocked_ioctl = memchar_ioctl,
.open = memchar_open,
.release = memchar_release,
};
/**************************************************** 初始化并注册 cdev*/
static void memchar_setup_cdev(struct memchar_dev *dev, int index)
{
int err, devno = MKDEV(memchar_major, index);
cdev_init(&dev->cdev, &memchar_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &memchar_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
/*------------------------------------------------------------------------------ 设备驱动模块加载函数*/
int memchar_init(void)
{
int result;
dev_t devno = MKDEV(memchar_major, 0);
/* 申请设备号*/
if (memchar_major)
result = register_chrdev_region(devno, 1, "memchar");
else /* 动态申请设备号 */
{
result = alloc_chrdev_region(&devno, 0, 1, "memchar");
memchar_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
memchar_devp = kmalloc(sizeof(struct memchar_dev), GFP_KERNEL);
if (!memchar_devp) /*申请失败*/
{
result = - ENOMEM;
goto fail_malloc;
}
memset(memchar_devp, 0, sizeof(struct memchar_dev));
memchar_setup_cdev(memchar_devp, 0);
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
/*------------------------------------------------------------------------------ 模块卸载函数*/
void memchar_exit(void)
{
cdev_del(&memchar_devp->cdev); /*注销 cdev*/
kfree(memchar_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(memchar_major, 0), 1); /*释放设备号*/
}
/*------------------------------------------------------------------------------ */
MODULE_AUTHOR("SZU HPC 2019");
MODULE_LICENSE("Dual BSD/GPL");
module_param(memchar_major, int, S_IRUGO);
module_init(memchar_init);
module_exit(memchar_exit);
#include <stdio.h>
#include <stdlib.h>
int main(int argc,void **argv)
{ int input_num,result;
input_num= atoi(argv[1]);
result=syscall(326,input_num);
printf("result of mysyscall(%d) is: %d.\n",input_num,result);
return 0;
}
\ No newline at end of file
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT " Hello World enter\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT " Hello World exit\n ");
printk(KERN_ALERT " \n ");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("Song Baohua");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
\ No newline at end of file
#include <linux/module.h> //1)1~22 行的头文件、全局变量、常量和数据结构;2)23~163 驱动程序主体部分;3)164~224 行是模块相关和设备初始化代码。
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#define MEMCHAR_SIZE 0x1000 /*设备所使用的内存大小 4KB*/
#define MEM_CLEAR 0x1 /*对所占内存清零*/
#define MEMCHAR_MAJOR 199 /* memchar 的主设备号*/
static int memchar_major = MEMCHAR_MAJOR;
struct memchar_dev /*memchar 设备结构体*/
{
struct cdev cdev; /*cdev 结构体*/
unsigned char mem[MEMCHAR_SIZE]; /*设备所用内存*/
};
struct memchar_dev *memchar_devp; /* memchar 设备结构体指针*/
/*--------------------------------------------------------------------------memchar设备文件的open函数*/
int memchar_open(struct inode *inode, struct file *filp)
{
filp->private_data = memchar_devp; /*将设备结构体指针赋值给文件私有数据指针*/
return 0;
}
/*------------------------------------------------------------------------------ 文件释放函数*/
int memchar_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*------------------------------------------------------------------------------ ioctl 设备控制函数 */
static long memchar_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct memchar_dev *dev = filp->private_data; /*获得设备结构体指针*/
switch (cmd)
{
case MEM_CLEAR:
memset(dev->mem, 0, MEMCHAR_SIZE);
printk(KERN_INFO "memchar is set to zero\n");
break;
default:
return - EINVAL;
}
return 0;
}
/*------------------------------------------------------------------------------ 读函数*/
static ssize_t memchar_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct memchar_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= MEMCHAR_SIZE)
return count ? - ENXIO: 0;
if (count > MEMCHAR_SIZE - p)
count = MEMCHAR_SIZE - p;
/*内核空间→用户空间*/
if (copy_to_user(buf, (void*)(dev->mem + p), count))
{
ret = - EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes(s) from %ld\n", count, p);
}
return ret;
}
/*------------------------------------------------------------------------------ 写函数*/
static ssize_t memchar_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct memchar_dev *dev = filp->private_data; /*获得设备结构体指针*/
/*分析和获取有效的写长度*/
if (p >= MEMCHAR_SIZE)
return count ? - ENXIO: 0;
if (count > MEMCHAR_SIZE - p)
count = MEMCHAR_SIZE - p;
/*用户空间→内核空间*/
if (copy_from_user(dev->mem + p, buf, count))
ret = - EFAULT;
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "written %d bytes(s) from %ld\n", count, p);
}
return ret;
}
/*------------------------------------------------------------------------------ seek 文件定位函数 */
static loff_t memchar_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig)
{
case 0: /*相对文件开始位置偏移*/
if (offset < 0)
{
ret = - EINVAL;
break;
}
if ((unsigned int)offset > MEMCHAR_SIZE)
{
ret = - EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1: /*相对文件当前位置偏移*/
if ((filp->f_pos + offset) > MEMCHAR_SIZE)
{
ret = - EINVAL;
break;
}
if ((filp->f_pos + offset) < 0)
{
ret = - EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret;
}
/*------------------------------------------------------------------------------ 文件操作函数列表结构体*/
static const struct file_operations memchar_fops =
{
.owner = THIS_MODULE,
.llseek = memchar_llseek,
.read = memchar_read,
.write = memchar_write,
.unlocked_ioctl = memchar_ioctl,
.open = memchar_open,
.release = memchar_release,
};
/**************************************************** 初始化并注册 cdev*/
static void memchar_setup_cdev(struct memchar_dev *dev, int index)
{
int err, devno = MKDEV(memchar_major, index);
cdev_init(&dev->cdev, &memchar_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &memchar_fops;
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}
/*------------------------------------------------------------------------------ 设备驱动模块加载函数*/
int memchar_init(void)
{
int result;
dev_t devno = MKDEV(memchar_major, 0);
/* 申请设备号*/
if (memchar_major)
result = register_chrdev_region(devno, 1, "memchar");
else /* 动态申请设备号 */
{
result = alloc_chrdev_region(&devno, 0, 1, "memchar");
memchar_major = MAJOR(devno);
}
if (result < 0)
return result;
/* 动态申请设备结构体的内存*/
memchar_devp = kmalloc(sizeof(struct memchar_dev), GFP_KERNEL);
if (!memchar_devp) /*申请失败*/
{
result = - ENOMEM;
goto fail_malloc;
}
memset(memchar_devp, 0, sizeof(struct memchar_dev));
memchar_setup_cdev(memchar_devp, 0);
return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return result;
}
/*------------------------------------------------------------------------------ 模块卸载函数*/
void memchar_exit(void)
{
cdev_del(&memchar_devp->cdev); /*注销 cdev*/
kfree(memchar_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(memchar_major, 0), 1); /*释放设备号*/
}
/*------------------------------------------------------------------------------ */
MODULE_AUTHOR("SZU HPC 2019");
MODULE_LICENSE("Dual BSD/GPL");
module_param(memchar_major, int, S_IRUGO);
module_init(memchar_init);
module_exit(memchar_exit);
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册