There are still questions,
1. How do I know which file name is displayed in the/dev directory after the device is registered?
Generate the file in class_device_create. Research is still pending.
The process of registering a character device
1. alloc_chrdev_region dynamically allocates device numbers
2. cdev_init initializes cdev and registers the fops
3. cdev_add register this character device to the kernel
After cdev_add, the specified file is not generated in the/dev directory.
Use mknod for Example
Mknod/dev/yw_device c 251 0
To generate a file under/dev, you can use this character device.
Generate a node under the/dev/File
1. class_create
2. class_device_create
In this way, you can generate a device file in the/dev directory.
Main. c
-------------------------------------------------------------------
# Include <linux/fs. h>
# Include <linux/module. h>
# Include <linux/cdev. h>
# Include <linux/device. h>
# Include <asm/uaccess. h>
# Include <asm/io. h>
/* Per-device (per-bank) structure */
Struct cmos_dev {
Unsigned short current_pointer;/* Current pointer within the bank */
Unsigned int size;/* Size of the bank */
Int bank_number;/* CMOS bank number */
S
Truct cdev
Cdev;/* The cdev structure is usually embedded in its own structure */
Char name [10];/* Name of I/O region */
/*... * // * Mutexes, spinlocks, wait queues ,..*/
} * Cmos_devp;
/* File operations structure. Defined in linux/fs. h */
Static struct file_operations cmos_fops = {
. Owner = THIS_MODULE,/* Owner */
//. Open = cmos_open,/* Open method */
//. Release = cmos_release,/* Release method */
//. Read = cmos_read,/* Read method */
//. Write = cmos_write,/* Write method */
//. Llseek = cmos_llseek,/* Seek method */
//. Ioctl = cmos_ioctl,/* Ioctl method */
};
Static dev_t cmos_dev_number;/* Allotted device number */
Struct class * cmos_class;/* Tie with the device model */
# Define NUM_CMOS_BANKS 2
# Define CMOS_BANK_SIZE (0xFF * 8)
# Define DEVICE_NAME "cmos"
# Define CMOS_BANK0_INDEX_PORT 0x70
# Define cmos_bank0_data_port 0x71
# Define cmos_bank1_index_port 0x72
# Define cmos_bank1_data_port 0x73
Unsigned char addrports [num_cmos_banks] = {cmos_bank0_index_port,
Cmos_bank1_index_port ,};
Unsigned char dataports [num_cmos_banks] = {cmos_bank0_data_port,
Cmos_bank1_data_port ,};
/*
* Open CMOS bank
*
*/
Int cmos_open (struct inode * inode, struct file * file)
{
Struct cmos_dev * cmos_devp;
/* Get the per-device structure that contains this cdev */
Cmos_devp = container_of (inode-> I _cdev, struct cmos_dev, cdev );
/* Easy access to cmose_devp from rest of the entry points */
/*When it is enabled, point private_data to cmos_devp.
*/
File-> private_data = cmos_devp;
/* Initialize some fields */
Cmos_devp-> size = CMOS_BANK_SIZE;
Cmos_devp-> current_pointer = 0;
Return 0;
}
/*
* Release CMOS bank
*/
Int cmos_release (struct inode * inode, struct file * file)
{
Struct cmos_dev * cmos_devp = file-> private_data;
/* Release file pointer */
Cmos_devp-> current_pointer = 0;
Return 0;
}
/*
* Read data from specified CMOS bank
*/
Unsigned char port_data_in (unsigned char offset, int Bank)
{
Unsigned char data;
If (unlikely (bank> = num_cmos_banks )){
Printk ("unknown CMOS bank/N ");
Return 0;
} Else {
Outb (offset, addrports [Bank]);/* read a byte */
Data = INB (dataports [Bank]);
}
Return data;
}
/*
* Read from a CMOS bank at bit-level Granularity
*/
Ssize_t cmos_read (struct file * file, char * Buf,
Size_t count, loff_t * PPOs)
{
Struct cmos_dev * cmos_devp = file-> private_data;
Char data [CMOS_BANK_SIZE];
Unsigned char mask;
Int xferred = 0, I = 0, l, zero_out;
Int start_byte = cmos_devp-> current_pointer/8;
Int start_bit = cmos_devp-> current_pointer % 8;
If (cmos_devp-> current_pointer> = cmos_devp-> size ){
Return 0;/* EOF */
}
/* Adjust count if it edges past the end of the CMOS bank */
If (cmos_devp-> current_pointer + count> cmos_devp-> size ){
Count = cmos_devp-> size-cmos_devp-> current_pointer;
}
/* Get the specified number of bits from the CMOS */
While (xferred <count ){
Data [I] = port_data_in (start_byte, cmos_devp-> bank_number)> start_bit;
Xferred + = (8-start_bit );
If (start_bit) & (count + start_bit> 8 )){
Data [I] | = (port_data_in (start_byte + 1,
Cmos_devp-> bank_number) <(8-start_bit ));
Xferred + = start_bit;
}
Start_byte ++;
I ++;
}
If (xferred> count ){
/* Zero out (xferred-count) bits from the MSB
Of the last data byte */
Zero_out = xferred-count;
Mask = 1 <(8-zero_out );
For (L = 0; L <zero_out; l ++ ){
Data [I-1 ~ Mask; mask <= 1;
}
Xferred = count;
}
If (! Xferred) Return-EIO;
/* Copy the read bits to the user buffer */
If (copy_to_user (BUF, (void *) data, (xferred/8) + 1 ))! = 0 ){
Return-EIO;
}
/* Increment the file pointer by the number of xferred bits */
Cmos_devp-> current_pointer + = xferred;
Return xferred;
}
/*
* Write data to specified CMOS bank
*/
Void port_data_out (unsigned char offset, unsigned char data, int bank)
{
If (unlikely (bank> = NUM_CMOS_BANKS )){
Printk ("Unknown CMOS Bank/n ");
Return;
} Else {
Outb (offset, addrports [bank]);/* Output a byte */
Outb (data, dataports [bank]);
}
Return;
}
/*
* Write to a CMOS bank at bit-level granularity. 'Count' holds
* Number of bits to be written.
*/
Ssize_t cmos_write (struct file * file, const char * buf, size_t count, loff_t * ppos)
{
Struct cmos_dev * cmos_devp = file-> private_data;
Int xferred = 0, I = 0, l, end_l, start_l;
Char * kbuf, tmp_kbuf;
Unsigned char tmp_data = 0, mask;
Int start_byte = cmos_devp-> current_pointer/8;
Int start_bit = cmos_devp-> current_pointer % 8;
If (cmos_devp-> current_pointer> = cmos_devp-> size ){
Return 0;/* EOF */
}
/* Adjust count if it edges past the end of the CMOS bank */
If (cmos_devp-> current_pointer + count> cmos_devp-> size ){
Count = cmos_devp-> size-cmos_devp-> current_pointer;
}
Kbuf = kmalloc (count/8) + 1, GFP_KERNEL );
If (kbuf = NULL)
Return-ENOMEM;
/* Get the bits from the user buffer */
If (copy_from_user (kbuf, buf, (count/8) + 1 )){
Kfree (kbuf );
Return-EFAULT;
}
/* Write the specified number of bits to the CMOS bank */
While (xferred <count ){
Tmp_data = port_data_in (start_byte, cmos_devp-> bank_number );
Mask = 1 <start_bit;
End_l = 8;
If (count-xferred) <(8-start_bit )){
End_l = (count-xferred) + start_bit;
}
For (l = start_bit; l <end_l; l ++ ){
Tmp_data & = ~ Mask; mask <= 1;
}
Tmp_kbuf = kbuf [I];
Mask = 1 <end_l;
For (l = end_l; l <8; l ++ ){
Tmp_kbuf & = ~ Mask;
Mask <= 1;
}
Port_data_out (start_byte, tmp_data | (tmp_kbuf <start_bit), cmos_devp-> bank_number );
Xferred + = (end_l-start_bit );
If (xferred <count) & (start_bit) & (count + start_bit> 8 )){
Tmp_data = port_data_in (start_byte + 1, cmos_devp-> bank_number );
Start_l = (start_bit + count) % 8 );
Mask = 1 <start_l;
For (l = 0; l <start_l; l ++ ){
Mask> = 1;
Tmp_data & = ~ Mask;
}
Port_data_out (start_byte + 1 ),
Tmp_data | (kbuf [I]> (8-start_bit )),
Cmos_devp-> bank_number );
Xferred + = start_l;
}
Start_byte ++;
I ++;
}
If (! Xferred) return-EIO;
/* Push the offset pointer forward */
Cmos_devp-> current_pointer + = xferred;
Return xferred;/* Return the number of written bits */
}
/*
* Driver Initialization
*/
Int _ init cmos_init (void)
{
Int I;
/* Request dynamic allocation of a device major number */
/* This function only assigns a device number and has no specific action */
/* This will create an enty in/Proc/devices
*/
If (Alloc_chrdev_region
(& Cmos_dev_number, 0,
NUM_CMOS_BANKS, DEVICE_NAME) <0 ){
Printk ("Can't register/n ");
}
Printk ("Major % d, Minor % d/n", MAJOR (cmos_dev_number), MINOR (cmos_dev_number ));
/* Populate sysfs entries, but don't see it */
Cmos_class =Class_create
(THIS_MODULE, DEVICE_NAME );
For (I = 0; I <num_cmos_banks; I ++ ){
/* Allocate memory for the per-device structure dynamic allocation */
Cmos_devp = kmalloc (sizeof (struct cmos_dev), gfp_kernel );
If (! Cmos_devp ){
Printk ("Bad kmalloc/N ");
Return 1;
}
/* Request I/O region */
/* The I/O region is displayed in/Proc/ioports
*/
Sprintf (cmos_devp-> name, "cmos % d", I );
If (! (Request_region (addrports [I], 2, cmos_devp-> name ))){
Printk ("cmos: I/O port 0x % x is not free./n", addrports [I]);
Return-EIO;
}
/* Fill in the bank number to correlate this
* Device with the corresponding CMOS bank */
Cmos_devp-> bank_number = I;
/* Connect the file operations with the cdev to initialize an important cdev Structure
*/
Cdev_init
(& Cmos_devp-> cdev, & cmos_fops );
Cmos_devp-> cdev. owner = THIS_MODULE;
/* Connect the major/minor number to the cdevIn this way, the device is added.
*/
If (Cdev_add
(& Cmos_devp-> cdev, (cmos_dev_number + I), 1 )){
Printk ("Bad cdev/n ");
Return 1;
}
/* Send uevents to udev, so it'll create/dev nodes */
/*After this command, the corresponding file named "CMOS % d" will be created in the/dev directory.
*/
/*However, the book says that rules should be added to/etc/udev/rules. D, but it seems that they can be generated without adding
*/
Class_device_create
(Cmos_class, null, (cmos_dev_number + I ),
Null, "CMOS % d", I );
}
Printk ("CMOS driver initialized./N ");
Return 0;
}
/* Driver exit */
Void _ exit
Cmos_cleanup (void)
{
Int I;
/* Remove the cdev created by cdev_add
*/
Cdev_del
(& Cmos_devp-> cdev );
/* Release the major number */
/* This is used to release the device major number required by alloc_chrdev_region
*/
Unregister_chrdev_region
(Cmos_dev_number, NUM_CMOS_BANKS );
/* Release I/O region */
For (I = 0; I <NUM_CMOS_BANKS; I ++ ){
Class_device_destroy (cmos_class, MKDEV (MAJOR (cmos_dev_number), I ));
Release_region (addrports [I], 2 );
}
/* Destroy cmos_class created by class_create
*/
Class_destroy (cmos_class );
Return;
}
Module_init (cmos_init );
Module_exit (cmos_cleanup );
MODULE_LICENSE ("GPL ");