Перейти к содержанию
    

Нужен драйвер клавиатуры на чипе CH423 для Linux Embedded.

 

Написать свой - не проблема, но написать для CH423 - оказалось проблемой.

Чип CH423  сидит на шине I2C вместе с другими устройствами, например с тачскрином.

Обычно I2C устройство имеет адрес. Т.е. на шине может быть куча устройств, у каждого свой адрес. При обращении к слейву мастер отправляет на шину сначала адрес устройства, потом адрес регистра, потом данные.

В ch423 нет адреса устройства. Есть много регистров, но нет адреса устройства. Например чтобы записать в системный регистр параметры, нужно отправить 0x48 0x01. где 48 - адрес регистра, 01 - данные.

чтобы прочитать I/O регистр, то ...

Read bidirectional I/O command (RD-IO Control Line in Figure)
The output byte 1 of this command is 01001101B, namely, 4DH; the input byte 2 is the current pin state of
bidirectional I/O pins IO7-IO0.

и т.д. по всем регистрам.

 

Вопрос - как будет выглядить такой драйвер в линуксе?

 Вот пример драйвера для клавиатуры I2C на чипе adp5588

 
 i2c {
        #address-cells = <1>;
        #size-cells = <0>;

        keys@34 {
            compatible = "adi,adp5588";
            reg = <0x34>;

            vcc-supply = <&vcc>;
            interrupts = <21 IRQ_TYPE_EDGE_FALLING>;

               ];

        };

Т.е. есть шина i2c, на этой шине есть микросхема adp5588. адрес, по которому обращаться к этой микросхеме - 0х34.

Далее, при загрузке ядра драйвера создается "устройство" клавиатура, которое является клиентом слэйвом на и2ц, у этого устройства есть указатель на структуру

struct i2c_client *client;

первым делом вызывается probe(). для и2ц он так обьявлен

static int adp5588_probe(struct i2c_client *client)

Т.е. самое первое, что

 

в драйвере adp5588 чтение регистра происходит как-то так

 

static int adp5588_read(struct i2c_client *client, u8 reg)
{
	int ret = i2c_smbus_read_byte_data(client, reg);

	if (ret < 0)
		dev_err(&client->dev, "Read Error\n");

	return ret;
}

а если у слейва нет адреса? Т.е. у CH423 порядка десятка-двух регистров и обращение к ним адет напрямую, без адреса слейва. как такой драйвер реализуется в Linux? Как такой драйвер должен выглядеть в дереве девайсов?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

53 минуты назад, ericN сказал:

Т.е. у CH423 порядка десятка-двух регистров и обращение к ним адет напрямую, без адреса слейва. как такой драйвер реализуется в Linux?

Напрямую через кучу i2c_new_dummy_device.

1 час назад, ericN сказал:

Как такой драйвер должен выглядеть в дереве девайсов?

Очень абстрактно, т.к. reg для него по сути не имеет значения, а важна только привязка к шине.

PS: См., например, https://elixir.bootlin.com/linux/latest/source/drivers/misc/eeprom/ee1004.c

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 16.03.2023 в 09:58, makc сказал:

Напрямую через кучу i2c_new_dummy_device.

Очень абстрактно, т.к. reg для него по сути не имеет значения, а важна только привязка к шине.

PS: См., например, https://elixir.bootlin.com/linux/latest/source/drivers/misc/eeprom/ee1004.c

да, я понял. спасибо!!!

 

только ещё нюанс.... в дереве девайсов все таки для клиента указать <reg> = 0x12. 

ядро, при загрузке создаст клиента struct i2c_client *client с адресом слейва 0х12, и вызовит 

static int ee1004_probe(struct i2c_client *client)

{

}

в моем драйвере (или int ch423_probe(struct i2c_client *client) )

А в probe() я уже наделаю свою кучу указателей с помощью кучу i2c_new_dummy_device. Это понятно. 

Но вот линукс/ядро, при создании i2c_client (до моего probe() ).... будет делать клиента и задаст ему адрес слейва из дерева (пусть будет 0х12). линукс до моего probe() будет пытаться достучаться до слейва 12 на шине и2ц?

 

и второе, если вообще не указать в дереве адрес слейва - кернел паник? или ядро нормально создаст с i2c_client с дефолтным адресом слейва?

Изменено пользователем ericN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, ericN сказал:

Но вот линукс/ядро, при создании i2c_client (до моего probe() ).... будет делать клиента и задаст ему адрес слейва из дерева (пусть будет 0х12). линукс до моего probe() будет пытаться достучаться до слейва 12 на шине и2ц?

Ядро само, на сколько я знаю, не лезет по этим адресам на шине. Оно передаёт этот адрес как параметр драйверу, а он уже решает что с ним и как делать. В этом как раз и есть принципиальное отличие i2c_new_client_device (занимает адрес на шине и другое устройство уже не может иметь тот же адрес) от i2c_new_dummy_device (не занимает адреса):
 

