现在还有疑问,
1.怎么知道注册了device后,在/dev目录下显示的是哪个文件名?
在class_device_create中生成该文件。 仍有待研究。
注册字符设备的过程
1. alloc_chrdev_region 动态分配设备号
2. cdev_init 初始化cdev,将fops注册
3. cdev_add 将这个字符设备注册到kernel
这样cdev_add后,在/dev目录下并不生成指定的文件。
使用 mknod 如
mknod /dev/yw_device c 251 0
生成一个/dev下的文件,就可以使用这个字符设备了。
产生/dev/文件下的node
1. class_create
2. class_device_create
这样就可以在/dev 目录下生成一个设备文件,使用了。
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 一般都内嵌在自己的结构内*/
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 */
/*打开的时候,将private_data指向cmos_devp,在read和write需要使用 */
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 the
* 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 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 动态分配*/
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 初始化重要的cdev结构 */
cdev_init (&cmos_devp->cdev, &cmos_fops);
cmos_devp->cdev.owner = THIS_MODULE;
/* Connect the major/minor number to the cdev 这样才算添加了设备 */
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 */
/*这条命令之后,就会在/dev目录下创建相应名字 “cmos%d” 的文件 */
/*不过书上说要在/etc/udev/rules.d中添加规则,但是貌似不加也可以生成 */
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");