Re: [RFC] What are the goals for the architecture of an in-kernel IR system?

Mauro Carvalho Chehab
2010-03-29T00:52:08Z

Mauro Carvalho Chehab wrote:
> Dmitry Torokhov wrote:
>> On Fri, Mar 26, 2010 at 11:40:41AM -0300, Mauro Carvalho Chehab wrote:
>>> David Härdeman wrote:
>>>> On Thu, Mar 25, 2010 at 11:42:33AM -0300, Mauro Carvalho Chehab wrote:
>>>>>> 10) extend keycode table replacement to support big/variable
>>>>>> sized scancodes;
>>>>> Pending.
>>>>>
>>>>> The current limit here is the scancode ioctl's are defined as:
>>>>>
>>>>> #define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */
>>>>> #define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */
>>>>>
>>>>> As int size is 32 bits, and we must pass both 64 (or even bigger) scancodes, associated
>>>>> with a keycode, there's not enough bits there for IR.
>>>>>
>>>>> The better approach seems to create an struct with an arbitrary long size, like:
>>>>>
>>>>> struct keycode_table_entry {
>>>>> unsigned keycode;
>>>>> char scancode[32]; /* 32 is just an arbitrary long array - maybe shorter */
>>>>> int len;
>>>>> }
>>>>>
>>>>> and re-define the ioctls. For example we might be doing:
>>>>>
>>>>> #define EVIOCGKEYCODEBIG _IOR('E', 0x04, struct keycode_table_entry)
>>>>> #define EVIOCSKEYCODEBIG _IOW('E', 0x04, struct keycode_table_entry)
>>>>> #define EVIOCLEARKEYCODEBIG _IOR('E', 0x04, void)
>>>>>
>>>>> Provided that the size for struct keycode_table_entry is different, _IO will generate
>>>>> a different magic number for those.
>>>>>
>>>>> Or, instead of using 0x04, just use another sequential number at the 'E' namespace.
>>>>>
>>>>> An specific function to clear the table is needed with big scancode space,
>>>>> as already discussed.
>>>>>
>>>> I'd suggest:
>>>>
>>>> struct keycode_table_entry {
>>>> unsigned keycode;
>>>> unsigned index;
>>>> unsigned len;
>>>> char scancode[];
>>>> };
>>>>
>>>> Use index in EVIOCGKEYCODEBIG to look up a keycode (all other fields are
>>>> ignored), that way no special function to clear the table is necessary,
>>>> instead you do a loop with:
>>>>
>>>> EVIOCGKEYCODEBIG (with index 0)
>>>> EVIOCSKEYCODEBIG (with the returned struct from EVIOCGKEYCODEBIG and
>>>> keycode = KEY_RESERVED)
>>>>
>>>> until EVIOCGKEYCODEBIG returns an error.
>>> Makes sense.
>> Yes, I think so too. Just need a nice way to handle transition, I'd
>> like in the end to have drivers implement only the improved methods and
>> map legacy methods in evdev.
>
> See the attached RFC barely tested patch.
>
> On this patch, I'm using the following definitions for the ioctl:
>
> struct keycode_table_entry {
> __u32 keycode; /* e.g. KEY_A */
> __u32 index; /* Index for the given scan/key table */
> __u32 len; /* Lenght of the scancode */
> __u32 reserved[2]; /* Reserved for future usage */
> char *scancode; /* scancode, in machine-endian */
> };
>
> #define EVIOCGKEYCODEBIG _IOR('E', 0x04, struct keycode_table_entry) /* get keycode */
> #define EVIOCSKEYCODEBIG _IOW('E', 0x04, struct keycode_table_entry) /* set keycode */
>
>
> I tried to do the compat backport on a nice way, on both directions, e. g.:
>
> 1) an userspace app using EVIO[CS]GKEYCODEBIG to work with a legacy driver.
> 2) a driver implementing the new methods to accept the legacy EVIO[CS]GKEYCODE;
>
> For the test of (1), I implemented the following clear keytable code:
>
> struct keycode_table_entry kt;
> uint32_t scancode, i;
>
> memset(&kt, 0, sizeof(kt));
> kt.len = sizeof(scancode);
> kt.scancode = (char *)&scancode;
>
> for (i = 0; rc == 0; i++) {
> kt.index = i;
> kt.keycode = KEY_RESERVED;
> rc = ioctl(fd, EVIOCSKEYCODEBIG, &kt);
> }
> fprintf(stderr, "Cleaned %i keycode(s)\n", i - 1);
>
> It worked properly. I didn't test (2) yet.
>
> The read keytable would also be trivial. However, there are some troubles when
> implementing the code to add/replace a value at the table, in a way that it
> would allow the legacy drivers to work:
>
> - With a real CODEBIG support, the index number will be different than the
> scancode number. So, let's say that this is the driver table:
>
> index scancode keycode
> ------------------------
> 0 0x1e00 KEY_0
> 1 0x1e01 KEY_1
> 2 0x1e02 KEY_2
> 3 0x1e03 KEY_3
> 4 0x1e04 KEY_4
> 5 0x1e05 KEY_5
> 6 0x1e06 KEY_6
> 7 0x1e07 KEY_7
> 8 0x1e08 KEY_8
> 9 0x1e09 KEY_9
>
> Let's suppose that the user wants to overwrite the entry 5, attributing a new scancode/keycode
> to entry 5 (for example, associating 0x1e0a with KEY-A).
>
> A valid EVIOCSKEYCODEBIG call to change this code would be:
>
> kt->index = 5;
> *(uint32_t *)kt->scancode = 0x1e0a;
> *(uint32_t *)kt->keycode = KEY_A;
> rc = ioctl(fd, EVIOCSKEYCODEBIG, &kt);
>
> With EVIOCSKEYCODE, this requires two separate operations:
>
> int codes[2];
> code[0] = 0x1e05;
> code[1] = KEY_RESERVED;
> rc = ioctl(fd, EVIOCSKEYCODE, &codes];
>
> code[0] = 0x1e0a;
> code[1] = KEY_A;
> rc = ioctl(fd, EVIOCSKEYCODE, &codes];
>
>
> In the case of EVIOCSKEYCODEBIG call, the driver will need to:
>
> 1) Check If the scancode is not being used yet on any entry different than index=5.
> If it is in use, it should return an error.
> If not, replace the scancode/keycode.
>
> 2) Check if index is equal to the length of the array + 1. If so, create a new entry.
>
> 3) check if the index is bigger than length + 1 and return an error, if so.
>
> For the EVIOSKEYCODE emulation by an EVIOCSKEYCODEBIG driver, index=5 won't work.
> The driver will need to use the scancode. However, if we do this way, the
> cleanup logic will break, as scancode is equal to zero.
>
> So, I think that having an index here is not a good idea: it will just create some
> implementation troubles. We can archive the same result without the index, and having
> a fast clean_table code by just reading the used scancodes and associating them
> with KEY_RESERVED.

I spoke too soon... removing the index causes a problem at the read ioctl: there's no way
to retrieve just the non-sparsed values.

There's one solution that would allow both read/write and compat to work nicely,
but the API would become somewhat asymmetrical:

At get (EVIOCGKEYCODEBIG):
use index/len as input and keycode/scancode as output;

At set (EVIOCSKEYCODEBIG):
use scancode/keycode/len as input (and, optionally, index as output).

Having it asymmetrical doesn't sound good, but, on the other hand, using index for
the set function also doesn't seem good, as the driver may reorder the entries after
setting, for example to work with a binary tree or with hashes.

I'll think a little more about it and do some experiments.

Cheers,
Mauro