В настоящее время я изучаю программирование драйвера устройства linux и хотел бы управлять аппаратным устройством с помощью драйвера блочного устройства.
Мое аппаратное устройство использует три контакта для связи с окружающей средой
- Часы - Выход -> Данные часы
- Данные - двунаправленный -> Строка, которая передает данные в / из устройства
- Мощность - выход -> Блок питания устройства
Это устройство использует собственный протокол. Он хранит данные, которые можно разделить на несколько файлов.
Что я сделал
В настоящее время я разработал символьное устройство, которое экспортируется в пользовательское пространство и реализует несколько ioctl. Затем я читаю / записываю данные с устройства на приложение пользователя, которое абстрагирует разделенную память устройства как одну зону памяти.
Реализованы следующие ioctl:
- POWER_ON
- power_off
- СБРОС
- SEND_COMMAND
Эта реализация полностью функциональна, и я хотел бы перейти к программированию драйвера устройства, преобразовав пару {char_driver, userspace_app} в драйвер устройства с одним блоком, который был бы доступен для чтения / записи напрямую.
Что я хочу сделать
Для драйвера char я заполнил дерево устройств соответствующей информацией, чтобы использовать его как platform_device . Информация о выводах ввода / вывода была загружена с него, и я использовал подсистемы gpio и regulator для определения ввода / вывода.
Я хотел бы реализовать драйвер блока по-другому, предоставив соответствующие контакты драйверу при вставке. Затем я буду использовать его как platform_device , но моя главная цель - понять основные механизмы, поэтому я хочу попробовать несколько решений.
Вот что я уже сделал
#define EMERG(fmt, ...) printk(KERN_EMERG fmt, ##__VA_ARGS__)
#define ALERT(fmt, ...) printk(KERN_ALERT fmt, ##__VA_ARGS__)
#define CRIT(fmt, ...) printk(KERN_CRIT fmt, ##__VA_ARGS__)
#define ERROR(fmt, ...) printk(KERN_ERR fmt, ##__VA_ARGS__)
#define WARNING(fmt, ...) printk(KERN_WARNING fmt, ##__VA_ARGS__)
#define NOTICE(fmt, ...) printk(KERN_NOTICE fmt, ##__VA_ARGS__)
#define INFO(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
#define DEBUG(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
/* Driver name */
#define DRIVER_NAME "devtest"
#define GPIO_CLK_NAME DRIVER_NAME"-clk"
#define GPIO_CLK_PIN 85
#define GPIO_IO_NAME DRIVER_NAME"-io"
#define GPIO_IO_PIN 98
#define GPIO_RST_NAME DRIVER_NAME"-rst"
#define GPIO_RST_PIN 73
static unsigned int device_counter = 0;
typedef struct {
const char *name;
int major;
int minor;
struct device *dev;
struct regulator *power_reg;
}devtest_dev_t;
static int devtest_io_init(devtest_dev_t* devtest, unsigned int io_pin, unsigned int clk_pin, unsigned int rst_pin)
{
int ret = 0;
if(0 > devm_gpio_request(devtest->dev, io_pin, GPIO_IO_NAME))
{
ERROR("%s : Unable to allocate IO pin (%d)\n", DRIVER_NAME, clk_pin);
ret = -EIO;
}
else if(0 > gpio_direction_input(io_pin))
{
ERROR("%s : Unable to set IO pin direction (%d)\n", DRIVER_NAME, io_pin);
ret = -EIO;
}
else if(0 > devm_gpio_request(devtest->dev, clk_pin, GPIO_CLK_NAME))
{
ERROR("%s : Unable to allocate CLK pin (%d)\n", DRIVER_NAME, clk_pin);
ret = -EIO;
}
else if(0 > gpio_direction_output(clk_pin, 0))
{
ERROR("%s : Unable to set CLK pin direction (%d)\n", DRIVER_NAME, clk_pin);
ret = -EIO;
}
else if(0 > devm_gpio_request(devtest->dev, rst_pin, GPIO_RST_NAME))
{
ERROR("%s : Unable to allocate RST pin (%d)\n", DRIVER_NAME, rst_pin);
ret = -EIO;
}
else if(0 > gpio_direction_output(rst_pin, 0))
{
ERROR("%s : Unable to set RST pin direction (%d)\n", DRIVER_NAME, rst_pin);
ret = -EIO;
}
return ret;
}
static int __init devtest_init(void)
{
struct regulator *pwr_reg = NULL;
nf4tag_dev_t* self = NULL;
if(NULL == (self = vmalloc(sizeof(devtest_dev_t))))
{
ERROR("%s : Unable to allocate memory for device\n", DRIVER_NAME);
return -1;
}
memset(self, 0, sizeof(devtest_dev_t));
DEBUG("%s : Device allocated and initialized\n", DRIVER_NAME);
if(NULL == (self->dev = vmalloc(sizeof(struct device))))
{
ERROR("%s : Unable to allocate memory for device structure\n", DRIVER_NAME);
return -2;
}
DEBUG("%s : Device struct allocated (%p)\n", DRIVER_NAME, self->dev);
if(NULL == (pwr_reg = devm_regulator_get(self->dev, "power_regulator")))
{
ERROR("%s : Unable to allocate power regulator\n", DRIVER_NAME);
return -EIO;
}
self->power_reg = pwr_reg;
DEBUG("%s : Power regulator initialized\n", DRIVER_NAME);
if(0 > devtest_io_init(self, GPIO_IO_PIN, GPIO_CLK_PIN, GPIO_RST_PIN))
{
ERROR("%s : Unable to allocate gpios\n", DRIVER_NAME);
return -EIO;
}
DEBUG("%s : gpios allocated\n", DRIVER_NAME);
device_counter += 1;
return 0;
}
module_init(devtest_init);
С помощью этой реализации я могу выделить gpios, но у меня возникли проблемы с подсистемой регулятора, так как у меня есть ядро Ой при его вставке.
# insmod devtest.ko
devtest: loading out-of-tree module taints kernel.
devtest : Device allocated and initialized
devtest : Device struct allocated (e089d000)
Unable to handle kernel paging request at virtual address e8bd8028
pgd = d4574000
[e8bd8028] *pgd=00000000
Internal error: Oops: 5 [#1] ARM
Modules linked in: nf4(O+)
CPU: 0 PID: 217 Comm: insmod Tainted: G O 4.9.30 #1
Hardware name: Atmel SAMA5
task: d457b400 task.stack: d44f4000
PC is at __of_find_property+0x10/0x64
LR is at of_phandle_iterator_init+0x38/0x8c
pc : [<c03f4e98>] lr : [<c03f69b4>] psr: a00d0093
sp : d44f5cb8 ip : 00000000 fp : 00000001
r10: 00000000 r9 : 00280000 r8 : 00000000
r7 : d44f5d80 r6 : d44f5ccc r5 : e8bd8010 r4 : d44f5cf4
r3 : 00000000 r2 : d44f5ccc r1 : d44f5d80 r0 : e8bd8010
Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment none
Control: 10c53c7d Table: 34574059 DAC: 00000051
Process insmod (pid: 217, stack limit = 0xd44f4208)
Stack: (0xd44f5cb8 to 0xd44f6000)
5ca0: d44f5cf4 e8bd8010
5cc0: 400d0013 c03f69b4 ffffffff d44f5d0c e089d000 00000000 d44f5dbc c084af48
5ce0: bf000110 c03f6c38 00000000 00000000 bf00010f 00000000 00000000 00000000
5d00: 00000000 00000000 00000000 00000000 00000000 00000000 c05a702c e089d000
5d20: bf000110 c03f6d00 00000000 d44f5d30 00000020 d44f5d80 ffffff00 ffff0a00
5d40: c083b7a0 e089d000 bf000110 d44f5dbc c084af48 bf000110 00280000 00000000
5d60: 00000001 c02c3fcc e089d000 d44f5d7c d44f5dbc c0305648 c05a702c c0305658
5d80: 65776f70 65725f72 616c7567 2d726f74 70707573 0000796c 00000000 600d0013
5da0: 00000000 00000000 e089d000 e089d000 c084af48 c0308e58 bf000348 ffffffed
5dc0: 00000000 00000000 d4518050 e089d000 bf000110 00000000 00000001 2bae759c
5de0: bf000300 c030b080 e089b000 bf002000 d4516240 bf000348 00000000 bf0020a0
5e00: bf000300 c01016e0 d45b3c00 c084e7cc 00000001 a00e0013 d4851000 c05a23c4
5e20: 00000000 d45b3c00 d40bb800 c0812f98 c0812f98 c01a1088 00000012 d4516280
5e40: 00000001 a00e0013 d4516240 e0898000 00000001 d4518a80 bf000300 00000001
5e60: d4516240 bf000348 d4518a80 c016b5bc 00000001 2bae759c d44f5f54 00000001
5e80: d4518a88 c0160720 bf00030c 00007fff bf000300 c015e0e4 bf00030c 000003e8
5ea0: bf000450 e0899610 c05015f0 bf00030c 00000000 00000000 d44f5f48 d44f5f44
5ec0: 00001688 c01ac6f4 00001688 00000000 00000000 00000000 00000000 00000000
5ee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5f00: 00000000 00000000 7fffffff 00000000 00000003 00093008 0000017b c0107664
5f20: d44f4000 00000000 00000000 c0160d40 7fffffff 00000000 00000003 a00e0013
5f40: d45506c0 e0898000 00001688 00000000 00000020 e0898000 00001688 e08992a0
5f60: e089918c e0898c84 00000484 000004c4 00000000 00000000 00000000 000008a0
5f80: 00000016 00000017 00000010 00000000 0000000d 00000000 00093008 bef79f3e
5fa0: 00000002 c01074a0 00093008 bef79f3e 00000003 00093008 00000000 bef79f3e
5fc0: 00093008 bef79f3e 00000002 0000017b bef79f3e 00000000 b6fc5000 00000000
5fe0: bef79ca0 bef79c90 0001f899 b6f43972 800e0030 00000003 00000000 00000000
[<c03f4e98>] (__of_find_property) from [<c03f69b4>] (of_phandle_iterator_init+0x38/0x8c)
[<c03f69b4>] (of_phandle_iterator_init) from [<c03f6c38>] (__of_parse_phandle_with_args+0x24/0xc4)
[<c03f6c38>] (__of_parse_phandle_with_args) from [<c03f6d00>] (of_parse_phandle+0x28/0x4c)
[<c03f6d00>] (of_parse_phandle) from [<c0305658>] (regulator_dev_lookup+0x60/0x190)
[<c0305658>] (regulator_dev_lookup) from [<c0308e58>] (_regulator_get+0x60/0x264)
[<c0308e58>] (_regulator_get) from [<c030b080>] (_devm_regulator_get+0x9c/0xc0)
[<c030b080>] (_devm_regulator_get) from [<bf0020a0>] (nf4tag_init+0xa0/0x260 [nf4])
[<bf0020a0>] (devtest_init [nf4]) from [<c01016e0>] (do_one_initcall+0x40/0x170)
[<c01016e0>] (do_one_initcall) from [<c016b5bc>] (do_init_module+0x60/0x1b0)
[<c016b5bc>] (do_init_module) from [<c0160720>] (load_module+0x1b74/0x1f80)
[<c0160720>] (load_module) from [<c0160d40>] (SyS_finit_module+0xa8/0xb8)
[<c0160d40>] (SyS_finit_module) from [<c01074a0>] (ret_fast_syscall+0x0/0x3c)
Code: e3500000 012fff1e e92d4070 e1a06002 (e5904018)
---[ end trace 39a2cd9292b80271 ]---
Очевидно, что я чего-то не понимаю. Я думаю, что проблема в том, что используемый struct device
выделен, но никогда не инициализирован, поэтому функция devm_regulator_get
не может найти регулятор с именем 'power_regulator'.
Итак, мои вопросы:
- Правильно ли мое предположение?
- Можно ли использовать
struct regulator
, когда драйвер устройства не проверяется?
- Если да, то как это сделать?
- Если нет, то какая лучшая альтернатива?