I'm trying to create FSG-3 buttons driver (currently only for power button) and need some help. The code is shown below. The problem is that after calling "netlink_broadcast(uevent_sock, skb, 0, 1, GFP_ATOMIC);", system craches with console message:
Synchronizing SCSI cache for disk sda:
System halted.
Can anybody help me? I'm out of ideas:(.
/*
* arch/arm/mach-ixp4xx/fsg-power.c
*
* Copyright (C) 2007 Zintis Petersons
*
* FSG buttons driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* ----------------------------------
* 1.00 Oct 12, 2007 -- Initial version.
*
* 1.01 Oct 13, 2007 -- Code rewritten
* added "SEEN" and "SEQNUM" to hotplug event.
* ----------------------------------
*
*/
/*
* Version Information
*/
#define DRIVER_VERSION "v1.01"
#define DRIVER_NAME "fsg-power.c"
#define DRIVER_DESC "FSG buttons driver"
#define DRIVER_AUTHOR "Zintis Petersons <Zintis.Petersons@e-mail.lv>"
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/mach-types.h>
#include <linux/timer.h>
#include <asm/uaccess.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <linux/kobject.h>
#define BUFFER_SIZE 2048 /* buffer for the variables */
//signed long SyncJiffies;
extern struct sock *uevent_sock;
extern u64 uevent_next_seqnum(void);
struct event_t {
struct work_struct wq;
char *name, *action;
// unsigned long seen;
};
static void add_msg(struct sk_buff *skb, char *msg)
{
char *scratch;
scratch = skb_put(skb, strlen(msg) + 1);
strcpy(scratch, msg);
}
static void hotplug_button(struct work_struct *wq)
{
struct event_t *event;
char *scratch;
static char buf[128];
struct sk_buff *skb;
u64 seq;
event = container_of(wq, struct event_t, wq);
if (!uevent_sock) {
printk(KERN_ERR DRIVER_NAME ": unable to create netlink socket!\n");
return -ENODEV;
}
skb = alloc_skb(BUFFER_SIZE, GFP_ATOMIC);
if (!skb)
return;
scratch = skb_put(skb, strlen(event->action) + 2);
sprintf(scratch, "%s@", event->action);
add_msg(skb, "HOME=/");
add_msg(skb, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
add_msg(skb, "SUBSYSTEM=button");
snprintf(buf, 128, "ACTION=%s", event->action);
add_msg(skb, buf);
snprintf(buf, 128, "BUTTON=%s", event->name);
add_msg(skb, buf);
// snprintf(buf, 128, "SEEN=%ld", event->seen);
// add_msg(skb, buf);
/* we will send an event, request a new sequence number */
seq = uevent_next_seqnum();
snprintf(buf, 128, "SEQNUM=%llu", (unsigned long long)seq);
add_msg(skb, buf);
printk("%s %l\n", event->name, (unsigned long long)seq);
/* to mcast group 1<<0 */
NETLINK_CB(skb).dst_group = 1;
/*multicast the message to all listening processes*/
// Kernel memory allocation type.
// Typically, GFP_ATOMIC is used if from interrupt context;
// GFP_KERNEL if otherwise.
netlink_broadcast(uevent_sock, skb, 0, 1, GFP_ATOMIC);
kfree(event);
}
static irqreturn_t fsg_sync_button_handler(int irq, void *dev_id)
{
int pressed;
struct event_t *event;
//check button status
gpio_line_get(FSG_SB_GPIO, &pressed);
//create event
event = (struct event_t *)kzalloc (sizeof(struct event_t), GFP_ATOMIC);
if (!event)
return IRQ_NONE;
event->action = pressed ? "released" : "pressed";
event->name = "sync";
/*
if (pressed) {
//button released
//calculate time seen
event->seen = (jiffies - SyncJiffies)/HZ;
}
else {
// button pressed
//fix start time
event->seen = 0;
SyncJiffies = jiffies;
}
*/
INIT_WORK(&event->wq, (void *)(void *)hotplug_button);
schedule_work(&event->wq);
return IRQ_HANDLED;
}
static int __init fsg_buttons_init(void)
{
if (!(machine_is_fsg()))
return 0;
printk("%s %s - %s\n", DRIVER_NAME, DRIVER_VERSION, DRIVER_DESC);
/* Configure interrupt input for SYNC button */
set_irq_type(FSG_SB_IRQ, IRQT_BOTHEDGE);
if (request_irq(FSG_SB_IRQ, &fsg_sync_button_handler, IRQF_DISABLED, "SYNC", NULL) < 0) {
printk(KERN_DEBUG DRIVER_NAME ": SYNC button IRQ %d not available\n", FSG_SB_IRQ);
return -EIO;
}
else
printk(DRIVER_NAME ": SYNC button registered on IRQ%d\n", FSG_SB_IRQ);
return 0;
}
static void __exit fsg_buttons_exit(void)
{
if (!(machine_is_fsg()))
return;
free_irq(FSG_SB_IRQ, NULL);
}
module_init(fsg_buttons_init);
module_exit(fsg_buttons_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");