Related
Environment
ubuntun 18.04
Linux ubuntu 5.4.0-122-generic #138~18.04.1-Ubuntu SMP Fri Jun 24 14:14:03 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
What I'm trying to do
I'm trying to write a kernel module that hooks sys_read, sys_write and sys_open, using ftrace.
The ftrace code is copied from some blogs, I'm sure ftrace usage is right.
But when i execute insmod my.ko, the vmware workstation shows up cpu disabled by guest operating system, I have no idea about it, the system crashed immediately so that i can't read the dmesg or do something else about debug.
Here is my code (maybe only fh_sys_read and GetPath make sense for this question):
#include <linux/module.h>
#include <linux/delay.h>
#include <asm/unistd.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/version.h>
//#include<linux/err.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/rtc.h>
#include <linux/string.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/unistd.h>
//#include<linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
//#include<linux/init.h>
#include <linux/dcache.h>
#include <linux/delay.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs_struct.h>
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/limits.h> //for PATH_MAX
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <stddef.h>
// #include <string.h>
MODULE_LICENSE("GPL");
#define F_LEN 256
static char* GetPath(int fd){
char *tmp;
char *pathname;
struct file *file;
struct path path;
struct files_struct *files = NULL;
files = current->files;
file = files->fdt->fd[fd];
if (!file) {
return NULL;
}
path = file->f_path;
path_get(&path);
tmp = (char *)__get_free_page(GFP_KERNEL);
if (!tmp) {
path_put(&path);
return NULL;
}
path_put(&path);
pathname = d_path(&path, tmp, PAGE_SIZE);
if (IS_ERR(pathname)) {
free_page((unsigned long)tmp);
return PTR_ERR(pathname);
}
printk("path:%s\n", pathname);
free_page((unsigned long)tmp);
return NULL;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
#include <linux/ftrace.h>
#ifndef CONFIG_X86_64
#error Currently only x86_64 architecture is supported
#endif // config_x86_64
#if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0))
#define PTREGS_SYSCALL_STUBS 1
#endif // config PTREGS
#ifdef PTREGS_SYSCALL_STUBS
#define SYSCALL_NAME(name) ("__x64_" name)
#else
#define SYSCALL_NAME(name) (name)
#endif // PTREGS_SYSCALL_STUBS
struct ftrace_hook {
const char *name;
void *function;
void *original;
unsigned long address;
struct ftrace_ops ops;
};
#define HOOK(_name, _function, _original) \
{ \
.name = SYSCALL_NAME(_name), .function = (_function), \
.original = (_original), \
}
static asmlinkage long (*real_sys_read)(unsigned int fd, char *buf, size_t count);
static asmlinkage long (*real_sys_write)(unsigned int fd, const char *buf, size_t count);
static asmlinkage long (*real_sys_open)(const char *filename, int flags, umode_t mode);
static asmlinkage long fh_sys_read(unsigned int fd, char __user *buf, size_t count) {
GetPath(fd);
return (*real_sys_read)(fd, buf, count);
}
static asmlinkage long fh_sys_write(unsigned int fd, const char __user *buf,
size_t count) {
return (*real_sys_write)(fd, buf, count);
}
static asmlinkage long fh_sys_open(const char __user *filename, int flags,
umode_t mode) {
return (*real_sys_open)(filename, flags, mode);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
static unsigned long lookup_name(const char *name) {
struct kprobe kp = {.symbol_name = name};
unsigned long retval;
if (register_kprobe(&kp) < 0) return 0;
retval = (unsigned long)kp.addr;
unregister_kprobe(&kp);
return retval;
}
#else
static unsigned long lookup_name(const char *name) {
return kallsyms_lookup_name(name);
}
#endif // lookup_name
static int fh_resolve_address(struct ftrace_hook *hook) {
pr_debug("start lookup_name");
hook->address = lookup_name(hook->name);
if (!hook->address) {
printk(KERN_ERR "lookup_name error");
printk(KERN_ERR "unresolved symbol: %s\n", hook->name);
return -ENOENT;
}
*((unsigned long *)hook->original) = hook->address;
return 0;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
#define ftrace_ops_fl_recursion ftrace_ops_fl_recursion_safe
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
#define ftrace_regs pt_regs
static __always_inline struct pt_regs *ftrace_get_regs(
struct ftrace_regs *fregs) {
return fregs;
}
#endif // versioncode 5.11
static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops,
struct ftrace_regs *regs) {
struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops);
/* Skip the function calls from the current module. */
if (!within_module(parent_ip, THIS_MODULE))
regs->ip = (unsigned long)hook->function;
}
int fh_install_hook(struct ftrace_hook *hook) {
int err = 0;
err = fh_resolve_address(hook);
if (err) {
printk(KERN_ERR "resolve_address error");
return err;
}
hook->ops.func = fh_ftrace_thunk;
hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY;
pr_info("start set ip");
err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0);
if (err) {
pr_debug("ftrace_set_filter_ip() failed: %d\n", err);
return err;
}
pr_debug("start register_ftrace_function");
err = register_ftrace_function(&hook->ops);
if (err) {
pr_debug("register_ftrace_function() failed: %d\n", err);
ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
return err;
}
return 0;
}
int fh_install_hooks(struct ftrace_hook *hooks, int count) {
int i = 0;
for (i = 0; i < count; i++) {
fh_install_hook(&hooks[i]);
}
return 0;
}
int fh_remove_hook(struct ftrace_hook *hook) {
int err;
err = unregister_ftrace_function(&hook->ops);
if (err) {
pr_debug("unregister_ftrace_function() failed: %d\n", err);
}
err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0);
if (err) {
pr_debug("ftrace_set_filter_ip() failed: %d\n", err);
}
return 0;
}
int fh_remove_hooks(struct ftrace_hook *hooks, int count) {
int i = 0;
for (i = 0; i < count; i++) {
fh_remove_hook(&hooks[i]);
}
return 0;
}
struct ftrace_hook hooks[] = {HOOK("sys_read", fh_sys_read, &real_sys_read),
HOOK("sys_open", fh_sys_open, &real_sys_open),
HOOK("sys_write", fh_sys_write, &real_sys_write)};
#endif // if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
static int __init kprobe_test_init(void) {
fh_install_hooks(hooks, 3);
return 0;
}
static void __exit kprobe_test_exit(void) {
fh_remove_hooks(hooks, 3);
}
module_init(kprobe_test_init);
module_exit(kprobe_test_exit);
My thoughts
I think the GetPath function caused the crash, and when I commment the line that calls GetPath, it works well, but it doesn't make sense.
I had tried to comment each line in GetPath, this GetPath wouldn't cause the crash, but it make no sense too.
static char* GetPath(int fd){
char *tmp;
char *pathname;
struct file *file;
struct path path;
struct files_struct *files = NULL;
files = current->files;
file = files->fdt->fd[fd];
if (!file) {
return NULL;
}
path = file->f_path;
// path_get(&path);
tmp = (char *)__get_free_page(GFP_KERNEL);
if (!tmp) {
path_put(&path);
return NULL;
}
return NULL;
// FIXME:
/*
path_put(&path);
pathname = d_path(&path, tmp, PAGE_SIZE);
if (IS_ERR(pathname)) {
free_page((unsigned long)tmp);
return PTR_ERR(pathname);
}
printk("path:%s\n", pathname);
free_page((unsigned long)tmp);
return NULL;
*/
}
Question
What is the problem, the GetPath function is just everal line and it looks all right.
How to debug kernel module that will cause system crashed? Like gdb, I can make some breakpoint and debug them step by step, but kernel module I don't know how.
Feel very sorry for my poor english.
Thanks for viewing.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a Linux Kernel Module that checks for the presence of a specific USB device and performs a printk upon a match. This code works fine and performs as i expect.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/list.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
static int HARD_VEND;
static int HARD_PROD;
static char *HARD_SERI;
struct USBCred
{
int vendor;
int product;
char serial[128];
};
static struct USBCred create_creds_device(struct usb_device *dev)
{
struct USBCred creds;
creds.vendor = dev->descriptor.idVendor;
creds.product = dev->descriptor.idProduct;
if((dev->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, dev->serial);
}
return creds;
}
static struct USBCred create_creds_hub(struct usb_bus *bus)
{
struct USBCred creds;
creds.vendor = bus->root_hub->descriptor.idVendor;
creds.product = bus->root_hub->descriptor.idProduct;
if((bus->root_hub->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, bus->root_hub->serial);
}
return creds;
}
static int check_usb_creds(struct USBCred usb_data)
{
if(usb_data.vendor != HARD_VEND && usb_data.product != HARD_PROD && strcmp(usb_data.serial, HARD_SERI))
{
return 1;
} else
{
printk(KERN_INFO "*********** RootPug Module - Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 0;
}
}
static int __init usb_fun_init (void)
{
int id;
int chix;
struct USBCred cred;
struct usb_bus *bus;
struct usb_device *dev, *childdev = NULL;
HARD_VEND = 0x26bd;
HARD_PROD = 0x9917;
HARD_SERI = "070172966462EB10";
mutex_lock(&usb_bus_idr_lock);
idr_for_each_entry(&usb_bus_idr, bus, id)
{
cred = create_creds_hub(bus);
//print_USBCred(cred);
check_usb_creds(cred);
dev = bus->root_hub;
usb_hub_for_each_child(dev, chix, childdev)
{
if(childdev)
{
usb_lock_device(childdev);
cred = create_creds_device(childdev);
//print_USBCred(cred);
check_usb_creds(cred);
usb_unlock_device(childdev);
}
}
}
mutex_unlock(&usb_bus_idr_lock);
return 0;
}
static void __exit usb_fun_exit (void)
{
printk(KERN_INFO "***************** RootPug Module - Exit *****************\n");
}
module_init(usb_fun_init);
module_exit(usb_fun_exit);
When moving over to an LSM, the LSM registers without an issue, and the hook registers correctly, yet the code is not behaving as I would like, and I can not understand why. I have added debugging statements to understand at which points the code is getting to, but it seems the idr_for_each_entry() is not being executed, as the debug statment ithin that loop is not being printed. I have used a flag based system to provide a mechanism for having -EPERM as the default return value, being overwritten if the usb is found.
I cannot understand why only the mutex is being performed, with no check to the hubs or usb devices, yet the same code works as an LKM. The actual code that is in question is within the appcl_inode_create hook. I have left out the other hooks for brevity, but they currently do nothing except return the expected value of 0.
#include <linux/init.h>
#include <linux/kd.h>
#include <linux/kernel.h>
#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/lsm_hooks.h>
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/capability.h>
#include <linux/unistd.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include <linux/magic.h>
#include <linux/ctype.h>
#include <linux/swap.h>
#include <linux/spinlock.h>
#include <linux/syscalls.h>
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/parser.h>
#include <linux/nfs_mount.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/posix-timers.h>
#include <linux/syslog.h>
#include <linux/user_namespace.h>
#include <linux/export.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/list.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/fsnotify.h>
#include <linux/path.h>
#include <linux/fdtable.h>
#include <linux/binfmts.h>
#include <linux/time.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include "include/appcl_lsm.h"
//#include "include/audit.h"
MODULE_LICENSE("GPLv3");
MODULE_AUTHOR("Jack Cuthbertson");
static int HARD_VEND;
static int HARD_PROD;
static char *HARD_SERI;
struct USBCred
{
int vendor;
int product;
char serial[128];
};
static struct USBCred create_creds_device(struct usb_device *dev)
{
struct USBCred creds;
creds.vendor = dev->descriptor.idVendor;
creds.product = dev->descriptor.idProduct;
if((dev->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, dev->serial);
}
return creds;
}
static struct USBCred create_creds_hub(struct usb_bus *bus)
{
struct USBCred creds;
creds.vendor = bus->root_hub->descriptor.idVendor;
creds.product = bus->root_hub->descriptor.idProduct;
if((bus->root_hub->serial) == NULL)
{
strcpy(creds.serial, "(null)");
} else {
strcpy(creds.serial, bus->root_hub->serial);
}
return creds;
}
static int check_usb_creds(struct USBCred usb_data)
{
if(usb_data.vendor != HARD_VEND && usb_data.product != HARD_PROD && strcmp(usb_data.serial, HARD_SERI))
{
printk(KERN_INFO "*********** RootPlug Module - Non Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 1;
} else
{
printk(KERN_INFO "*********** RootPlug Module - Match ***********");
printk(KERN_INFO "Vendor ID = %x, HC Vendor ID = %x", usb_data.vendor, HARD_VEND);
printk(KERN_INFO "Product ID = %x, HC Product ID = %x", usb_data.product, HARD_PROD);
printk(KERN_INFO "Serial = %s, HC Serial= %s", usb_data.serial, HARD_SERI);
return 0;
}
}
static int appcl_lsm_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int id;
int chix;
int flag = -EPERM;
struct USBCred cred;
struct usb_bus *bus;
struct usb_device *dev, *childdev = NULL;
HARD_VEND = 0x26bd;
HARD_PROD = 0x9917;
HARD_SERI = "070172966462EB10";
printk(KERN_ALERT "Rootplug: Beginning check\n");
mutex_lock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Mutex locked the bus list\n");
//loop though all usb buses
idr_for_each_entry(&usb_bus_idr, bus, id)
{
printk(KERN_ALERT "Rootplug: Checking hubs.\n");
cred = create_creds_hub(bus);
//Check creds of usb buses
if(check_usb_creds(cred) == 0)
{
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock hub success!\n");
flag = 0;
} else {
printk(KERN_ALERT "Rootplug: Unlock hub fail!\n");
}
dev = bus->root_hub;
usb_hub_for_each_child(dev, chix, childdev)
{
if(childdev)
{
printk(KERN_ALERT "Rootplug: Checking USB devices\n");
usb_lock_device(childdev);
cred = create_creds_device(childdev);
if(check_usb_creds(cred) == 0)
{
usb_unlock_device(childdev);
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock USB success!\n");
} else {
usb_unlock_device(childdev);
printk(KERN_ALERT "Rootplug: Unlock USB Failure!\n");
}
} else {
printk(KERN_ALERT "Rootplug: No child dev\n");
}
}
}
mutex_unlock(&usb_bus_idr_lock);
printk(KERN_ALERT "Rootplug: Unlock failed - Flag: %i\n", flag);
return flag;
}
static struct security_hook_list appcl_hooks[] = {
/*
* XATTR HOOKS
*/
LSM_HOOK_INIT(inode_setxattr, appcl_lsm_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, appcl_lsm_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, appcl_lsm_inode_getxattr),
LSM_HOOK_INIT(inode_removexattr, appcl_lsm_inode_removexattr),
LSM_HOOK_INIT(d_instantiate, appcl_lsm_d_instantiate),
LSM_HOOK_INIT(inode_setsecurity, appcl_lsm_inode_setsecurity),
LSM_HOOK_INIT(inode_init_security, appcl_lsm_inode_init_security),
/*
* INODE HOOKS
*/
LSM_HOOK_INIT(inode_alloc_security, appcl_lsm_inode_alloc_security),
LSM_HOOK_INIT(inode_free_security, appcl_lsm_inode_free_security),
/*
* General permission mask
*/
LSM_HOOK_INIT(inode_permission, appcl_lsm_inode_permission),
/*
* Specific permission hooks
*/
LSM_HOOK_INIT(inode_create, appcl_lsm_inode_create),
LSM_HOOK_INIT(inode_rename, appcl_lsm_inode_rename),
LSM_HOOK_INIT(inode_link, appcl_lsm_inode_link),
LSM_HOOK_INIT(inode_unlink, appcl_lsm_inode_unlink),
LSM_HOOK_INIT(inode_symlink, appcl_lsm_inode_symlink),
LSM_HOOK_INIT(inode_mkdir, appcl_lsm_inode_mkdir),
LSM_HOOK_INIT(inode_rmdir, appcl_lsm_inode_rmdir),
LSM_HOOK_INIT(inode_mknod, appcl_lsm_inode_mknod),
LSM_HOOK_INIT(inode_readlink, appcl_lsm_inode_readlink),
LSM_HOOK_INIT(inode_follow_link, appcl_lsm_inode_follow_link),
//LSM_HOOK_INIT(inode_setattr, appcl_lsm_inode_setattr),
//LSM_HOOK_INIT(inode_getattr, appcl_lsm_inode_getattr),
/*
* FILE HOOKS
*/
LSM_HOOK_INIT(file_alloc_security, appcl_lsm_file_alloc_security),
LSM_HOOK_INIT(file_free_security, appcl_lsm_file_free_security),
LSM_HOOK_INIT(file_permission, appcl_lsm_file_permission),
//LSM_HOOK_INIT(file_fcntl, appcl_lsm_file_fcntl),
LSM_HOOK_INIT(file_open, appcl_lsm_file_open),
LSM_HOOK_INIT(file_receive, appcl_lsm_file_receive),
/*
* CRED HOOKS
*/
LSM_HOOK_INIT(cred_alloc_blank, appcl_lsm_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, appcl_lsm_cred_free),
LSM_HOOK_INIT(cred_prepare, appcl_lsm_cred_prepare),
LSM_HOOK_INIT(cred_transfer, appcl_lsm_cred_transfer),
LSM_HOOK_INIT(bprm_set_creds, appcl_lsm_bprm_set_creds),
/*
* SUPERBLOCK HOOKS
*/
LSM_HOOK_INIT(sb_alloc_security, appcl_lsm_sb_alloc_security),
LSM_HOOK_INIT(sb_free_security, appcl_lsm_sb_free_security),
};
static __init int appcl_init(void)
{
printk(KERN_ALERT "Rootplug: Module loading... \n");
/*
* Set security attributes for initial task
*/
security_add_hooks(appcl_hooks, ARRAY_SIZE(appcl_hooks), "appcl");
printk(KERN_ALERT "Rootplug: module initialised\n");
return 0;
}
DEFINE_LSM(appcl) = {
.name = "appcl",
.init = appcl_init,
};
The function that handled the matching used a different alert level for the printk function. As such, the messages were not displayed to the console at that stage in the boot process.
What was:
printk(KERN_INFO "message");
Should be:
printk(KERN_ALERT "message");
The matching function was being called, after recompiling the kernel with the hooks forced to return 0, i could see from /var/log/* that the function was being executed and outputting the correct information.
I'v written a simple C shared object library which calls v4l2(Video for Linux two) API e.g. v4l2_open(). Then I'm trying to poll() on the returned device handle but it always return POLLERR in the revents. I tried different parameters with timeout but it does not help. Here is the complete code.
/*
* libwebcam.h
*
* Created on: 13.04.2016
* Author: max
*/
#ifndef LIBWEBCAM_H_
#define LIBWEBCAM_H_
#include <stdint.h>
struct webcam
{
int fd;
uint32_t width;
uint32_t height;
uint32_t sizeimage;
uint32_t bytesperline;
uint8_t *image_buffer;
void *priv_data;
};
int webcam_open(struct webcam *w);
int webcam_close(struct webcam *w);
int webcam_take_image(struct webcam *w);
int webcam_poll(struct webcam *w);
#endif /* LIBWEBCAM_H_ */
/*
* libwebcam.c
*
* Created on: 13.04.2016
* Author: max
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <libv4l2.h>
#include <poll.h>
#include "libwebcam.h"
int webcam_open(struct webcam *w)
{
struct v4l2_capability caps;
struct v4l2_format fmt;
int dev_index;
int dev;
char buffer[255];
for (dev_index = 0; dev_index < 64; dev_index++) {
memset(&buffer, 0, sizeof(buffer));
sprintf(buffer, "/dev/video%d", dev_index);
#ifdef DEBUG
printf("libwebcam: Probing %s\n", buffer);
#endif
dev = v4l2_open(buffer, O_RDWR | O_NONBLOCK, 0);
if (dev != -1) {
memset(&caps, 0, sizeof(caps));
if (v4l2_ioctl(dev, VIDIOC_QUERYCAP, &caps) == -1) {
return -1;
}
if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
#ifdef DEBUG
printf("libwebcam: %s is video capture device\n", buffer);
#endif
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) == -1) {
return -1;
}
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
if(v4l2_ioctl(dev, VIDIOC_S_FMT, &fmt) == -1) {
return -1;
}
if(v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) == -1) {
return -1;
}
if(w)
{
w->fd = dev;
w->bytesperline = fmt.fmt.pix.bytesperline;
w->height = fmt.fmt.pix.height;
w->sizeimage = fmt.fmt.pix.sizeimage;
w->width = fmt.fmt.pix.width;
w->image_buffer = calloc(fmt.fmt.pix.sizeimage, sizeof(uint8_t));
if(w->image_buffer == NULL)
return -1;
w->priv_data = calloc(1, sizeof(struct pollfd));
if(w->priv_data == NULL)
return -1;
return 0;
}
else
{
errno = EINVAL;
return -1;
}
}
}
}
errno = ENODEV;
return -1;
}
int webcam_close(struct webcam *w)
{
if(w)
{
if(w->image_buffer != NULL){
free(w->image_buffer);
w->image_buffer = NULL;
}
if(w->priv_data != NULL) {
free(w->priv_data);
w->priv_data = NULL;
}
if(w->fd != -1)
if(v4l2_close(w->fd) == -1)
return -1;
return 0;
}
errno = EINVAL;
return -1;
}
int webcam_take_image(struct webcam *w)
{
if(w)
{
return v4l2_read(w->fd, w->image_buffer, w->sizeimage * sizeof(uint8_t));
}
errno = EINVAL;
return -1;
}
int webcam_poll(struct webcam *w)
{
if(w)
{
((struct pollfd *)w->priv_data)->events = POLLIN;
((struct pollfd *)w->priv_data)->revents = 0;
((struct pollfd *)w->priv_data)->fd = w->fd;
if(poll(((struct pollfd*)w->priv_data), 1, -1) == -1)
{
return -1;
}
if(((struct pollfd*)w->priv_data)->revents & POLLIN) {
#ifdef DEBUG
printf("libwebcam: Data is available...\n");
#endif
return 1;
}
if(((struct pollfd*)w->priv_data)->revents & POLLERR) {
#ifdef DEBUG
printf("libwebcam: Error in poll...\n");
#endif
return -1;
}
#ifdef DEBUG
printf("libwebcam: Timeout...\n");
#endif
return 0;
}
#ifdef DEBUG
printf("libwebcam: Struct not valid...\n");
#endif
errno = EINVAL;
return -1;
}
/*
* test.c
*
* Created on: 14.04.2016
* Author: max
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libwebcam.h>
int main(int argc, char **argv)
{
struct webcam w;
int polled;
memset(&w, 0, sizeof(w));
if(webcam_open(&w) == -1)
{
perror("Unable to find webcam");
return EXIT_FAILURE;
}
while(1)
{
polled = webcam_poll(&w);
if(polled == -1)
{
perror("Error in poll");
return EXIT_FAILURE;
}
if(polled == 1)
{
webcam_take_image(&w);
}
}
if(webcam_close(&w) == -1)
{
perror("Unable to close webcam");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Can anyone tell me what is going on in the code?
I tested your code, and on my system the poll call is returning 0. From the man page, "A value of 0 indicates that the call timed out and no file descriptors were ready." So you should test for 0 in addition to -1 and 1.
I don't know why it fills in revents with POLLERR, but I would only check the resulting revents field if poll returned 1.
Also, not all V4L2 devices support read(), so you should check that your device supports it by testingcaps.capabilities & V4L2_CAP_READWRITE. My laptop webcam where I tested this does not support read/write.
See this example program for reference.
Im working on little kernel module. Im trying to use IOCTL (in ioctl_add), but I get ENOTTY when I call it, which is checked in switch, on the bottom of main. The code is below. Has anyone got any idea what am I doing wrong?
user.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
void cleanup()
{
if(f>=0) {
close(f);
}
}
int ioctl_add(int f)
{
int ret;
ret = ioctl(f, IOCTL_ADD);
printf("Add \n");
return ret;
}
int main(int argc, char * argv[])
{
int fd;
int *ptr;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("error");
}
posix_memalign((void **)&ptr, 4096, 4096);
* ptr = atoi(argv[2]);
write(fd, ptr, 4096);
ioctl_add(fd);
printf("data is %d\n", *ptr);
close(fd);
switch(errno){
case EBADF:
printf("errno: EBADF \n");
break;
case EFAULT:
printf("errno: EFAULT \n");
break;
case EINVAL:
printf("errno: EINVAL \n");
break;
case ENOTTY:
printf("errno: ENOTTY \n");
break;
default:
printf("errno: none \n");
return 0;
}
return 0;
}
module.c:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
//#include <linux/mm.h>
//#include <linux/config.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <linux/ioctl.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
#include <linux/mm.h>
#include <linux/pagemap.h>
#define DEVICE_NAME "acc_priv"
MODULE_LICENSE("GPL v2");
int ress, tmp;
struct page *page;
int *myaddr;
ssize_t acc_read(struct file *filp,
char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Opened\n\r");
return 0;
}
ssize_t acc_write(struct file *filp,
const char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Write\n\r");
printk(KERN_INFO "%s\n", __FUNCTION__);
down_read(¤t->mm->mmap_sem);
ress = get_user_pages(current, current->mm,(unsigned long)buf,1,1,1,&page,NULL);
if (ress) {
printk(KERN_INFO "Got mmaped.\n");
myaddr = kmap(page);
printk(KERN_INFO "%d\n", *myaddr);
tmp = *myaddr;
tmp = tmp * 2;
printk(KERN_INFO "the result of multiplying: %d\n", tmp);
* myaddr = tmp;
page_cache_release(page);
}
up_read(¤t->mm->mmap_sem);
return (0);
}
static int acc_open(struct inode *inode,
struct file *file)
{
printk(KERN_INFO "Opened inode:%p, file:%p\n", inode, file);
return 0;
}
long acc_ioctl(struct file *filp,
unsigned int cmd,unsigned long arg)
{
if(cmd == IOCTL_ADD)
printk(KERN_INFO "Do specified job \n");
return 0;
{
int acc_release(struct inode *inode,
struct file *file)
{
printk (KERN_INFO "device_release(%p,%p)\n", inode, file);
return 0;
}
struct file_operations Fops = {
.owner=THIS_MODULE,
.read=acc_read,
.write=acc_write,
.open=acc_open,
.unlocked_ioctl=acc_ioctl,
.release=acc_release,
};
dev_t my_dev=0;
struct cdev * my_cdev = NULL;
static struct class *class_acc_priv = NULL;
void clean_up(void)
{
if(my_dev && class_acc_priv) {
device_destroy(class_acc_priv,my_dev);
}
if(my_cdev) {
cdev_del(my_cdev);
my_cdev=NULL;
}
if(my_dev) {
unregister_chrdev_region(my_dev, 1);
}
if(class_acc_priv) {
class_destroy(class_acc_priv);
class_acc_priv=NULL;
}
}
int init_acc_priv(void)
{
int res=0;
res=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME);
if(res) {
printk (KERN_ALERT "Alocation of the device number for %s failed\n",
DEVICE_NAME);
return res;
};
class_acc_priv = class_create(THIS_MODULE, "acc_class");
if (IS_ERR(class_acc_priv)) {
printk(KERN_ERR "Error creating rs_class.\n");
res=PTR_ERR(class_acc_priv);
goto err1;
}
my_cdev = cdev_alloc( );
my_cdev->ops = &Fops;
my_cdev->owner = THIS_MODULE;
res=cdev_add(my_cdev, my_dev, 1);
if(res) {
printk (KERN_ALERT "Registration of the device number for %s failed\n",
DEVICE_NAME);
res=-EFAULT;
goto err1;
};
device_create(class_acc_priv,NULL,my_dev,NULL,"acc_priv%d",MINOR(my_dev));
printk (KERN_ALERT "%s The major device number is %d.\n",
"Registeration is a success.",
MAJOR(my_dev));
return res;
err1:
clean_up();
return res;
}
module_init(init_acc_priv);
void cleanup_acc_priv( void )
{
clean_up();
}
module_exit(cleanup_acc_priv);
When 32bit application is run on 64bit OS, it uses compat_ioctl syscall instead of unlocked_ioctl one for perform ioctl command. The reason of special syscall is that size of ioctl argument may differ for 64bit and 32bit applications.
So you need to implement .compat_ioctl file operation.
I am in the middle of writing a user level C program, that reads a SPI device and translate the results to a keyboard event.
I am now trying to pass emulate key event to /dev/uinput. A weird thing is happening, file operation is failing.
my_kbd.c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#define PROG_NAME "c2h2_spi_kbd"
int fire_key(__u16);
int setup_uinputfd(const char *);
int close_uinputfd();
void write_uinput(__u16, __u16, __s32);
/*fd for uinput, we do need kernel to support uinput */
static int uinputfd = -1;
int main(int argc, char **argv){
puts("Welcome to use SPI keyboard program v0.1!");
uinputfd = setup_uinputfd(PROG_NAME);
if(uinputfd != -1){
fire_key(KEY_A);
}else{
puts("where is uinput ? do you have permission?\n");
}
close_uinputfd();
exit(0);
}
int fire_key(__u16 key){
write_uinput(EV_KEY, key, 1);
return 0;
}
void write_uinput(__u16 type, __u16 code, __s32 value){
struct input_event ie;
sleep(1);
memset(&ie, 0, sizeof(ie));
ie.type = type;
ie.code = code;
ie.value = value;
if(write(uinputfd, &ie, sizeof(ie)) != sizeof(ie)) puts("ERR1");
memset(&ie, 0, sizeof(ie));
ie.type = EV_SYN;
ie.code = SYN_REPORT;
ie.value = 0;
if(write(uinputfd, &ie, sizeof(ie)) != sizeof(ie)) puts("ERR2");
}
int close_uinputfd(){
close(uinputfd);
return 0;
}
int setup_uinputfd(const char *name){
int fd;
int key;
struct uinput_user_dev dev;
fd = open("/dev/input/uinput", O_RDWR);
if (fd == -1) {
fd = open("/dev/uinput", O_RDWR);
if (fd == -1) {
fd = open("/dev/misc/uinput", O_RDWR);
if (fd == -1) {
fprintf(stderr, "could not open %s\n", "uinput");
perror(NULL);
return -1;
}
}
}
memset(&dev, 0, sizeof(dev));
strncpy(dev.name, name, sizeof(dev.name));
dev.name[sizeof(dev.name) - 1] = 0;
if (write(fd, &dev, sizeof(dev)) != sizeof(dev) ||
ioctl(fd, UI_SET_EVBIT, EV_KEY) != 0
|| ioctl(fd, UI_SET_EVBIT, EV_REP) != 0) {
goto setup_error;
}
for (key = KEY_RESERVED; key <= KEY_UNKNOWN; key++) {
if (ioctl(fd, UI_SET_KEYBIT, key) != 0) {
goto setup_error;
}
}
if (ioctl(fd, UI_DEV_CREATE) != 0) {
goto setup_error;
}
return fd;
setup_error:
fprintf(stderr, "could not setup %s\n", "uinput");
perror(NULL);
close(fd);
return -1;
}
gcc -Wall my_kbd.c && sudo ./a.out
without sleep(1); I never have uinput event happen. with sleep(1);, this works perfect. (emulate keys output to focused program)
I do suspect this could be a cache problem, but fsync(fd); did not help. Please help.