title: Reading from USB device and writing to physical address
body: <p>I have a USB device that outputs data of size of one byte, and I want to pass these bytes to FPGA component that exists on AXI bridge, FPGA and CPU are on the same chip... it's SoC FPGA Altera Cyclone V. CPU is ARM Cortex-A9. Kernel version 3.7.0.</p>
  
  <p>There is a software that reads from the USB device and writes to a dump file... it works just fine. I tried to use <code>mmap()</code> to map the FPGA address to the virtual space and write to it from the userspace. When doing so... after say a minute, the kernel seem to crash.</p>
  
  <p>I wrote a driver for my FPGA component and I passed the driver path to that software as a file, so that it writes to it, and eventually to my FPGA component, but the same result... kernel crashes again after a random time.</p>
  
  <p>I also wrote a simple program that reads bytes from a local file and pass it to FPGA... this works fine either ways (using <code>mmap()</code> or driver module), the file passes through to the FPGA with no problems at all no matter how big is the file.</p>
  
  <p>So the problem is when passing from USB device to FPGA, either using <code>mmap()</code> or a driver module.</p>
  
  <p>Here is a sample crash message:</p>
  
  <pre><code>  Internal error: Oops - undefined instruction: 0 [#1] SMP ARM
    Modules linked in: ipv6
    CPU: 1    Not tainted  (3.7.0 #106)
    PC is at scheduler_ipi+0x8/0x4c
    LR is at handle_IPI+0x10c/0x19c
    pc : [&lt;800521a0&gt;]    lr : [&lt;800140d4&gt;]    psr: 80000193
    sp : bf87ff58  ip : 8056acc8  fp : 00000000
    r10: 00000000  r9 : 413fc090  r8 : 00000001
    r7 : 00000000  r6 : bf87e000  r5 : 80535018  r4 : 8053eec0
    r3 : 8056ac80  r2 : bf87ff58  r1 : 00000482  r0 : 00000481
    Flags: Nzcv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
    Control: 10c5387d  Table: 3f0c404a  DAC: 00000015
    Process swapper/1 (pid: 0, stack limit = 0xbf87e240)
    Stack: (0xbf87ff58 to 0xbf880000)
    ff40:                                                       00000000 800140d4
    ff60: fffec10c 8053e418 bf87ff90 fffec100 8000f6e0 8000851c 8000f708 8000f70c
    ff80: 60000013 ffffffff bf87ffc4 8000e180 00000000 00000000 00000001 00000000
    ffa0: bf87e000 80565688 803ddfb0 80541fc8 8000f6e0 413fc090 00000000 00000000
    ffc0: 8053e9b8 bf87ffd8 8000f708 8000f70c 60000013 ffffffff 00000020 8000f894
    ffe0: 3f86c06a 00000015 10c0387d 805658d8 0000406a 003d1ee8 31ca2085 5c1021c3
    Code: eaffffad 80564700 e92d4800 e1a0200d (4c4c9b50)
    ---[ end trace 9e492cde975c41f9 ]---
  </code></pre>
  
  <p>Other crash messages start like:</p>
  
  <pre><code>  Unable to handle kernel paging request at virtual address 2a7a4390
    Internal error: Oops - bad syscall: ebcffb [#1] SMP ARM
    pgd = bf318000
    [2a7a4390] *pgd=00000000
  </code></pre>
  
  <p>And:</p>
  
  <pre><code> Internal error: Oops - undefined instruction: 0 [#2] SMP ARM
   Modules linked in: ipv6
   CPU: 1    Tainted: G      D       (3.7.0 #106)
  </code></pre>
  
  <p>Here is the full <a href="http://pastebin.com/y5jFqn72">crash messages</a>.</p>
  
  <p>I noticed that all the crash messages I get intersect with the PC and LR locations, but actually I don't have previous experience with Linux kernel. I found similar error messages online but none of the proposed solutions worked for me.</p>
  
  <p><strong>Source Code:</strong></p>
  
  <p>This is function is called whenever a new buffer of bytes arrives from USB:</p>
  
  <pre><code>static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
  {
      if (ctx) {
          if (do_exit)
              return;
  
          if ((bytes_to_read &gt; 0) &amp;&amp; (bytes_to_read &lt; len)) {
              len = bytes_to_read;
              do_exit = 1;
              rtlsdr_cancel_async(dev);
          }
  
  /*      if (fwrite(buf, 1, len, (FILE*)ctx) != len) {
              fprintf(stderr, "Short write, samples lost, exiting!\n");
              rtlsdr_cancel_async(dev);
          }
  */
          if (fm_receiver_addr == NULL)
          {
              virtual_base = mmap(NULL, HPS2FPGA_SPAN, PROT_WRITE, MAP_PRIVATE, fd, HPS2FPGA_BASE);
              if (virtual_base == MAP_FAILED)
              {
                  perror("mmap");
                  close(fd);
                  exit(1);
              }
  
              fm_receiver_addr = (unsigned char*)(virtual_base + FM_DEMOD_OFFSET);
          }
  
          int i, j;
          for (i = 0; i &lt; len; i++)
          {
              *fm_receiver_addr = buf[i];
              for (j = 0; j &lt; 150; j++);
          }
  
          if (bytes_to_read &gt; 0)
              bytes_to_read -= len;
      }
  }
  </code></pre>
  
  <p>You see I commented <code>fwrite()</code> function (it's used by the original code to write to files) and replaced it with my code that writes to my FPGA component: <code>*fm_receiver_addr = buf[i];</code>. Before that I check the address to see if it's valid and obtain another address if it's not.</p>
  
  <p>For the other way, the driver module, I wrote this code:</p>
  
  <pre><code>#include &lt;linux/init.h&gt;
  #include &lt;linux/module.h&gt;
  #include &lt;linux/kernel.h&gt;
  #include &lt;linux/device.h&gt;
  #include &lt;linux/platform_device.h&gt;
  #include &lt;linux/uaccess.h&gt;
  #include &lt;linux/ioport.h&gt;
  #include &lt;linux/io.h&gt;
  
  #define HPS2FPGA_BASE       0xC0000000
  #define HPS2FPGA_SPAN       PAGE_SIZE
  
  void* fm_demod_addr;
  int i;
  
  // Get a driver entry in Sysfs
  static struct device_driver fm_demod_driver = 
  {
      .name = "fm-demodulator",   // Name of the driver
      .bus = &amp;platform_bus_type,  // Which bus does the device exist
  };
  
  // Function that is used when we read from the file in /sys, but we won't use it
  ssize_t fm_demod_read(struct device_driver* drv, char* buf)
  { return 0; }
  
  // Function that is called when we write to the file in /sys
  ssize_t fm_demod_write_sample(struct device_driver* drv, const char* buf, size_t count)
  {
      if (buf == NULL)
      {
          pr_err("Error! String must not be NULL!\n");
          return -EINVAL;
      }
  
      for (i = 0; i &lt; count; i++)
      {
          iowrite8(buf[i], fm_demod_addr);
      }
  
      return count;
  }
  
  // Set our module's pointers and set permissions mode
  static DRIVER_ATTR(fm_demod, S_IWUSR, fm_demod_read, fm_demod_write_sample);
  
  // Set module information
  MODULE_LICENSE("Dual BSD/GPL");
  MODULE_AUTHOR("Siraj Muhammad &lt;sirajmuhammad@outlook.com&gt;");
  MODULE_DESCRIPTION("Driver for FPGA component 'FM Demodulator'");
  
  static int __init fm_demod_init(void)
  {
      int ret;
      struct resource* res;
  
      // Register driver in kernel
      ret = driver_register(&amp;fm_demod_driver);
      if (ret &lt; 0)
          return ret;
  
      // Create file system in /sys
      ret = driver_create_file(&amp;fm_demod_driver, &amp;driver_attr_fm_demod);
      if (ret &lt; 0)
      {
          driver_unregister(&amp;fm_demod_driver);
          return ret;
      }
  
      // Request exclusive access to the memory region we want to write to
      res = request_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN, "fm-demodulator");
      if (res == NULL)
      {
          driver_remove_file(&amp;fm_demod_driver, &amp;driver_attr_fm_demod);
          driver_unregister(&amp;fm_demod_driver);
          return -EBUSY;
      }
  
      // Map the address into virtual memory
      fm_demod_addr = ioremap(HPS2FPGA_BASE, HPS2FPGA_SPAN);
      if (fm_demod_addr == NULL)
      {
          driver_remove_file(&amp;fm_demod_driver, &amp;driver_attr_fm_demod);
          driver_unregister(&amp;fm_demod_driver);
          release_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN);
          return -EFAULT;
      }
  
      return 0;
  }
  
  static void __exit fm_demod_exit(void)
  {
      // Remove file system from /sys
      driver_remove_file(&amp;fm_demod_driver, &amp;driver_attr_fm_demod);
      // Unregister the driver
      driver_unregister(&amp;fm_demod_driver);
      // Release requested memory
      release_mem_region(HPS2FPGA_BASE, HPS2FPGA_SPAN);
      // Un-map address
      iounmap(fm_demod_addr);
  
  }
  
  module_init(fm_demod_init);
  module_exit(fm_demod_exit);
  </code></pre>
  
  <p>And I revert the userspace code to its original state, and pass the driver path: <code>/sys/bus/platform/drivers/fm-demodulator/fm_demod</code> to the userspace app to write to it.</p>
  
  <p>Any thought about it?</p>