Why this proc driver crashes? - linux-kernel

I am trying to write a proc driver that will print the driver history up till 10 last updates. In this driver, I haven't added the kernel data structure that I want to print. But, I am relying on the 'i' value to print the value up till 10. Here is my code.
#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h> /** This is for KERN_ALERT **/
#include <linux/proc_fs.h> /** This is for procfs **/
#include <linux/seq_file.h>
#include <linux/cdev.h> /** character device **/
#include <linux/device.h> /** for sys device registration in /dev/ and /sys/class **/
/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");
#define PROCFS_NAME "basicProcfs1"
static struct cdev basicCdev;
static struct class *basicDriverClass;
struct proc_dir_entry *procFileEntry = NULL;
static int basicMajorNumber = 0;
#define NUMBER_OF_MINOR_DEVICE (0)
#define NUM_MSG_HIST_ENTRIES (10)
static int gui32CmdMsgHistoryStartIndex=0;
/** This File operation table for proc file system **/
static int av_cmd_hist_show( struct seq_file *filp, void *v )
{
int i = *((int *)v);
printk("the av_cmd_hist_show called\r\n");
if ( i == 0)
{
seq_printf(filp, "Sequential print for debugging-- called for i-times i.e. 10 times \r\n");
}
seq_printf(filp, "Hello SJ proc! %d\r\n", i);
return 0;
}
static void av_cmd_hist_stop( struct seq_file *filp, void *v )
{
printk("av_cmd_hist_stop called..\r\n");
} /* av_cmd_hist_stop */
static void *av_cmd_hist_next( struct seq_file *filp, void *v, loff_t *pos )
{
(*pos)++;
printk("av_cmd_hist_next called..\r\n");
return( ( *pos < NUM_MSG_HIST_ENTRIES ) ? pos : NULL );
} /* av_cmd_hist_next */
static void *av_cmd_hist_start( struct seq_file *filp, loff_t *pos )
{
if( *pos == 0 )
{
printk("av_cmd_hist_start.. Initial..\r\n");
gui32CmdMsgHistoryStartIndex = 5;
}
printk("av_cmd_hist_start.. the *pos=0..\r\n");
return( ( *pos < NUM_MSG_HIST_ENTRIES ) ? pos : NULL );
} /* av_cmd_hist_start */
static struct seq_operations av_seq_cmd_hist_fops =
{
.start = av_cmd_hist_start,
.next = av_cmd_hist_next,
.stop = av_cmd_hist_stop,
.show = av_cmd_hist_show
};
static int basicProcShow(struct seq_file *m, void *v) {
seq_printf(m, "Hello SJ proc!\n");
return 0;
}
static int basicProcOpen(struct inode *inode, struct file *file)
{
int i;
i = seq_open( file, &av_seq_cmd_hist_fops );
return i;
//return single_open(file, basicProcShow, NULL);
}
/** Put data into the proc fs file **/
static const struct file_operations basic_proc_fops =
{
.owner = THIS_MODULE,
.open = basicProcOpen,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct file_operations fops = {
.read = NULL,
.write = NULL,
.open = NULL,
.release = NULL
};
static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
int err = -1;
/** MKDEV call creates a device number i.e. combination of major and minor number **/
int devno = MKDEV(basicMajorNumber, minor);
/** Initiliaze character dev with fops **/
cdev_init(dev, fops);
/**owner and operations initialized **/
dev->owner = THIS_MODULE;
dev->ops = fops;
/** add the character device to the system**/
/** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
err = cdev_add (dev, devno, 1);
if (err)
{
printk (KERN_NOTICE "Couldn't add cdev");
}
}
static int chrDriverInit(void)
{
int result;
dev_t dev;
printk("Welcome!! Device Init now..");
/** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); **/
/** dev -> The dev_t variable type,which will get the major number that the kernel allocates. **/
/**The same name will appear in /proc/devices. **/
/** it is registering the character device **/
/** a major number will be dynamically allocated here **/
/** alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv1");
if( result < 0 )
{
printk("Error in allocating device");
return -1;
}
/** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
/** creating class, and then device created removes the dependency of calling mknod **/
/** A good method - the mknod way is depreciated **/
/** mknod way is - mknod /dev/<driver_name> c <majorNumber> <minorNumber>
/** add the driver to /sys/class/chardrv **/
if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL) //$ls /sys/class
{
unregister_chrdev_region(dev, 1);
return -1;
}
/** add the driver to /dev/pSeudoDrv -- here **/
if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
/** let's see what major number was assigned by the Kernel **/
basicMajorNumber = MAJOR(dev);
printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );
/** Now setup the cdev **/
setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);
/** Setup Proc Entry here **/
/** 0644 means -
* 0 - owning (user) : read and write - 110
* Group - only read - 100
* Other - only read - 100 **/
procFileEntry = proc_create(PROCFS_NAME, 0, NULL, &basic_proc_fops);
if ( procFileEntry == NULL)
{
remove_proc_entry(PROCFS_NAME, NULL);
}
return 0;
}
static void chrDriverExit(void)
{
/** A reverse - destroy mechansim -- the way it was created **/
printk("Releasing Simple Devs -- %s\r\n", __FUNCTION__);
/** delete the character driver added **/
cdev_del(&basicCdev);
/** destroy the device created **/
device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
/** destroy the class created **/
class_destroy(basicDriverClass);
/** unregister the chr dev **/
unregister_chrdev(basicMajorNumber, NUMBER_OF_MINOR_DEVICE);
remove_proc_entry(PROCFS_NAME, NULL);
}
module_init(chrDriverInit);
module_exit(chrDriverExit);
The dmesg logs are the following.
# dmesg
[14102.921743] Releasing Simple Devs -- chrDriverExit
[14163.285107] Welcome!! Device Init now..Kernel assigned major number is 244 ..
[14174.979098] av_cmd_hist_start.. Initial..
[14174.979103] av_cmd_hist_start.. the *pos=0..
[14174.979104] the av_cmd_hist_show called
[14174.979107] av_cmd_hist_next called..
[14174.979108] the av_cmd_hist_show called
[14174.979109] av_cmd_hist_next called..
[14174.979110] the av_cmd_hist_show called
[14174.979112] av_cmd_hist_next called..
[14174.979113] the av_cmd_hist_show called
[14174.979114] av_cmd_hist_next called..
[14174.979115] the av_cmd_hist_show called
[14174.979117] av_cmd_hist_next called..
[14174.979118] the av_cmd_hist_show called
[14174.979119] av_cmd_hist_next called..
[14174.979120] the av_cmd_hist_show called
[14174.979121] av_cmd_hist_next called..
[14174.979122] the av_cmd_hist_show called
[14174.979124] av_cmd_hist_next called..
[14174.979125] the av_cmd_hist_show called
[14174.979126] av_cmd_hist_next called..
[14174.979127] the av_cmd_hist_show called
[14174.979128] av_cmd_hist_next called..
[14174.979130] av_cmd_hist_stop called..
[14174.979231] av_cmd_hist_start.. the *pos=0..
[14174.979233] av_cmd_hist_stop called..
[14174.979250] ------------[ cut here ]------------
[14174.979252] kernel BUG at mm/slub.c:3483!
[14174.979254] invalid opcode: 0000 [#2] SMP
[14174.979258] Modules linked in: procfs_driver1(O) procfs_driver(O-) tcp_lp nfsv3 nfsv4 nfs fscache dns_resolver fuse vboxpci(O) vboxnetadp(O) vboxnetflt(O) 8021q garp stp llc binfmt_misc vboxdrv(O) tpm_bios snd_hda_codec_hdmi snd_hda_codec_realtek fglrx(PO) snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm iTCO_wdt iTCO_vendor_support r8169 snd_timer mii e1000e snd i2c_i801 lpc_ich i2c_core soundcore snd_page_alloc coretemp kvm_intel kvm serio_raw video dcdbas microcode uinput nfsd lockd nfs_acl auth_rpcgss sunrpc crc32c_intel [last unloaded: procfs_driver1]
[14174.979297] Pid: 19055, comm: cat Tainted: P B D C O 3.6.11-4.fc16.i686 #1 Dell Inc. OptiPlex 9010/00F82W
[14174.979300] EIP: 0060:[<c0530891>] EFLAGS: 00210246 CPU: 5
The proc output is the following -
# cat /proc/basicProcfs1
Sequential print for debugging-- called for i-times i.e. 10 times
Hello SJ proc! 0
Hello SJ proc! 1
Hello SJ proc! 2
Hello SJ proc! 3
Hello SJ proc! 4
Hello SJ proc! 5
Hello SJ proc! 6
Hello SJ proc! 7
Hello SJ proc! 8
Hello SJ proc! 9
Segmentation fault

Sorry, I got the fix.
Erroneous code
static const struct file_operations basic_proc_fops =
{
.owner = THIS_MODULE,
.open = basicProcOpen,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
Right code-
static const struct file_operations basic_proc_fops =
{
.owner = THIS_MODULE,
.open = basicProcOpen,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
I would appreciate, if someone can explain why it makes the difference.

It is crashing becasue of this return( ( *pos < NUM_MSG_HIST_ENTRIES ) ? pos : NULL );

(I do not have enough reputation to comment, so adding it as an answer) Regarding your question on why seq_release and not single_release, 'release' functions should compliment 'open' functions. Your open function here 'basicProcOpen' calls 'seq_open' so you should be calling seq_release.
If you look at single_release, in addition to seq_release, there is a kfree(op) which causes your crash.
623 int single_release(struct inode *inode, struct file *file)
624 {
625 const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
626 int res = seq_release(inode, file);
627 kfree(op);
628 return res;
629 }
630 EXPORT_SYMBOL(single_release);

Related

LINUX KERNEL driver hangs/freeze after handling mapped register

I'm completely new developing in LINUX kernel, and I'm having some problems in a new LINUX driver I'm developing.
After I map NXP PWM registers using ioremap()/ioremap_nocache() and then I try to write to the register mappend my system hags/freeze.
Could you please help me understanding what it is happening?
My Driver is this:
#include <linux/device.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kthread.h> // for threads
#include <linux/fs.h>
#include <linux/sched.h> // for task_struct
#include <linux/delay.h> // for ndelay
#include <linux/uaccess.h> // Required for the copy to user function
#include <asm/io.h> // for ioremap()
#include <linux/interrupt.h>
#include <linux/gpio.h>
#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4)
#define DEVICE_NAME "pwm_cus_drv"
#define CLASS_NAME "pwm_custom_driver"
static volatile void __iomem *mmio_pwm1_base = NULL;
static volatile void __iomem *mmio_pwm2_base = NULL;
static int majorNumber;
static struct class* vfd_char_dev_class = NULL;
static struct device* vfd_char_dev = NULL;
static struct device_driver vfd_driver;
static int dev_open(struct inode *inodep, struct file *file_ptr);
static int dev_release(struct inode *inodep, struct file *file_ptr);
static ssize_t dev_read(struct file *file_ptr, char *buffer, size_t len, loff_t *offset);
static ssize_t dev_write(struct file *file_ptr, char *buffer, size_t len, loff_t *offset);
static long dev_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg);
static irqreturn_t pwm_imx_futaba_isr(int irq, void *dev_id);
static bool Initialize_PWM_Signals(void);
static void pwm_init(void);
/**
* ISR used to attend PWM rising edge interrupt activation.
*/
irqreturn_t pwm_imx_futaba_isr(int irq, void *dev_id)
{
if(NULL != mmio_pwm1_base)
{
writel(0x00000078, mmio_pwm1_base + 0x04);
gpio_set_value(47, 1);
gpio_set_value(47, 0);
}
return IRQ_HANDLED;
}
/**
*
*/
bool Initialize_PWM_Signals(void)
{
u32 cs_pin_dir_value = 0;
u32 cs_pin_out_value = 0;
u32 duty_cycles = 0;
u32 period_cycles = 0;
u32 cr_1 = 0;
u32 cr_2 = 0;
pwm_init();
period_cycles = ((24000000)/(4000)) - 2; /* 4 KHz */
duty_cycles = period_cycles / 2; /* duty = 50% */
printk(KERN_NOTICE "PWM data. PERIOD[%d] DUTY[%d]\n", period_cycles, duty_cycles);
cr_1 = MX3_PWMCR_PRESCALER(1) | (1 << 24) | (1 << 23) | (2 << 16);
cr_2 = MX3_PWMCR_PRESCALER(1) | (1 << 24) | (1 << 23) | (2 << 16);
printk(KERN_NOTICE "Disabling IMX6UL PWMs \n");
/*******************************/
/* AFTER THIS, THE KERNEL HANGS*/
/*******************************/
writel(cr_1, mmio_pwm1_base + 0x00);
writel(cr_2, mmio_pwm2_base + 0x00);
printk(KERN_NOTICE "PWMs disabled\n");
if (1)
{
/* Configure IMX6UL PWM1 */
printk(KERN_NOTICE " Configuring PWM1 \n");
writel(duty_cycles, mmio_pwm1_base + 0x0C);
writel(period_cycles, mmio_pwm1_base + 0x10);
/* Configure IMX6UL PWM2 */
printk(KERN_NOTICE " Configuring PWM2 \n");
writel(duty_cycles, mmio_pwm2_base + 0x0C);
writel(period_cycles, mmio_pwm2_base + 0x10);
cr_1 |= (1 << 0);
cr_2 |= (1 << 0);
printk(KERN_NOTICE "Enabling IRQs !!\n");
writel(0x00000002, mmio_pwm1_base + 0x08);
/* Enabling IMX6UL PWMs */
printk(KERN_NOTICE " Enabling PWMs \n");
writel(cr_1, mmio_pwm1_base + 0x00);
writel(cr_2, mmio_pwm2_base + 0x00);
}
return 0;
}
/**
*
*/
int dev_open(struct inode *inodep, struct file *file_ptr)
{
printk(KERN_NOTICE "\n[%s]\n", __func__);
Initialize_PWM_Signals();
printk(KERN_NOTICE "[%s] Driver initialized \n", __func__);
}
/**
*
*/
int dev_release(struct inode *inodep, struct file *file_ptr)
{
printk(KERN_NOTICE "\n[%s]\n", __func__);
}
/**
*
*/
ssize_t dev_read(struct file *file_ptr, char *buffer, size_t len, loff_t *offset)
{
printk(KERN_NOTICE "\n[%s]\n", __func__);
}
/**
*
*/
ssize_t dev_write(struct file *file_ptr, char *buffer, size_t len, loff_t *offset)
{
printk(KERN_NOTICE "\n[%s]\n", __func__);
}
/**
*
*/
long dev_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg)
{
printk(KERN_NOTICE "\n[%s]\n", __func__);
}
/**
*
*/
void pwm_init(void)
{
printk(KERN_ALERT "[%s]\n", __func__);
if(NULL != request_mem_region(0x2080000, 0x4000, DEVICE_NAME))
{
mmio_pwm1_base = ioremap_nocache(0x2080000, 0x4000);
if(IS_ERR(mmio_pwm1_base))
{
printk(KERN_NOTICE "Failed to map memory 1\n");
}
}
else
{
printk(KERN_NOTICE "Failed to map memory 2\n");
}
if(NULL != request_mem_region(0x2084000, 0x4000, DEVICE_NAME))
{
mmio_pwm2_base = ioremap_nocache(0x2084000, 0x4000);
if(IS_ERR(mmio_pwm2_base))
{
printk(KERN_NOTICE "Failed to map memory 3\n");
}
}
else
{
printk(KERN_NOTICE "Failed to map memory 4\n");
}
printk(KERN_NOTICE "PWMs memory mapped \n");
}
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = dev_open,
.read = dev_read,
.write = dev_write,
.release = dev_release,
.unlocked_ioctl = dev_ioctl,
.compat_ioctl = dev_ioctl,
};
struct bus_type futaba_bus_type =
{
.name = DEVICE_NAME,
};
static int pwm_driver_init(void)
{
unsigned irqflags = 0;
unsigned ret = 0;
const char *dev_name = "pwm1_irq";
u32 pwm_irq = 25;
majorNumber = register_chrdev(0, DEVICE_NAME, &fops);
if (majorNumber < 0)
{
printk(KERN_NOTICE "EBBChar failed to register a major number\n");
return majorNumber;
}
vfd_char_dev_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(vfd_char_dev_class))
{
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_NOTICE "Failed to register device class\n");
return PTR_ERR(vfd_char_dev_class);
}
vfd_char_dev = device_create(vfd_char_dev_class, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
if (IS_ERR(vfd_char_dev))
{
class_destroy(vfd_char_dev_class);
unregister_chrdev(majorNumber, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(vfd_char_dev);
}
ret = request_irq(pwm_irq, pwm_imx_futaba_isr, irqflags, dev_name, DEVICE_NAME);
if (0 != ret)
{
printk(KERN_NOTICE "can't get irq: %d\n", ret);
}
return 0;
}
static void pwm_driver_exit(void)
{
device_destroy(vfd_char_dev_class, MKDEV(majorNumber, 0));
class_unregister(vfd_char_dev_class);
class_destroy(vfd_char_dev_class);
unregister_chrdev(majorNumber, DEVICE_NAME);
iounmap(mmio_pwm1_base);
iounmap(mmio_pwm2_base);
}
module_init(pwm_driver_init);
module_exit(pwm_driver_exit);
MODULE_AUTHOR("New Drivers developer");
MODULE_DESCRIPTION(" PWM Handler ");
MODULE_LICENSE("GPL");
Thanks for all your answers. I solved this issue after using some NXP recommendations. At the end the problem was caused due to the IPG/PER clocks were not enabled. According NXP, clocks shall be enabled first before starting to modify registers associated with the module(in my case for PWM).
Typical reason of hang at register access is that hardware module owning the register is either powered down, or not clocked.
Per imx6ul device tree from mainline kernel (arch/arm/boot/dts/imx6ul.dtsi), there are clocks to enable:
pwm1: pwm#2080000 {
compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
reg = <0x02080000 0x4000>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_PWM1>,
<&clks IMX6UL_CLK_PWM1>;
clock-names = "ipg", "per";
#pwm-cells = <2>;
status = "disabled";
};
By the way, driver for this module is available, drivers/pwm/pwm-imx.c

