summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/dahdi/dahdi-base.c58
1 files changed, 54 insertions, 4 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index ac411dc..b29e645 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -289,7 +289,56 @@ static struct dahdi_dialparams global_dialparams = {
static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit);
#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
+#if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_I386)
+struct fpu_save_buf {
+ unsigned long cr0;
+ unsigned long fpu_buf[128];
+};
+
+static DEFINE_PER_CPU(struct fpu_save_buf, fpu_buf);
+
+/** dahdi_kernel_fpu_begin() - Save floating point registers
+ *
+ * This function is similar to kernel_fpu_begin() . However it is
+ * designed to work in an interrupt context. Restoring must be done with
+ * dahdi_kernel_fpu_end().
+ *
+ * Furthermore, the whole code between the call to
+ * dahdi_kernel_fpu_begin() and dahdi_kernel_fpu_end() must reside
+ * inside a spinlock. Otherwise the context might be restored to the
+ * wrong process.
+ *
+ * Current implementation is x86/ia32-specific and will not even build on
+ * x86_64)
+ * */
+static inline void dahdi_kernel_fpu_begin(void)
+{
+ struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf);
+ __asm__ __volatile__ ("movl %%cr0,%0; clts" : "=r" (buf->cr0));
+ __asm__ __volatile__ ("fnsave %0" : "=m" (buf->fpu_buf));
+}
+
+/** dahdi_kernel_fpu_end() - restore floating point context
+ *
+ * Must be used with context saved by dahdi_kernel_fpu_begin(). See its
+ * documentation for further information.
+ */
+static inline void dahdi_kernel_fpu_end(void)
+{
+ struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf);
+ __asm__ __volatile__ ("frstor %0" : "=m" (buf->fpu_buf));
+ __asm__ __volatile__ ("movl %0,%%cr0" : : "r" (buf->cr0));
+}
+
+#else /* We haven't fixed FP context saving/restoring yet */
+/* Very strange things can happen when the context is not properly
+ * restored. OTOH, some people do report success with this. Hence we
+ * so far just issue a warning */
+#warning CONFIG_DAHDI_MMX may behave randomly on this platform
#define dahdi_kernel_fpu_begin kernel_fpu_begin
+#define dahdi_kernel_fpu_end kernel_fpu_end
+#endif
+
#endif
struct dahdi_timer {
@@ -6659,7 +6708,7 @@ static inline void __dahdi_ec_chunk(struct dahdi_chan *ss, unsigned char *rxchun
}
#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
- kernel_fpu_end();
+ dahdi_kernel_fpu_end();
#endif
}
spin_unlock_irqrestore(&ss->lock, flags);
@@ -6992,6 +7041,7 @@ static inline void __putbuf_chunk(struct dahdi_chan *ss, unsigned char *rxb, int
int res;
int left, x;
+
while(bytes) {
#if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP)
skb = NULL;
@@ -7553,7 +7603,7 @@ static void __dahdi_transmit_chunk(struct dahdi_chan *chan, unsigned char *buf)
#endif
__dahdi_process_getaudio_chunk(chan, buf);
#ifdef CONFIG_DAHDI_MMX
- kernel_fpu_end();
+ dahdi_kernel_fpu_end();
#endif
}
}
@@ -7638,7 +7688,7 @@ static void __dahdi_receive_chunk(struct dahdi_chan *chan, unsigned char *buf)
#endif
__dahdi_process_putaudio_chunk(chan, buf);
#ifdef CONFIG_DAHDI_MMX
- kernel_fpu_end();
+ dahdi_kernel_fpu_end();
#endif
}
__dahdi_putbuf_chunk(chan, buf);
@@ -7860,7 +7910,7 @@ int dahdi_receive(struct dahdi_span *span)
}
}
#ifdef CONFIG_DAHDI_MMX
- kernel_fpu_end();
+ dahdi_kernel_fpu_end();
#endif
}
/* do all the pseudo/conferenced channel transmits (putbuf's) */