17 agosto, 2006

Cambios en mi vida

Es oficial, llevo 2 semanas viviendo en San Vicente del Raspeig. Y de momento todo bien, salvo que tenemos la casa patas arriba (lo malo de irse con las obras a mitad).

Como las obras las estamos realizando en ratos libres van bastante lentas y no me dejan mucho tiempo para escribir en el blog (aunque anteriormente tampoco lo hacía muy a menudo). Además, de momento, no dispongo de conexión a internet en mi piso.

He escrito un nuevo post que espero le guste a la gente (si alguien lo lee). Aunque este blog parece ya un monográfico de los lectores de tarjetas Omnikey, esa no es mi intención y espero escribir sobre otras cosas pronto.

Por si el cambio de residencia no fuese bastante (con cambio de pueblo incluído), nos hemos traído con nosotros un compañero peludo: un gatito de 2 meses. Es muy revoltoso, muy nervioso, y tiene las uñas muy afiladas (de momento, jeje, quien ríe el último ríe mejor). Además nos ha ocurrido una cosa curiosa (que parece ser muy normal con los gatos y las personas que no los conocen bien). Nos trajimos a San Vicente una gata (hembra) blanca con ojos azules y cuando salimos del veterinario de ponerle su primera vacuna era un gato (macho) gris con ojos grises... así, en cuestión de 20 minutos. Sí notábamos que el pelo se le oscurecía, pero a nosotros nos parecían azules sus ojos, mientras que para el veterinario eran grises. Pero que fuese macho... ¿y las pelotillas? ¿Y el aparato reproductor? !Pues vaya machote está hecho, si encima la esconde¡ ¿Es que tiene vergüenza?

En fin, estamos muy contentos con nuestro gato y nos pasamos casi todo el tiempo libre jugando con él. Incluso pienso que lo mimamos demasiado, ya tiene más trastos en casa que nosotros mismos.

Saludos a toda la gente de San Vicente. Ya os pediré que me hagáis una pequeña guía de sitios a dónde ir por este pueblo.

P.D. ¿Cuál es el gentilicio de San Vicente? Se lo he preguntado a gente de San Vicente y no han sabido responderme, ni siquiera he sabido encontrarlo en la página del ayuntamiento... ¿Es secreto de estado?. El gentilicio de San Vicente es sanvicentero, que suena bastante lógico.

Cómo modificar un driver sin conocer el API del kernel Linux

En este artículo voy a detallar los pasos que seguí para actualizar el módulo del lector de tarjetas Omnikey Cardman 2020. Nunca antes había modificado un driver del kernel Linux, no me considero un buen programador y haciendo esto no gano ni gané nada.

En primer lugar, en la página de Omnikey tenemos disponible el código fuente del driver de Linux para este lector. Lamentablemente, a partir del kernel 2.6.15 no compila. Así que me puse en contacto con el soporte de Omnikey y me dijeron que ese producto ya no se fabricaba, por lo que no le daban más soporte. Pero, ya que dejan en su página el código fuente, me dije: ¿Y por qué no puedo arreglarlo yo?

