// SPDX-License-Identifier: GPL-2.0
/*
* linux / drivers / char / misc . c
*
* Generic misc open routine by Johan Myreen
*
* Based on code from Linus
*
* Teemu Rantanen ' s Microsoft Busmouse support and Derrick Cole ' s
* changes incorporated into 0 . 97 pl4
* by Peter Cervasio ( pete % q106fm . uucp @ wupost . wustl . edu ) ( 08 SEP92 )
* See busmouse . c for particulars .
*
* Made things a lot mode modular - easy to compile in just one or two
* of the misc drivers , as they are now completely independent . Linus .
*
* Support for loadable modules . 8 - Sep - 95 Philip Blundell < pjb27 @ cam . ac . uk >
*
* Fixed a failing symbol register to free the device registration
* Alan Cox < alan @ lxorguk . ukuu . org . uk > 21 - Jan - 96
*
* Dynamic minors and / proc / mice by Alessandro Rubini . 26 - Mar - 96
*
* Renamed to misc and miscdevice to be more accurate . Alan Cox 26 - Mar - 96
*
* Handling of mouse minor numbers for kerneld :
* Idea by Jacques Gelinas < jack @ solucorp . qc . ca > ,
* adapted by Bjorn Ekwall < bj0rn @ blox . se >
* corrected by Alan Cox < alan @ lxorguk . ukuu . org . uk >
*
* Changes for kmod ( from kerneld ) :
* Cyrus Durgin < cider @ speakeasy . org >
*
* Added devfs support . Richard Gooch < rgooch @ atnf . csiro . au > 10 - Jan - 1998
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
/*
* Head entry for the doubly linked miscdevice list
*/
static LIST_HEAD(misc_list);
static DEFINE_MUTEX(misc_mtx);
/*
* Assigned numbers .
*/
static DEFINE_IDA(misc_minors_ida);
static int misc_minor_alloc(int minor)
{
int ret = 0 ;
if (minor == MISC_DYNAMIC_MINOR) {
/* allocate free id */
ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1 ,
MINORMASK, GFP_KERNEL);
} else {
ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
}
return ret;
}
static void misc_minor_free(int minor)
{
ida_free(&misc_minors_ida, minor);
}
#ifdef CONFIG_PROC_FS
static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
{
mutex_lock(&misc_mtx);
return seq_list_start(&misc_list, *pos);
}
static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
return seq_list_next(v, &misc_list, pos);
}
static void misc_seq_stop(struct seq_file *seq, void *v)
{
mutex_unlock(&misc_mtx);
}
static int misc_seq_show(struct seq_file *seq, void *v)
{
const struct miscdevice *p = list_entry(v, struct miscdevice, list);
seq_printf(seq, "%3i %s\n" , p->minor, p->name ? p->name : "" );
return 0 ;
}
static const struct seq_operations misc_seq_ops = {
.start = misc_seq_start,
.next = misc_seq_next,
.stop = misc_seq_stop,
.show = misc_seq_show,
};
#endif
static int misc_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct miscdevice *c = NULL, *iter;
int err = -ENODEV;
const struct file_operations *new_fops = NULL;
mutex_lock(&misc_mtx);
list_for_each_entry(iter, &misc_list, list) {
if (iter->minor != minor)
continue ;
c = iter;
new_fops = fops_get(iter->fops);
break ;
}
/* Only request module for fixed minor code */
if (!new_fops && minor < MISC_DYNAMIC_MINOR) {
mutex_unlock(&misc_mtx);
request_module("char-major-%d-%d" , MISC_MAJOR, minor);
mutex_lock(&misc_mtx);
list_for_each_entry(iter, &misc_list, list) {
if (iter->minor != minor)
continue ;
c = iter;
new_fops = fops_get(iter->fops);
break ;
}
}
if (!new_fops)
goto fail;
/*
* Place the miscdevice in the file ' s
* private_data so it can be used by the
* file operations , including f_op - > open below
*/
file->private_data = c;
err = 0 ;
replace_fops(file, new_fops);
if (file->f_op->open)
err = file->f_op->open(inode, file);
fail:
mutex_unlock(&misc_mtx);
return err;
}
static char *misc_devnode(const struct device *dev, umode_t *mode)
{
const struct miscdevice *c = dev_get_drvdata(dev);
if (mode && c->mode)
*mode = c->mode;
if (c->nodename)
return kstrdup(c->nodename, GFP_KERNEL);
return NULL;
}
static const struct class misc_class = {
.name = "misc" ,
.devnode = misc_devnode,
};
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};
/**
* misc_register - register a miscellaneous device
* @ misc : device structure
*
* Register a miscellaneous device with the kernel . If the minor
* number is set to % MISC_DYNAMIC_MINOR a minor number is assigned
* and placed in the minor field of the structure . For other cases
* the minor number requested is used .
*
* The structure passed is linked into the kernel and may not be
* destroyed until it has been unregistered . By default , an open ( )
* syscall to the device sets file - > private_data to point to the
* structure . Drivers don ' t need open in fops for this .
*
* A zero is returned on success and a negative errno code for
* failure .
*/
int misc_register(struct miscdevice *misc)
{
dev_t dev;
int err = 0 ;
bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
if (is_dynamic) {
int i = misc_minor_alloc(misc->minor);
if (i < 0 ) {
err = -EBUSY;
goto out;
}
misc->minor = i;
} else {
struct miscdevice *c;
int i;
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
err = -EBUSY;
goto out;
}
}
i = misc_minor_alloc(misc->minor);
if (i < 0 ) {
err = -EBUSY;
goto out;
}
}
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device =
device_create_with_groups(&misc_class, misc->parent, dev,
misc, misc->groups, "%s" , misc->name);
if (IS_ERR(misc->this_device)) {
misc_minor_free(misc->minor);
if (is_dynamic) {
misc->minor = MISC_DYNAMIC_MINOR;
}
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front , so that later devices can " override "
* earlier defaults
*/
list_add(&misc->list, &misc_list);
out:
mutex_unlock(&misc_mtx);
return err;
}
EXPORT_SYMBOL(misc_register);
/**
* misc_deregister - unregister a miscellaneous device
* @ misc : device to unregister
*
* Unregister a miscellaneous device that was previously
* successfully registered with misc_register ( ) .
*/
void misc_deregister(struct miscdevice *misc)
{
if (WARN_ON(list_empty(&misc->list)))
return ;
mutex_lock(&misc_mtx);
list_del_init(&misc->list);
device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor));
misc_minor_free(misc->minor);
if (misc->minor > MISC_DYNAMIC_MINOR)
misc->minor = MISC_DYNAMIC_MINOR;
mutex_unlock(&misc_mtx);
}
EXPORT_SYMBOL(misc_deregister);
static int __init misc_init(void )
{
int err;
struct proc_dir_entry *misc_proc_file;
misc_proc_file = proc_create_seq("misc" , 0 , NULL, &misc_seq_ops);
err = class_register(&misc_class);
if (err)
goto fail_remove;
err = __register_chrdev(MISC_MAJOR, 0 , MINORMASK + 1 , "misc" , &misc_fops);
if (err < 0 )
goto fail_printk;
return 0 ;
fail_printk:
pr_err("unable to get major %d for misc devices\n" , MISC_MAJOR);
class_unregister(&misc_class);
fail_remove:
if (misc_proc_file)
remove_proc_entry("misc" , NULL);
return err;
}
subsys_initcall(misc_init);
Messung V0.5 in Prozent C=57 H=91 G=75
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland