6.5.2 Реализация драйвера kapi |
Предыдущая Содержание Следующая |
Драйвер kapi является символьным драйвером, реализованным в виде модуля ядра. В этом разделе обсуждаются детали реализации драйвера для ядра 2.6. Существуют две основные структуры данных.
▪struct file_operations kapi_fops: эта таблица содержит функции файловых операций, такие как open, close, ioctl и другие для данного драйвера. ▪Обратите внимание, что файловые операции read, write и lseek установлены в NULL. Эти операции недопустимы для драйвера kapi, так как все операции выполняются через интерфейс ioctl. ▪struct miscdevice kapi_dev: драйвер kapi зарегистрирован как символьный драйвер, выполняющий разнообразные операции (miscellaneous character driver). Младшим номером является KAPI_MINOR (111). Старшим номером любого символьного драйвера, выполняющего разнообразные операции, является 10. ▪Каждый модуль ядра имеет функцию инициализации модуля и функцию освобождения модуля. Драйвер 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);
|
Предыдущая Содержание Следующая |