mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
usb: gadget: u_audio: add uevent for set_alt and set_srate
This patch add uevent to notify the application layer when it should playback or capture and setting the accuration sample rate. The event consists of three parts, namely USB_STATE, STREAM_DIRECTION and STREAM_STATE. For example: g_audio_work: sent uac uevent USB_STATE=SET_INTERFACE STREAM_DIRECTION=IN STREAM_STATE=OFF g_audio_work: sent uac uevent USB_STATE=SET_INTERFACE STREAM_DIRECTION=OUT STREAM_STATE=ON g_audio_work: sent uac uevent USB_STATE=SET_SAMPLE_RATE STREAM_DIRECTION=OUT SAMPLE_RATE=44100 Signed-off-by: Ren Jianing <jianing.ren@rock-chips.com> Change-Id: Ia01572d92d0c3ae40b19e772571282b8806da9e5
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
#define PRD_SIZE_MAX PAGE_SIZE
|
||||
#define MIN_PERIODS 4
|
||||
|
||||
static struct class *audio_class;
|
||||
|
||||
struct uac_req {
|
||||
struct uac_rtd_params *pp; /* parent param */
|
||||
struct usb_request *req;
|
||||
@@ -426,6 +428,9 @@ int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
|
||||
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (params->c_srate[i] == srate) {
|
||||
audio_dev->usb_state[SET_SAMPLE_RATE_OUT] = true;
|
||||
schedule_work(&audio_dev->work);
|
||||
|
||||
params->c_srate_active = srate;
|
||||
snd_ctl_notify(audio_dev->uac->card,
|
||||
SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
|
||||
@@ -447,6 +452,9 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
|
||||
|
||||
for (i = 0; i < UAC_MAX_RATES; i++) {
|
||||
if (params->p_srate[i] == srate) {
|
||||
audio_dev->usb_state[SET_SAMPLE_RATE_IN] = true;
|
||||
schedule_work(&audio_dev->work);
|
||||
|
||||
params->p_srate_active = srate;
|
||||
snd_ctl_notify(audio_dev->uac->card,
|
||||
SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
|
||||
@@ -471,6 +479,10 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
||||
struct uac_params *params = &audio_dev->params;
|
||||
int req_len, i;
|
||||
|
||||
audio_dev->usb_state[SET_INTERFACE_OUT] = true;
|
||||
audio_dev->stream_state[STATE_OUT] = true;
|
||||
schedule_work(&audio_dev->work);
|
||||
|
||||
ep = audio_dev->out_ep;
|
||||
prm = &uac->c_prm;
|
||||
config_ep_by_speed(gadget, &audio_dev->func, ep);
|
||||
@@ -508,6 +520,10 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
free_ep(&uac->c_prm, audio_dev->out_ep);
|
||||
|
||||
audio_dev->usb_state[SET_INTERFACE_OUT] = true;
|
||||
audio_dev->stream_state[STATE_OUT] = false;
|
||||
schedule_work(&audio_dev->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_stop_capture);
|
||||
|
||||
@@ -524,6 +540,10 @@ int u_audio_start_playback(struct g_audio *audio_dev)
|
||||
const struct usb_endpoint_descriptor *ep_desc;
|
||||
int req_len, i;
|
||||
|
||||
audio_dev->usb_state[SET_INTERFACE_IN] = true;
|
||||
audio_dev->stream_state[STATE_IN] = true;
|
||||
schedule_work(&audio_dev->work);
|
||||
|
||||
dev_dbg(dev, "start playback with rate %d\n", params->p_srate_active);
|
||||
ep = audio_dev->in_ep;
|
||||
prm = &uac->p_prm;
|
||||
@@ -585,9 +605,66 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
|
||||
struct snd_uac_chip *uac = audio_dev->uac;
|
||||
|
||||
free_ep(&uac->p_prm, audio_dev->in_ep);
|
||||
|
||||
audio_dev->usb_state[SET_INTERFACE_IN] = true;
|
||||
audio_dev->stream_state[STATE_IN] = false;
|
||||
schedule_work(&audio_dev->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
|
||||
|
||||
static void g_audio_work(struct work_struct *data)
|
||||
{
|
||||
struct g_audio *audio = container_of(data, struct g_audio, work);
|
||||
struct uac_params *params = &audio->params;
|
||||
struct usb_gadget *gadget = audio->gadget;
|
||||
struct device *dev = &gadget->dev;
|
||||
char *uac_event[4] = { NULL, NULL, NULL, NULL };
|
||||
char srate_str[19];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!audio->usb_state[i])
|
||||
continue;
|
||||
|
||||
switch (i) {
|
||||
case SET_INTERFACE_OUT:
|
||||
uac_event[0] = "USB_STATE=SET_INTERFACE";
|
||||
uac_event[1] = "STREAM_DIRECTION=OUT";
|
||||
uac_event[2] = audio->stream_state[STATE_OUT] ?
|
||||
"STREAM_STATE=ON" : "STREAM_STATE=OFF";
|
||||
break;
|
||||
case SET_INTERFACE_IN:
|
||||
uac_event[0] = "USB_STATE=SET_INTERFACE";
|
||||
uac_event[1] = "STREAM_DIRECTION=IN";
|
||||
uac_event[2] = audio->stream_state[STATE_IN] ?
|
||||
"STREAM_STATE=ON" : "STREAM_STATE=OFF";
|
||||
break;
|
||||
case SET_SAMPLE_RATE_OUT:
|
||||
uac_event[0] = "USB_STATE=SET_SAMPLE_RATE";
|
||||
uac_event[1] = "STREAM_DIRECTION=OUT";
|
||||
snprintf(srate_str, sizeof(srate_str), "SAMPLE_RATE=%d",
|
||||
params->c_srate_active);
|
||||
uac_event[2] = srate_str;
|
||||
break;
|
||||
case SET_SAMPLE_RATE_IN:
|
||||
uac_event[0] = "USB_STATE=SET_SAMPLE_RATE";
|
||||
uac_event[1] = "STREAM_DIRECTION=IN";
|
||||
snprintf(srate_str, sizeof(srate_str), "SAMPLE_RATE=%d",
|
||||
params->p_srate_active);
|
||||
uac_event[2] = srate_str;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
audio->usb_state[i] = false;
|
||||
kobject_uevent_env(&audio->device->kobj, KOBJ_CHANGE,
|
||||
uac_event);
|
||||
dev_dbg(dev, "%s: sent uac uevent %s %s %s\n", __func__,
|
||||
uac_event[0], uac_event[1], uac_event[2]);
|
||||
}
|
||||
}
|
||||
|
||||
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
const char *card_name)
|
||||
{
|
||||
@@ -696,6 +773,17 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||
}
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto snd_fail;
|
||||
|
||||
g_audio->device = device_create(audio_class, NULL, MKDEV(0, 0), NULL,
|
||||
"%s", g_audio->uac->card->longname);
|
||||
if (IS_ERR(g_audio->device)) {
|
||||
err = PTR_ERR(g_audio->device);
|
||||
goto snd_fail;
|
||||
}
|
||||
|
||||
INIT_WORK(&g_audio->work, g_audio_work);
|
||||
|
||||
if (!err)
|
||||
return 0;
|
||||
@@ -721,6 +809,10 @@ void g_audio_cleanup(struct g_audio *g_audio)
|
||||
if (!g_audio || !g_audio->uac)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&g_audio->work);
|
||||
device_destroy(g_audio->device->class, g_audio->device->devt);
|
||||
g_audio->device = NULL;
|
||||
|
||||
uac = g_audio->uac;
|
||||
card = uac->card;
|
||||
if (card)
|
||||
@@ -734,6 +826,27 @@ void g_audio_cleanup(struct g_audio *g_audio)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(g_audio_cleanup);
|
||||
|
||||
static int __init u_audio_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
audio_class = class_create(THIS_MODULE, "u_audio");
|
||||
if (IS_ERR(audio_class)) {
|
||||
err = PTR_ERR(audio_class);
|
||||
audio_class = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
module_init(u_audio_init);
|
||||
|
||||
static void __exit u_audio_exit(void)
|
||||
{
|
||||
if (audio_class)
|
||||
class_destroy(audio_class);
|
||||
}
|
||||
module_exit(u_audio_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
|
||||
MODULE_AUTHOR("Ruslan Bilovol");
|
||||
|
||||
@@ -28,7 +28,24 @@ struct uac_params {
|
||||
int req_number; /* number of preallocated requests */
|
||||
};
|
||||
|
||||
enum usb_state_index {
|
||||
SET_INTERFACE_OUT,
|
||||
SET_INTERFACE_IN,
|
||||
SET_SAMPLE_RATE_OUT,
|
||||
SET_SAMPLE_RATE_IN,
|
||||
};
|
||||
|
||||
enum stream_state_index {
|
||||
STATE_OUT,
|
||||
STATE_IN,
|
||||
};
|
||||
|
||||
struct g_audio {
|
||||
struct device *device;
|
||||
bool usb_state[4];
|
||||
bool stream_state[2];
|
||||
struct work_struct work;
|
||||
|
||||
struct usb_function func;
|
||||
struct usb_gadget *gadget;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user