Char Device Driver Example for Kernel 2.6

现在还有疑问,

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");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值