'scull' device from ldd3 is not shown under /dev/

Following the book ldd3 (- Linux Device Drivers 3 ed.) and using, also, source code files available here (as suggested by another stackoverflow's user here), I am able to compile the scull device module and load it on my Linux-based OS. For more details:
$ insmod scull.ko
$ lsmod
Module Size Used by
scull 20480 0
$ dmesg | tail -...
[...] scullsingle registered at f300008
[...] sculluid registered at f300009
[...] scullwuid registered at f30000a
[...] sullpriv registered at f30000b
However, in the /dev/ directory, there is NOT the scull0 device entry as I was expecting. So I cannot use it.
In addition, when executing:
$ rmmod scull
The system displays this error:
rmmod: ERROR: ../libkmod/libkmod.c:514 lookup_builtin_file() could not open builtin file '/lib/modules/4.9.0/modules.builtin.bin'
The funny phenomena is that now, executing lsmod, the device seems disappeared and before, as you can see from the snipped, it was present.
MORE INFO (may be they can be useful in some way):
using a 4.9.0 kernel version
currently, I am cross-compiling the module
gcc used: aarch64-Linux-gnu-gcc
ARCH=arm64
snipped code of scull:
CODE:
/*
* main.c -- the bare scull char module
*
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> /* copy_*_user */
#include "scull.h" /* local definitions */
/*
* Our parameters which can be set at load time.
*/
int scull_major = SCULL_MAJOR;
int scull_minor = 0;
int scull_nr_devs = SCULL_NR_DEVS; /* number of bare scull devices */
int scull_quantum = SCULL_QUANTUM;
int scull_qset = SCULL_QSET;
module_param(scull_major, int, S_IRUGO);
module_param(scull_minor, int, S_IRUGO);
module_param(scull_nr_devs, int, S_IRUGO);
module_param(scull_quantum, int, S_IRUGO);
module_param(scull_qset, int, S_IRUGO);
MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");
MODULE_LICENSE("Dual BSD/GPL");
struct scull_dev *scull_devices; /* allocated in scull_init_module */
/*
* Empty out the scull device; must be called with the device
* semaphore held.
*/
int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next, *dptr;
int qset = dev->qset; /* "dev" is not-null */
int i;
for (dptr = dev->data; dptr; dptr = next) { /* all the list items */
if (dptr->data) {
for (i = 0; i < qset; i++)
kfree(dptr->data[i]);
kfree(dptr->data);
dptr->data = NULL;
}
next = dptr->next;
kfree(dptr);
}
dev->size = 0;
dev->quantum = scull_quantum;
dev->qset = scull_qset;
dev->data = NULL;
return 0;
}
#ifdef SCULL_DEBUG /* use proc only if debugging */
/*
* The proc filesystem: function to read and entry
*/
int scull_read_procmem(struct seq_file *s, void *v)
{
int i, j;
int limit = s->size - 80; /* Don't print more than this */
for (i = 0; i < scull_nr_devs && s->count <= limit; i++) {
struct scull_dev *d = &scull_devices[i];
struct scull_qset *qs = d->data;
if (down_interruptible(&d->sem))
return -ERESTARTSYS;
seq_printf(s,"\nDevice %i: qset %i, q %i, sz %li\n",
i, d->qset, d->quantum, d->size);
for (; qs && s->count <= limit; qs = qs->next) { /* scan the list */
seq_printf(s, " item at %p, qset at %p\n",
qs, qs->data);
if (qs->data && !qs->next) /* dump only the last item */
for (j = 0; j < d->qset; j++) {
if (qs->data[j])
seq_printf(s, " % 4i: %8p\n",
j, qs->data[j]);
}
}
up(&scull_devices[i].sem);
}
return 0;
}
/*
* Here are our sequence iteration methods. Our "position" is
* simply the device number.
*/
static void *scull_seq_start(struct seq_file *s, loff_t *pos)
{
if (*pos >= scull_nr_devs)
return NULL; /* No more to read */
return scull_devices + *pos;
}
static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (*pos >= scull_nr_devs)
return NULL;
return scull_devices + *pos;
}
static void scull_seq_stop(struct seq_file *s, void *v)
{
/* Actually, there's nothing to do here */
}
static int scull_seq_show(struct seq_file *s, void *v)
{
struct scull_dev *dev = (struct scull_dev *) v;
struct scull_qset *d;
int i;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n",
(int) (dev - scull_devices), dev->qset,
dev->quantum, dev->size);
for (d = dev->data; d; d = d->next) { /* scan the list */
seq_printf(s, " item at %p, qset at %p\n", d, d->data);
if (d->data && !d->next) /* dump only the last item */
for (i = 0; i < dev->qset; i++) {
if (d->data[i])
seq_printf(s, " % 4i: %8p\n",
i, d->data[i]);
}
}
up(&dev->sem);
return 0;
}
/*
* Tie the sequence operators up.
*/
static struct seq_operations scull_seq_ops = {
.start = scull_seq_start,
.next = scull_seq_next,
.stop = scull_seq_stop,
.show = scull_seq_show
};
/*
* Now to implement the /proc files we need only make an open
* method which sets up the sequence operators.
*/
static int scullmem_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, scull_read_procmem, NULL);
}
static int scullseq_proc_open(struct inode *inode, struct file *file)
{
return seq_open(file, &scull_seq_ops);
}
/*
* Create a set of file operations for our proc files.
*/
static struct file_operations scullmem_proc_ops = {
.owner = THIS_MODULE,
.open = scullmem_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release
};
static struct file_operations scullseq_proc_ops = {
.owner = THIS_MODULE,
.open = scullseq_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
/*
* Actually create (and remove) the /proc file(s).
*/
static void scull_create_proc(void)
{
proc_create_data("scullmem", 0 /* default mode */,
NULL /* parent dir */, &scullmem_proc_ops,
NULL /* client data */);
proc_create("scullseq", 0, NULL, &scullseq_proc_ops);
}
static void scull_remove_proc(void)
{
/* no problem if it was not registered */
remove_proc_entry("scullmem", NULL /* parent dir */);
remove_proc_entry("scullseq", NULL);
}
#endif /* SCULL_DEBUG */
/*
* Open and close
*/
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
scull_trim(dev); /* ignore errors */
up(&dev->sem);
}
return 0; /* success */
}
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*
* Follow the list
*/
struct scull_qset *scull_follow(struct scull_dev *dev, int n)
{
struct scull_qset *qs = dev->data;
/* Allocate first qset explicitly if need be */
if (! qs) {
qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs == NULL)
return NULL; /* Never mind */
memset(qs, 0, sizeof(struct scull_qset));
}
/* Then follow the list */
while (n--) {
if (!qs->next) {
qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
if (qs->next == NULL)
return NULL; /* Never mind */
memset(qs->next, 0, sizeof(struct scull_qset));
}
qs = qs->next;
continue;
}
return qs;
}
/*
* Data management: read and write
*/
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr; /* the first listitem */
int quantum = dev->quantum, qset = dev->qset;
int itemsize = quantum * qset; /* how many bytes in the listitem */
int item, s_pos, q_pos, rest;
ssize_t retval = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= dev->size)
goto out;
if (*f_pos + count > dev->size)
count = dev->size - *f_pos;
/* find listitem, qset index, and offset in the quantum */
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum; q_pos = rest % quantum;
/* follow the list up to the right position (defined elsewhere) */
dptr = scull_follow(dev, item);
if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
goto out; /* don't fill holes */
/* read only up to the end of this quantum */
if (count > quantum - q_pos)
count = quantum - q_pos;
if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum, qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
/* find listitem, qset index and offset in the quantum */
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum; q_pos = rest % quantum;
/* follow the list up to the right position */
dptr = scull_follow(dev, item);
if (dptr == NULL)
goto out;
if (!dptr->data) {
dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data)
goto out;
memset(dptr->data, 0, qset * sizeof(char *));
}
if (!dptr->data[s_pos]) {
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
if (!dptr->data[s_pos])
goto out;
}
/* write only up to the end of this quantum */
if (count > quantum - q_pos)
count = quantum - q_pos;
if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
/* update the size */
if (dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
/*
* The ioctl() implementation
*/
long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0, tmp;
int retval = 0;
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
switch(cmd) {
case SCULL_IOCRESET:
scull_quantum = SCULL_QUANTUM;
scull_qset = SCULL_QSET;
break;
case SCULL_IOCSQUANTUM: /* Set: arg points to the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCTQUANTUM: /* Tell: arg is the value */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_quantum = arg;
break;
case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */
retval = __put_user(scull_quantum, (int __user *)arg);
break;
case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */
return scull_quantum;
case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_quantum;
retval = __get_user(scull_quantum, (int __user *)arg);
if (retval == 0)
retval = __put_user(tmp, (int __user *)arg);
break;
case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_quantum;
scull_quantum = arg;
return tmp;
case SCULL_IOCSQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
retval = __get_user(scull_qset, (int __user *)arg);
break;
case SCULL_IOCTQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
scull_qset = arg;
break;
case SCULL_IOCGQSET:
retval = __put_user(scull_qset, (int __user *)arg);
break;
case SCULL_IOCQQSET:
return scull_qset;
case SCULL_IOCXQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_qset;
retval = __get_user(scull_qset, (int __user *)arg);
if (retval == 0)
retval = put_user(tmp, (int __user *)arg);
break;
case SCULL_IOCHQSET:
if (! capable (CAP_SYS_ADMIN))
return -EPERM;
tmp = scull_qset;
scull_qset = arg;
return tmp;
/*
* The following two change the buffer size for scullpipe.
* The scullpipe device uses this same ioctl method, just to
* write less code. Actually, it's the same driver, isn't it?
*/
case SCULL_P_IOCTSIZE:
scull_p_buffer = arg;
break;
case SCULL_P_IOCQSIZE:
return scull_p_buffer;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return retval;
}
/*
* The "extended" operations -- only seek
*/
loff_t scull_llseek(struct file *filp, loff_t off, int whence)
{
struct scull_dev *dev = filp->private_data;
loff_t newpos;
switch(whence) {
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = dev->size + off;
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0) return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.unlocked_ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
/*
* Finally, the module stuff
*/
/*
* The cleanup function is used to handle initialization failures as well.
* Thefore, it must be careful to work correctly even if some of the items
* have not been initialized
*/
void scull_cleanup_module(void)
{
int i;
dev_t devno = MKDEV(scull_major, scull_minor);
/* Get rid of our char dev entries */
if (scull_devices) {
for (i = 0; i < scull_nr_devs; i++) {
scull_trim(scull_devices + i);
cdev_del(&scull_devices[i].cdev);
}
kfree(scull_devices);
}
#ifdef SCULL_DEBUG /* use proc only if debugging */
scull_remove_proc();
#endif
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, scull_nr_devs);
/* and call the cleanup functions for friend devices */
scull_p_cleanup();
scull_access_cleanup();
}
/*
* Set up the char_dev structure for this device.
*/
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}
int scull_init_module(void)
{
int result, i;
dev_t dev = 0;
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);
if (!scull_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));
/* Initialize each device. */
for (i = 0; i < scull_nr_devs; i++) {
scull_devices[i].quantum = scull_quantum;
scull_devices[i].qset = scull_qset;
sema_init(&scull_devices[i].sem, 1);
scull_setup_cdev(&scull_devices[i], i);
}
/* At this point call the init function for any friend device */
dev = MKDEV(scull_major, scull_minor + scull_nr_devs);
dev += scull_p_init(dev);
dev += scull_access_init(dev);
#ifdef SCULL_DEBUG /* only when debugging */
scull_create_proc();
#endif
return 0; /* succeed */
fail:
scull_cleanup_module();
return result;
}
module_init(scull_init_module);
module_exit(scull_cleanup_module);
SOLUTION:
Of course, I was doing the things in the wrong way: in order to load CORRECTLY the scull device module, in the book's source code and, also, in the other link of the question, there is load_scull script that does everything for you.
# source scull_load
# lsmod
Module Size Used by
scull 20480 0
# ls -l /dev/ | grep scull
lrwxrwxrwx 1 root root 6 Dec 19 15:00 scull -> scull0
crw-rw-r-- 1 root staff 243, 0 Dec 19 15:00 scull0
crw-rw-r-- 1 root staff 243, 1 Dec 19 15:00 scull1
crw-rw-r-- 1 root staff 243, 2 Dec 19 15:00 scull2
crw-rw-r-- 1 root staff 243, 3 Dec 19 15:00 scull3
lrwxrwxrwx 1 root root 10 Dec 19 15:00 scullpipe -> scullpipe0
crw-rw-r-- 1 root staff 243, 4 Dec 19 15:00 scullpipe0
crw-rw-r-- 1 root staff 243, 5 Dec 19 15:00 scullpipe1
crw-rw-r-- 1 root staff 243, 6 Dec 19 15:00 scullpipe2
crw-rw-r-- 1 root staff 243, 7 Dec 19 15:00 scullpipe3
crw-rw-r-- 1 root staff 243, 11 Dec 19 15:00 scullpriv
crw-rw-r-- 1 root staff 243, 8 Dec 19 15:00 scullsingle
crw-rw-r-- 1 root staff 243, 9 Dec 19 15:00 sculluid
crw-rw-r-- 1 root staff 243, 10 Dec 19 15:00 scullwuid

