BACKPORT: 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>

(backported from develop-4.19 commit 55c7a54911)
Change-Id: I9115a8aa57875a440df96b67bb02c3d50f237b29
Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
This commit is contained in:
Ren Jianing
2020-06-12 11:23:39 +08:00
committed by Tao Huang
parent 02c6e7f24b
commit 1abe6982f8
2 changed files with 129 additions and 0 deletions

View File

@@ -97,6 +97,8 @@ static const struct snd_pcm_hardware uac_pcm_hardware = {
.periods_min = MIN_PERIODS,
};
static struct class *audio_class;
static void u_audio_set_fback_frequency(enum usb_device_speed speed,
struct usb_ep *out_ep,
unsigned long long freq,
@@ -520,6 +522,8 @@ int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate)
if (params->c_srates[i] == srate) {
spin_lock_irqsave(&prm->lock, flags);
prm->srate = srate;
audio_dev->usb_state[SET_SAMPLE_RATE_OUT] = true;
schedule_work(&audio_dev->work);
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
@@ -559,6 +563,8 @@ int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate)
if (params->p_srates[i] == srate) {
spin_lock_irqsave(&prm->lock, flags);
prm->srate = srate;
audio_dev->usb_state[SET_SAMPLE_RATE_IN] = true;
schedule_work(&audio_dev->work);
spin_unlock_irqrestore(&prm->lock, flags);
return 0;
}
@@ -595,6 +601,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);
prm = &uac->c_prm;
dev_dbg(dev, "start capture with rate %d\n", prm->srate);
ep = audio_dev->out_ep;
@@ -674,6 +684,10 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
if (audio_dev->in_ep_fback)
free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback);
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);
@@ -691,6 +705,10 @@ int u_audio_start_playback(struct g_audio *audio_dev)
int req_len, i;
unsigned int p_pktsize;
audio_dev->usb_state[SET_INTERFACE_IN] = true;
audio_dev->stream_state[STATE_IN] = true;
schedule_work(&audio_dev->work);
prm = &uac->p_prm;
dev_dbg(dev, "start playback with rate %d\n", prm->srate);
ep = audio_dev->in_ep;
@@ -754,6 +772,10 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
set_active(&uac->p_prm, false);
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);
@@ -1152,6 +1174,60 @@ static struct snd_kcontrol_new u_audio_controls[] = {
},
};
static void g_audio_work(struct work_struct *data)
{
struct g_audio *audio = container_of(data, struct g_audio, work);
struct usb_gadget *gadget = audio->gadget;
struct snd_uac_chip *uac = audio->uac;
struct uac_rtd_params *prm = &uac->c_prm;
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",
prm->srate);
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",
prm->srate);
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)
{
@@ -1394,6 +1470,17 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
NULL, 0, BUFF_SIZE_MAX);
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;
@@ -1419,6 +1506,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)
@@ -1432,6 +1523,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");

View File

@@ -57,7 +57,24 @@ struct uac_params {
int fb_max; /* upper frequency drift feedback limit per-mil */
};
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;