title: Kernel panics : trying to write / read on tiny tty driver
body: <p>I'm a beginner to the Linux programming and trying my hands on some device driver examples while practising.
  The below code (a trimmed down version of <code>tiny_tty.c</code>) loads perfectly using insmod and I'm able to see it in <code>/proc/tty/drivers</code> , <code>/proc/modules</code> and device nodes are getting created under /dev. When I try to write to device file like <code>echo "abcd" &gt; /dev/ttyms0</code> (I hope this is fine) or read like <code>cat /dev/ttyms0</code>, the kernel panics with a call trace on the screen. I'm on kernel 3.5.0 under Ubuntu. Unfortunately I'm not able to capture the trace , as when it panics I'm left with no option but reboot using power button. I believe some issue with timer is here, as the trace shows a line on top saying :
  <code>"*kernel bug at /build/buildd/linux-3.5.0/kernel/timer.c:901*</code>", then the call trace , followed by 
  <code>"*EIP is at add_timer+0x18/0x20*"</code>
   Below is the full code. Any guidance is very much appreciated in anticipation.</p>
  
  <p><strong>10May2013</strong> : I tried initializing the timer in open function and this time below is the call trace for "kernel panic - not syncing : fatal exception in interrupt panic occurred, switching back to text console":</p>
  
  <pre><code>update_sd_lb_stats+0xcd/0x4b0
  find_busiest_group+0x2e/0x420
  enqueue_entity+0xcb/0x510
  load_balance+0x7e/0x5e0
  rebalance_domains+0xed/0x150
  __do_softirq+0xdb/0x180
  local_bh_enable_ip+0x90/0x90
  &lt;IRQ&gt;
  copy_to_user0x41/0x60
  sys_gettimeofday+0x2a/0x70
  sysenter_do_call0x12/0x20
  
  
  
    #include &lt;linux/init.h&gt;
      #include &lt;linux/kernel.h&gt;
      #include &lt;linux/module.h&gt;
      #include &lt;linux/fs.h&gt;
      #include &lt;linux/kdev_t.h&gt;
      #include &lt;linux/wait.h&gt;
      #include &lt;linux/errno.h&gt;
      #include &lt;linux/slab.h&gt;     /* kmalloc() */
      #include &lt;linux/tty_driver.h&gt;
      #include &lt;linux/tty.h&gt;
      #include &lt;linux/tty_flip.h&gt;
      #include &lt;linux/mutex.h&gt;
      #include &lt;linux/serial.h&gt;
      #include &lt;linux/sched.h&gt;
      #include &lt;asm/uaccess.h&gt;
      #include &lt;asm/termios.h&gt;
  
  
      MODULE_LICENSE("GPL");
  
      #define MS_TTY_MAJOR 250    //test value
      #define MS_TTY_NUM_DEV 2
      #define DELAY_TIME      HZ * 2      /* 2 seconds per character */
      #define TINY_DATA_CHARACTER 't'
  
      static int major_num;
      //static int minor_num=0;
      //static int num_tty_dev=2;
  
      /* Below structure is a wrapper for device specific fields */
      struct ms_tty_serial {
          struct tty_struct   *tty;       /* pointer to the tty for this device */
          int         open_count; /* number of times this port has been opened */
          struct semaphore    sem;        /* locks this structure */
          struct timer_list   *timer;
  
      };
  
      static struct ms_tty_serial *ms_tty_table[MS_TTY_NUM_DEV];  /* initially all NULL */
  
      static void ms_tty_timer(unsigned long timer_data)
      {
          struct ms_tty_serial *ms_ptr = (struct ms_tty_serial *)timer_data;
          struct tty_struct *tty;
          char data[1] = {TINY_DATA_CHARACTER};
  
  
      int data_size = 1;
  
          if (!ms_ptr)
              return;
  
          tty = ms_ptr-&gt;tty;
          tty-&gt;low_latency=1;
          /* send the data to the tty layer for users to read.  This doesn't
           * actually push the data through unless tty-&gt;low_latency is set */
          tty_buffer_request_room (tty, data_size);
              tty_insert_flip_string(tty, data, data_size);
              tty_flip_buffer_push(tty);
  
          /* resubmit the timer again */
          ms_ptr-&gt;timer-&gt;expires = jiffies + DELAY_TIME;
          add_timer(ms_ptr-&gt;timer);
      }
  
  
  
      //// Define the open function ////
  
     static int tty_ms_open(struct tty_struct *tty_this, struct file *file_this)
  {
  
      printk(KERN_ALERT "tty_ms driver: OPENED ...\n");
      struct ms_tty_serial *ms_ptr;
      struct timer_list *timer;
      int index;
  
      /* initialize the pointer in case something fails */
      tty_this-&gt;driver_data = NULL;
  
      /* get the serial object associated with this tty pointer */
      index = tty_this-&gt;index;
      ms_ptr = ms_tty_table[index];
      if (ms_ptr == NULL) {
          /* first time accessing this device, create it */
          ms_ptr = kmalloc(sizeof(*ms_ptr), GFP_KERNEL);
          if (!ms_ptr)
              return -ENOMEM;
  
  //      init_MUTEX(&amp;ms_ptr-&gt;sem);       /* didn't work for this kernel version 3.5.0 */
  
          #ifndef init_MUTEX      /* sema_init is to be used for kernel 2.6.37 and above */
          sema_init(&amp;ms_ptr-&gt;sem,1);
          #else
          init_MUTEX(&amp;ms_ptr-&gt;sem);
          #endif
  
          ms_ptr-&gt;open_count = 0;
          ms_ptr-&gt;timer = NULL;
  
          ms_tty_table[index] = ms_ptr;
      }
  
      down(&amp;ms_ptr-&gt;sem);
  
      /* save our structure within the tty structure */
      tty_this-&gt;driver_data = ms_ptr;
      ms_ptr-&gt;tty = tty_this;
      ms_ptr-&gt;filp = file_this;       // to be tried
      ++ms_ptr-&gt;open_count;
      if (ms_ptr-&gt;open_count == 1) {
          /* this is the first time this port is opened */
          /* do any hardware initialization needed here */
  
          /* create timer and submit it */
          if (!ms_ptr-&gt;timer) {
              timer = kmalloc(sizeof(*timer), GFP_KERNEL);
              if (!timer) {
                  up(&amp;ms_ptr-&gt;sem);
                  return -ENOMEM;
              }
              ms_ptr-&gt;timer = timer;
          }
  
          init_timer (ms_ptr-&gt;timer);     // to be tried
          ms_ptr-&gt;timer-&gt;data = (unsigned long )ms_ptr;
          ms_ptr-&gt;timer-&gt;expires = jiffies + DELAY_TIME;
          ms_ptr-&gt;timer-&gt;function = ms_tty_timer;
          add_timer(ms_ptr-&gt;timer);
      }
  
      up(&amp;ms_ptr-&gt;sem);
      return 0;
  }
  
      //// Define the close function ////
  
      static void do_close(struct ms_tty_serial *ms_ptr)
      {
          down(&amp;ms_ptr-&gt;sem);
  
          if (!ms_ptr-&gt;open_count) {
              /* port was never opened */
              goto exit;
          }
  
          --ms_ptr-&gt;open_count;
          if (ms_ptr-&gt;open_count &lt;= 0) {
              /* The port is being closed by the last user. */
              /* Do any hardware specific stuff here */
  
              /* shut down our timer */
              del_timer(ms_ptr-&gt;timer);
          }
      exit:
          up(&amp;ms_ptr-&gt;sem);
      }
  
      static void tty_ms_close(struct tty_struct *tty_this, struct file *file_this)
      {
  
          printk(KERN_ALERT "tty_ms driver: CLOSING ...\n");
          struct ms_tty_serial *ms_ptr = tty_this-&gt;driver_data;
  
          if (ms_ptr)
              do_close(ms_ptr);
      }
  
  
      //// Define the write function ////
  
      static int tty_ms_write(struct tty_struct *tty_this, const unsigned char *tty_buff, int count)
      {
  
          printk(KERN_ALERT "tty_ms driver: WRITING ...\n");
      struct ms_tty_serial *ms_ptr = tty_this-&gt;driver_data;
          int i;
          int retval = -EINVAL;
  
          if (!ms_ptr)
              return -ENODEV;
  
          down(&amp;ms_ptr-&gt;sem);
  
          if (!ms_ptr-&gt;open_count)
              /* port was not opened */
              {
              up(&amp;ms_ptr-&gt;sem);
              return retval;
              }
          /* fake sending the data out a hardware port by
           * writing it to the kernel debug log.
           */
          printk(KERN_DEBUG "%s - ", __FUNCTION__);
          for (i = 0; i &lt; count; ++i)
              printk("%02x ", tty_buff[i]);
          printk("\n");
          return 0;
  
      }
  
      // Define the operations for tty driver in tty_operations struct //
  
      static struct tty_operations tty_ms_ops = {
      .open = tty_ms_open,
      .close = tty_ms_close,
      .write = tty_ms_write,
      //.set_termios = tty_ms_set_termios,
      };
  
      ///////////////////////////////////////// Module Initialization Starts ////////////////////////////////////
  
      static struct tty_driver *tty_ms_driver;
  
      static int tty_ms_init(void)
      {
      //  static int result;
          static int retval,iter;
  
      // Allocate the tty_driver struct for this driver //
  
          tty_ms_driver = alloc_tty_driver(MS_TTY_NUM_DEV);
          if (!tty_ms_driver)
          return -ENOMEM;         // Error NO Memory , allocation failed
          else
          printk(KERN_INFO "tty_driver structure allocated..!!");     //debug line
  
      // Initialize the allocated tty_driver structure //
          tty_ms_driver-&gt;magic=TTY_DRIVER_MAGIC;
          tty_ms_driver-&gt;owner = THIS_MODULE;
          tty_ms_driver-&gt;driver_name = "tty_ms";
          tty_ms_driver-&gt;name = "ttyms";
          tty_ms_driver-&gt;major = MS_TTY_MAJOR,
          tty_ms_driver-&gt;type = TTY_DRIVER_TYPE_SERIAL,
          tty_ms_driver-&gt;subtype = SERIAL_TYPE_NORMAL,
          tty_ms_driver-&gt;flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
          tty_ms_driver-&gt;init_termios = tty_std_termios;
          tty_ms_driver-&gt;init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
          tty_set_operations(tty_ms_driver, &amp;tty_ms_ops);
          printk(KERN_INFO "allocated tty_driver structure -INITIALIZED.");   //debug line
      //// Register this driver with the tty core ////
  
          retval = tty_register_driver(tty_ms_driver);
          if (retval) {
          printk(KERN_ERR "failed to register tty_ms driver\n tty registration returned %d", retval);
          put_tty_driver(tty_ms_driver);
          return retval;
          }
  
      //// Register the tty devices(nodes) with the tty core ////
  
          for (iter = 0; iter &lt; MS_TTY_NUM_DEV ; ++iter)
          tty_register_device(tty_ms_driver, iter, NULL);
          return 0;       // All initializations done
  
      }           // init func ends
  
      ///////////////////////////////////////// Module Initialization Ends ////////////////////////////////////
  
      ///////////////////////////////////////// Module cleanup Starts ////////////////////////////////////
  
      static void tty_ms_terminate(void)
      {
          static int iter;
          struct ms_tty_serial *tty_ser;
          printk(KERN_ALERT "tty_ms driver: Unloading...\n");
  
          for(iter=1;iter&lt;=MS_TTY_NUM_DEV;iter++)
           tty_unregister_device(tty_ms_driver,iter);   //unregister all the devices, from tty layer 
          tty_unregister_driver(tty_ms_driver);       // Now unregister the driver from tty layer
  
          /* shut down all of the timers and free the memory */
          for (iter = 0; iter &lt; MS_TTY_NUM_DEV; ++iter) {
              tty_ser = ms_tty_table[iter];
              if (tty_ser) {
                  /* close the port */
                  while (tty_ser-&gt;open_count)
                      do_close(tty_ser);
  
                  /* shut down our timer and free the memory */
                  del_timer(tty_ser-&gt;timer);
                  kfree(tty_ser-&gt;timer);
                  kfree(tty_ser);
                  ms_tty_table[iter] = NULL;
              }
          }   
  
           dev_t devno=MKDEV(major_num,0);  // wrap major/minor numbers in a dev_t structure , to pass for deassigning.
           unregister_chrdev_region(devno,MS_TTY_NUM_DEV); 
  
      }
  
      ///////////////////////////////////////// Module cleanup ends ////////////////////////////////////
  
      module_init(tty_ms_init);
      module_exit(tty_ms_terminate);
  </code></pre>