mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 11:50:43 +09:00
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:
@@ -97,6 +97,8 @@ static const struct snd_pcm_hardware uac_pcm_hardware = {
|
|||||||
.periods_min = MIN_PERIODS,
|
.periods_min = MIN_PERIODS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct class *audio_class;
|
||||||
|
|
||||||
static void u_audio_set_fback_frequency(enum usb_device_speed speed,
|
static void u_audio_set_fback_frequency(enum usb_device_speed speed,
|
||||||
struct usb_ep *out_ep,
|
struct usb_ep *out_ep,
|
||||||
unsigned long long freq,
|
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) {
|
if (params->c_srates[i] == srate) {
|
||||||
spin_lock_irqsave(&prm->lock, flags);
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
prm->srate = srate;
|
prm->srate = srate;
|
||||||
|
audio_dev->usb_state[SET_SAMPLE_RATE_OUT] = true;
|
||||||
|
schedule_work(&audio_dev->work);
|
||||||
spin_unlock_irqrestore(&prm->lock, flags);
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
return 0;
|
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) {
|
if (params->p_srates[i] == srate) {
|
||||||
spin_lock_irqsave(&prm->lock, flags);
|
spin_lock_irqsave(&prm->lock, flags);
|
||||||
prm->srate = srate;
|
prm->srate = srate;
|
||||||
|
audio_dev->usb_state[SET_SAMPLE_RATE_IN] = true;
|
||||||
|
schedule_work(&audio_dev->work);
|
||||||
spin_unlock_irqrestore(&prm->lock, flags);
|
spin_unlock_irqrestore(&prm->lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -595,6 +601,10 @@ int u_audio_start_capture(struct g_audio *audio_dev)
|
|||||||
struct uac_params *params = &audio_dev->params;
|
struct uac_params *params = &audio_dev->params;
|
||||||
int req_len, i;
|
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;
|
prm = &uac->c_prm;
|
||||||
dev_dbg(dev, "start capture with rate %d\n", prm->srate);
|
dev_dbg(dev, "start capture with rate %d\n", prm->srate);
|
||||||
ep = audio_dev->out_ep;
|
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)
|
if (audio_dev->in_ep_fback)
|
||||||
free_ep_fback(&uac->c_prm, 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);
|
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);
|
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;
|
int req_len, i;
|
||||||
unsigned int p_pktsize;
|
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;
|
prm = &uac->p_prm;
|
||||||
dev_dbg(dev, "start playback with rate %d\n", prm->srate);
|
dev_dbg(dev, "start playback with rate %d\n", prm->srate);
|
||||||
ep = audio_dev->in_ep;
|
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);
|
set_active(&uac->p_prm, false);
|
||||||
free_ep(&uac->p_prm, audio_dev->in_ep);
|
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);
|
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,
|
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
|
||||||
const char *card_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);
|
NULL, 0, BUFF_SIZE_MAX);
|
||||||
|
|
||||||
err = snd_card_register(card);
|
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)
|
if (!err)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1419,6 +1506,10 @@ void g_audio_cleanup(struct g_audio *g_audio)
|
|||||||
if (!g_audio || !g_audio->uac)
|
if (!g_audio || !g_audio->uac)
|
||||||
return;
|
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;
|
uac = g_audio->uac;
|
||||||
card = uac->card;
|
card = uac->card;
|
||||||
if (card)
|
if (card)
|
||||||
@@ -1432,6 +1523,27 @@ void g_audio_cleanup(struct g_audio *g_audio)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(g_audio_cleanup);
|
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_LICENSE("GPL");
|
||||||
MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
|
MODULE_DESCRIPTION("USB gadget \"ALSA sound card\" utilities");
|
||||||
MODULE_AUTHOR("Ruslan Bilovol");
|
MODULE_AUTHOR("Ruslan Bilovol");
|
||||||
|
|||||||
@@ -57,7 +57,24 @@ struct uac_params {
|
|||||||
int fb_max; /* upper frequency drift feedback limit per-mil */
|
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 g_audio {
|
||||||
|
struct device *device;
|
||||||
|
bool usb_state[4];
|
||||||
|
bool stream_state[2];
|
||||||
|
struct work_struct work;
|
||||||
|
|
||||||
struct usb_function func;
|
struct usb_function func;
|
||||||
struct usb_gadget *gadget;
|
struct usb_gadget *gadget;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user