Why the number of minor devices are not listed here in this program in /dev?

I want to create the number of minor character drivers for my module. I want to see it at the /dev/ . However, I see only one driver. What is the problem in my code? What should be the right code?
#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h> /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h> /** character device **/
#include <linux/device.h> /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>
/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");
static struct cdev basicCdev;
static struct class *basicDriverClass;
static int basicMajorNumber = 0;
#define NUMBER_OF_MINOR_DEVICE (5)
/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/
static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);
static int basicOspen(struct inode *inode, struct file *file);
/** File Operations function pointer table **/
/** There are plenty of file operations **/
static struct file_operations fops = {
.read = basicRead,
.write = NULL,
.open = basicOspen,
.release = NULL
};
static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
char msg[1024] = "Hello SJ_read\0";
printk(KERN_ALERT "The Read operation called\r\n");
copy_to_user( buffer, msg, sizeof(msg) );
return sizeof(msg);
}
static int basicOspen(struct inode *inode, struct file *file)
{
printk("Kernel.Basic Driver Opened now!!\r\n");
return 0;
}
static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
int err = -1;
/** MKDEV call creates a device number i.e. combination of major and minor number **/
int devno = MKDEV(basicMajorNumber, minor);
/** Initiliaze character dev with fops **/
cdev_init(dev, fops);
/**owner and operations initialized **/
dev->owner = THIS_MODULE;
dev->ops = fops;
/** add the character device to the system**/
/** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
err = cdev_add (dev, devno, 1);
if (err)
{
printk (KERN_NOTICE "Couldn't add cdev");
}
}
static int chrDriverInit(void)
{
int result;
dev_t dev;
printk("Welcome!! Device Init now..");
/** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); **/
/** dev -> The dev_t variable type,which will get the major number that the kernel allocates. **/
/**The same name will appear in /proc/devices. **/
/** it is registering the character device **/
/** a major number will be dynamically allocated here **/
/** alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");
if( result < 0 )
{
printk("Error in allocating device");
return -1;
}
/** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
/** creating class, and then device created removes the dependency of calling mknod **/
/** A good method - the mknod way is depreciated **/
/** mknod way is - mknod /dev/<driver_name> c <majorNumber> <minorNumber>
/** add the driver to /sys/class/chardrv **/
if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL) //$ls /sys/class
{
unregister_chrdev_region(dev, 1);
return -1;
}
/** add the driver to /dev/pSeudoDrv -- here **/
if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
/** let's see what major number was assigned by the Kernel **/
basicMajorNumber = MAJOR(dev);
printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );
/** Now setup the cdev **/
setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);
return 0;
}
static void chrDriverExit(void)
{
/** A reverse - destroy mechansim -- the way it was created **/
printk("Releasing Simple Devs -- %s\r\n", __FUNCTION__);
/** delete the character driver added **/
cdev_del(&basicCdev);
/** destroy the device created **/
device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
/** destroy the class created **/
class_destroy(basicDriverClass);
/** unregister the chr dev **/
unregister_chrdev(basicMajorNumber, NUMBER_OF_MINOR_DEVICE);
}
module_init(chrDriverInit);
module_exit(chrDriverExit);
I get errors, if I modify the init function to the following.
static int chrDriverInit(void)
{
int result;
dev_t dev;
dev_t dev2;
printk("Welcome!! Device Init now..");
/** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name); **/
/** dev -> The dev_t variable type,which will get the major number that the kernel allocates. **/
/**The same name will appear in /proc/devices. **/
/** it is registering the character device **/
/** a major number will be dynamically allocated here **/
/** alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");
if( result < 0 )
{
printk("Error in allocating device");
return -1;
}
/** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
/** creating class, and then device created removes the dependency of calling mknod **/
/** A good method - the mknod way is depreciated **/
/** mknod way is - mknod /dev/<driver_name> c <majorNumber> <minorNumber>
/** add the driver to /sys/class/chardrv **/
if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL) //$ls /sys/class
{
unregister_chrdev_region(dev, 1);
return -1;
}
/** add the driver to /dev/pSeudoDrv -- here **/
if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
/** let's see what major number was assigned by the Kernel **/
basicMajorNumber = MAJOR(dev);
printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );
dev2 = MKDEV(basicMajorNumber,2);
#if 1
if (device_create(basicDriverClass, NULL, dev2, NULL, "pSeudoDrv2") == NULL) //$ls /dev/
{
class_destroy(basicDriverClass);
unregister_chrdev_region(dev, 1);
return -1;
}
#endif
/** Now setup the cdev **/
setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);
return 0;
}
Here is the crash -
[19554.180535] CPU: 1 PID: 16636 Comm: insmod Tainted: GF W O 3.13.5-101.fc19.x86_64 #1
[19554.180537] Hardware name: Dell Inc. Studio 1558/0G939P, BIOS A07 06/24/2010
[19554.180539] 0000000000000009 ffff8800667d7ad8 ffffffff81680664 ffff8800667d7b20
[19554.180543] ffff8800667d7b10 ffffffff8106d35d ffff880130f9e810 00000000ffffffef
[19554.180547] 0000000000000000 ffff880133432378 0000000000000000 ffff8800667d7b70
[19554.180551] Call Trace:
[19554.180555] [<ffffffff81680664>] dump_stack+0x45/0x56
[19554.180558] [<ffffffff8106d35d>] warn_slowpath_common+0x7d/0xa0
[19554.180562] [<ffffffff8106d3cc>] warn_slowpath_fmt+0x4c/0x50
[19554.180566] [<ffffffff81315be4>] kobject_add_internal+0x214/0x320
[19554.180570] [<ffffffff81316115>] kobject_add+0x65/0xb0
[19554.180574] [<ffffffff81315550>] ? kobject_put+0x30/0x60
[19554.180578] [<ffffffff814138b5>] device_add+0x125/0x640
[19554.180582] [<ffffffff81413fe0>] device_create_groups_vargs+0xe0/0x110
[19554.180586] [<ffffffffa078d0e0>] ? basicRead+0xc0/0xc0 [character_driver]
[19554.180590] [<ffffffff81414071>] device_create+0x41/0x50
[19554.180595] [<ffffffffa078d1d3>] chrDriverInit+0xf3/0x160 [character_driver]
[19554.180598] [<ffffffff8100214a>] do_one_initcall+0xfa/0x1b0
[19554.180602] [<ffffffff81054803>] ? set_memory_nx+0x43/0x50
[19554.180609] [<ffffffff810e060d>] load_module+0x1e1d/0x25b0
[19554.180613] [<ffffffff810dc320>] ? store_uevent+0x40/0x40
[19554.180617] [<ffffffff810e0f16>] SyS_finit_module+0x86/0xb0
[19554.180622] [<ffffffff8168f629>] system_call_fastpath+0x16/0x1b
[19554.180624] ---[ end trace dd8ae4ccda100ad8 ]---
Your problem is that you only call device_create() for the first device. alloc_chrdev_region() only allocate major/minor numbers but it does not actually create the device structures in the kernel. The dev_t returned by alloc_chrdev_region() represents the first of your five major/minor numbers. You need to use a for loop, increment the minor number and then call device_create() on all of them.
Don't forget to destroy all of them in the exit function.
The kernel does not create device nodes automatically.
Something in userspace must do it, using mknod(2) or the mknod utility.
This can be done manually or when a file system image is created, by a script on boot, or by a daemon such as udev.

What is the modern version of `procfs1.c` for Linux 2.6.x kernels?

I have been following the Linux 2.6 Kernel Module Programming Guide, when I ran into the first example from Chapter 5, called procfs1.c.
It would not compile out of the box, and after checking various related questions, it still took me quite some time to figure out the correct changes to make it compile and work as intended.
Therefore, I am posting my updated code for people in the future. I am running kernel 2.6.32-279.el6.x86_64.
Here is an updated version of the example that works with kernel version 2.6.32-279.el6.x86_64.
/*
* procfs1.c - create a "file" in /proc
*
*/
#include <linux/module.h> /* Specifically, a module */
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
#define procfs_name "helloworld"
/**
* This structure hold information about the /proc file
*
*/
struct proc_dir_entry *Our_Proc_File;
static ssize_t procfile_read(struct file *filp,
char *buffer,
size_t length,
loff_t * offset)
{
int ret;
printk(KERN_INFO "procfile_read (/proc/%s) called\n", procfs_name);
if (*offset > 0) {
/* we have finished to read, return 0 */
ret = 0;
} else {
/* fill the buffer, return the buffer size */
ret = sprintf(buffer, "HelloWorld!\n");
*offset = ret;
}
return ret;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = procfile_read,
};
int init_module()
{
Our_Proc_File = proc_create(procfs_name, S_IFREG | S_IRUGO, NULL, &fops);
if (Our_Proc_File == NULL) {
remove_proc_entry(procfs_name, NULL);
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
procfs_name);
return -ENOMEM;
}
Our_Proc_File->uid = 0;
Our_Proc_File->gid = 0;
Our_Proc_File->size = 37;
printk(KERN_INFO "/proc/%s created\n", procfs_name);
return 0; /* everything is ok */
}
void cleanup_module()
{
remove_proc_entry(procfs_name, NULL);
printk(KERN_INFO "/proc/%s removed\n", procfs_name);
}

