suspend & resume implementations

This commit is contained in:
serifpersia 2025-07-04 20:31:05 +02:00
parent 76a3719cf9
commit df642295da
1 changed files with 125 additions and 30 deletions

View File

@ -21,9 +21,20 @@ MODULE_LICENSE("GPL");
#define DRIVER_NAME "snd-usb-us144mkii"
/*
* TODO:
* - Implement audio input capture.
* - Implement MIDI IN/OUT.
* - Expose hardware features via the ALSA Control API (mixers):
* - Digital output format selection.
*/
/* --- Module Parameters --- */
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int dev_idx;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the US-144MKII soundcard.");
@ -41,7 +52,7 @@ MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard.");
#define EP_AUDIO_OUT 0x02
#define EP_MIDI_IN 0x83
#define EP_MIDI_OUT 0x04
#define EP_CAPTURE_DATA 0x86
#define EP_AUDIO_IN 0x86
/* --- USB Control Message Protocol --- */
#define RT_H2D_CLASS_EP (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT)
@ -83,6 +94,16 @@ MODULE_PARM_DESC(enable, "Enable this US-144MKII soundcard.");
struct tascam_card;
static struct usb_driver tascam_alsa_driver;
/* --- Forward Declarations --- */
static void playback_urb_complete(struct urb *urb);
static void feedback_urb_complete(struct urb *urb);
static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int rate);
static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void tascam_disconnect(struct usb_interface *intf);
static int tascam_suspend(struct usb_interface *intf, pm_message_t message);
static int tascam_resume(struct usb_interface *intf);
/* --- Rate-to-Packet Fixing Data --- */
static const unsigned int patterns_48khz[5][8] = {
{5, 6, 6, 6, 5, 6, 6, 6}, {5, 6, 6, 6, 6, 6, 6, 6},
{6, 6, 6, 6, 6, 6, 6, 6}, {7, 6, 6, 6, 6, 6, 6, 6},
@ -104,6 +125,7 @@ static const unsigned int patterns_44khz[5][8] = {
{6, 6, 6, 5, 6, 6, 6, 5}
};
/* --- Main Driver Data Structure --- */
struct tascam_card {
struct usb_device *dev;
struct usb_interface *iface0;
@ -141,9 +163,6 @@ struct tascam_card {
static const unsigned int playback_iso_packet_counts_tiers[] = { 8, 24, 40 };
static const unsigned int hardware_feedback_packet_counts[] = { 1, 2, 5 };
static void playback_urb_complete(struct urb *urb);
static void feedback_urb_complete(struct urb *urb);
static const struct snd_pcm_hardware tascam_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID |
@ -312,7 +331,7 @@ static int us144mkii_configure_device_for_rate(struct tascam_card *tascam, int r
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), VENDOR_REQ_MODE_CONTROL, RT_H2D_VENDOR_DEV, MODE_VAL_CONFIG, 0x0000, NULL, 0, USB_CTRL_TIMEOUT_MS);
if (err < 0) goto fail;
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, EP_CAPTURE_DATA, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS);
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, EP_AUDIO_IN, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS);
if (err < 0) goto fail;
err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, RT_H2D_CLASS_EP, UAC_SAMPLING_FREQ_CONTROL, EP_AUDIO_OUT, rate_payload_buf, 3, USB_CTRL_TIMEOUT_MS);
if (err < 0) goto fail;
@ -687,13 +706,87 @@ static void tascam_card_private_free(struct snd_card *card)
}
}
/**
* tascam_suspend - Called when the device is being suspended.
* @intf: The USB interface.
* @message: Power management message.
*
* Stops all active audio streams to prepare for system sleep.
*
* Returns: 0 on success.
*/
static int tascam_suspend(struct usb_interface *intf, pm_message_t message)
{
struct tascam_card *tascam = usb_get_intfdata(intf);
if (!tascam || !tascam->pcm)
return 0;
snd_pcm_suspend_all(tascam->pcm);
return 0;
}
/**
* tascam_resume - Called when the device is being resumed.
* @intf: The USB interface.
*
* Re-initializes the device hardware after system resume, restoring its
* alternate settings and sample rate configuration. This is necessary because
* the device may lose its state during suspend.
*
* Returns: 0 on success, or a negative error code on failure.
*/
static int tascam_resume(struct usb_interface *intf)
{
struct tascam_card *tascam = usb_get_intfdata(intf);
struct usb_device *dev;
int err;
if (!tascam)
return 0;
dev = tascam->dev;
dev_info(&intf->dev, "Resuming and re-initializing device...\n");
/* Re-establish alternate settings for both interfaces */
err = usb_set_interface(dev, 0, 1);
if (err < 0) {
dev_err(&intf->dev, "Resume: Set Alt Setting on Intf 0 failed: %d\n", err);
return err;
}
err = usb_set_interface(dev, 1, 1);
if (err < 0) {
dev_err(&intf->dev, "Resume: Set Alt Setting on Intf 1 failed: %d\n", err);
return err;
}
/*
* Re-configure the device for the last used sample rate.
* If no stream was ever started, current_rate will be 0, and we skip this.
* The ALSA core will handle resuming the PCM streams, which will
* trigger our .prepare and .trigger ops as needed.
*/
if (tascam->current_rate > 0) {
dev_info(&intf->dev, "Restoring sample rate to %d Hz\n", tascam->current_rate);
err = us144mkii_configure_device_for_rate(tascam, tascam->current_rate);
if (err < 0) {
dev_err(&intf->dev, "Resume: Failed to restore sample rate configuration\n");
/* Invalidate the rate so the next hw_params will re-configure fully. */
tascam->current_rate = 0;
return err;
}
}
return 0;
}
static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *usb_id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct tascam_card *tascam;
struct snd_card *card;
int err;
static int dev_idx;
u8 *handshake_buf;
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
@ -820,6 +913,8 @@ static struct usb_driver tascam_alsa_driver = {
.probe = tascam_probe,
.disconnect = tascam_disconnect,
.id_table = tascam_id_table,
.suspend = tascam_suspend,
.resume = tascam_resume,
};
module_usb_driver(tascam_alsa_driver);