11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * Rate conversion Plug-In
3c1017a4cSJaroslav Kysela * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * This library is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds * it under the terms of the GNU Library General Public License as
81da177e4SLinus Torvalds * published by the Free Software Foundation; either version 2 of
91da177e4SLinus Torvalds * the License, or (at your option) any later version.
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
141da177e4SLinus Torvalds * GNU Library General Public License for more details.
151da177e4SLinus Torvalds *
161da177e4SLinus Torvalds * You should have received a copy of the GNU Library General Public
171da177e4SLinus Torvalds * License along with this library; if not, write to the Free Software
181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
191da177e4SLinus Torvalds *
201da177e4SLinus Torvalds */
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds #include <linux/time.h>
231da177e4SLinus Torvalds #include <sound/core.h>
241da177e4SLinus Torvalds #include <sound/pcm.h>
251da177e4SLinus Torvalds #include "pcm_plugin.h"
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds #define SHIFT 11
281da177e4SLinus Torvalds #define BITS (1<<SHIFT)
291da177e4SLinus Torvalds #define R_MASK (BITS-1)
301da177e4SLinus Torvalds
311da177e4SLinus Torvalds /*
321da177e4SLinus Torvalds * Basic rate conversion plugin
331da177e4SLinus Torvalds */
341da177e4SLinus Torvalds
356ac77bc1STakashi Iwai struct rate_channel {
361da177e4SLinus Torvalds signed short last_S1;
371da177e4SLinus Torvalds signed short last_S2;
386ac77bc1STakashi Iwai };
391da177e4SLinus Torvalds
406ac77bc1STakashi Iwai typedef void (*rate_f)(struct snd_pcm_plugin *plugin,
416ac77bc1STakashi Iwai const struct snd_pcm_plugin_channel *src_channels,
426ac77bc1STakashi Iwai struct snd_pcm_plugin_channel *dst_channels,
431da177e4SLinus Torvalds int src_frames, int dst_frames);
441da177e4SLinus Torvalds
456ac77bc1STakashi Iwai struct rate_priv {
461da177e4SLinus Torvalds unsigned int pitch;
471da177e4SLinus Torvalds unsigned int pos;
481da177e4SLinus Torvalds rate_f func;
491da177e4SLinus Torvalds snd_pcm_sframes_t old_src_frames, old_dst_frames;
509478bd43SGustavo A. R. Silva struct rate_channel channels[];
516ac77bc1STakashi Iwai };
521da177e4SLinus Torvalds
rate_init(struct snd_pcm_plugin * plugin)536ac77bc1STakashi Iwai static void rate_init(struct snd_pcm_plugin *plugin)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds unsigned int channel;
566ac77bc1STakashi Iwai struct rate_priv *data = (struct rate_priv *)plugin->extra_data;
571da177e4SLinus Torvalds data->pos = 0;
581da177e4SLinus Torvalds for (channel = 0; channel < plugin->src_format.channels; channel++) {
591da177e4SLinus Torvalds data->channels[channel].last_S1 = 0;
601da177e4SLinus Torvalds data->channels[channel].last_S2 = 0;
611da177e4SLinus Torvalds }
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds
resample_expand(struct snd_pcm_plugin * plugin,const struct snd_pcm_plugin_channel * src_channels,struct snd_pcm_plugin_channel * dst_channels,int src_frames,int dst_frames)646ac77bc1STakashi Iwai static void resample_expand(struct snd_pcm_plugin *plugin,
656ac77bc1STakashi Iwai const struct snd_pcm_plugin_channel *src_channels,
666ac77bc1STakashi Iwai struct snd_pcm_plugin_channel *dst_channels,
671da177e4SLinus Torvalds int src_frames, int dst_frames)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds unsigned int pos = 0;
701da177e4SLinus Torvalds signed int val;
711da177e4SLinus Torvalds signed short S1, S2;
720534ab42STakashi Iwai signed short *src, *dst;
731da177e4SLinus Torvalds unsigned int channel;
741da177e4SLinus Torvalds int src_step, dst_step;
751da177e4SLinus Torvalds int src_frames1, dst_frames1;
766ac77bc1STakashi Iwai struct rate_priv *data = (struct rate_priv *)plugin->extra_data;
776ac77bc1STakashi Iwai struct rate_channel *rchannels = data->channels;
781da177e4SLinus Torvalds
791da177e4SLinus Torvalds for (channel = 0; channel < plugin->src_format.channels; channel++) {
801da177e4SLinus Torvalds pos = data->pos;
811da177e4SLinus Torvalds S1 = rchannels->last_S1;
821da177e4SLinus Torvalds S2 = rchannels->last_S2;
831da177e4SLinus Torvalds if (!src_channels[channel].enabled) {
841da177e4SLinus Torvalds if (dst_channels[channel].wanted)
851da177e4SLinus Torvalds snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
861da177e4SLinus Torvalds dst_channels[channel].enabled = 0;
871da177e4SLinus Torvalds continue;
881da177e4SLinus Torvalds }
891da177e4SLinus Torvalds dst_channels[channel].enabled = 1;
900534ab42STakashi Iwai src = (signed short *)src_channels[channel].area.addr +
910534ab42STakashi Iwai src_channels[channel].area.first / 8 / 2;
920534ab42STakashi Iwai dst = (signed short *)dst_channels[channel].area.addr +
930534ab42STakashi Iwai dst_channels[channel].area.first / 8 / 2;
940534ab42STakashi Iwai src_step = src_channels[channel].area.step / 8 / 2;
950534ab42STakashi Iwai dst_step = dst_channels[channel].area.step / 8 / 2;
961da177e4SLinus Torvalds src_frames1 = src_frames;
971da177e4SLinus Torvalds dst_frames1 = dst_frames;
981da177e4SLinus Torvalds while (dst_frames1-- > 0) {
991da177e4SLinus Torvalds if (pos & ~R_MASK) {
1001da177e4SLinus Torvalds pos &= R_MASK;
1011da177e4SLinus Torvalds S1 = S2;
1021da177e4SLinus Torvalds if (src_frames1-- > 0) {
1030534ab42STakashi Iwai S2 = *src;
1041da177e4SLinus Torvalds src += src_step;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
1081da177e4SLinus Torvalds if (val < -32768)
1091da177e4SLinus Torvalds val = -32768;
1101da177e4SLinus Torvalds else if (val > 32767)
1111da177e4SLinus Torvalds val = 32767;
1120534ab42STakashi Iwai *dst = val;
1131da177e4SLinus Torvalds dst += dst_step;
1141da177e4SLinus Torvalds pos += data->pitch;
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds rchannels->last_S1 = S1;
1171da177e4SLinus Torvalds rchannels->last_S2 = S2;
1181da177e4SLinus Torvalds rchannels++;
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds data->pos = pos;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds
resample_shrink(struct snd_pcm_plugin * plugin,const struct snd_pcm_plugin_channel * src_channels,struct snd_pcm_plugin_channel * dst_channels,int src_frames,int dst_frames)1236ac77bc1STakashi Iwai static void resample_shrink(struct snd_pcm_plugin *plugin,
1246ac77bc1STakashi Iwai const struct snd_pcm_plugin_channel *src_channels,
1256ac77bc1STakashi Iwai struct snd_pcm_plugin_channel *dst_channels,
1261da177e4SLinus Torvalds int src_frames, int dst_frames)
1271da177e4SLinus Torvalds {
1281da177e4SLinus Torvalds unsigned int pos = 0;
1291da177e4SLinus Torvalds signed int val;
1301da177e4SLinus Torvalds signed short S1, S2;
1310534ab42STakashi Iwai signed short *src, *dst;
1321da177e4SLinus Torvalds unsigned int channel;
1331da177e4SLinus Torvalds int src_step, dst_step;
1341da177e4SLinus Torvalds int src_frames1, dst_frames1;
1356ac77bc1STakashi Iwai struct rate_priv *data = (struct rate_priv *)plugin->extra_data;
1366ac77bc1STakashi Iwai struct rate_channel *rchannels = data->channels;
1371da177e4SLinus Torvalds
1381da177e4SLinus Torvalds for (channel = 0; channel < plugin->src_format.channels; ++channel) {
1391da177e4SLinus Torvalds pos = data->pos;
1401da177e4SLinus Torvalds S1 = rchannels->last_S1;
1411da177e4SLinus Torvalds S2 = rchannels->last_S2;
1421da177e4SLinus Torvalds if (!src_channels[channel].enabled) {
1431da177e4SLinus Torvalds if (dst_channels[channel].wanted)
1441da177e4SLinus Torvalds snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format);
1451da177e4SLinus Torvalds dst_channels[channel].enabled = 0;
1461da177e4SLinus Torvalds continue;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds dst_channels[channel].enabled = 1;
1490534ab42STakashi Iwai src = (signed short *)src_channels[channel].area.addr +
1500534ab42STakashi Iwai src_channels[channel].area.first / 8 / 2;
1510534ab42STakashi Iwai dst = (signed short *)dst_channels[channel].area.addr +
1520534ab42STakashi Iwai dst_channels[channel].area.first / 8 / 2;
1530534ab42STakashi Iwai src_step = src_channels[channel].area.step / 8 / 2;
1540534ab42STakashi Iwai dst_step = dst_channels[channel].area.step / 8 / 2;
1551da177e4SLinus Torvalds src_frames1 = src_frames;
1561da177e4SLinus Torvalds dst_frames1 = dst_frames;
1571da177e4SLinus Torvalds while (dst_frames1 > 0) {
1581da177e4SLinus Torvalds S1 = S2;
1591da177e4SLinus Torvalds if (src_frames1-- > 0) {
1605370d96fSSteve Chen S2 = *src;
1611da177e4SLinus Torvalds src += src_step;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds if (pos & ~R_MASK) {
1641da177e4SLinus Torvalds pos &= R_MASK;
1651da177e4SLinus Torvalds val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
1661da177e4SLinus Torvalds if (val < -32768)
1671da177e4SLinus Torvalds val = -32768;
1681da177e4SLinus Torvalds else if (val > 32767)
1691da177e4SLinus Torvalds val = 32767;
1700534ab42STakashi Iwai *dst = val;
1711da177e4SLinus Torvalds dst += dst_step;
1721da177e4SLinus Torvalds dst_frames1--;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds pos += data->pitch;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds rchannels->last_S1 = S1;
1771da177e4SLinus Torvalds rchannels->last_S2 = S2;
1781da177e4SLinus Torvalds rchannels++;
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds data->pos = pos;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds
rate_src_frames(struct snd_pcm_plugin * plugin,snd_pcm_uframes_t frames)1836ac77bc1STakashi Iwai static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames)
1841da177e4SLinus Torvalds {
1856ac77bc1STakashi Iwai struct rate_priv *data;
1861da177e4SLinus Torvalds snd_pcm_sframes_t res;
1871da177e4SLinus Torvalds
1887eaa943cSTakashi Iwai if (snd_BUG_ON(!plugin))
1897eaa943cSTakashi Iwai return -ENXIO;
1901da177e4SLinus Torvalds if (frames == 0)
1911da177e4SLinus Torvalds return 0;
1926ac77bc1STakashi Iwai data = (struct rate_priv *)plugin->extra_data;
1931da177e4SLinus Torvalds if (plugin->src_format.rate < plugin->dst_format.rate) {
1941da177e4SLinus Torvalds res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
1951da177e4SLinus Torvalds } else {
196*6b5edf1dSLars-Peter Clausen res = DIV_ROUND_CLOSEST(frames << SHIFT, data->pitch);
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds if (data->old_src_frames > 0) {
1991da177e4SLinus Torvalds snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
2001da177e4SLinus Torvalds while (data->old_src_frames < frames1) {
2011da177e4SLinus Torvalds frames1 >>= 1;
2021da177e4SLinus Torvalds res1 <<= 1;
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds while (data->old_src_frames > frames1) {
2051da177e4SLinus Torvalds frames1 <<= 1;
2061da177e4SLinus Torvalds res1 >>= 1;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds if (data->old_src_frames == frames1)
2091da177e4SLinus Torvalds return res1;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds data->old_src_frames = frames;
2121da177e4SLinus Torvalds data->old_dst_frames = res;
2131da177e4SLinus Torvalds return res;
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds
rate_dst_frames(struct snd_pcm_plugin * plugin,snd_pcm_uframes_t frames)2166ac77bc1STakashi Iwai static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t frames)
2171da177e4SLinus Torvalds {
2186ac77bc1STakashi Iwai struct rate_priv *data;
2191da177e4SLinus Torvalds snd_pcm_sframes_t res;
2201da177e4SLinus Torvalds
2217eaa943cSTakashi Iwai if (snd_BUG_ON(!plugin))
2227eaa943cSTakashi Iwai return -ENXIO;
2231da177e4SLinus Torvalds if (frames == 0)
2241da177e4SLinus Torvalds return 0;
2256ac77bc1STakashi Iwai data = (struct rate_priv *)plugin->extra_data;
2261da177e4SLinus Torvalds if (plugin->src_format.rate < plugin->dst_format.rate) {
227*6b5edf1dSLars-Peter Clausen res = DIV_ROUND_CLOSEST(frames << SHIFT, data->pitch);
2281da177e4SLinus Torvalds } else {
2291da177e4SLinus Torvalds res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds if (data->old_dst_frames > 0) {
2321da177e4SLinus Torvalds snd_pcm_sframes_t frames1 = frames, res1 = data->old_src_frames;
2331da177e4SLinus Torvalds while (data->old_dst_frames < frames1) {
2341da177e4SLinus Torvalds frames1 >>= 1;
2351da177e4SLinus Torvalds res1 <<= 1;
2361da177e4SLinus Torvalds }
2371da177e4SLinus Torvalds while (data->old_dst_frames > frames1) {
2381da177e4SLinus Torvalds frames1 <<= 1;
2391da177e4SLinus Torvalds res1 >>= 1;
2401da177e4SLinus Torvalds }
2411da177e4SLinus Torvalds if (data->old_dst_frames == frames1)
2421da177e4SLinus Torvalds return res1;
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds data->old_dst_frames = frames;
2451da177e4SLinus Torvalds data->old_src_frames = res;
2461da177e4SLinus Torvalds return res;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds
rate_transfer(struct snd_pcm_plugin * plugin,const struct snd_pcm_plugin_channel * src_channels,struct snd_pcm_plugin_channel * dst_channels,snd_pcm_uframes_t frames)2496ac77bc1STakashi Iwai static snd_pcm_sframes_t rate_transfer(struct snd_pcm_plugin *plugin,
2506ac77bc1STakashi Iwai const struct snd_pcm_plugin_channel *src_channels,
2516ac77bc1STakashi Iwai struct snd_pcm_plugin_channel *dst_channels,
2521da177e4SLinus Torvalds snd_pcm_uframes_t frames)
2531da177e4SLinus Torvalds {
2541da177e4SLinus Torvalds snd_pcm_uframes_t dst_frames;
2556ac77bc1STakashi Iwai struct rate_priv *data;
2561da177e4SLinus Torvalds
2577eaa943cSTakashi Iwai if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
2587eaa943cSTakashi Iwai return -ENXIO;
2591da177e4SLinus Torvalds if (frames == 0)
2601da177e4SLinus Torvalds return 0;
2611da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG
2621da177e4SLinus Torvalds {
2631da177e4SLinus Torvalds unsigned int channel;
2641da177e4SLinus Torvalds for (channel = 0; channel < plugin->src_format.channels; channel++) {
2657eaa943cSTakashi Iwai if (snd_BUG_ON(src_channels[channel].area.first % 8 ||
2667eaa943cSTakashi Iwai src_channels[channel].area.step % 8))
2677eaa943cSTakashi Iwai return -ENXIO;
2687eaa943cSTakashi Iwai if (snd_BUG_ON(dst_channels[channel].area.first % 8 ||
2697eaa943cSTakashi Iwai dst_channels[channel].area.step % 8))
2707eaa943cSTakashi Iwai return -ENXIO;
2711da177e4SLinus Torvalds }
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds #endif
2741da177e4SLinus Torvalds
2751da177e4SLinus Torvalds dst_frames = rate_dst_frames(plugin, frames);
2761da177e4SLinus Torvalds if (dst_frames > dst_channels[0].frames)
2771da177e4SLinus Torvalds dst_frames = dst_channels[0].frames;
2786ac77bc1STakashi Iwai data = (struct rate_priv *)plugin->extra_data;
2791da177e4SLinus Torvalds data->func(plugin, src_channels, dst_channels, frames, dst_frames);
2801da177e4SLinus Torvalds return dst_frames;
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds
rate_action(struct snd_pcm_plugin * plugin,enum snd_pcm_plugin_action action,unsigned long udata)2836ac77bc1STakashi Iwai static int rate_action(struct snd_pcm_plugin *plugin,
2846ac77bc1STakashi Iwai enum snd_pcm_plugin_action action,
28547eaebfdSTakashi Iwai unsigned long udata)
2861da177e4SLinus Torvalds {
2877eaa943cSTakashi Iwai if (snd_BUG_ON(!plugin))
2887eaa943cSTakashi Iwai return -ENXIO;
2891da177e4SLinus Torvalds switch (action) {
2901da177e4SLinus Torvalds case INIT:
2911da177e4SLinus Torvalds case PREPARE:
2921da177e4SLinus Torvalds rate_init(plugin);
2931da177e4SLinus Torvalds break;
2941da177e4SLinus Torvalds default:
2951da177e4SLinus Torvalds break;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds return 0; /* silenty ignore other actions */
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds
snd_pcm_plugin_build_rate(struct snd_pcm_substream * plug,struct snd_pcm_plugin_format * src_format,struct snd_pcm_plugin_format * dst_format,struct snd_pcm_plugin ** r_plugin)3006ac77bc1STakashi Iwai int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
3016ac77bc1STakashi Iwai struct snd_pcm_plugin_format *src_format,
3026ac77bc1STakashi Iwai struct snd_pcm_plugin_format *dst_format,
3036ac77bc1STakashi Iwai struct snd_pcm_plugin **r_plugin)
3041da177e4SLinus Torvalds {
3051da177e4SLinus Torvalds int err;
3066ac77bc1STakashi Iwai struct rate_priv *data;
3076ac77bc1STakashi Iwai struct snd_pcm_plugin *plugin;
3081da177e4SLinus Torvalds
3097eaa943cSTakashi Iwai if (snd_BUG_ON(!r_plugin))
3107eaa943cSTakashi Iwai return -ENXIO;
3111da177e4SLinus Torvalds *r_plugin = NULL;
3121da177e4SLinus Torvalds
3137eaa943cSTakashi Iwai if (snd_BUG_ON(src_format->channels != dst_format->channels))
3147eaa943cSTakashi Iwai return -ENXIO;
3157eaa943cSTakashi Iwai if (snd_BUG_ON(src_format->channels <= 0))
3167eaa943cSTakashi Iwai return -ENXIO;
3177eaa943cSTakashi Iwai if (snd_BUG_ON(src_format->format != SNDRV_PCM_FORMAT_S16))
3187eaa943cSTakashi Iwai return -ENXIO;
3197eaa943cSTakashi Iwai if (snd_BUG_ON(dst_format->format != SNDRV_PCM_FORMAT_S16))
3207eaa943cSTakashi Iwai return -ENXIO;
3217eaa943cSTakashi Iwai if (snd_BUG_ON(src_format->rate == dst_format->rate))
3227eaa943cSTakashi Iwai return -ENXIO;
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds err = snd_pcm_plugin_build(plug, "rate conversion",
3251da177e4SLinus Torvalds src_format, dst_format,
32692bfa664SGustavo A. R. Silva struct_size(data, channels,
32792bfa664SGustavo A. R. Silva src_format->channels),
3281da177e4SLinus Torvalds &plugin);
3291da177e4SLinus Torvalds if (err < 0)
3301da177e4SLinus Torvalds return err;
3316ac77bc1STakashi Iwai data = (struct rate_priv *)plugin->extra_data;
3321da177e4SLinus Torvalds if (src_format->rate < dst_format->rate) {
3331da177e4SLinus Torvalds data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
3341da177e4SLinus Torvalds data->func = resample_expand;
3351da177e4SLinus Torvalds } else {
3361da177e4SLinus Torvalds data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
3371da177e4SLinus Torvalds data->func = resample_shrink;
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds data->pos = 0;
3401da177e4SLinus Torvalds rate_init(plugin);
3411da177e4SLinus Torvalds data->old_src_frames = data->old_dst_frames = 0;
3421da177e4SLinus Torvalds plugin->transfer = rate_transfer;
3431da177e4SLinus Torvalds plugin->src_frames = rate_src_frames;
3441da177e4SLinus Torvalds plugin->dst_frames = rate_dst_frames;
3451da177e4SLinus Torvalds plugin->action = rate_action;
3461da177e4SLinus Torvalds *r_plugin = plugin;
3471da177e4SLinus Torvalds return 0;
3481da177e4SLinus Torvalds }
349