在2.6.32出现各种错误,自己调试修正了一下,ioctl除外,欢迎大家拍砖
cmos.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define NUM_CMOS_BANKS 2
struct cmos_dev {
unsigned
short current_pointer;
unsigned int
size; int
bank_number; struct cdev
cdev; char
name[10]; } *cmos_devp[NUM_CMOS_BANKS];
int cmos_open(struct inode *inode, struct file *file);
int cmos_release(struct inode *inode, struct file *file);
ssize_t cmos_read(struct file *file, char *buf, size_t count,
loff_t *ppos);
ssize_t cmos_write(struct file *file, const char *buf, size_t
count, loff_t *ppos);
static loff_t cmos_llseek(struct file *file, loff_t offset, int
orig);
static int cmos_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
unsigned char port_data_in(unsigned char offset, int bank);
void port_data_out(unsigned char offset, unsigned char data,int
bank);
static struct file_operations cmos_fops = {
.owner =
THIS_MODULE, .open =
cmos_open, .release =
cmos_release, .read =
cmos_read, .write =
cmos_write, .llseek =
cmos_llseek, .ioctl =
cmos_ioctl, };
static dev_t
cmos_dev_number; struct class
*cmos_class;
#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,};
int
cmos_open(struct inode *inode, struct file *file)
{
struct
cmos_dev *cmos_devp;
cmos_devp =
container_of(inode->i_cdev, struct cmos_dev, cdev);
file->private_data = cmos_devp;
cmos_devp->size = CMOS_BANK_SIZE;
cmos_devp->current_pointer = 0;
return
0;
}
int
cmos_release(struct inode *inode, struct file *file)
{
struct
cmos_dev *cmos_devp = file->private_data;
cmos_devp->current_pointer = 0;
return
0;
}
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;
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;
}
if
(cmos_devp->current_pointer + count > cmos_devp->size)
{
count = cmos_devp->size - cmos_devp->current_pointer;
}
data =
(char *)kmalloc(CMOS_BANK_SIZE, GFP_ATOMIC);
if (data ==
NULL) {
printk("cmos_read kmalloc fail\n");
return 0;
}
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;
mask = 1 << (8 - zero_out);
for (l=0; l < zero_out; l++) {
data[i-1] &= ~mask; mask <<= 1;
}
xferred = count;
}
if
(!xferred) {
kfree(data);
data = NULL;
return -EIO;
}
if
(copy_to_user(buf, (void *)data, ((xferred/8)+1)) != 0) {
kfree(data);
data = NULL;
return -EIO;
}
cmos_devp->current_pointer += xferred;
kfree(data);
data =
NULL;
return
xferred;
}
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;
}
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;
if
(copy_from_user(kbuf,buf,(count/8)+1)) {
kfree(kbuf);
return -EFAULT;
}
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;
cmos_devp->current_pointer += xferred;
return
xferred;
}
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]);
data = inb(dataports[bank]);
}
return
data;
}
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]);
outb(data, dataports[bank]);
}
return;
}
static loff_t
cmos_llseek(struct file *file, loff_t offset,
int orig)
{
struct
cmos_dev *cmos_devp = file->private_data;
switch
(orig) {
case
0:
if (offset >= cmos_devp->size) {
return -EINVAL;
}
cmos_devp->current_pointer = offset;
break;
case
1:
if ((cmos_devp->current_pointer + offset) >=
cmos_devp->size) {
return -EINVAL;
}
cmos_devp->current_pointer = offset;
break;
case
2:
return -EINVAL;
default:
return -EINVAL;
}
return(cmos_devp->current_pointer);
}
#define CMOS_ADJUST_CHECKSUM 1
#define CMOS_VERIFY_CHECKSUM 2
#define CMOS_BANK1_CRC_OFFSET 0x1E
static int
cmos_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned
short crc = 0;
unsigned
char buf;
switch
(cmd) {
#if 0
case
CMOS_ADJUST_CHECKSUM:
crc = adjust_cmos_crc(0, 0);
crc = adjust_cmos_crc(1, crc);
port_data_out(CMOS_BANK1_CRC_OFFSET,
(unsigned char)(crc & 0xFF), 1);
port_data_out((CMOS_BANK1_CRC_OFFSET + 1),
(unsigned char) (crc >> 8), 1);
break;
case
CMOS_VERIFY_CHECKSUM:
crc = adjust_cmos_crc(0, 0);
crc = adjust_cmos_crc(1, crc);
buf = port_data_in(CMOS_BANK1_CRC_OFFSET, 1);
if (buf != (unsigned char) (crc & 0xFF)) return -EINVAL;
buf = port_data_in((CMOS_BANK1_CRC_OFFSET+1), 1);
if (buf != (unsigned char)(crc >> 8)) return -EINVAL;
break;
#endif default:
return -EIO;
}
return
0;
}
static int __init cmos_init(void)
{
int i,
ret;
if
(alloc_chrdev_region(&cmos_dev_number, 0,
NUM_CMOS_BANKS, DEVICE_NAME) < 0) {
printk(KERN_DEBUG "CMOS Can't register device\n"); return -1;
}
else {
printk(KERN_DEBUG "CMOS Major dev num = %d\n",
MAJOR(cmos_dev_number));
}
cmos_class =
class_create(THIS_MODULE, DEVICE_NAME);
if
(IS_ERR(cmos_class)) {
printk(KERN_ERR "CMOS %s:
couldn't create class\n", __FILE__);
return
PTR_ERR(cmos_class);
}
else {
printk(KERN_ERR "CMOS %s: create class success\n", __FILE__);
}
for (i=0;
i
cmos_devp[i] = kmalloc(sizeof(struct cmos_dev), GFP_KERNEL);
if (!cmos_devp[i]) {
printk("CMOS Bad Kmalloc\n"); return -ENOMEM;
}
sprintf(cmos_devp[i]->name, "cmos%d", i);
//必须增加,否则报input output error
release_region(addrports[i], 2);
if (!(request_region(addrports[i], 2, cmos_devp[i]->name)))
{
printk("CMOS cmos: I/O port 0x%x is not free.\n",
addrports[i]);
return -EIO;
}
cmos_devp[i]->bank_number = i;
cdev_init(&cmos_devp[i]->cdev, &cmos_fops);
cmos_devp[i]->cdev.owner = THIS_MODULE;
ret = cdev_add(&cmos_devp[i]->cdev, (cmos_dev_number + i),
1);
if (ret) {
printk(KERN_ERR "CMOS %s: Bad cdev %d\n", __FILE__, i);
return ret;
}
else
{ printk(KERN_ERR "CMOS %s: good cdev %d\n", __FILE__, i);
}
//源代码有问题,最新的版本需要增加一个参数,也就是第四个参数
device_create(cmos_class, NULL, MKDEV(MAJOR(cmos_dev_number),
i),
NULL, "cmos%d", i);
}
printk(KERN_ERR "CMOS %s: Driver Initialized.\n", __FILE__);
return 0;
}
module_init(cmos_init);
static void __exit cmos_cleanup(void)
{
int i;
unregister_chrdev_region((cmos_dev_number), NUM_CMOS_BANKS);
for (i=0;
i
device_destroy (cmos_class, MKDEV(MAJOR(cmos_dev_number),
i));
release_region(addrports[i], 2);
cdev_del(&cmos_devp[i]->cdev);
kfree(cmos_devp[i]);
}
class_destroy(cmos_class);
return;
}
module_exit(cmos_cleanup);
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("Driver for PC-style RTCs");
MODULE_LICENSE("GPL");
------------------------------------------------
Makefile:
# Comment/uncomment the following line to enable/disable
debugging
#DEBUG = y
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULLC_DEBUG # "-O" is needed
to expand inlines
else
DEBFLAGS = -O2
endif
EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINC)
TARGET = cmos
ifneq ($(KERNELRELEASE),)
scullc-objs := cmos.o
obj-m := cmos.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)
modules
endif
install:
install -d $(INSTALLDIR)
install -c $(TARGET).o $(INSTALLDIR)
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
.tmp_versions
depend .depend dep:
$(CC) $(EXTRA_CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif
---------------------------------------------------
编译执行:
$ uname -a
Linux powertree 2.6.32-358.el6.i686 #1 SMP Tue Jan 29 11:48:01 EST
2013 i686 i686 i386 GNU/Linux
make
make -C /lib/modules/2.6.32-358.el6.i686/build M=/elinuxdriver/cmos
LDDINC =elinuxdriver/cmos modules
make[1]: Entering directory
`/usr/src/kernels/2.6.32-358.el6.i686'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory
`/usr/src/kernels/2.6.32-358.el6.i686'
$ sudo insmod cmos
$ ls /dev/cmos*
/dev/cmos0 /dev/cmos1