I am adding an IOctl call from the native OpenGL framework on an Android device such that I can log frame information in the kernel via a tracing kernel module. In my kernel module I am creating the cdev for the IOctl calls but am having the problem that my dev's default permissions are 6000 and since the user-land IOctl calls are coming from a non-root process the open call is unable to open the file decriptor required for the ioctl call.
My cdev approach is very much standard and as follows (note devnode is an attempt to fix permissions, see this).
static dev_t dev;
static struct cdev c_dev;
static struct class *cl;
static char *device_node(struct device *dev, umode_t *mode)
{
if(!mode)
return NULL;
*mode=0666;
return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
}
static int IOctlInit(void)
{
int ret;
struct device *dev_ret;
if((ret = alloc_chrdev_region(&dev, FIRST_MINOR, MINOR_CNT, EGL_SYSLOGGER_NAME)))
return ret;
cdev_init(&c_dev, &syslog_EGL_fops);
if((ret = cdev_add(&c_dev, dev, MINOR_CNT)) < 0)
return ret;
if(IS_ERR( cl = class_create(THIS_MODULE, EGL_SYSLOGGER_NAME "char")))
{
cdev_del(&c_dev);
unregister_chrdev_region(dev, MINOR_CNT);
return PTR_ERR(cl);
}
cl->devnode = device_node;
if(IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, EGL_SYSLOGGER_NAME)))
{
class_destroy(cl);
cdev_del(&c_dev);
unregister_chrdev_region(dev, MINOR_CNT);
return PTR_ERR(cl);
}
return 0;
}
And similarly using a misc dev with .mode=0666 I have the same problem
static struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = EGL_SYSLOGGER_NAME,
.fops = &syslog_EGL_fops,
/** .mode = S_IRWXUGO, */
.mode = 0666,
};
static `enter code here`int IOctlInit(void)
{
int ret;
ret = misc_register(&misc_dev);
if(ret)
printk("Unable to register EGL IOctl misc dev\n");
printk("Misc dev registered\n");
return 0;
}
Both approaches work once running chmod 666 /dev/$EGL_SYSLOGGER_NAME but I am hoping to find a solution that does not need this intervention. According to this post the misc dev approach should solve my problem but I have not had any success.
I am not sure what I have missed and would greatly appreciate some tips.
Cheers
Related
I've been building a RTDM UART driver by using the Linux version as an example. The UART base address is supposed to be 0x80070000, and when using the linux driver there is no problem as dmesg show:
80070000.serial: ttyAPP3 at MMIO 0x80070000 (irq = 234, base_baud = 1500000) is a 80070000.serial
mxs-auart 80070000.serial: Found APPUART 3.1.0
However, when I'm using the driver I'm builing I get a different adress each time I'm loading the module (such as 0xc8df6000) and none of them are correct.
Here's the probe function I'm using:
static int rt_mxs_auart_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxs_auart_dt_ids, &pdev->dev);
int ret;
struct resource *r;
struct rtdm_device *dev;
struct rt_mxs_auart_port *s;
//allocate managed kernel memory
s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
ret = rt_serial_mxs_probe_dt(s, pdev);
if (ret < 0){
return ret;
}
if (of_id) {
pdev->id_entry = of_id->data;
s->devtype = pdev->id_entry->driver_data;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENXIO;
//get irq line
s->irq = platform_get_irq(pdev, 0);
if (s->irq < 0)
return -ENODEV;
//base memory
s->membase = ioremap(r->start, resource_size(r));
if (IS_ERR(s->membase))
return PTR_ERR(s->membase);
/*define the rtdm_device*/
dev = &s->rtdm_dev;
dev->driver = &ida_driver;
dev->label = "xeno_ida";
dev->device_data = s;
ret = mxs_get_clks(s, pdev); //activate clocks
if (ret)
return ret;
s->uartclk = clk_get_rate(s->clk);
s->fifosize = MXS_AUART_FIFO_SIZE;
mxs_init_regs(s);
ret = rtdm_dev_register(dev); //register driver in RTDM space
if (ret)
return ret;
platform_set_drvdata(pdev, s); //add device-related data to device struct
pr_info("Probe: Successful\n");
pr_info("%s on IMX UART%d: membase=0x%p irq=%d uartclk=%d\n",
dev->name, pdev->id, s->membase, s->irq, s->uartclk);
return 0;
}
which is based on mxs-auart.c and rt_imx_uart.c.
For membase I'm using the same code as mxs-auart.c which is why I don't understand why I'm having this issue. My driver also freezes after some time when I try to rmmod it so I wonder if it is caused by this.
Would appreciate any help!
The return value of ioremap is a virtual address, not a physical one. It could be anywhere in the virtual address space.
The physical address is stored in r->start (the input of ioremap).
I'm new to Linux USB driver development, and I absolutely love it so far! I'm currently creating a driver for an Xbox One controller, and I have a question for you guys. In the below code, you will see that I fill an interrupt IN URB in the open function, and I would like to print the contents of the URB buffer in the xb1_int_in_callback() function. What would be the best way to do this? At the moment, I'm using printk(KERN_INFO "int_in_buffer: %s", dev->int_in_buffer), however I don't see the entire URB buffer contents printed, and get a weird string printed to dmesg.
Sorry if this is a simple question, I'm new to programming and C, so I'm still learning as I go, but I absolutely love it so far!
Code:
static void xb1_int_in_callback(struct urb *int_in_urb) {
struct xb1_controller *dev = int_in_urb->context;
printk(KERN_INFO "xb1_int_in_callback successfully called");
printk(KERN_INFO "int_in_buffer: %s", dev->int_in_buffer);
}
static int xb1_open(struct inode *inode, struct file *file) {
printk(KERN_INFO "open function called..");
struct xb1_controller *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
subminor = iminor(inode);
interface = usb_find_interface(&xb1_driver, subminor);
if(!interface) {
printk(KERN_INFO "Unable to locate interface in open
function");
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if(!dev) {
printk(KERN_INFO "Unable to locate dev structure in open
function");
retval = -ENODEV;
goto exit;
}
usb_fill_int_urb(dev->int_in_urb, dev->udev, usb_rcvintpipe(dev-
>udev, dev->int_in_endpoint->bEndpointAddress),
dev->int_in_buffer, dev->int_in_endpoint-
>wMaxPacketSize, xb1_int_in_callback,
dev, dev->int_in_endpoint->bInterval);
dev->int_in_running = 1;
retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL);
if(retval) {
printk(KERN_INFO "Unable to submit int_in_urb in open
function");
dev->int_in_running = 0;
goto exit;
}
file->private_data = dev;
exit:
return retval;
}
static int xb1_probe(struct usb_interface *interface, const struct
usb_device_id *id) {
struct usb_device *udev = interface_to_usbdev(interface);
struct xb1_controller *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int i;
int retval = -ENODEV;
if(!udev) {
printk(KERN_INFO "udev is NULL in probe function");
xb1_abort(dev);
return retval;
}
dev = kzalloc(sizeof(struct xb1_controller), GFP_KERNEL);
if(!dev) {
printk(KERN_INFO "Unable to allocate memory for dev in probe
function");
xb1_abort(dev);
return retval;
}
dev->udev = udev;
dev->interface = interface;
iface_desc = interface->cur_altsetting;
for(i=0; i<iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if(((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
USB_DIR_IN)
&& ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT)) {
dev->int_in_endpoint = endpoint;
}
}
if(!dev->int_in_endpoint) {
printk(KERN_INFO "Unable to locate interrupt in endpoint for
interface in probe function");
xb1_abort(dev);
return retval;
}
else {
printk(KERN_INFO "Interrupt in endpoint found!");
}
For printing a small buffers (up to 64 bytes long) use printk format:
Raw buffer as a hex string:
%*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f
%*phD 00-01-02- ... -3f
%*phN 000102 ... 3f
For larger buffers use print_hex_dump(). Reference here.
I have shared memory segment created in kernel using mmap. I need to access this mapped memory from both kernel and user space. What mechanism should I use to protect the memory from concurrent access ?
I want to have something like:
Kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mm.h>
#ifndef VM_RESERVED
# define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
struct dentry *file;
struct mmap_info
{
char *data;
int reference;
};
void mmap_open(struct vm_area_struct *vma)
{
struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
info->reference++;
}
void mmap_close(struct vm_area_struct *vma)
{
struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
info->reference--;
}
static int mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct page *page;
struct mmap_info *info;
info = (struct mmap_info *)vma->vm_private_data;
if (!info->data)
{
printk("No data\n");
return 0;
}
page = virt_to_page(info->data);
get_page(page);
vmf->page = page;
return 0;
}
struct vm_operations_struct mmap_vm_ops =
{
.open = mmap_open,
.close = mmap_close,
.fault = mmap_fault,
};
int op_mmap(struct file *filp, struct vm_area_struct *vma)
{
vma->vm_ops = &mmap_vm_ops;
vma->vm_flags |= VM_RESERVED;
vma->vm_private_data = filp->private_data;
mmap_open(vma);
return 0;
}
int mmapfop_close(struct inode *inode, struct file *filp)
{
struct mmap_info *info = filp->private_data;
free_page((unsigned long)info->data);
kfree(info);
filp->private_data = NULL;
return 0;
}
int mmapfop_open(struct inode *inode, struct file *filp)
{
struct mmap_info *info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
info->data = (char *)get_zeroed_page(GFP_KERNEL);
memcpy(info->data, "hello from kernel this is file: ", 32);
memcpy(info->data + 32, filp->f_dentry->d_name.name, strlen(filp->f_dentry->d_name.name));
/* assign this info struct to the file */
filp->private_data = info;
return 0;
}
static const struct file_operations mmap_fops = {
.open = mmapfop_open,
.release = mmapfop_close,
.mmap = op_mmap,
};
static int __init mmapexample_module_init(void)
{
file = debugfs_create_file("mmap_example", 0644, NULL, NULL, &mmap_fops);
return 0;
}
static void __exit mmapexample_module_exit(void)
{
debugfs_remove(file);
}
module_init(mmapexample_module_init);
module_exit(mmapexample_module_exit);
MODULE_LICENSE("GPL");
User space:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#define PAGE_SIZE 4096
int main ( int argc, char **argv )
{
int configfd;
char * address = NULL;
configfd = open("/sys/kernel/debug/mmap_example", O_RDWR);
if(configfd < 0)
{
perror("Open call failed");
return -1;
}
address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, configfd, 0);
if (address == MAP_FAILED)
{
perror("mmap operation failed");
return -1;
}
printf("Initial message: %s\n", address);
memcpy(address + 11 , "*user*", 6);
printf("Changed message: %s\n", address);
close(configfd);
return 0;
}
but with locks.
Kernel space and user space have no shared mechanisms for concurrent access protection. If you want them, you need to implement them by yourself.
It can be some sort of mutex, implemented within you kernel module, and accessed from user space via special ioctl requests:
Kernel:
DECLARE_WAIT_QUEUE_HEAD(wq);
int my_mutex_val = 0;
/*
* Lock mutex.
*
* May be used directly by the kernel or via 'ioctl(MY_CMD_LOCK)' by user.
*/
void my_mutex_lock(void)
{
spin_lock(&wq.lock);
wait_event_interruptible_locked(&wq, my_mutex_val == 0);
my_mutex_val = 1;
spin_unlock(&wq.lock);
}
/*
* Unlock mutex.
*
* May be used directly by the kernel or via 'ioctl(MY_CMD_UNLOCK)' by user.
*/
void my_mutex_unlock(void)
{
spin_lock(&wq.lock);
my_mutex_val = 0;
wake_up(&wq);
spin_unlock(&wq.lock);
}
long unlocked_ioctl (struct file * filp, unsigned int cmd, unsigned long val)
{
switch(cmd) {
case MY_CMD_LOCK:
my_mutex_lock();
break;
case MY_CMD_UNLOCK:
my_mutex_unlock();
break;
}
}
User:
int main()
{
...
ioctl(MY_CMD_LOCK);
<read data>
ioctl(MY_CMD_UNLOCK);
...
}
It can be some sort of spinlock, which value is stored in mmap-ed area (so visible both for kernel space and user space).
In any case, kernel module should be prepared for the case, when user space application doesn't follow locking conventions. This, probably, would cancel any expectation about mmap-ed area content, generated by the kernel, but kernel module shouldn't crash in that case. [This is why standard kernel's struct mutex is not used in the code above: user space may use it incorrectly].
The problem with the ioctl is you need a kernel switch every time you want to access the share info->data. If that is okay then the ioctl is good - but then why not just do a standard character read/write file operation instead?
You can also try a lock-less mechanism. In the shared info->data area add a barrier variable. When the user needs access, it will do an atomic_compare_and_xchg on the barrier variable until it is set to 0 (unused) and then set it to 1. When the kernel needs access it will do the same but set it to 2. See the gcc atomic builtin documentation.
I'm trying to develop a sound card driver with ALSA.
My goal eventually is to use:
PCM through I2S.
Control the volume through SPI.
For simplicity I started with volume control ONLY, no PCM.
I wrote the driver and compiled but I'm having 2 issues:
I cannot successfully call snd_card_free in the module exit. I tried storing the snd_card pointer which is successfully allocated in the module init in my custom "snd_adsp21479_chip" structure and use container_of to get the snd_card but the card is always null in the module_exit (it was not null in the module_init and is successfully allocated, I have the checks, I could also successfully call snd_card_free(card) in the module init function, but nowhere else). So every time I reload the module to the kernel, I end up with one more sound card and a new control in /dev/snd/ and proc/asound/devices
My biggest problem now is after loading the module I do see a new control (control1) in /dev/snd/ and a new card there, however using amixer I do not see the card's Volume control. I run amixer conctrols, and I do not see my new control.
Here's the code:
#include <linux/wait.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#define LOW_SPEED_SPIDEV_SPI_BUS 0
#define LOW_SPEED_SPIDEV_SPI_CS 0
#define LOW_SPEED_SPIDEV_MAX_CLK_HZ 100000
#define SOUND_CARD_NAME "adsp21479"
static struct spi_board_info cal_spi_board_info = {
.modalias = "spidev",
.bus_num = LOW_SPEED_SPIDEV_SPI_BUS,
.chip_select = LOW_SPEED_SPIDEV_SPI_CS,
.max_speed_hz = LOW_SPEED_SPIDEV_MAX_CLK_HZ,
};
static struct spi_device *spi = NULL;
struct snd_adsp21479_chip {
struct spi_device *spi;
struct snd_card *card;
};
/*******************************CONTROL METHODS *******************************/
static int snd_adsp21479_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info * uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 100;
return 0;
}
static int snd_adsp21479_stereo_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static int snd_adsp21479_stereo_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
return 0;
}
static struct snd_kcontrol_new volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.info = snd_adsp21479_stereo_info,
.get = snd_adsp21479_stereo_get,
.put = snd_adsp21479_stereo_put
};
static int snd_adsp21479_init(struct spi_device *spi)
{
struct snd_card * card = NULL;
int return_value;
char id[10] = SOUND_CARD_NAME;
struct snd_adsp21479_chip *chip;
pr_info("snd_adsp21479_init start\n");
return_value = snd_card_new(&spi->dev,-1,id,THIS_MODULE,sizeof(struct snd_adsp21479_chip),&card);
if(return_value<0)
{
pr_err("snd_card_new failed\n");
return return_value;
}
if(!card)
{
pr_err("snd_card_new did not allocate card");
return -ENODEV;
}
pr_info("sound card=%p created successfully",card);
chip = card->private_data;
chip->card = card;
chip->spi = spi;
strcpy(card->mixername,"adsp21479 Mixer");
return_value = snd_ctl_add(card,snd_ctl_new1(&volume_control,chip));
if(return_value<0)
{
pr_err("snd_ctl_add faile adding control\n");
return return_value;
}
pr_info("created a mixer control");
static struct snd_device_ops ops = { NULL};
strcpy(card->driver,SOUND_CARD_NAME);
strcpy(card->shortname,SOUND_CARD_NAME);
strcpy(card->longname,"DAQRI ADSP21479 Sound Driver");
//create an ALSA device component
return_value = snd_device_new(card, SNDRV_DEV_LOWLEVEL,chip,&ops);
if(return_value<0)
{
printk(KERN_ALERT"creating an ALSA device component failed");
return return_value;
}
pr_info("created an ASLA device SNDRV_DEV_LOWLEVEL");
return_value = snd_card_register(card);
if(return_value<0)
{
pr_err("snd_card_register failed\n");
return return_value;
}
if(!card)
{
pr_err("card turned into null\n");
}
pr_info("registered sound card\n");
pr_info("adsp21479_snd_driver loaded successfully\n");
return 0;
// card here is always ok, and if I call snd_card_free(card) here it works.
}
static int __init adsp21479_spi_module_init(void)
{
struct spi_master *master=NULL;
int err;
pr_info("module init\n");
err = -ENODEV;
master = spi_busnum_to_master(LOW_SPEED_SPIDEV_SPI_BUS);
pr_info("master=%p\n", master);
if (!master)
{
pr_err("spi_busnum_to_master failed");
return err;
}
spi = spi_new_device(master, &cal_spi_board_info);
pr_info("spi device =%p\n", spi);
if (!spi)
{
pr_err("spi_new_device failed");
return err;
}
pr_info("spi device registered\n");
err = 0;
return snd_adsp21479_init(spi);
}
static void __exit adsp21479_spi_module_exit(void)
{
struct snd_adsp21479_chip *chip;
pr_info("module exit");
if (spi)
{
chip = container_of(&spi,struct snd_adsp21479_chip,spi);
if(chip)
{
pr_info("chip is not null\n");
if(chip->spi == spi)
{
pr_info("spi match\n");
}
pr_info("card = %p",chip->card);
if(chip->card != NULL)
{
pr_info("freeing sound card=%p\n",chip->card); // I never get here, it's always null, or if I store card in a global variable, I get an exception. And if I store it with drv_set_drvdata(&spi->dev,card) in the init function and try to obtain it here with a dev_get_drvdata(&spi->dev); and try to free the card with snd_card_free I get an exception as well.
snd_card_free(chip->card);
}
}
pr_info("unregistering spi device\n");
spi_unregister_device(spi);
}
}
module_init(adsp21479_spi_module_init);
module_exit(adsp21479_spi_module_exit);
/***************************SPI DRIVER METHODS ****************************/
MODULE_LICENSE("GPL"); ///WE NEED TO CHANGE IT LATER MAYBE
MODULE_DESCRIPTION(" Sound driver for the ADSP-21479");
So to reiterate: No PCM here ,I'm just trying to control the volume through SPI, and my control doesn't show up in amixer, and I can't free my card. I'm using the spidev as my SPI BUS master, and this driver is for the Sound card SPI slave.
I am writting a simple char driver. I create node in sysfs with device_create() and it is created properly. I get my node in /dev automatically as well. My problem is that class_distroy() and device_destroy() don't clean the /sys/devices/virtual/tdmcdev/tdm/ directory crated on init.
My init and close code below
...
/* Node in the /dev/ */
tdm->dev_major = 0; //for dynamic major
tdm_dev = MKDEV(tdm->dev_major, 0);
tdm->dev_major = MAJOR(tdm_dev);
err = alloc_chrdev_region(&tdm_dev, 0, 1, "tdm"); //One node to read/write data frame
if (err) {
printk("can't alloc minor for /dev/tdm\n");
return -ENODEV;
}
cdev_init(&(tdm->cdev), &tdm_dev_fops);
tdm->cdev.owner = THIS_MODULE;
err = cdev_add(&(tdm->cdev), tdm_dev, 1);
if (err) {
printk("cdev_add() failed for /dev/tdm\n");
unregister_chrdev_region(tdm_dev, 1);
return -ENODEV;
}
/* Node /sys/devices/virtual/tdmcdev/tdm/ */
tdm->dev_class = class_create(THIS_MODULE, "tdmcdev");
if (IS_ERR(device_create(tdm->dev_class, NULL, tdm_dev, NULL, "tdm"))) {
printk("device_create() failed for the tdm device\n");
class_destroy(tdm->dev_class);
cdev_del(&(tdm->cdev));
unregister_chrdev_region(tdm_dev, 1);
return -ENOMEM;
}
...
my close code
dev_t tdm_dev = MKDEV(tdm->dev_major, 0);
device_destroy(tdm->dev_class, tdm_dev);
class_destroy(tdm->dev_class);
cdev_del(&(tdm->cdev));
unregister_chrdev_region(tdm_dev, 1);
...
It is on Linux OpenWrt 3.10.49 on MIPS CPU.
Anyone seeing something not in order?
Thanks.
Dimitar
It appeared a stupid one.
I am using dynamic major so start from value 0.
I have to move tdm->dev_major = MAJOR(tdm_dev) after the alloc_chrdev_region() of course so get the proper major which I can clean afterwords
Thanks
Dimitar