/* chardev.c
* Copyright (C) 1998-1999 by Ori Pomerantz
*
* Create a character device (read only)
*/
/* The necessary header files */
/* Standard in kernel modules */
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
/* For character devices */
#include <linux/fs.h> /* The character device definitions are here */
#include <linux/wrapper.h> /* A wrapper which does next to nothing at present, but may help for compatibility with future versions of Linux */
/* In 2.2.3 /usr/include/linux/version.h includes a macro for this, but 2.0.35 doesn't - so I add it here if necessary. */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c)
((a)*65536+(b)*256+(c))
#endif
/* Conditional compilation. LINUX_VERSION_CODE is the code (as per KERNEL_VERSION) of this version. */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
#include <asm/uaccess.h> /* for put_user */
#endif
#define SUCCESS 0
/* Device Declarations **************************** */
/* The name for our device, as it will appear in /proc/devices */
#define DEVICE_NAME "char_dev"
/* The maximum length of the message from the device */
#define BUF_LEN 80
/* Is the device open right now? Used to prevent concurent access into the same device */
static int Device_Open = 0;
/* The message the device will give when asked */
static char Message[BUF_LEN];
/* How far did the process reading the message get? Useful if the message is larger than the size of the buffer we get to fill in device_read. */
static char *Message_Ptr;
/* This function is called whenever a process attempts to open the device file */
static int device_open(struct inode *inode, struct file *file) {
static int counter = 0;
#ifdef DEBUG
printk("device_open(%p,%p)\n", inode, file);
#endif
/* This is how you get the minor device number in case you have more than one physical device using the driver. */
printk("Device: %d.%d\n", inode->i_rdev >> 8, inode->i_rdev & 0xFF);
/* We don't want to talk to two processes at the same time */
if (Device_Open) return -EBUSY;
/* If this was a process, we would have had to be
* more careful here.
*
* In the case of processes, the danger would be
* that one process might have check Device_Open
* and then be replaced by the schedualer by another
* process which runs this function. Then, when the
* first process was back on the CPU, it would assume
* the device is still not open.
*
* However, Linux guarantees that a process won't be
* replaced while it is running in kernel context.
*
* In the case of SMP, one CPU might increment
* Device_Open while another CPU is here, right after
* the check. However, in version 2.0 of the
* kernel this is not a problem because there's a lock
* to guarantee only one CPU will be kernel module at
* the same time. This is bad in terms of
* performance, so version 2.2 changed it.
* Unfortunately, I don't have access to an SMP box
* to check how it works with SMP. */
Device_Open++;
/* Initialize the message. */
sprintf(Message, "If I told you once, I told you %d times - %s", counter++, "Hello, world\n");
/* The only reason we're allowed to do this sprintf
* is because the maximum length of the message
* (assuming 32 bit integers - up to 10 digits
* with the minus sign) is less than BUF_LEN, which
* is 80. BE CAREFUL NOT TO OVERFLOW BUFFERS,
* ESPECIALLY IN THE KERNEL!!! */
Message_Ptr = Message;
/* Make sure that the module isn't removed while
* the file is open by incrementing the usage count
* (the number of opened references to the module, if
* it's not zero rmmod will fail)
*/
MOD_INC_USE_COUNT;
return SUCCESS;
}
/* This function is called when a process closes the
* device file. It doesn't have a return value in
* version 2.0.x because it can't fail (you must ALWAYS
* be able to close a device). In version 2.2.x it is
* allowed to fail - but we won't let it. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static int device_release(struct inode *inode, struct file *file)
#else
static void device_release(struct inode *inode, struct file *file)
#endif
{
#ifdef DEBUG
printk("device_release(%p,%p)\n", inode, file);
#endif
/* We're now ready for our next caller */
Device_Open--;
/* Decrement the usage count, otherwise once you opened the file you'll never get rid of the module. */
MOD_DEC_USE_COUNT;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
return 0;
#endif
}
/* This function is called whenever a process which
* have already opened the device file attempts to
* read from it. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t device_read(struct file *file,
char *buffer, /* The buffer to fill with data */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
#else
static int device_read(struct inode *inode, struct file *file,
char *buffer, /* The buffer to fill with the data */
int length) /* The length of the buffer (mustn't write beyond that!) */
#endif
{
/* Number of bytes actually written to the buffer */
int bytes_read = 0;
/* If we're at the end of the message, return 0 (which signifies end of file) */
if (*Message_Ptr == 0) return 0;
/* Actually put the data into the buffer */
while (length && *Message_Ptr) {
/* Because the buffer is in the user data segment,
* not the kernel data segment, assignment wouldn't
* work. Instead, we have to use put_user which
* copies data from the kernel data segment to the
* user data segment. */
put_user(*(Message_Ptr++), buffer++);
length--;
bytes_read++;
}
#ifdef DEBUG
printk("Read %d bytes, %d left\n", bytes_read, length);
#endif
/* Read functions are supposed to return the number of bytes actually inserted into the buffer */
return bytes_read;
}
/* This function is called when somebody tries to write
* into our device file - unsupported in this example. */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static ssize_t device_write(struct file *file,
const char *buffer, /* The buffer */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
#else
static int device_write(struct inode *inode, struct file *file, const char *buffer, int length)
#endif
{
return -EINVAL;
}
/* Module Declarations ***************************** */
/* The major device number for the device. This is
* global (well, static, which in this context is global
* within this file) because it has to be accessible * both for registration and for release. */
static int Major;
/* This structure will hold the functions to be
* called when a process does something to the device
* we created. Since a pointer to this structure is
* kept in the devices table, it can't be local to
* init_module. NULL is for unimplemented functions. */
struct file_operations Fops = {
NULL, /* seek */
device_read, device_write,
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
device_open,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
NULL, /* flush */
#endif
device_release /* a.k.a. close */
};
/* Initialize the module - Register the character device */
int init_module() {
/* Register the character device (atleast try) */
Major = module_register_chrdev(0, DEVICE_NAME, &Fops);
/* Negative values signify an error */
if (Major < 0) {
printk("%s device failed with %d\n", "Sorry, registering the character", Major);
return Major;
}
printk("%s The major device number is %d.\n", "Registeration is a success.", Major);
printk("If you want to talk to the device driver,\n");
printk("you'll have to create a device file. \n");
printk("We suggest you use:\n");
printk("mknod <name> c %d <minor>\n", Major);
printk("You can try different minor numbers %s", "and see what happens.\n");
return 0;
}
/* Cleanup - unregister the appropriate file from /proc */
void cleanup_module() {
int ret;
/* Unregister the device */
ret = module_unregister_chrdev(Major, DEVICE_NAME);
/* If there's an error, report it */
if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret);
}
ΠΡΡ ΠΎΠ΄Π½ΠΈΠΊΠΈ Π΄Π»Ρ ΡΠ°Π·Π½ΡΡ Π²Π΅ΡΡΠΈΠΉ ΡΠ΄ΡΠ° Files
Π‘ΠΈΡΡΠ΅ΠΌΠ½ΡΠ΅ Π²ΡΠ·ΠΎΠ²Ρ, ΠΊΠΎΡΠΎΡΡΠ΅ ΡΠ²Π»ΡΡΡΡΡ Π³Π»Π°Π²Π½ΡΠΌ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠΎΠΌ ΡΠ΄ΡΠ°, Π΄Π»Ρ ΠΏΡΠΎΡΠ΅ΡΡΠΎΠ² Π²ΡΠ³Π»ΡΠ΄ΡΡ ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎ, Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΠΎ ΠΎΡ Π²Π΅ΡΡΠΈΠΈ. ΠΠΎΠ²ΡΠΉ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ² ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½, Π½ΠΎ ΡΡΠ°ΡΡΠ΅ ΠΎΠ±ΡΡΠ½ΠΎ Π±ΡΠ΄ΡΡ Π²Π΅ΡΡΠΈ ΡΠ΅Π±Ρ ΡΠΎΡΠ½ΠΎ ΡΠ°ΠΊ, ΠΊΠ°ΠΊ ΠΈ ΡΠ°Π½ΡΡΠ΅. ΠΡΠΎ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π»Ρ ΠΎΠ±ΡΠ°ΡΠ½ΠΎΠΉ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΠΌΠΎΡΡΠΈ Π½ΠΎΠ²Π°Ρ Π²Π΅ΡΡΠΈΡ ΡΠ΄ΡΠ°, ΠΊΠ°ΠΊ ΠΏΡΠ΅Π΄ΠΏΠΎΠ»Π°Π³Π°Π΅ΡΡΡ, Π½Π΅ ΡΠ°Π·ΡΡΠ²Π°Π΅Ρ ΡΠ΅Π³ΡΠ»ΡΡΠ½ΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΡΡ. Π Π±ΠΎΠ»ΡΡΠΈΠ½ΡΡΠ²Π΅ ΡΠ»ΡΡΠ°Π΅Π², ΡΠ°ΠΉΠ»Ρ ΡΡΡΡΠΎΠΉΡΡΠ²Π° ΡΠ°ΠΊΠΆΠ΅ ΠΎΡΡΠ°Π½ΡΡΡΡ ΡΠ΅ΠΌΠΈ ΠΆΠ΅ ΡΠ°ΠΌΡΠΌΠΈ. Π‘ Π΄ΡΡΠ³ΠΎΠΉ ΡΡΠΎΡΠΎΠ½Ρ, Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠ΅ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ ΡΠ΄ΡΠ° ΠΌΠΎΠ³ΡΡ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡΡΡ ΠΌΠ΅ΠΆΠ΄Ρ Π²Π΅ΡΡΠΈΡΠΌΠΈ.
ΠΠ΅ΡΡΠΈΠΈ ΡΠ΄ΡΠ° Linux ΡΠ°Π·Π΄Π΅Π»Π΅Π½Ρ ΠΌΠ΅ΠΆΠ΄Ρ ΡΡΡΠΎΠΉΡΠΈΠ²ΡΠΌΠΈ Π²Π΅ΡΡΠΈΡΠΌΠΈ (n.<Π§Π΅ΡΠ½ΠΎΠ΅ ΡΠΈΡΠ»ΠΎ>.m) ΠΈ Π²Π΅ΡΡΠΈΠΈ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ (n.<ΠΠ΅ΡΠ΅ΡΠ½ΠΎΠ΅ ΡΠΈΡΠ»ΠΎ>.m). ΠΠ΅ΡΡΠΈΠΈ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ Π²ΠΊΠ»ΡΡΠ°ΡΡ Π²ΡΠ΅ Π½ΠΎΠ²ΡΠ΅ ΠΈΠ΄Π΅ΠΈ, Π²ΠΊΠ»ΡΡΠ°Ρ ΡΠ΅, ΠΊΠΎΡΠΎΡΡΠ΅ Π±ΡΠ΄ΡΡ ΡΡΠΈΡΠ°ΡΡΡΡ ΠΎΡΠΈΠ±ΠΊΠΎΠΉ ΠΈΠ»ΠΈ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎ Π²ΡΠΏΠΎΠ»Π½Π΅Π½Ρ Π² ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ Π²Π΅ΡΡΠΈΠΈ. Π ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠ΅ ΠΡ Π½Π΅ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄ΠΎΠ²Π΅ΡΡΡΡ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ Π² ΡΠΎΠΌ ΠΏΠ»Π°Π½Π΅, ΡΡΠΎ ΠΎΠ½ ΠΎΡΡΠ°Π½Π΅ΡΡΡ ΡΠ΅ΠΌ ΠΆΠ΅ ΡΠ°ΠΌΡΠΌ Π² Π²Π΅ΡΡΠΈΡΡ ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠΈ. Π ΡΡΡΠΎΠΉΡΠΈΠ²ΡΡ Π²Π΅ΡΡΠΈΡΡ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠΆΠΈΠ΄Π°ΡΡ, ΡΡΠΎ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ ΠΎΡΡΠ°Π½Π΅ΡΡΡ ΡΠ΅ΠΌ ΠΆΠ΅ ΡΠ°ΠΌΡΠΌ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΠΎ ΠΎΡ Π²Π΅ΡΡΠΈΠΈ ΠΈΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΎΡΠΈΠ±ΠΎΠΊ (ΡΠΈΡΠ»ΠΎ m).
ΠΡΠ° Π²Π΅ΡΡΠΈΡ MPG Π²ΠΊΠ»ΡΡΠ°Π΅Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΡ Π΄Π»Ρ Π²Π΅ΡΡΠΈΠΈ 2.0.x ΠΈ Π²Π΅ΡΡΠΈΠΈ ΡΠ΄ΡΠ° Linux. Π’Π°ΠΊ ΠΊΠ°ΠΊ ΠΈΠΌΠ΅ΡΡΡΡ ΡΠ°Π·Π»ΠΈΡΠΈΡ ΠΌΠ΅ΠΆΠ΄Ρ Π½ΠΈΠΌΠΈ, ΡΡΠ΅Π±ΡΠ΅ΡΡΡ ΡΡΠ»ΠΎΠ²Π½Π°Ρ ΡΡΠ°Π½ΡΠ»ΡΡΠΈΡ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ Π²Π΅ΡΡΠΈΠΈ. Π‘ΠΏΠΎΡΠΎΠ± ΡΠ΄Π΅Π»Π°ΡΡ ΡΡΠΎ ΡΠ²ΠΎΠ΄ΠΈΡΡΡ ΠΊ ΡΠΎΠΌΡ, ΡΡΠΎΠ±Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΌΠ°ΠΊΡΠΎΠΊΠΎΠΌΠ°Π½Π΄Ρ LINUX_VERSION_CODE. Π Π²Π΅ΡΡΠΈΠΈ a.b.c ΡΠ΄ΡΠ° Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΡΡΠΎΠΉ ΠΌΠ°ΠΊΡΠΎΠΊΠΎΠΌΠ°Π½Π΄Ρ Π±ΡΠ»ΠΎ Π±Ρ 216a+28b+c. Π§ΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΡΠ°ΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ Π΄Π»Ρ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΉ Π²Π΅ΡΡΠΈΠΈ, ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΌΠ°ΠΊΡΠΎΠΊΠΎΠΌΠ°Π½Π΄Ρ KERNEL_VERSION. Π’Π°ΠΊ ΠΊΠ°ΠΊ ΡΡΠΎΡ ΠΌΠ°ΠΊΡΠΎΡ Π½Π΅ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ Π² 2.0.35, ΠΌΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ Π΅Π³ΠΎ ΡΠ°ΠΌΠΈ Π² ΡΠ»ΡΡΠ°Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΠΈ.