Red de conocimiento informático - Computadora portátil - ¿Los auriculares Android plug and play no se pueden registrar globalmente?

¿Los auriculares Android plug and play no se pueden registrar globalmente?

1. Principio de hardware de detección de auriculares

La detección general de auriculares incluye la detección de auriculares normales y la detección de auriculares con micrófono. Estos dos tipos de auriculares se denominan colectivamente Auriculares, mientras que los auriculares sin micrófono generalmente se denominan Auriculares.

La detección de inserción del dispositivo Headphone generalmente se realiza a través del Jack, es decir, el jack de auriculares. El principio general es utilizar el mecanismo de detección de la toma de auriculares para conectar el pin de detección a la interrupción GPIO. Cuando se insertan los auriculares, el metal del enchufe de los auriculares entrará en contacto con el pin de detección, lo que provocará que el nivel del pin de detección cambie. , provocando una interrupción. Esto permite leer el valor del GPIO en la función de manejo de interrupciones y determina además si los auriculares están conectados o desconectados.

Para detectar si los auriculares tienen micrófono se requiere la función de corriente de polarización de micrófono adicional del códec.

La inserción y extracción de auriculares Android se puede lograr mediante dos mecanismos:

1.InputEvent

2.UEvent

Entre ellos UEvent es el mecanismo predeterminado de conexión y desconexión de auriculares del sistema Android, por lo que el código aquí se basa en la implementación de UEvent y se utiliza para el mecanismo InputEvent. Esto es sólo una apariencia general, no una implementación específica.

1.1 Cambiar entre dos mecanismos

Android proporciona dos soluciones de forma predeterminada, por lo que también debe proporcionar dos métodos de cambio, lo que proporciona un método llamado config_useDevInputEventForAudioJack (cuando sea verdadero, use linux / subsistema dev/input/event para detectar cambios de interruptor en el conector de auriculares/micrófono. Si es falso, use el marco uevent anterior), realice una búsqueda global del código fuente de Android. Puede ver que está ubicado en frameworks/base/core/res/res/values/config.xml. El valor predeterminado es falso, lo que significa que el método InputEvent no se utiliza. Además, también se puede encontrar en el fabricante. -carpeta relacionada en el paquete fuente Configuración, de la siguiente manera:

/android/4.2/device/asus/flo/overlay/frameworks/base/core/res/res/values/config.xml

falso

/android/4.2/device/samsung/manta/overlay /frameworks/base/core/res/res/values/config.xml

Verdadero

Puede ver que algunos proveedores utilizan InputEvent para la detección de auriculares. En config.xml, no hay ninguna configuración para config_useDevInputEventForAudioJack.

1.2 Proceso de detección de marcación e inserción de auriculares Android

2 InputEvent

2.1 Mecanismo para procesar InputEvent en la capa del marco

El procesamiento principal de InputEvent Completado en frameworks/base/services/java/com/android/server/input/

InputManagerService.java. En el constructor InputManagerService, pase la siguiente función,

mUseDevInputEventForAudioJack = context.getResources().

getBoolean(R.bool.config_useDevInputEventForAudioJack);

Determine si la detección de enchufe de auriculares está implementada actualmente a través de InputEvent.

Cuando Android obtenga InputEvent, llamará a la función notifySwitch en InputManagerService.java, que a su vez irá a la función notifyWiredAccessoryChanged en el archivo WiredAccessoryManager.java. El proceso posterior es el mismo que UEvent, que. se discutirá más adelante en el capítulo.

2.2 Procesamiento de la capa del núcleo

El procesamiento del evento de entrada y desconexión de auriculares por parte de la capa del núcleo se implementa principalmente mediante input_report_key/input_report_switch (include/linux/input.h). aplicaciones prácticas, ASOC ha empaquetado para nosotros. ASOC nos ha encapsulado la función de interfaz Jack correspondiente, que se puede utilizar siempre que cumpla con las especificaciones. A continuación se enumeran varias funciones de interfaz de uso común (/sound/soc/soc-jack.c).

int snd_soc_jack_new(structsnd_soc_codec *codec,

const char *id, int type, struct snd_soc_jack *jack)

Generar un nuevo objeto jack, definir el detectado tipo de conector, es decir, los tipos de dispositivos que se pueden conectar a él. Generalmente se define como SND_JACK_HEADSET. Dependiendo del tipo de interfaz admitida, también se pueden agregar SND_JACK_LINEOUT, SND_JACK_AVOUT, etc.

Esta función llama a snd_jack_new. En snd_jack_new, puedes ver que se llama a input_allocate_device() para asignar un dispositivo de entrada, que se puede utilizar para generar eventos de entrada en el futuro.

int snd_soc_jack_add_pins(structsnd_soc_jack *jack, int count, struct snd_soc_jack_pin *pins)

Agrega pines previamente definidos al componente dapm para facilitar la administración unificada por parte de dapm. Este paso no está necesariamente relacionado con InputEvent y se puede llamar sin InputEvent. Esto se debe principalmente a que puede definir la toma de auriculares como un widget y agregarla a dapm para administrar el ahorro de energía.

viod snd_soc_jack_report(structsnd_soc_jack *jack, int status, int mask)

Informar el estado de inserción y extracción del jack puede realizar las dos tareas siguientes: a) Según la inserción y El estado de eliminación actualiza el estado de inserción y eliminación del conector que ha sido actualizado previamente por snd_soc_jack_add_pins agregado al estado del pin dapm para administrar su encendido y apagado.

b) Llame a snd_jack_report para informar eventos de entrada a la capa superior a través de input_report_key/input_report_switch.

Según las funciones anteriores, la conexión y desconexión de auriculares según el mecanismo InputEvent se puede detectar mediante los siguientes métodos:

a) snd_soc_jack_new crea un objeto jack

b ) snd_soc_jack_add_pins Agréguelo a dapm wigets

c) request irq solicita la interrupción de conexión/desconexión del auricular y luego, en la función de manejo de interrupciones, determina si el auricular está detectando los niveles alto y bajo. de la línea Enchufe o desconecte.

Determine si el auricular está enchufado o desconectado y lea el registro del códec para determinar si es un auricular o un auricular

d) Llame a snd_soc_jack_report y envíe un InputEvent según el resultado del juicio

Además, ASOC también proporciona Se crea una función empaquetada para implementar las funciones de los pasos c) yd) anteriores:

Elimine los siguientes pasos:

class_destroy(switch_class); }

module_init(switch_class_init );

module_exit(switch_class_exit);

La función init llama a create_switch_class->class_create para crear la clase de dispositivo switch_class. La salida correspondiente destruye la clase de dispositivo.

Este archivo exporta dos funciones para llamadas realizadas por otros controladores de dispositivos de conmutación, que se utilizan para registrar dispositivos de conmutación switch_dev_register y cancelar el registro de switch_dev_unregister (drivers/switch/switch_class.c).

int switch_dev_register(struct switch_dev *sdev)

{

int ret;

if(!switch_class) {

ret = create_switch_class( );

si (ret < 0)

devuelve ret;

}

sdev-> index = atomic_inc_return(&device_count);

sdev->dev = device_create(switch_class, NULL,

MKDEV(0, sdev->index), NULL, sdev->nombre);

Si (IS_ERR(sdev->dev))

Devuelve PTR _ERR(sdev->dev);

ret = device_create_file(sdev->dev , &dev_attr_state);

if (ret < 0)

ir a err_create_file_1 ;

ret = device_create_file(sdev->dev, &

if (ret < 0)

ir a err_create_file_2;

dev_set_ drvdata(sdev->dev, sdev);

sdev->estado = 0; /p>

devuelve 0;

err_create_file_2:

device_remove_file(sdev->dev, &dev_attr_state);

err_create_file_1:

device_destroy(switch_class, MKDEV(0, sdev->index));

printk(KERN_ERR " switch: No se pudo registrar el controlador %s\n", sdev->name);

return ret;

}

EXPORT_SYMBOL_GPL(switch_dev_register);

void switch_dev_unregister(struct switch_dev *sdev)

{< / p>

device_remove_file(sdev->dev,&dev_attr_name);

device_remove_file(sdev->dev,&dev_attr_state);

device_set_drvdata(sdev->dev, NULL);

device_destroy(switch_class, MKDEV(0, sdev->index));

}

EXPORT_SYMBOL_GPL(switch_dev_unregister);

Entonces Hay dos funciones de operación de sysfs (state_show y name_show). Cuando el usuario lee la entrada del interruptor correspondiente (/sys/class/switch/<dev_name&).

gt;/name y /sys/class/switch//state), el sistema llamará automáticamente a estas dos funciones para devolver el nombre y el estado del dispositivo conmutador al usuario.

estático ssize_t state_show(struct dispositivo *dev, struct dispositivo_attribute *attr, char *buf)

{

struct switch_dev *sdev = (struct switch_dev *) dev_get_drvdata(dev);

if (sdev->print_state) {

int ret = sdev->print_state(sdev, buf);

if (ret >= 0)

return ret;

}

return sprintf(buf, "%d\n", sdev->state); p>

}

estático ssize_t name_show (struct dispositivo *dev, struct dispositivo_attribute *attr,

char *buf)

{

struct switch_dev *sdev = (struct switch_dev *)dev_get_drvdata(dev);

if (sdev->print_name) {

int ret = sdev->print_name( sdev, buf);

if (ret >= 0)

return ret;

}

return sprintf(buf, " %s \n", sdev->name);

}

Como puede ver, estas dos funciones simplemente llaman directamente a las funciones print_state y print_name en el switch_dev correspondiente; si esto no está definido Dos funciones, llame a sprintf para imprimir información en el búfer buf.

Finalmente, está la función switch_set_state, que el kernel usa internamente y no será llamada por el usuario. Hace dos cosas: llama a name_show y state_show para generar el nombre y el estado del dispositivo de conmutación; al sistema de archivos sysfs; enviar uevent para notificar al usuario que intercambie información del dispositivo (nombre del dispositivo).

El archivo switch_gpio.c implementa el controlador del dispositivo del conmutador gpio según la clase del conmutador. El proceso de implementación es el siguiente: según el marco del dispositivo/controlador de la plataforma, la inicialización se completa en la función de sonda, incluida la obtención. el permiso para usar gpio, establecer la dirección de entrada de gpio, registrar el dispositivo switch_dev, asignar interrupciones a gpio, especificar la rutina del servicio de interrupción, inicializar el trabajo gpio_switch_work y finalmente leer el estado inicial de gpio.

Cuando el estado del pin GPIO cambia, se activará una interrupción. Se llama a Schedule_work en el programa de servicio de interrupción. El trabajo programado es gpio_switch_work inicializado. Finalmente, se ejecuta la función gpio_switch_work. leerá el nivel GPIO actual, llamará a switch_set_state para actualizar sysfs y notificará a la aplicación superior a través de uevent.

Este controlador de dispositivo solo implementa la función print_state: switch_gpio_print_state, pero no implementa la función print_name. Al ejecutar gpio_switch_work, se llama internamente a switch_set_state->switch_gpio_print_state para enviar el estado de GPIO a sysfs. 2 La interfaz de usuario del módulo de conmutación

Los archivos sysfs son sys/ class/switch//name, sys/class/switch//state y las variables de entorno uevent son SWITCH_NAME =, SWITCH_STATE=.

El mecanismo UEvent es relativamente simple. Se basa en el controlador del conmutador. El controlador del conmutador creará el directorio del complemento de auriculares /sys/devices/virtual/switch/h2w en este directorio. hay un archivo llamado state El nodo del dispositivo, el controlador notifica a la capa superior de Android sobre los cambios en el estado de los auriculares actualizando el valor del estado.

3.2 Mecanismo de capa de marco para procesar UEvent

Android implementa el mecanismo para procesar UEvent en frameworks/base/services/java/com/android/server/WiredAccessoryManager.java. Mecanismo de evento.

En este archivo, la clase WiredAccessoryObserver hereda de UEventObserver, y los eventos a observar se agregan al sistema UEvent en makeObservedUEventList:

if(! mUseDevInputEventForAudioJack)

{

uei = new UEventInfo(NAME_H2W,BIT_HEADSET, BIT_HEADSET_NO_MIC);

......

}

Puede Como puede ver, el evento UEvent solo se agregará cuando no se use InputEvent, y NAME_H2W es el nombre del controlador del interruptor correspondiente al auricular. BIT_HEADSET y BIT_HEADSET_NO_MIC son los dos valores del nodo STATE. representando auriculares con micrófono y sin micrófono respectivamente.

Cuando llega el evento UEvent, se volverá a llamar a la función onUEvent sobrecargada en la clase WiredAccessoryObserver, lo que resultará en una llamada a updateStateLocked(devPath,name, state), donde el valor de state está a través de /sys/ devices/ El nodo virtual/switch/h2w/state pasa el valor del nodo de estado al auricular.

Finalmente, el programa va a la función setDeviceStateLocked para su procesamiento, configura el dispositivo de acuerdo con el valor de estado en setDeviceStateLocked y luego llama a mAudioManager.setWiredDeviceConnectionState. Finalmente, llegamos a AudioPolicyManagerBase::setDeviceConnectionState.

3.3 Mecanismo de capa de kernel

Como dije antes, el mecanismo de detección de auriculares basado en UEvent necesita implementar un controlador de conmutador, que creará un directorio /sysdirectory/ para la detección de auriculares. . Crea un directorio /sys/devices/virtual/switch/h2w para la detección de enchufes de auriculares, con un nodo de dispositivo llamado state en él. El controlador del conmutador notifica a la capa superior de Android sobre los cambios de estado de los auriculares actualizando el valor del estado.

El directorio del controlador del conmutador se encuentra en el directorio drivers/staging/android/switch del kernel de Linux. Como puede ver por el nombre del directorio, este controlador se creó específicamente para Android. Hay dos archivos existentes en el directorio del conmutador: switch_class.c es la implementación interna del controlador del conmutador, que proporciona parte de la API requerida por el controlador del conmutador switch_gpio.c es un ejemplo del controlador del conmutador, que implementa basado en GPIO; interrumpe el controlador del interruptor.

Además, hay los mismos archivos en el directorio drivers/switch. La diferencia es que las ubicaciones de los nodos generados por los dos son diferentes en Android si desea implementar el controlador switch en drivers/. cambie de directorio, luego es necesario cambiar el archivo WiredAccessoryManager.java.

A continuación se explica cómo agregar un controlador de conmutador. El proceso de agregar un controlador de conmutador es muy simple y se puede modelar a partir de switch_gpio.c. Los pasos son aproximadamente los siguientes: a) Cree un nuevo controlador de plataforma en el directorio drivers/staging/android/switch, que contiene una variable global denominada switch_gpio.c. Contiene una variable global struct switch_dev sdev, que es el dispositivo de conmutación que se registrará.

b) Llame a switch_dev_register en la función de sonda del controlador de la plataforma para registrar el sdev anterior en el sistema.

c) Solicitar la función de manejo de interrupciones para la detección de auriculares. Para conectar y desconectar auriculares, pueden ocurrir múltiples interrupciones debido a la velocidad de conexión y desconexión del usuario. Por lo tanto, generalmente es necesario implementar una cola de trabajo retrasada, es decir, INIT_DELAYED_WORK, en la función de procesamiento de interrupciones, y realizar el juicio real en la. función de devolución de llamada de la cola.

d) Cuando ocurre una interrupción, establezca el valor del nodo de estado a través de switch_set_state, que debe ser consistente con el valor definido en el archivo WiredAccessoryManager.java, consulte las definiciones de BIT_HEADSET y BIT_HEADSET_NO_MIC. La función switch_set_state llama a kobject_uevent_env/kobject_uevent, que son las funciones principales del kernel para notificar el espacio del usuario a través de uevent. El kernel notifica al espacio del usuario a través de uevent.