Драйвер символьного устройства

Функции обратного вызова read() и write() в нашем драйвере устройства cmosram.c передают только один байт данных при каждом вызове, поэтому для считывания всех мест хранения RTC требуется 128 системных вызовов!

Можете ли вы повысить эффективность этого драйвера, изменив его функции read() и write(), чтобы они передавали столько допустимых байтов, сколько может вместить предоставленный буфер?

код выглядит следующим образом

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

2 ответа

Вы должны использовать len параметр и цикл inb/outb соответственно для чтения / записи максимальное количество байтов, которое помещается в данный буфер. затем return len (количество прочитанных байтов!) вместо return 1,

Я не буду давать вам пример кода, так как вы должны лучше знать, как читать этот материал CMOS.

Вы могли бы сделать get_user в цикле, но 128 вызовов функции, вероятно, не супер эффективны. Вы можете сделать все это одним выстрелом, используя следующий подход.

Прежде всего, вам нужно copy_from_user buf в буфер на стороне ядра. Вы не знаете размер буфера заблаговременно, поэтому вы должны k/vmalloc его (РЕДАКТИРОВАТЬ: вы можете пропустить выделение памяти, так как ваши данные <= 128 байт, возможно, иметь локальный буфер в стеке)

u8 * kernel_buf;

kernel_buf = kmalloc(len, GFP_KERNEL);
if(!kernel_buf)
   return -ENOMEM;
.
.
kfree(kernel_buf); // no memory leaks please

Вы также должны убедиться, что вы можете читать / писать len байты из буфера пользовательского пространства, затем скопируйте в / из буфера стороны ядра, который вы только что выделили.

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;

Очевидно, вам следует перепроверить мои предложения, поскольку я их не компилировал и не проверял, но, надеюсь, это поможет.

Удачи и приятного времяпровождения!

Другие вопросы по тегам