6.5.2 Реализация драйвера kapi

Предыдущая  Содержание  Следующая V*D*V

Драйвер kapi является символьным драйвером, реализованным в виде модуля ядра. В этом разделе обсуждаются детали реализации драйвера для ядра 2.6.

Существуют две основные структуры данных.

 

struct file_operations kapi_fops: эта таблица содержит функции файловых операций, такие как open, close, ioctl и другие для данного драйвера.
 
static struct file_operations kapi_fops = {
 .owner   = THIS_MODULE,
 .llseek  = NULL,
 .read    = NULL,
 .write   = NULL,
 .ioctl   = kapi_ioctl,
 .open    = kapi_open,
 .release = kapi_release,
};
 

Обратите внимание, что файловые операции read, write и lseek установлены в NULL. Эти операции недопустимы для  драйвера kapi, так как все операции выполняются через интерфейс ioctl.

struct miscdevice kapi_dev: драйвер kapi зарегистрирован как символьный драйвер, выполняющий разнообразные операции (miscellaneous character driver). Младшим номером является KAPI_MINOR (111). Старшим номером любого символьного драйвера, выполняющего разнообразные операции, является 10.
 
static struct miscdevice kapi_dev = {
 KAPI_MINOR,
 "kapi",
 &kapi_fops,
};
 

Каждый модуль ядра имеет функцию инициализации модуля и функцию освобождения модуля. Драйвер kapi предоставляет kapi_init и kapi_cleanup_module в качестве своих функций инициализации и очистки, соответственно.

 

Функция kapi_init регистрирует драйвер kapi как символьный драйвер, выполняющий разнообразные операции, с младшим номером KAPI_MINOR. Функция вызывается при загрузке модуля.

 

static int __init

kapi_init(void)

{

  int ret;

  ret = misc_register(&kapi_dev);

  if (ret)

    printk(KERN_ERR "kapi: can't misc_register on

                     minor=%d\n", KAPI_MINOR);

  return ret;

}

 

kapi_cleanup_module вызывается, когда модуль выгружается. Эта функция отменяет регистрацию драйвера.

 

static void __exit

kapi_cleanup_module(void)

{

  misc_deregister(&kapi_dev);

}

 

Процедуры open и close просто отслеживают количество одновременно работающих пользователей данного драйвера. Они используются в основном для отладки.

 

static int

kapi_open(struct inode *inode, struct file *file)

{

  kapi_open_cnt++;

  return 0;

}

 

static int

kapi_release(struct inode *inode, struct file *file)

{

  kapi_open_cnt--;

  return 0;

}

 

Давайте теперь обсудим ядро драйвера kapi, функцию kapi_ioctl. kapi_ioctl зарегистрирована в таблице fops как операция ioctl для данного драйвера.

Функция kapi_ioctl выполняет следующие операции:

 

1.Копирование передаваемого пользователем объекта kfunc_t в объект ядра kfunc_t.

2.Выделение памяти для DIR_IN и DIR_OUT аргументов.

3.Копирование из пользовательских буферов всех аргументов, которые имеют флаг направления DIR_IN, в буферы ядра.

4.Вызов запрошенной функции ядра.

5.Копирование из буферов ядра всех аргументов с флагом направления DIR_OUT в пользовательские буферы.

6.Наконец, копирование возвращаемого значения функции ядра в пространство пользователя и освобождение всех выделенных буферов ядра.

 

Аргументом для kapi_ioctl является указатель на объект типа kfunc_t. Первый шаг заключается в копировании объекта kfunc_t в память ядра.

 

static int

kapi_ioctl(struct inode *inode, struct file *file,

           unsigned int cmd, unsigned long arg)

{

  int i,err;

  kfunc_t kdata,udata;

 

  if(copy_from_user(&udata, (kfunc_t *)arg,

                    sizeof(kfunc_t)))

return -EFAULT;

 

Выделяем буферы ядра для всех аргументов и возвращаемого значения. Выполняем копирование всех аргументов, имеющих направление DIR_IN.

 

for (i = 0 ; i < udata.num ; i++){

  kdata.arg[i].val = kmalloc(udata.arg[i].size,

                             GFP_KERNEL);

  if (udata.arg[i].dir == DIR_IN){

    if (copy_from_user(kdata.arg[i].val, udata.arg[i].val,

                       udata.arg[i].size))

      goto error;

  }

}

kdata.ret.val = kmalloc(udata.ret.size, GFP_KERNEL);

 

Вызываем запрошенную функцию ядра. В этом примере мы предоставили ioctl для функции ядра my_kernel_func. Вам необходимо таким же образом добавить свои функции в инструкцию switch. Идентификатор функции также должен быть добавлен в перечисление function_id в kapi.h. Возвращаемое значение вызываемой функции следует сохранить в kdata.ret.val.

 

switch (cmd) {

 

case MY_KERNEL_FUNC:

  *(int *)(kdata.ret.val) =

      my_kernel_func(*(int *)kdata.arg[0].val,

                     (char *)kdata.arg[1].val,

                     (char *)kdata.arg[2].val);

  break;

 

default:

  return -EINVAL;

}

 

Теперь пришло время отправить результат работы вызываемой функции ядра обратно в функцию-заглушку пространства пользователя. Копируем все аргументы, имеющие направление DIR_OUT, и возвращаемое значение функции в пользовательское пространство. Также освобождаем выделенные буферы ядра.

 

err = 0;

for (i = 0 ; i < udata.num ; i++){

  if (udata.arg[i].dir == DIR_OUT){

    if (copy_to_user(udata.arg[i].val, kdata.arg[i].val,

                     udata.arg[i].size))

      err = -EFAULT;

  }

  kfree(kdata.arg[i].val);

}

 

/* копирование возвращаемого значения */

if (copy_to_user(udata.ret.val, kdata.ret.val,

                 udata.ret.size))

  err = -EFAULT;

 

kfree(kdata.ret.val);

return err;

}

 

Наконец, my_kernel_func просто выводит пользовательский ввод и возвращает целое значение 2. Для простоты мы поместили эту функцию в kapi-kernel.c. Вы не должны добавлять свои функции в этот файл. Также не забудьте проэкспортировать функцию, используя EXPORT_SYMBOL, так как драйвер kapi загружается как модуль ядра.

 

int my_kernel_func(int val, char *in_str, char *out_str){

  printk(KERN_DEBUG"val = %d, str = %s\n", val, in_str);

  strcpy(out_str, "Hello User Space");

  return 2;

}

 

EXPORT_SYMBOL(my_kernel_func);

 

Предыдущая  Содержание  Следующая