Pues bien, los pasos que seguí fueron los siguientes (suponiendo que ya tengamos los kernel-headers de nuestro kernel instalados):

  1. Bajarse el código fuente.
  2. En la página web de Omnikey, sección Soporte [Support], opción Drivers, podemos descargarnos el código fuente del driver.

    El archivo que descargamos se llama cm2020_installer_v2_4_1_src.tar.gz.

  3. Descomprimir.
  4. Cambiamos al directorio donde hayamos descargado el driver en el paso anterior y lo descomprimimos:

    ~$ tar xvzf cm2020_installer_v2_4_1_src.tar.gz
  5. Compilar por primera vez.
  6. Antes de compilar vamos a realizar un pequeño cambio en el archivo "install". Este archivo tiene un error y si no realizamos este cambio nos sobreescribirá el script de inicio de pcscd, y con este nuevo archivo a mí no me ha funcionado. Como supongo que pcscd viene empaquetado para la mayoría (si no todas) de las distribuciones, no veo necesario que se compile e instale a partir del código fuente que viene con este driver que acabamos de descargar.

    ~$ cp install install.old
    ~$ vi install
    ~$ diff -u install.old install
    --- install.old 2006-08-16 10:14:29.000000000 +0200
    +++ install 2006-08-16 10:10:20.000000000 +0200
    @@ -231,7 +231,7 @@
     echo OK
     fi
     mkdir -p /usr/local/pcsc/drivers
    -fi #if [ $nopcscd = 0 ];
    +#fi #if [ $nopcscd = 0 ];
     #
     # make initscript
     #
    @@ -565,7 +565,7 @@
    
     fi
    
    -#fi #if [ $nopcscd = 0 ];
    +fi #if [ $nopcscd = 0 ];
    
     #
     # copy the pcsc-shared library to its proper place
    

    Como vemos el cambio consiste en comentar la línea 234 y descomentar la línea 568.

    Hecho este cambio procedemos a compilar. Para ello nos advierte que debemos ejecutar el script como root, por lo que deberemos cambiar a root o ejecutar el script mediante sudo:

    ~$ sudo sh ./install --nopcscd
    Password:
    
    Installing OMNIKEY Cardman USB Smartcard reader...
    
    Distribution is debian
    Found PCSC version 1.3.1 in /usr/sbin/pcscd
    /usr/lib/pcsc/drivers/
    The kernel module for 2.6.16-2-686 isn't prebuilt!
    Try to compile it for you!
    rm -f *.o 2>/dev/null
    rm -f *.ko 2>/dev/null
    rm -f cardman.mod* 2>/dev/null
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    Couldn't build kernel module.
    Compile it by yourself or
    contact OMNIKEY for further details.
    (e-mail: support.linux@omnikey.com)
    

    Primer intento fallido, pero nos dice que tratemos de compilarlo por nosotros mismos (y es lo que vamos a hacer, ya que no nos indica el error por el cuál ha fallado la compilación).

    ~$ cd src/cm2020
    ~$ make
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:168: error: unknown field 'mode' specified in initializer
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:175: error: unknown field 'owner' specified in initializer
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:175: warning: initialization from incompatible pointer type
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_read':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1442: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_write':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1540: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_ioctl':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1778: warning: implicit declaration of function 'verify_area'
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1829: warning: passing argument 1 of 'copy_to_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1953: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2008: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1829: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1842: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1942: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1943: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1944: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1945: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1953: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2008: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: At top level:
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2565: fatal error: opening dependency file /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/.cardman.o.d: Permiso denegado
    compilation terminated.
    make[2]: *** [/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o] Error 1
    make[1]: *** [_module_/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020] Error 2
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    make: *** [default] Error 2
    

    Buf! Muchos errores, así que vamos a pasar al siguiente punto.

  7. ¿Dónde buscar?
  8. Cabe aclarar que este mismo driver lo estaba utilizando en el kernel 2.6.12, funcionando correctamente. Así que al cambiar a un kernel posterior y no poder compilar el driver supuse que sería por cambios en el kernel (cuando modifiqué el driver yo utilizaba un kernel 2.6.15). Solamente tenía que buscar una página donde explicasen los cambios que había sufrido el kernel en sucesivas versiones. La solución se encuentra en dos páginas principalmente: Kernel Newbies y en LWN.net. En la primera (Kernel Newbies) explican los cambios de una manera más comprensible, aunque solamente de la última release del kernel. Es un buen punto de partida si utilizas la última versión del kernel. Como no es mi caso busqué en la segunda página (LWN).

    Además, podemos echarle un vistazo al código fuente del kernel (o a sus cabeceras).

  9. Arreglando errores.
  10. El primer error que aparece es el siguiente:

    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:168: error: unknown field 'mode' specified in initializer

    Este error no pude localizarlo en los cambios del API de la páginas anteriormente citadas. Así que hice una búsqueda con la herramienta de la página con las siguientes palabras: "usb_class_driver mode field". Solamente apareció un resultado: 2.6.15-rc1 short log, y buscando "mode field" en la página encontramos lo siguiente:

    devfs: Remove the mode field from usb_class_driver as it's no longer needed

    Como dice el mensaje, a partir del kernel 2.6.15 ya no se utilizará este campo. Así que lo comentamos de la siguiente manera:

    ~$ vi cardman.c
    ~$ diff -u cardman.c.old cardman.c
    --- cardman.c.old 2006-08-16 10:55:03.000000000 +0200
    +++ cardman.c 2006-08-16 10:56:07.000000000 +0200
    @@ -165,7 +165,9 @@
     static struct usb_class_driver cmu_class = {
     .name= "usb/cm%d",
     .fops= &cmu_fops,
    + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
     .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
    + #endif
     .minor_base= CM2020_MINOR,
     };
     #endif
    

    Las líneas añadidas son para que solamente añada ese campo si la version del kernel es menor que la versión 2.6.15. Esto tampoco sabía cómo se hacía, pero en el mismo archivo cardman.c se utiliza esta sentencia, así que simplemente lo copié y modifiqué.

    Si compilamos veremos que el error anterior desaparece (¡PERFECTO!).

    Pasamos al siguiente error:

    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:175: error: unknown field 'owner' specified in initializer

    Buscamos la palabra owner en la página de cambios en el kernel (LWN). En ella encontramos lo siguiente en la sección correspondiente al kernel 2.6.16:

     The usb_driver structure has a new field (no_dynamic_id) which lets a driver disable the addition of dynamic device IDs. The owner field has also been removed from this structure.

    Así que editamos el archivo cardman.c de la siguiente forma:

    ~$ vi cardman.c
    ~$ diff -u cardman.c.old cardman.c
    --- cardman.c.old 2006-08-16 10:55:03.000000000 +0200
    +++ cardman.c 2006-08-16 11:06:14.000000000 +0200
    @@ -165,13 +165,15 @@
     static struct usb_class_driver cmu_class = {
     .name= "usb/cm%d",
     .fops= &cmu_fops,
    + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
     .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
    + #endif
     .minor_base= CM2020_MINOR,
     };
     #endif
    
     STATIC struct usb_driver cmu_driver = {
    -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
    +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
     .owner= THIS_MODULE,
     #endif
     .name= "cardman",
    

    En esta ocasión añadimos una segunda comprobación a la que ya se tenía (versión del kernel 2.4.20), por lo que deduzco que este campo se añadió a partir de esa versión y ahora se ha eliminado.

    Si compilamos, podemos comprobar que en esta ocasión la compilación termina, obteniendo el archivo "cardman.ko":

    ~$ make
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_read':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1444: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_write':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1542: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c: In function 'cmu_ioctl':
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1780: warning: implicit declaration of function 'verify_area'
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1831: warning: passing argument 1 of 'copy_to_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1955: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2010: warning: passing argument 2 of 'copy_from_user' makes pointer from integer without a cast
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1831: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1844: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1944: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1945: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1946: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1947: warning: ignoring return value of 'copy_to_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1955: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:2010: warning: ignoring return value of 'copy_from_user', declared with attribute warn_unused_result
     Building modules, stage 2.
     MODPOST
    *** Warning: "verify_area" [/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.ko] undefined!
     CC /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.mod.o
     LD [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.ko
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    

    Pero este módulo no funciona, y nos da el error de que verify_area no está definido. Indagando en los warning que nos da la compilación (que no errores) vemos el siguiente mensaje:

    /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.c:1780: warning: implicit declaration of function 'verify_area'

    Es decir, que estamos utilizando una función que no está definida en las cabeceras del kernel. Nuevamente buscamos los cambios efectuados sobre esta función, encontrando el siguiente artículo: Patch: remove verify_area(), donde dice que está deprecado y que se sustituye por access_ok(). Si buscamos referencias a los valores devueltos por verify_area() en los headers del kernel:

    ~$ grep -r "int verify_area(" /usr/src/kernel-headers-2.6.xx/
    /usr/src/kernel-headers-2.6.xx/include/asm-i386/uaccess.h:static inline int verify_area(int type, const void __user * addr, unsigned long size)
    /usr/src/kernel-headers-2.6.xx/include/asm/uaccess.h:static inline int verify_area(int type, const void __user * addr, unsigned long size)
    

    En cualquiera de estos archivos encontramos el siguiente comentario en la declaración de la función verify_area():

    /**
     * verify_area: - Obsolete, use access_ok()
     * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE
     * @addr: User space pointer to start of block to check
     * @size: Size of block to check
     *
     * Context: User context only.  This function may sleep.
     *
     * This function has been replaced by access_ok().
     *
     * Checks if a pointer to a block of memory in user space is valid.
     *
     * Returns zero if the memory block may be valid, -EFAULT
     * if it is definitely invalid.
     *
     * See access_ok() for more details.
     */
    

    Como nos dice, vamos a mirar la función access_ok() para más detalles (se encuentra en el mismo archivo que verify_access():

    /**
     * access_ok: - Checks if a user space pointer is valid
     * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
     *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
     *        to write to a block, it is always safe to read from it.
     * @addr: User space pointer to start of block to check
     * @size: Size of block to check
     *
     * Context: User context only.  This function may sleep.
     *
     * Checks if a pointer to a block of memory in user space is valid.
     *
     * Returns true (nonzero) if the memory block may be valid, false (zero)
     * if it is definitely invalid.
     *
     * Note that, depending on architecture, this function probably just
     * checks that the pointer is in the user space range - after calling
     * this function, memory access functions may still return -EFAULT.
     */
    

    Resumiendo, verify_access() devuelve 0 o -EFAULT, mientras que access_ok() devuelve true o false. Buscamos referencias a esta función y encontramos que en distintos parches se utiliza de la siguiente forma:

    if (!access_ok(VERIFY_WRITE, buf, space))
     return -EFAULT;
    

    Así que buscamos dónde utilizamos la función verify_area() y la modificamos. En este caso no hace falta hacer comprobaciones de la versión del kernel, ya que sobre la función access_ok(), la primera referencia que he encontrado es en el kernel 2.4.17:

    ~$ vi cardman.c
    ~$ diff -u cardman.c.old cardman.c
    --- cardman.c.old 2006-08-16 10:55:03.000000000 +0200
    +++ cardman.c 2006-08-16 11:44:40.000000000 +0200
    @@ -165,13 +165,15 @@
     static struct usb_class_driver cmu_class = {
     .name= "usb/cm%d",
     .fops= &cmu_fops,
    + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
     .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
    + #endif
     .minor_base= CM2020_MINOR,
     };
     #endif
    
     STATIC struct usb_driver cmu_driver = {
    -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
    +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
     .owner= THIS_MODULE,
     #endif
     .name= "cardman",
    @@ -1775,12 +1777,12 @@
     if (_IOC_DIR(cmd)&_IOC_READ) {
     DEBUG(5,"ioctl: _ioc_dir=read\n");
     if (arg==0) rc=-EFAULT;
    - else rc=verify_area(VERIFY_WRITE,(void *)arg,size);
    + else rc=(access_ok(VERIFY_WRITE,(void *)arg,size))?0:-EFAULT;
     }
     if (_IOC_DIR(cmd)&_IOC_WRITE) {
     DEBUG(5,"ioctl: _ioc_dir=write\n");
     if (arg==0) rc=-EFAULT;
    - else rc=verify_area(VERIFY_READ,(void *)arg,size);
    + else rc=(access_ok(VERIFY_READ,(void *)arg,size))?0:-EFAULT;
     }
     if (rc) {
     DEBUG(4, "<- ioctl (unknown comamnd)\n");
    

    Como en esta función devolvemos el valor de rc, utilizamos una única sentencia para darle el valor 0 (todo OK) o el valor -EFAULT (error). Ahora ya podemos compilarlo correctamente.

  11. Compilar e instalar.
  12. Volvemos al directorio donde hayamos descomprimido el driver del lector e instalamos:

    ~$ sh ./install --nopcscd
    
    Installing OMNIKEY Cardman USB Smartcard reader...
    
    Distribution is debian
    Found PCSC version 1.3.1 in /usr/sbin/pcscd
    /usr/lib/pcsc/drivers/
    The kernel module for 2.6.16-2-686 isn't prebuilt!
    Try to compile it for you!
    rm -f *.o 2>/dev/null
    rm -f *.ko 2>/dev/null
    rm -f cardman.mod* 2>/dev/null
    make -C /lib/modules/2.6.16-2-686/build SUBDIRS=/home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020 modules
    make[1]: se ingresa al directorio `/usr/src/linux-headers-2.6.16-2-686'
     CC [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.o
     Building modules, stage 2.
     MODPOST
     CC /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.mod.o
     LD [M] /home/samba/OVSI/asanchez/download/cm2020_installer_v2_4_1_src/src/cm2020/cardman.ko
    make[1]: se sale del directorio `/usr/src/linux-headers-2.6.16-2-686'
    Copy cardman.ko to /lib/modules/2.6.16-2-686/kernel/drivers/usb/misc
    Create init-script and symlink into runlevels
    ./install: 840: declare: not found
    [: 840: 6: unexpected operator
    Attempting to start the cm_usb kernel module
    Starting cm_usb
    Installation finished
    

    Da un error al ejecutar el script de instalación en la línea 840:

    declare -i i=2

    Pero no es importante, ya que es para instalar en los distintos runlevels un script que cargue el driver al arrancar, aunque yo no he tenido problemas para que el kernel cargue el driver cuando enchufo la tarjeta al equipo (hotplug o udev), así que podemos eliminar esas líneas o escribir antes de la comprobación una declaración de la variable initdir:

    initdir=""
  13. Cambiar reglas de udev.
  14. En udev ha habido una serie de cambios, por lo que el archivo que viene junto al driver tampoco funciona correctamente. Para que funcione debemos realizar los siguientes cambios:

    ~$ vi 10-cardmanusb.rules
    ~$ diff -u 10-cardmanusb.rules.old 10-cardmanusb.rules
    --- 10-cardmanusb.rules.old 2006-01-31 16:36:41.000000000 +0100
    +++ 010_cardmanusb.rules 2006-08-16 12:18:01.000000000 +0200
    @@ -1,3 +1,3 @@
    -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="0596", NAME="cm%n", SYMLINK="usb/cm%n"
    -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="1784", NAME="cm%n", SYMLINK="usb/cm%n"
    -BUS="usb", SYSFS{idVendor}="08d4", SYSFS{idProduct}="0009", NAME="cm%n", SYMLINK="usb/cm%n"
    +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="0596",SYMLINK="usb/cm%n"
    +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="1784",SYMLINK="usb/cm%n"
    +BUS=="usb",SYSFS{idVendor}=="08d4",SYSFS{idProduct}=="0009",SYMLINK="usb/cm%n"
    
  15. Resumen.
  16. Hemos averiguado los errores que nos daba nuestro driver al compilarlo, hemos buscado soluciones en el API del kernel y lo hemos solucionado. En definitiva, tenemos funcionando un periférico al que la compañía que lo fabrica ya no le brinda soporte, pero que es perfectamente funcional y las únicas diferencias que le he hallado con respecto a versiones más modernas de lectores es la velocidad de lectura de las tarjetas (aunque la diferencia no es abismal) y el que no utilice estándares (las nuevas versiones de lector son enchufar y funcionar, sin compilar nada, incluso más fácilmente que en Window$) para lo que tenemos este driver.

    Ha quedado un poco largo, pero espero que se entienda el proceso que he seguido y lo fácil que resulta realizar cambios sobre un driver que funciona. Entiendo que se podría estudiar el código de dicho driver y mejorarlo, pero eso no entra dentro de mis planes más inmediatos.

  17. Parches conseguidos.
  18. Aquí tenemos los parches que hemos conseguido con nuestros cambios. Para aplicarlos solamente tenéis que copiarlos en un archivo, en el mismo directorio donde se encuentra el original y ejecutar el siguiente comando, en ese directorio :

    ~$ patch -p1 < archivo.patch
    • Archivo: install.patch
    • --- install.old 2006-08-16 10:14:29.000000000 +0200
      +++ install 2006-08-16 12:14:18.000000000 +0200
      @@ -231,7 +231,7 @@
       echo OK
       fi
       mkdir -p /usr/local/pcsc/drivers
      -fi #if [ $nopcscd = 0 ];
      +#fi #if [ $nopcscd = 0 ];
       #
       # make initscript
       #
      @@ -565,7 +565,7 @@
      
       fi
      
      -#fi #if [ $nopcscd = 0 ];
      +fi #if [ $nopcscd = 0 ];
      
       #
       # copy the pcsc-shared library to its proper place
      @@ -609,9 +609,9 @@
       # copy cardman kernel module
       #
      
      -if [ $min == 2 ]; then
      +if [ $min -eq 2 ]; then
       mod_min=misc
      -elif [ $min == 4 ]; then
      +elif [ $min -eq 4 ]; then
       mod_min=kernel/drivers/usb
       elif [ $min -ge 5 ]; then
       mod_min=kernel/drivers/usb/misc
      @@ -622,7 +622,7 @@
       echo Copy cardman.$kext to $moddir/$rel/$mod_min
       cp ./modules/cardman.$rel.$kext $moddir/$rel/$mod_min/cardman.$kext
       chmod 644 $moddir/$rel/$mod_min/cardman.$kext
      -if [ $? == 1 ]; then
      +if [ $? -eq 1 ]; then
       echo Could not copy cardman-module, exiting
       exit 0
       fi
      @@ -834,6 +834,7 @@
       echo "Couldn't make any runlevel entries (Don't mind if you are using Hotplugging!)"
       fi
      
      +initdir=""
       if [ -n "$initdir" ]; then
       chmod a+x $initdir/$iname
      
    • Archivo: cardman.patch
    • --- src/cm2020/cardman.c.old 2006-08-16 10:55:03.000000000 +0200
      +++ src/cm2020/cardman.c 2006-08-16 11:44:40.000000000 +0200
      @@ -165,13 +165,15 @@
       static struct usb_class_driver cmu_class = {
       .name= "usb/cm%d",
       .fops= &cmu_fops,
      + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
       .mode= S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
      + #endif
       .minor_base= CM2020_MINOR,
       };
       #endif
      
       STATIC struct usb_driver cmu_driver = {
      -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)
      +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
       .owner= THIS_MODULE,
       #endif
       .name= "cardman",
      @@ -1775,12 +1777,12 @@
       if (_IOC_DIR(cmd)&_IOC_READ) {
       DEBUG(5,"ioctl: _ioc_dir=read\n");
       if (arg==0) rc=-EFAULT;
      - else rc=verify_area(VERIFY_WRITE,(void *)arg,size);
      + else rc=(access_ok(VERIFY_WRITE,(void *)arg,size))?0:-EFAULT;
       }
       if (_IOC_DIR(cmd)&_IOC_WRITE) {
       DEBUG(5,"ioctl: _ioc_dir=write\n");
       if (arg==0) rc=-EFAULT;
      - else rc=verify_area(VERIFY_READ,(void *)arg,size);
      + else rc=(access_ok(VERIFY_READ,(void *)arg,size))?0:-EFAULT;
       }
       if (rc) {
       DEBUG(4, "<- ioctl (unknown comamnd)\n");
      
    • Archivo: 10-cardmanusb.rules.patch
    • --- 10-cardmanusb.rules.old 2006-08-16 12:48:21.000000000 +0200
      +++ 10-cardmanusb.rules 2006-08-16 12:48:37.000000000 +0200
      @@ -1,3 +1,3 @@
      -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="0596", NAME="cm%n", SYMLINK="usb/cm%n"
      -BUS="usb", SYSFS{idVendor}="076b", SYSFS{idProduct}="1784", NAME="cm%n", SYMLINK="usb/cm%n"
      -BUS="usb", SYSFS{idVendor}="08d4", SYSFS{idProduct}="0009", NAME="cm%n", SYMLINK="usb/cm%n"
      +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="0596",SYMLINK="usb/cm%n"
      +BUS=="usb",SYSFS{idVendor}=="076b",SYSFS{idProduct}=="1784",SYMLINK="usb/cm%n"
      +BUS=="usb",SYSFS{idVendor}=="08d4",SYSFS{idProduct}=="0009",SYMLINK="usb/cm%n"