Kernel panics : trying to write / read on tiny tty driver

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 tiny_tty.c) loads perfectly using insmod and I'm able to see it in /proc/tty/drivers , /proc/modules and device nodes are getting created under /dev. When I try to write to device file like echo "abcd" > /dev/ttyms0 (I hope this is fine) or read like cat /dev/ttyms0, 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 :
"*kernel bug at /build/buildd/linux-3.5.0/kernel/timer.c:901*", then the call trace , followed by
"*EIP is at add_timer+0x18/0x20*"
Below is the full code. Any guidance is very much appreciated in anticipation.
10May2013 : 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":
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
<IRQ>
copy_to_user0x41/0x60
sys_gettimeofday+0x2a/0x70
sysenter_do_call0x12/0x20
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/slab.h> /* kmalloc() */
#include <linux/tty_driver.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mutex.h>
#include <linux/serial.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/termios.h>
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->tty;
tty->low_latency=1;
/* send the data to the tty layer for users to read. This doesn't
* actually push the data through unless tty->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->timer->expires = jiffies + DELAY_TIME;
add_timer(ms_ptr->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->driver_data = NULL;
/* get the serial object associated with this tty pointer */
index = tty_this->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(&ms_ptr->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(&ms_ptr->sem,1);
#else
init_MUTEX(&ms_ptr->sem);
#endif
ms_ptr->open_count = 0;
ms_ptr->timer = NULL;
ms_tty_table[index] = ms_ptr;
}
down(&ms_ptr->sem);
/* save our structure within the tty structure */
tty_this->driver_data = ms_ptr;
ms_ptr->tty = tty_this;
ms_ptr->filp = file_this; // to be tried
++ms_ptr->open_count;
if (ms_ptr->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->timer) {
timer = kmalloc(sizeof(*timer), GFP_KERNEL);
if (!timer) {
up(&ms_ptr->sem);
return -ENOMEM;
}
ms_ptr->timer = timer;
}
init_timer (ms_ptr->timer); // to be tried
ms_ptr->timer->data = (unsigned long )ms_ptr;
ms_ptr->timer->expires = jiffies + DELAY_TIME;
ms_ptr->timer->function = ms_tty_timer;
add_timer(ms_ptr->timer);
}
up(&ms_ptr->sem);
return 0;
}
//// Define the close function ////
static void do_close(struct ms_tty_serial *ms_ptr)
{
down(&ms_ptr->sem);
if (!ms_ptr->open_count) {
/* port was never opened */
goto exit;
}
--ms_ptr->open_count;
if (ms_ptr->open_count <= 0) {
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */
/* shut down our timer */
del_timer(ms_ptr->timer);
}
exit:
up(&ms_ptr->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->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->driver_data;
int i;
int retval = -EINVAL;
if (!ms_ptr)
return -ENODEV;
down(&ms_ptr->sem);
if (!ms_ptr->open_count)
/* port was not opened */
{
up(&ms_ptr->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 < 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->magic=TTY_DRIVER_MAGIC;
tty_ms_driver->owner = THIS_MODULE;
tty_ms_driver->driver_name = "tty_ms";
tty_ms_driver->name = "ttyms";
tty_ms_driver->major = MS_TTY_MAJOR,
tty_ms_driver->type = TTY_DRIVER_TYPE_SERIAL,
tty_ms_driver->subtype = SERIAL_TYPE_NORMAL,
tty_ms_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
tty_ms_driver->init_termios = tty_std_termios;
tty_ms_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tty_ms_driver, &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 < 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<=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 < MS_TTY_NUM_DEV; ++iter) {
tty_ser = ms_tty_table[iter];
if (tty_ser) {
/* close the port */
while (tty_ser->open_count)
do_close(tty_ser);
/* shut down our timer and free the memory */
del_timer(tty_ser->timer);
kfree(tty_ser->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);

Resources