character device driver
The read() and write() callback functions in our ‘cmosram.c’ device-driver only transfer a single byte of data for each time called, so it takes 128 system-calls to read all of the RTC storage-locations!
Can you improve this driver’s efficiency, by modifying its read() and write() functions, so they’ll transfer as many valid bytes as the supplied buffer’s space could hold?
code is as follows
char modname[] = "cmosram"; // name of this kernel module
char devname[] = "cmos"; // name for the device's file
int my_major = 70; // major ID-number for driver
int cmos_size = 128; // total bytes of cmos memory
int write_max = 9; // largest 'writable' address
ssize_t my_read( struct file *file, char *buf, size_t len, loff_t *pos )
{
unsigned char datum;
if ( *pos >= cmos_size ) return 0;
outb( *pos, 0x70 ); datum = inb( 0x71 );
if ( put_user( datum, buf ) ) return -EFAULT;
*pos += 1;
return 1;
}
ssize_t my_write( struct file *file, const char *buf, size_t len, loff_t *pos )
{
unsigned char datum;
if ( *pos >= cmos_size ) return 0;
if ( *pos > write_max ) return -EPERM;
if ( get_user( datum, buf ) ) return -EFAULT;
outb( *pos, 0x70 ); outb( datum, 0x71 );
*pos += 1;
return 1;
}
loff_t my_llseek( struct file *file, loff_t pos, int whence )
{
loff_t newpos = -1;
switch ( whence )
{
case 0: newpos = pos; break; // SEEK_SET
开发者_运维百科 case 1: newpos = file->f_pos + pos; break; // SEEK_CUR
case 2: newpos = cmos_size + pos; break; // SEEK_END
}
if (( newpos < 0 )||( newpos > cmos_size )) return -EINVAL;
file->f_pos = newpos;
return newpos;
}
struct file_operations my_fops = {
owner: THIS_MODULE,
llseek: my_llseek,
write: my_write,
read: my_read,
};
static int __init my_init( void )
{
printk( "<1>\nInstalling \'%s\' module ", devname );
printk( "(major=%d) \n", my_major );
return register_chrdev( my_major, devname, &my_fops );
}
static void __exit my_exit(void )
{
unregister_chrdev( my_major, devname );
printk( "<1>Removing \'%s\' module\n", devname );
}
module_init( my_init );
module_exit( my_exit );
MODULE_LICENSE("GPL");
You should use the len
parameter and loop inb
/outb
respectively to read/write the maximum number of bytes that fits into the given buffer. Then return len
(the number of read bytes!) instead of return 1
.
I won't give you example code as you should know better how to read that CMOS stuff.
You could do the get_user
in a loop, but 128 invocations of the function is probably not super efficient. You can do it all in one shot with the following approach.
First of all, you need to copy_from_user
the buf into a kernel-side buffer. You don't know the size of the buffer ahead of time, so you should k/vmalloc it (EDIT: you can skip the memory allocation since your data is <= 128 bytes, maybe have a local buffer on the stack)
u8 * kernel_buf;
kernel_buf = kmalloc(len, GFP_KERNEL);
if(!kernel_buf)
return -ENOMEM;
.
.
kfree(kernel_buf); // no memory leaks please
You also need to verify you can read/write len
bytes from the userspace buffer, then copy to/from the kernel side buffer you just allocated.
if(!access_ok(VERIFY_WRITE, buf, len))
{
kfree(kernel_buf);
return -EFAULT;
}
if(copy_from_user(kernel_buf, buf, len))
return -EFAULT;
// now do your business in a for loop
for(i = 0; i < len; i++)
{
outb(*pos + i, 0x70);
outb(kernel_buf[i], 0x71),
}
// cleanup kernel buf
kfree(kernel_buf);
return len;
Obviously you should double-check my suggestions as I haven't compiled or tested them, but hopefully rhis helps.
Good luck and have fun!
精彩评论