Цитата

struct i2c_client * i2c_new_dummy_device(struct i2c_adapter * adapter, u16 address)

Description

This returns an I2C client bound to the “dummy” driver, intended for use with devices that consume multiple addresses. Examples of such chips include various EEPROMS (like 24c04 and 24c08 models).
These dummy devices have two main uses. First, most I2C and SMBus calls except i2c_transfer() need a client handle; the dummy will be that handle. And second, this prevents the specified address from being bound to a different driver.
This returns the new i2c client, which should be saved for later use with i2c_unregister_device(); or an ERR_PTR to describe the error.

 

9 минут назад, ericN сказал:

и второе, если вообще не указать в дереве адрес слейва - кернел паник? или ядро нормально создаст с i2c_client с дефолтным адресом слейва?

Полагаю, что эта информация не будет доступна для экземпляра объекта из device tree. Но она вам и не нужна, т.к. нет смысла к ней обращаться. Поэтому я не понимаю, откуда вообще взялся этот вопрос.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 16.03.2023 в 11:41, makc сказал:

Поэтому я не понимаю, откуда вообще взялся этот вопрос.

вопрос вот от куда взялся

 

писать дерево девайсов так

i2c {
        #address-cells = <1>;
        #size-cells = <0>;
        .....
        keys@12 {
            compatible = "my,ch423";
            reg = <0x12>;
            .....

        };

или так

 i2c {
        #address-cells = <1>;
        #size-cells = <0>;
        ....
        keys {
            compatible = "my,ch423";
            ....

        };

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Честно говоря я не пробовал описывать I2C устройства без reg, поэтому по-моему проще попробовать и сделать выводы, чем пытаться определить поведение исходя из теории.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 16.03.2023 в 12:01, makc сказал:

Честно говоря я не пробовал описывать I2C устройства без reg, поэтому по-моему проще попробовать и сделать выводы, чем пытаться определить поведение исходя из теории.

спаисбо

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 часов назад, ericN сказал:

В ch423 нет адреса устройства. Есть много регистров, но нет адреса устройства. Например чтобы записать в системный регистр параметры, нужно отправить 0x48 0x01. где 48 - адрес регистра, 01 - данные.

Не понятно почему вы называете это "регистрами"... Мануал CH423 называет первый байт посылки - "командой". Если у чипа нет адреса слэйва, то видимо можно считать, что он имеет множество адресов слэйвов. Соответствующих множеству его команд. Так и работать с ним - через множество адресов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 16.03.2023 в 15:31, jcxz сказал:

Не понятно почему вы называете это "регистрами"... Мануал CH423 называет первый байт посылки - "командой". Если у чипа нет адреса слэйва, то видимо можно считать, что он имеет множество адресов слэйвов. Соответствующих множеству его команд. Так и работать с ним - через множество адресов.

И? Как работать с ним через множество адресов? Как в дереве девайсов для одного узла на шине I2C задавать "множество адресов"?

не понятно, почему вы называете это "адреса слэйвов"? Мануал CH423 называет первый байт посылки - "командой". Если у чипа нет адреса слэйва, то видимо можно считать, что он имеет множество регистров. Соответствующих множеству его команд.

 

Как угодно можно называть первый байт в CH423. Суть от этого не меняется. Структура i2c_client имеет поле адрес.  Могу для вас перефразировать вопрос в вашей терминологии:

 

 У CH423 порядка десятка-двух регистров команд (считайте множество адресов) и обращение к ним идет напрямую, без адреса слейва. как такой драйвер реализуется в Linux? Как такой драйвер должен выглядеть в дереве девайсов?

Изменено пользователем ericN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 16.03.2023 в 09:58, makc сказал:

что-то пошло не так. написал по этому примеру - есть проблема. Проблема в следующем... вот в этом примере есть такое место в probe()

#define EE1004_NUM_PAGES        2
for (cnr = 0; cnr < EE1004_NUM_PAGES; cnr++) {
            struct i2c_client *cl;

            cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr);
            if (IS_ERR(cl)) {
                err = PTR_ERR(cl);
                goto err_clients;
            }
            ee1004_set_page[cnr] = cl;
        }

именно тут происходит создание кучи i2c_client с помощью i2c_new_dummy_device. Вопрос знатокам СИ: сколько раз выполниться тело цикла for? Сколько создастся структур struct i2c_client?

Я думал 2. На моем процессоре (imx6q)  три раза!!! WTF?

Это не баг а фича вся загвоздка оказалась в IS_ERR(). Повернул код в профиль...

 

#define EE1004_NUM_PAGES		2
  
int flag, flag2;
int cnr = 0;

do{
	struct i2c_client *cl;
   	cl = i2c_new_dummy_device(client->adapter, EE1004_ADDR_SET_PAGE + cnr);
    flag2 = IS_ERR(cl); 
  	if(flag2)
      goto err_clients;
	cnr++;
	flag = (cnr < EE1004_NUM_PAGES);
  	printk(KERN_ERR "cnr = %d, flag = %d\n", flag);
 }while(flag);

строка "cnr = %d, flag = %d" выводится 2 раза. последний раз

cnr = 2, flag = 1

В do заходим в 3-ий раз и через goto покидаем цикл.

 

Если убрать вызов IS_ERR(), то тело do-while выполняется 2 раза. Копаю дальше.... в файле include/linux/err.h определены всякие проверки

#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)

static inline bool __must_check IS_ERR(__force const void *ptr)
{
    return IS_ERR_VALUE((unsigned long)ptr);
}

вот тут кроется проблема. unlikely()!!! Почитал про эту функцию.... почитал ещё раз... и ещё... и ещё... в двух словах - это предсказание ветвлений. Хорошо, пусть будет unlikely вместо if.

Но на моём процессоре это дает ошибку. Может ветвление и ускоряется, но в результате возникает ошибка в ветвлении. На первый взгляд в печку эту книгу вместо unlikely использовать обычную проверку в своем драйвере и забыть про unlikely, но эта функция используется в других драйверах майнстрима. Что делать с ними? Хотелось бы понять что не так с моим драйвером?

в других драйверах unlikely тоже будет давать ошибку? может в код драйвера нужно включит какое нибудь указание компилятору? Может какую-нибудь опцию ядру?

Т.е. иными словами - беру buildroot исходники ядра,  в menuconfig выбираю свою архитектруру, свой процессор, вбираю драйвера, выбираю Device Drivers->Misc devices->EEPROM support->SPD EEPROMs on DDR4 memory modules и при сборке собирается драйвер ee1004. При старте ядра цикл вместо 2-х раз пройдет 3 раза. драйвер аварийно прервется и далее железо работать не будет. Как это всё должно работать?

 

 

 

 

Изменено пользователем ericN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 минут назад, ericN сказал:

именно тут происходит создание кучи i2c_client с помощью i2c_new_dummy_device. Вопрос знатокам СИ: сколько раз выполниться тело цикла for? Сколько создастся структур struct i2c_client?

Я думал 2. На моем процессоре (imx6q)  три раза!!! WTF?

Выложите актуальный исходный текст компилируемого модуля и получаемый в результате объектный файл. Пока какие-либо выводы ещё делать рано, но эффект интересный.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

при старте ядра вывод вот такой для этого кода

Цитата

 dmesg |grep ch423
[    2.289268] Enter to ch423_probe()
[    2.292679] ch423: Try7 0 to create new i2c device whith adr - 0x48
[    2.299107] ch423: asd cnr = 0
[    2.299114] ch423: Try7 1 to create new i2c device whith adr - 0x44
[    2.308588] ch423: asd cnr = 1
[    2.308596] ch423: Try7 2 to create new i2c device whith adr - 0x46
[    2.318044] ch423: asd cnr = 2
[    2.318052] ch423: Try7 3 to create new i2c device whith adr - 0x60
[    2.327507] ch423: asd cnr = 3
[    2.327513] ch423: Try7 4 to create new i2c device whith adr - 0x0
[    2.341829] ch423: asd cnr = 4
[    2.341838] ch423_rez_agro 1-0022: address 0x00 unavailable
[    2.350527] ch423: err cnr = 4
[    2.350532] ch423: err_clients cnr2 = 4
[    2.353609] ch423: cnr2 = 3
[    2.357538] ch423: cnr2 = 2
[    2.360412] ch423: cnr2 = 1
[    2.363310] ch423: cnr2 = 0

Я ожидаю, что тело for выполниться 4 раза, и я ожидаю 4-ре раза вывод "Try7 3 to create new i2c device whith adr - 0x**", но получаю 5 раз. на 5-ом разе улетаю в err_clients.

ch423.o

 

ch423.c

Изменено пользователем ericN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, ericN сказал:

Я ожидаю, что тело for выполниться 4 раза, и я ожидаю 4-ре раза вывод "Try7 3 to create new i2c device whith adr - 0x**", но получаю 5 раз. на 5-ом разе улетаю в err_clients.

У вас явная логическая ошибка в коде. Компилятор порождает следующий код (получен с помощью Гидры):
 

  if (ch423_dev_count == 1) {
    unaff_r8 = 0;
    puVar6 = &ch423_regs;
    while( true ) {
      puVar8 = puVar8 + 1;
      uVar1 = *puVar6;
      printk(&.LC4,unaff_r8,uVar1);
      uVar2 = i2c_new_dummy_device(*(undefined4 *)(param_1 + 0x18),uVar1);
      printk(&.LC5,unaff_r8);
      if (0xfffff000 < uVar2) break;
      unaff_r8 = unaff_r8 + 1;
      *puVar8 = uVar2;
      puVar6 = puVar6 + 1;
    }
    _dev_err(iVar10,"address 0x%02x unavailable\n",uVar1);
    iVar7 = *(int *)((int)&ch423_set_page + unaff_r8 * 4);
    printk(&.LC3,unaff_r8);
  }

Происходит это из-за того, что у вас после успешного завершения цикла и выдачи сообщения об успехе всё равно устанавливается код ошибки и выполняется переход на код ошибки:

		printk(KERN_ERR "ch423: hapy end");

		error =  -EOPNOTSUPP;
		goto err_clients;

Соответственно компилятор понимает, что положительный результат IS_ERR() и завершение цикла есть суть одно и то же, вот вы и видите такой интересный результат. Кроме того у вас есть и другая ошибка в выдаче:

error = PTR_ERR(ch423_set_page[cnr]);

А должно быть (см. исходный модуль):

error = PTR_ERR(cl);

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 21.03.2023 в 12:12, makc сказал:

Кроме того у вас есть и другая ошибка в выдаче

да, это поспешил. вообще изночально код писал без cl, напрямую с ch423_set_page[cnr]. cl добавил, что было как в ee1004 в ядре 6*. в 5.10 драйвер ee1004 без cl. ну да ладно....

 

В 21.03.2023 в 12:12, makc сказал:

Происходит это из-за того, что у вас после успешного завершения цикла и выдачи сообщения об успехе всё равно устанавливается код ошибки и выполняется переход на код ошибки:

всё равно как-то не понятно... я не вижу логической ошибки в коде. у меня после завершения кода, не выставляется "кода ошибки", у меня задается некое значение некой переменной типа int, и делается goto на некую метку. Хорошо, допустим внутри тела for, я меняю туже переменную на тоже значение и делаю тот же переход, но в теле for под if-ом у меня ещё dev_err(), а снаружи нет, в ифе у меня PTR_ERR(), а снаружи нет. Более того, в результате того, что компилятор понимает, что положительный результат IS_ERR() и завершение цикла есть суть одно и то же - он выполняет дополнительно, 5-ый раз, ненужное выделение памяти и лезет за границы массива ch423_regs. Это вообще не допустимо. 

Ну хорошо.... убрал я после цикла goto err_clients; - результат тот же.... 5 раз в теле цикла. 

Может какой-нибудь директивой сказать компилятору, чтоб он не оптимизировал и сделал то, что от него просят? Ни как не могу заставить компилятор пройти по циклу 4 раза!

ch423.c ch423.o

Изменено пользователем ericN

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

далее... я сделал так

    if (++ch423_dev_count == 1)
    {
        for(cnr = 0; cnr<CH423_REG_MAX; cnr++)
        {
            struct i2c_client *cl;
            printk(KERN_ERR "ch423: Try9 %d to create new i2c device whith adr - 0x%x\n", cnr, ch423_regs[cnr]);

            cl = i2c_new_dummy_device(client->adapter, ch423_regs[cnr]);

            printk(KERN_ERR "ch423: asd cnr = %d", cnr);
            if( IS_ERR(cl) )
            {
                dev_err(&client->dev, "address 0x%02x unavailable\n", ch423_regs[cnr]);
                error = PTR_ERR(cl);
                printk(KERN_ERR "ch423: err cnr = %d", cnr);
                break;
            }
            ch423_set_page[cnr] = cl;
        }
        printk(KERN_ERR "ch423: hapy end");
    }

Тело цикла выполняется 5 раз, вместо 4-х.

# dmesg |grep ch423
[    2.289488] Enter to ch423_probe()
[    2.292897] ch423: Try9 0 to create new i2c device whith adr - 0x48
[    2.299328] ch423: asd cnr = 0
[    2.299334] ch423: Try9 1 to create new i2c device whith adr - 0x44
[    2.308809] ch423: asd cnr = 1
[    2.308816] ch423: Try9 2 to create new i2c device whith adr - 0x46
[    2.318268] ch423: asd cnr = 2
[    2.318275] ch423: Try9 3 to create new i2c device whith adr - 0x60
[    2.327730] ch423: asd cnr = 3
[    2.327737] ch423: Try9 4 to create new i2c device whith adr - 0x0
[    2.342058] ch423: asd cnr = 4
[    2.342066] ch423_rez_agro 1-0022: address 0x00 unavailable
[    2.350754] ch423: err cnr = 4
[    2.350759] ch423: hapy end
[    2.353841] ch423: devm_kzalloc()

Компилятор меня делает. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...