From 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 22 Oct 2024 10:36:11 +0000 Subject: [PATCH] 修改4g拨号为QMI,需要在系统里后台执行quectel-CM --- kernel/sound/core/timer.c | 621 ++++++++++++++++++++++++++++++++++--------------------- 1 files changed, 383 insertions(+), 238 deletions(-) diff --git a/kernel/sound/core/timer.c b/kernel/sound/core/timer.c index 4920ec4..764d2b1 100644 --- a/kernel/sound/core/timer.c +++ b/kernel/sound/core/timer.c @@ -1,22 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Timers abstract layer * Copyright (c) by Jaroslav Kysela <perex@perex.cz> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/delay.h> @@ -38,6 +23,7 @@ /* internal flags */ #define SNDRV_TIMER_IFLG_PAUSED 0x00010000 +#define SNDRV_TIMER_IFLG_DEAD 0x00020000 #if IS_ENABLED(CONFIG_SND_HRTIMER) #define DEFAULT_TIMER_LIMIT 4 @@ -58,6 +44,28 @@ MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); MODULE_ALIAS("devname:snd/timer"); +enum timer_tread_format { + TREAD_FORMAT_NONE = 0, + TREAD_FORMAT_TIME64, + TREAD_FORMAT_TIME32, +}; + +struct snd_timer_tread32 { + int event; + s32 tstamp_sec; + s32 tstamp_nsec; + unsigned int val; +}; + +struct snd_timer_tread64 { + int event; + u8 pad1[4]; + s64 tstamp_sec; + s64 tstamp_nsec; + unsigned int val; + u8 pad2[4]; +}; + struct snd_timer_user { struct snd_timer_instance *timeri; int tread; /* enhanced read with timestamps and events */ @@ -69,15 +77,39 @@ int queue_size; bool disconnected; struct snd_timer_read *queue; - struct snd_timer_tread *tqueue; + struct snd_timer_tread64 *tqueue; spinlock_t qlock; unsigned long last_resolution; unsigned int filter; - struct timespec tstamp; /* trigger tstamp */ + struct timespec64 tstamp; /* trigger tstamp */ wait_queue_head_t qchange_sleep; - struct fasync_struct *fasync; + struct snd_fasync *fasync; struct mutex ioctl_lock; }; + +struct snd_timer_status32 { + s32 tstamp_sec; /* Timestamp - last update */ + s32 tstamp_nsec; + unsigned int resolution; /* current period resolution in ns */ + unsigned int lost; /* counter of master tick lost */ + unsigned int overrun; /* count of read queue overruns */ + unsigned int queue; /* used queue size */ + unsigned char reserved[64]; /* reserved */ +}; + +#define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32) + +struct snd_timer_status64 { + s64 tstamp_sec; /* Timestamp - last update */ + s64 tstamp_nsec; + unsigned int resolution; /* current period resolution in ns */ + unsigned int lost; /* counter of master tick lost */ + unsigned int overrun; /* count of read queue overruns */ + unsigned int queue; /* used queue size */ + unsigned char reserved[64]; /* reserved */ +}; + +#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64) /* list of timers */ static LIST_HEAD(snd_timer_list); @@ -102,12 +134,11 @@ /* * create a timer instance with the given owner string. - * when timer is not NULL, increments the module counter */ -static struct snd_timer_instance *snd_timer_instance_new(char *owner, - struct snd_timer *timer) +struct snd_timer_instance *snd_timer_instance_new(const char *owner) { struct snd_timer_instance *timeri; + timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); if (timeri == NULL) return NULL; @@ -122,22 +153,27 @@ INIT_LIST_HEAD(&timeri->slave_list_head); INIT_LIST_HEAD(&timeri->slave_active_head); - timeri->timer = timer; - if (timer && !try_module_get(timer->module)) { - kfree(timeri->owner); - kfree(timeri); - return NULL; - } - return timeri; } +EXPORT_SYMBOL(snd_timer_instance_new); + +void snd_timer_instance_free(struct snd_timer_instance *timeri) +{ + if (timeri) { + if (timeri->private_free) + timeri->private_free(timeri); + kfree(timeri->owner); + kfree(timeri); + } +} +EXPORT_SYMBOL(snd_timer_instance_free); /* * find a timer instance from the given timer id */ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) { - struct snd_timer *timer = NULL; + struct snd_timer *timer; list_for_each_entry(timer, &snd_timer_list, device_list) { if (timer->tmr_class != tid->dev_class) @@ -177,6 +213,28 @@ #endif +/* move the slave if it belongs to the master; return 1 if match */ +static int check_matching_master_slave(struct snd_timer_instance *master, + struct snd_timer_instance *slave) +{ + if (slave->slave_class != master->slave_class || + slave->slave_id != master->slave_id) + return 0; + if (master->timer->num_instances >= master->timer->max_instances) + return -EBUSY; + list_move_tail(&slave->open_list, &master->slave_list_head); + master->timer->num_instances++; + spin_lock_irq(&slave_active_lock); + spin_lock(&master->timer->lock); + slave->master = master; + slave->timer = master->timer; + if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) + list_add_tail(&slave->active_list, &master->slave_active_head); + spin_unlock(&master->timer->lock); + spin_unlock_irq(&slave_active_lock); + return 1; +} + /* * look for a master instance matching with the slave id of the given slave. * when found, relink the open_link of the slave. @@ -187,27 +245,18 @@ { struct snd_timer *timer; struct snd_timer_instance *master; + int err = 0; /* FIXME: it's really dumb to look up all entries.. */ list_for_each_entry(timer, &snd_timer_list, device_list) { list_for_each_entry(master, &timer->open_list_head, open_list) { - if (slave->slave_class == master->slave_class && - slave->slave_id == master->slave_id) { - if (master->timer->num_instances >= - master->timer->max_instances) - return -EBUSY; - list_move_tail(&slave->open_list, - &master->slave_list_head); - master->timer->num_instances++; - spin_lock_irq(&slave_active_lock); - slave->master = master; - slave->timer = master->timer; - spin_unlock_irq(&slave_active_lock); - return 0; - } + err = check_matching_master_slave(master, slave); + if (err != 0) /* match found or error */ + goto out; } } - return 0; + out: + return err < 0 ? err : 0; } /* @@ -219,43 +268,29 @@ static int snd_timer_check_master(struct snd_timer_instance *master) { struct snd_timer_instance *slave, *tmp; + int err = 0; /* check all pending slaves */ list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { - if (slave->slave_class == master->slave_class && - slave->slave_id == master->slave_id) { - if (master->timer->num_instances >= - master->timer->max_instances) - return -EBUSY; - list_move_tail(&slave->open_list, &master->slave_list_head); - master->timer->num_instances++; - spin_lock_irq(&slave_active_lock); - spin_lock(&master->timer->lock); - slave->master = master; - slave->timer = master->timer; - if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) - list_add_tail(&slave->active_list, - &master->slave_active_head); - spin_unlock(&master->timer->lock); - spin_unlock_irq(&slave_active_lock); - } + err = check_matching_master_slave(master, slave); + if (err < 0) + break; } - return 0; + return err < 0 ? err : 0; } -static int snd_timer_close_locked(struct snd_timer_instance *timeri, - struct device **card_devp_to_put); +static void snd_timer_close_locked(struct snd_timer_instance *timeri, + struct device **card_devp_to_put); /* * open a timer instance * when opening a master, the slave id must be here given. */ -int snd_timer_open(struct snd_timer_instance **ti, - char *owner, struct snd_timer_id *tid, +int snd_timer_open(struct snd_timer_instance *timeri, + struct snd_timer_id *tid, unsigned int slave_id) { struct snd_timer *timer; - struct snd_timer_instance *timeri = NULL; struct device *card_dev_to_put = NULL; int err; @@ -273,22 +308,13 @@ err = -EBUSY; goto unlock; } - timeri = snd_timer_instance_new(owner, NULL); - if (!timeri) { - err = -ENOMEM; - goto unlock; - } timeri->slave_class = tid->dev_sclass; timeri->slave_id = tid->device; timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; list_add_tail(&timeri->open_list, &snd_timer_slave_list); num_slaves++; err = snd_timer_check_slave(timeri); - if (err < 0) { - snd_timer_close_locked(timeri, &card_dev_to_put); - timeri = NULL; - } - goto unlock; + goto list_added; } /* open a master instance */ @@ -318,45 +344,40 @@ err = -EBUSY; goto unlock; } - timeri = snd_timer_instance_new(owner, timer); - if (!timeri) { - err = -ENOMEM; + if (!try_module_get(timer->module)) { + err = -EBUSY; goto unlock; } /* take a card refcount for safe disconnection */ - if (timer->card) + if (timer->card) { get_device(&timer->card->card_dev); - timeri->slave_class = tid->dev_sclass; - timeri->slave_id = slave_id; + card_dev_to_put = &timer->card->card_dev; + } if (list_empty(&timer->open_list_head) && timer->hw.open) { err = timer->hw.open(timer); if (err) { - kfree(timeri->owner); - kfree(timeri); - timeri = NULL; - - if (timer->card) - card_dev_to_put = &timer->card->card_dev; module_put(timer->module); goto unlock; } } + timeri->timer = timer; + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = slave_id; + list_add_tail(&timeri->open_list, &timer->open_list_head); timer->num_instances++; err = snd_timer_check_master(timeri); - if (err < 0) { +list_added: + if (err < 0) snd_timer_close_locked(timeri, &card_dev_to_put); - timeri = NULL; - } unlock: mutex_unlock(®ister_mutex); /* put_device() is called after unlock for avoiding deadlock */ - if (card_dev_to_put) + if (err < 0 && card_dev_to_put) put_device(card_dev_to_put); - *ti = timeri; return err; } EXPORT_SYMBOL(snd_timer_open); @@ -365,20 +386,27 @@ * close a timer instance * call this with register_mutex down. */ -static int snd_timer_close_locked(struct snd_timer_instance *timeri, - struct device **card_devp_to_put) +static void snd_timer_close_locked(struct snd_timer_instance *timeri, + struct device **card_devp_to_put) { - struct snd_timer *timer = NULL; + struct snd_timer *timer = timeri->timer; struct snd_timer_instance *slave, *tmp; - list_del(&timeri->open_list); - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) - num_slaves--; + if (timer) { + spin_lock_irq(&timer->lock); + timeri->flags |= SNDRV_TIMER_IFLG_DEAD; + spin_unlock_irq(&timer->lock); + } + + if (!list_empty(&timeri->open_list)) { + list_del_init(&timeri->open_list); + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + num_slaves--; + } /* force to stop the timer */ snd_timer_stop(timeri); - timer = timeri->timer; if (timer) { timer->num_instances--; /* wait, until the active callback is finished */ @@ -393,6 +421,7 @@ /* remove slave links */ spin_lock_irq(&slave_active_lock); spin_lock(&timer->lock); + timeri->timer = NULL; list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) { list_move_tail(&slave->open_list, &snd_timer_slave_list); @@ -410,11 +439,6 @@ timer = NULL; } - if (timeri->private_free) - timeri->private_free(timeri); - kfree(timeri->owner); - kfree(timeri); - if (timer) { if (list_empty(&timer->open_list_head) && timer->hw.close) timer->hw.close(timer); @@ -423,28 +447,24 @@ *card_devp_to_put = &timer->card->card_dev; module_put(timer->module); } - - return 0; } /* * close a timer instance */ -int snd_timer_close(struct snd_timer_instance *timeri) +void snd_timer_close(struct snd_timer_instance *timeri) { struct device *card_dev_to_put = NULL; - int err; if (snd_BUG_ON(!timeri)) - return -ENXIO; + return; mutex_lock(®ister_mutex); - err = snd_timer_close_locked(timeri, &card_dev_to_put); + snd_timer_close_locked(timeri, &card_dev_to_put); mutex_unlock(®ister_mutex); /* put_device() is called after unlock for avoiding deadlock */ if (card_dev_to_put) put_device(card_dev_to_put); - return err; } EXPORT_SYMBOL(snd_timer_close); @@ -479,12 +499,12 @@ struct snd_timer *timer = ti->timer; unsigned long resolution = 0; struct snd_timer_instance *ts; - struct timespec tstamp; + struct timespec64 tstamp; if (timer_tstamp_monotonic) - ktime_get_ts(&tstamp); + ktime_get_ts64(&tstamp); else - getnstimeofday(&tstamp); + ktime_get_real_ts64(&tstamp); if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || event > SNDRV_TIMER_EVENT_PAUSE)) return; @@ -519,6 +539,10 @@ return -EINVAL; spin_lock_irqsave(&timer->lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { + result = -EINVAL; + goto unlock; + } if (timer->card && timer->card->shutdown) { result = -ENODEV; goto unlock; @@ -563,11 +587,16 @@ bool start) { unsigned long flags; + int err; spin_lock_irqsave(&slave_active_lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { + err = -EINVAL; + goto unlock; + } if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { - spin_unlock_irqrestore(&slave_active_lock, flags); - return -EBUSY; + err = -EBUSY; + goto unlock; } timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; if (timeri->master && timeri->timer) { @@ -578,8 +607,10 @@ SNDRV_TIMER_EVENT_CONTINUE); spin_unlock(&timeri->timer->lock); } + err = 1; /* delayed start */ + unlock: spin_unlock_irqrestore(&slave_active_lock, flags); - return 1; /* delayed start */ + return err; } /* stop/pause a master timer */ @@ -741,41 +772,62 @@ timer->sticks = ticks; } -/* - * timer tasklet - * - */ -static void snd_timer_tasklet(unsigned long arg) +/* call callbacks in timer ack list */ +static void snd_timer_process_callbacks(struct snd_timer *timer, + struct list_head *head) { - struct snd_timer *timer = (struct snd_timer *) arg; struct snd_timer_instance *ti; - struct list_head *p; unsigned long resolution, ticks; - unsigned long flags; - if (timer->card && timer->card->shutdown) - return; - - spin_lock_irqsave(&timer->lock, flags); - /* now process all callbacks */ - while (!list_empty(&timer->sack_list_head)) { - p = timer->sack_list_head.next; /* get first item */ - ti = list_entry(p, struct snd_timer_instance, ack_list); + while (!list_empty(head)) { + ti = list_first_entry(head, struct snd_timer_instance, + ack_list); /* remove from ack_list and make empty */ - list_del_init(p); + list_del_init(&ti->ack_list); - ticks = ti->pticks; - ti->pticks = 0; - resolution = ti->resolution; - - ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; - spin_unlock(&timer->lock); - if (ti->callback) - ti->callback(ti, resolution, ticks); - spin_lock(&timer->lock); - ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; + if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { + ticks = ti->pticks; + ti->pticks = 0; + resolution = ti->resolution; + ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; + spin_unlock(&timer->lock); + if (ti->callback) + ti->callback(ti, resolution, ticks); + spin_lock(&timer->lock); + ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; + } } +} + +/* clear pending instances from ack list */ +static void snd_timer_clear_callbacks(struct snd_timer *timer, + struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(&timer->lock, flags); + while (!list_empty(head)) + list_del_init(head->next); + spin_unlock_irqrestore(&timer->lock, flags); +} + +/* + * timer work + * + */ +static void snd_timer_work(struct work_struct *work) +{ + struct snd_timer *timer = container_of(work, struct snd_timer, task_work); + unsigned long flags; + + if (timer->card && timer->card->shutdown) { + snd_timer_clear_callbacks(timer, &timer->sack_list_head); + return; + } + + spin_lock_irqsave(&timer->lock, flags); + snd_timer_process_callbacks(timer, &timer->sack_list_head); spin_unlock_irqrestore(&timer->lock, flags); } @@ -788,16 +840,18 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) { struct snd_timer_instance *ti, *ts, *tmp; - unsigned long resolution, ticks; - struct list_head *p, *ack_list_head; + unsigned long resolution; + struct list_head *ack_list_head; unsigned long flags; - int use_tasklet = 0; + bool use_work = false; if (timer == NULL) return; - if (timer->card && timer->card->shutdown) + if (timer->card && timer->card->shutdown) { + snd_timer_clear_callbacks(timer, &timer->ack_list_head); return; + } spin_lock_irqsave(&timer->lock, flags); @@ -811,6 +865,8 @@ */ list_for_each_entry_safe(ti, tmp, &timer->active_list_head, active_list) { + if (ti->flags & SNDRV_TIMER_IFLG_DEAD) + continue; if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) continue; ti->pticks += ticks_left; @@ -828,7 +884,7 @@ --timer->running; list_del_init(&ti->active_list); } - if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || + if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) || (ti->flags & SNDRV_TIMER_IFLG_FAST)) ack_list_head = &timer->ack_list_head; else @@ -860,30 +916,14 @@ } /* now process all fast callbacks */ - while (!list_empty(&timer->ack_list_head)) { - p = timer->ack_list_head.next; /* get first item */ - ti = list_entry(p, struct snd_timer_instance, ack_list); - - /* remove from ack_list and make empty */ - list_del_init(p); - - ticks = ti->pticks; - ti->pticks = 0; - - ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; - spin_unlock(&timer->lock); - if (ti->callback) - ti->callback(ti, resolution, ticks); - spin_lock(&timer->lock); - ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; - } + snd_timer_process_callbacks(timer, &timer->ack_list_head); /* do we have any slow callbacks? */ - use_tasklet = !list_empty(&timer->sack_list_head); + use_work = !list_empty(&timer->sack_list_head); spin_unlock_irqrestore(&timer->lock, flags); - if (use_tasklet) - tasklet_schedule(&timer->task_queue); + if (use_work) + queue_work(system_highpri_wq, &timer->task_work); } EXPORT_SYMBOL(snd_timer_interrupt); @@ -896,7 +936,7 @@ { struct snd_timer *timer; int err; - static struct snd_device_ops ops = { + static const struct snd_device_ops ops = { .dev_free = snd_timer_dev_free, .dev_register = snd_timer_dev_register, .dev_disconnect = snd_timer_dev_disconnect, @@ -927,8 +967,7 @@ INIT_LIST_HEAD(&timer->ack_list_head); INIT_LIST_HEAD(&timer->sack_list_head); spin_lock_init(&timer->lock); - tasklet_init(&timer->task_queue, snd_timer_tasklet, - (unsigned long)timer); + INIT_WORK(&timer->task_work, snd_timer_work); timer->max_instances = 1000; /* default limit per timer */ if (card != NULL) { timer->module = card->module; @@ -1031,7 +1070,7 @@ return 0; } -void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp) +void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp) { unsigned long flags; unsigned long resolution = 0; @@ -1159,9 +1198,9 @@ return 0; } -static struct snd_timer_hardware snd_timer_system = +static const struct snd_timer_hardware snd_timer_system = { - .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, + .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK, .resolution = 1000000000L / HZ, .ticks = 10000000L, .close = snd_timer_s_close, @@ -1241,8 +1280,8 @@ list_for_each_entry(ti, &timer->open_list_head, open_list) snd_iprintf(buffer, " Client %s : %s\n", ti->owner ? ti->owner : "unknown", - ti->flags & (SNDRV_TIMER_IFLG_START | - SNDRV_TIMER_IFLG_RUNNING) + (ti->flags & (SNDRV_TIMER_IFLG_START | + SNDRV_TIMER_IFLG_RUNNING)) ? "running" : "stopped"); } mutex_unlock(®ister_mutex); @@ -1306,12 +1345,12 @@ } __wake: spin_unlock(&tu->qlock); - kill_fasync(&tu->fasync, SIGIO, POLL_IN); + snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, - struct snd_timer_tread *tread) + struct snd_timer_tread64 *tread) { if (tu->qused >= tu->queue_size) { tu->overrun++; @@ -1324,11 +1363,11 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, int event, - struct timespec *tstamp, + struct timespec64 *tstamp, unsigned long resolution) { struct snd_timer_user *tu = timeri->callback_data; - struct snd_timer_tread r1; + struct snd_timer_tread64 r1; unsigned long flags; if (event >= SNDRV_TIMER_EVENT_START && @@ -1338,12 +1377,13 @@ return; memset(&r1, 0, sizeof(r1)); r1.event = event; - r1.tstamp = *tstamp; + r1.tstamp_sec = tstamp->tv_sec; + r1.tstamp_nsec = tstamp->tv_nsec; r1.val = resolution; spin_lock_irqsave(&tu->qlock, flags); snd_timer_user_append_to_tqueue(tu, &r1); spin_unlock_irqrestore(&tu->qlock, flags); - kill_fasync(&tu->fasync, SIGIO, POLL_IN); + snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } @@ -1360,8 +1400,8 @@ unsigned long ticks) { struct snd_timer_user *tu = timeri->callback_data; - struct snd_timer_tread *r, r1; - struct timespec tstamp; + struct snd_timer_tread64 *r, r1; + struct timespec64 tstamp; int prev, append = 0; memset(&r1, 0, sizeof(r1)); @@ -1374,14 +1414,15 @@ } if (tu->last_resolution != resolution || ticks > 0) { if (timer_tstamp_monotonic) - ktime_get_ts(&tstamp); + ktime_get_ts64(&tstamp); else - getnstimeofday(&tstamp); + ktime_get_real_ts64(&tstamp); } if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { r1.event = SNDRV_TIMER_EVENT_RESOLUTION; - r1.tstamp = tstamp; + r1.tstamp_sec = tstamp.tv_sec; + r1.tstamp_nsec = tstamp.tv_nsec; r1.val = resolution; snd_timer_user_append_to_tqueue(tu, &r1); tu->last_resolution = resolution; @@ -1395,14 +1436,16 @@ prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; r = &tu->tqueue[prev]; if (r->event == SNDRV_TIMER_EVENT_TICK) { - r->tstamp = tstamp; + r->tstamp_sec = tstamp.tv_sec; + r->tstamp_nsec = tstamp.tv_nsec; r->val += ticks; append++; goto __wake; } } r1.event = SNDRV_TIMER_EVENT_TICK; - r1.tstamp = tstamp; + r1.tstamp_sec = tstamp.tv_sec; + r1.tstamp_nsec = tstamp.tv_nsec; r1.val = ticks; snd_timer_user_append_to_tqueue(tu, &r1); append++; @@ -1410,14 +1453,14 @@ spin_unlock(&tu->qlock); if (append == 0) return; - kill_fasync(&tu->fasync, SIGIO, POLL_IN); + snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); } static int realloc_user_queue(struct snd_timer_user *tu, int size) { struct snd_timer_read *queue = NULL; - struct snd_timer_tread *tqueue = NULL; + struct snd_timer_tread64 *tqueue = NULL; if (tu->tread) { tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); @@ -1446,7 +1489,7 @@ struct snd_timer_user *tu; int err; - err = nonseekable_open(inode, file); + err = stream_open(inode, file); if (err < 0) return err; @@ -1473,9 +1516,12 @@ tu = file->private_data; file->private_data = NULL; mutex_lock(&tu->ioctl_lock); - if (tu->timeri) + if (tu->timeri) { snd_timer_close(tu->timeri); + snd_timer_instance_free(tu->timeri); + } mutex_unlock(&tu->ioctl_lock); + snd_fasync_free(tu->fasync); kfree(tu->queue); kfree(tu->tqueue); kfree(tu); @@ -1715,6 +1761,7 @@ tu = file->private_data; if (tu->timeri) { snd_timer_close(tu->timeri); + snd_timer_instance_free(tu->timeri); tu->timeri = NULL; } if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { @@ -1724,9 +1771,11 @@ sprintf(str, "application %i", current->pid); if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; - err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid); - if (err < 0) + tu->timeri = snd_timer_instance_new(str); + if (!tu->timeri) { + err = -ENOMEM; goto __err; + } tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; tu->timeri->callback = tu->tread @@ -1734,6 +1783,12 @@ tu->timeri->ccallback = snd_timer_user_ccallback; tu->timeri->callback_data = (void *)tu; tu->timeri->disconnect = snd_timer_user_disconnect; + + err = snd_timer_open(tu->timeri, &tselect.id, current->pid); + if (err < 0) { + snd_timer_instance_free(tu->timeri); + tu->timeri = NULL; + } __err: return err; @@ -1845,11 +1900,11 @@ tu->qhead = tu->qtail = tu->qused = 0; if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { - struct snd_timer_tread tread; + struct snd_timer_tread64 tread; memset(&tread, 0, sizeof(tread)); tread.event = SNDRV_TIMER_EVENT_EARLY; - tread.tstamp.tv_sec = 0; - tread.tstamp.tv_nsec = 0; + tread.tstamp_sec = 0; + tread.tstamp_nsec = 0; tread.val = 0; snd_timer_user_append_to_tqueue(tu, &tread); } else { @@ -1870,17 +1925,41 @@ return err; } -static int snd_timer_user_status(struct file *file, - struct snd_timer_status __user *_status) -{ +static int snd_timer_user_status32(struct file *file, + struct snd_timer_status32 __user *_status) + { struct snd_timer_user *tu; - struct snd_timer_status status; + struct snd_timer_status32 status; tu = file->private_data; if (!tu->timeri) return -EBADFD; memset(&status, 0, sizeof(status)); - status.tstamp = tu->tstamp; + status.tstamp_sec = tu->tstamp.tv_sec; + status.tstamp_nsec = tu->tstamp.tv_nsec; + status.resolution = snd_timer_resolution(tu->timeri); + status.lost = tu->timeri->lost; + status.overrun = tu->overrun; + spin_lock_irq(&tu->qlock); + status.queue = tu->qused; + spin_unlock_irq(&tu->qlock); + if (copy_to_user(_status, &status, sizeof(status))) + return -EFAULT; + return 0; +} + +static int snd_timer_user_status64(struct file *file, + struct snd_timer_status64 __user *_status) +{ + struct snd_timer_user *tu; + struct snd_timer_status64 status; + + tu = file->private_data; + if (!tu->timeri) + return -EBADFD; + memset(&status, 0, sizeof(status)); + status.tstamp_sec = tu->tstamp.tv_sec; + status.tstamp_nsec = tu->tstamp.tv_nsec; status.resolution = snd_timer_resolution(tu->timeri); status.lost = tu->timeri->lost; status.overrun = tu->overrun; @@ -1903,7 +1982,10 @@ snd_timer_stop(tu->timeri); tu->timeri->lost = 0; tu->last_resolution = 0; - return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0; + err = snd_timer_start(tu->timeri, tu->ticks); + if (err < 0) + return err; + return 0; } static int snd_timer_user_stop(struct file *file) @@ -1914,7 +1996,10 @@ tu = file->private_data; if (!tu->timeri) return -EBADFD; - return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; + err = snd_timer_stop(tu->timeri); + if (err < 0) + return err; + return 0; } static int snd_timer_user_continue(struct file *file) @@ -1929,7 +2014,10 @@ if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) return snd_timer_user_start(file); tu->timeri->lost = 0; - return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; + err = snd_timer_continue(tu->timeri); + if (err < 0) + return err; + return 0; } static int snd_timer_user_pause(struct file *file) @@ -1940,7 +2028,40 @@ tu = file->private_data; if (!tu->timeri) return -EBADFD; - return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0; + err = snd_timer_pause(tu->timeri); + if (err < 0) + return err; + return 0; +} + +static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu, + unsigned int cmd, bool compat) +{ + int __user *p = argp; + int xarg, old_tread; + + if (tu->timeri) /* too late */ + return -EBUSY; + if (get_user(xarg, p)) + return -EFAULT; + + old_tread = tu->tread; + + if (!xarg) + tu->tread = TREAD_FORMAT_NONE; + else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 || + (IS_ENABLED(CONFIG_64BIT) && !compat)) + tu->tread = TREAD_FORMAT_TIME64; + else + tu->tread = TREAD_FORMAT_TIME32; + + if (tu->tread != old_tread && + realloc_user_queue(tu, tu->queue_size) < 0) { + tu->tread = old_tread; + return -ENOMEM; + } + + return 0; } enum { @@ -1951,7 +2072,7 @@ }; static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) + unsigned long arg, bool compat) { struct snd_timer_user *tu; void __user *argp = (void __user *)arg; @@ -1963,23 +2084,9 @@ return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; case SNDRV_TIMER_IOCTL_NEXT_DEVICE: return snd_timer_user_next_device(argp); - case SNDRV_TIMER_IOCTL_TREAD: - { - int xarg, old_tread; - - if (tu->timeri) /* too late */ - return -EBUSY; - if (get_user(xarg, p)) - return -EFAULT; - old_tread = tu->tread; - tu->tread = xarg ? 1 : 0; - if (tu->tread != old_tread && - realloc_user_queue(tu, tu->queue_size) < 0) { - tu->tread = old_tread; - return -ENOMEM; - } - return 0; - } + case SNDRV_TIMER_IOCTL_TREAD_OLD: + case SNDRV_TIMER_IOCTL_TREAD64: + return snd_timer_user_tread(argp, tu, cmd, compat); case SNDRV_TIMER_IOCTL_GINFO: return snd_timer_user_ginfo(file, argp); case SNDRV_TIMER_IOCTL_GPARAMS: @@ -1992,8 +2099,10 @@ return snd_timer_user_info(file, argp); case SNDRV_TIMER_IOCTL_PARAMS: return snd_timer_user_params(file, argp); - case SNDRV_TIMER_IOCTL_STATUS: - return snd_timer_user_status(file, argp); + case SNDRV_TIMER_IOCTL_STATUS32: + return snd_timer_user_status32(file, argp); + case SNDRV_TIMER_IOCTL_STATUS64: + return snd_timer_user_status64(file, argp); case SNDRV_TIMER_IOCTL_START: case SNDRV_TIMER_IOCTL_START_OLD: return snd_timer_user_start(file); @@ -2017,7 +2126,7 @@ long ret; mutex_lock(&tu->ioctl_lock); - ret = __snd_timer_user_ioctl(file, cmd, arg); + ret = __snd_timer_user_ioctl(file, cmd, arg, false); mutex_unlock(&tu->ioctl_lock); return ret; } @@ -2027,19 +2136,35 @@ struct snd_timer_user *tu; tu = file->private_data; - return fasync_helper(fd, file, on, &tu->fasync); + return snd_fasync_helper(fd, file, on, &tu->fasync); } static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) { + struct snd_timer_tread64 *tread; + struct snd_timer_tread32 tread32; struct snd_timer_user *tu; long result = 0, unit; int qhead; int err = 0; tu = file->private_data; - unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read); + switch (tu->tread) { + case TREAD_FORMAT_TIME64: + unit = sizeof(struct snd_timer_tread64); + break; + case TREAD_FORMAT_TIME32: + unit = sizeof(struct snd_timer_tread32); + break; + case TREAD_FORMAT_NONE: + unit = sizeof(struct snd_timer_read); + break; + default: + WARN_ONCE(1, "Corrupt snd_timer_user\n"); + return -ENOTSUPP; + } + mutex_lock(&tu->ioctl_lock); spin_lock_irq(&tu->qlock); while ((long)count - result >= unit) { @@ -2078,14 +2203,34 @@ tu->qused--; spin_unlock_irq(&tu->qlock); - if (tu->tread) { - if (copy_to_user(buffer, &tu->tqueue[qhead], - sizeof(struct snd_timer_tread))) + tread = &tu->tqueue[qhead]; + + switch (tu->tread) { + case TREAD_FORMAT_TIME64: + if (copy_to_user(buffer, tread, + sizeof(struct snd_timer_tread64))) err = -EFAULT; - } else { + break; + case TREAD_FORMAT_TIME32: + memset(&tread32, 0, sizeof(tread32)); + tread32 = (struct snd_timer_tread32) { + .event = tread->event, + .tstamp_sec = tread->tstamp_sec, + .tstamp_nsec = tread->tstamp_nsec, + .val = tread->val, + }; + + if (copy_to_user(buffer, &tread32, sizeof(tread32))) + err = -EFAULT; + break; + case TREAD_FORMAT_NONE: if (copy_to_user(buffer, &tu->queue[qhead], sizeof(struct snd_timer_read))) err = -EFAULT; + break; + default: + err = -ENOTSUPP; + break; } spin_lock_irq(&tu->qlock); -- Gitblit v1.6.2