Linux serdev mfd driver is not probed - linux

I'm trying to write a MFD driver with accompanying GPIO driver using the new Serial Device Bus in Linux (using kernel 4.11rc6).
I am running under qemu for arm devices and I have modified arch/arm/boot/dts/versatile-ab.dts so that uart2 reads:
uart2: uart#101f3000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x101f3000 0x1000>;
interrupts = <14>;
clocks = <&xtal24mhz>, <&pclk>;
clock-names = "uartclk", "apb_pclk";
fcd16999 {
compatible = "ev,fcd16999";
fcd16999gpio: fcd16999-gpio {
compatible = "ev,fcd16999-gpio";
};
};
};
/proc/device-tree/amba/uart#101f3000/fcd16999/compatible reads ev,fcd16999 and the child node fcd16999-gpio/compatible is ev,fcd16999-gpio.
Still the init function of both devices gets called but not their probe functions. Am I missing something obvious here? The compatible flags matches and the device tree is loaded so it should just work, right?
Files below.
drivers/mfd/fcd16999.c
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/serdev.h>
#include <linux/slab.h>
/*#include <linux/mfd/tps6507x.h>*/
static const struct mfd_cell fcd16999_devs[] = {
{
.name = "fcd16999-gpio",
.of_compatible = "ev,fcd16999-gpio",
},
/*
{
.name = "fcd16999-adc",
},
{
.name = "fcd16999-thermometer",
},
*/
};
static int fcd16999_serdev_probe(struct serdev_device *serdev)
{
dev_warn(&serdev->dev, "fcd16999_serdev_probe\n");
return devm_mfd_add_devices(&serdev->dev, 1, fcd16999_devs,
ARRAY_SIZE(fcd16999_devs), NULL, 0, NULL);
}
void fcd16999_serdev_remove(struct serdev_device *serdev)
{
dev_warn(&serdev->dev, "fcd16999_serdev_remove\n");
}
static const struct of_device_id fcd16999_of_match[] = {
{.compatible = "ev,fcd16999", },
{},
};
MODULE_DEVICE_TABLE(of, fcd16999_of_match);
static struct serdev_device_driver fcd16999_driver = {
.driver = {
.name = "fcd16999",
.of_match_table = of_match_ptr(fcd16999_of_match),
},
.probe = fcd16999_serdev_probe,
.remove = fcd16999_serdev_remove,
};
static int __init fcd16999_serdev_init(void)
{
int ret = 101;
printk("Hello from fcd16999!\n");
ret = serdev_device_driver_register(&fcd16999_driver);
printk("serdev_device_driver_register returned %d\n", ret);
return ret;
}
/* init early so consumer devices can complete system boot */
subsys_initcall(fcd16999_serdev_init);
static void __exit fcd16999_serdev_exit(void)
{
printk("Goodbye from fcd16999!\n");
serdev_device_driver_unregister(&fcd16999_driver);
}
module_exit(fcd16999_serdev_exit);
drivers/gpio/gpio-fcd16999.c
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
/* #include <linux/mfd/stmpe.h> */
static int fcd16999_gpio_probe(struct platform_device *pdev)
{
printk("Hellow, fcd16999\n");
dev_warn(&pdev->dev, "fcd16999_gpio probing...\n");
return 0;
}
static struct platform_driver fcd16999_gpio_driver = {
.driver = {
.suppress_bind_attrs = true,
.name = "fcd16999-gpio",
},
.probe = fcd16999_gpio_probe,
};
static int __init fcd16999_gpio_init(void)
{
printk("Init Hellow, gpio-fcd16999\n");
return platform_driver_register(&fcd16999_gpio_driver);
}
subsys_initcall(fcd16999_gpio_init);
static void __exit fcd16999_gpio_exit(void)
{
printk("Goodbye from gpio-fcd16999!\n");
platform_driver_unregister(&fcd16999_gpio_driver);
}
module_exit(fcd16999_gpio_exit);

It turns out that I needed to enable the SERIAL_DEV_CTRL_TTYPORT option in the kernel to get it to probe.

Related

Why this kernel module does not handle open and read syscall?

I'm learning about Linux kernel module with Ubuntu 20.04 (Linux kernel 5.4.0-37 generic). The following is the code that actual code.
I have expected to following module to that pass single byte random number between 0-255 (get_random_bytes(&c, 1)) to user space buffer when it handle ->read() syscall then print a message to dmesg.
But Unfortunately, for now, it does not work. It seems like does not add handle of ->read() and ->open() syscalls.
Why it does not handle ->read() and ->open() syscall?
User space applicattion code (open and read device file) app.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main() {
char c[8];
memset(c, '\0', 8);
int fd = open("/dev/devsaikoro0", O_RDONLY);
read(fd, &c, 1);
//printf("Hello\n");
printf("%s\n", c);
}
Kernel module code:
#include <linux/init.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/random.h>
MODULE_LICENSE("Dual BSD/GPL");
static int devsaikoro_num = 3;
static int devsaikoro_major = 0;
static int devsaikoro_minor = 0;
static struct cdev devsaikoro_cdev;
static struct class *devsaikoro_class = NULL;
static dev_t saikoro_dev;
ssize_t read_num(struct file * filp, char __user *buf, size_t count, loff_t *f_pos)
{
int retval;
char c;
get_random_bytes(&c, 1);
if (copy_to_user(buf, &c, 1)) {
printk("devsaikoro read failed\n");
retval = -EFAULT;
return retval;
} else {
printk("devsaikoro read succeeded\n");
return 1;
}
}
struct file_operations fops = {
.read = read_num,
};
int saikoro_open(struct inode *inode, struct file *file) {
printk("devsaikoro open\n");
file->f_op = &fops;
return 0;
}
struct file_operations fops2 = {
.open = saikoro_open,
};
static int devsaikoro_init(void)
{
dev_t dev = MKDEV(devsaikoro_major, 0);
int alloc_ret = 0;
int major;
int cdev_err = 0;
struct device *class_dev = NULL;
alloc_ret = alloc_chrdev_region(&dev, 0, devsaikoro_num, "devsaikoro");
if (alloc_ret) {
goto error;
}
devsaikoro_major = major = MAJOR(dev);
cdev_init(&devsaikoro_cdev, &fops2);
devsaikoro_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&devsaikoro_cdev, MKDEV(devsaikoro_major, 0), devsaikoro_num);
if (cdev_err)
goto error;
devsaikoro_class = class_create(THIS_MODULE, "devsaikoro");
if (IS_ERR(devsaikoro_class))
goto error;
saikoro_dev = MKDEV(devsaikoro_major, devsaikoro_minor);
class_dev = device_create(devsaikoro_class, NULL, saikoro_dev, NULL, "devsaikoro%d", devsaikoro_minor);
printk(KERN_ALERT "devsaikoro_driver (major %d) installed\n", major);
return 0;
error:
if (cdev_err == 0) {
cdev_del(&devsaikoro_cdev);
}
if (alloc_ret == 0) {
unregister_chrdev_region(dev, devsaikoro_num);
}
return -1;
}
static void devsaikoro_exit(void)
{
dev_t dev = MKDEV(devsaikoro_major, 0);
device_destroy(devsaikoro_class, saikoro_dev);
class_destroy(devsaikoro_class);
cdev_del(&devsaikoro_cdev);
unregister_chrdev_region(dev, devsaikoro_num);
printk(KERN_ALERT "devsaikoro driver removed\n");
}
module_init(devsaikoro_init);
module_exit(devsaikoro_exit);

Sending structure through sockets in Qt

I am writing a client server application to transfer data in linux platform. I am developing a GUI application for client side in QT.I am just a beginner in QT and please help in transferring a structure from server side to client side.
The server side code written for non-GUI environment
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#pragma pack(1)
struct basestruct
{
int element1;
int element2;
};
#pragma pack(0)
struct basestruct newstruct;
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0,n=0;
struct sockaddr_in serv_addr;
char sendBuff[1025];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
memset(sendBuff, '0', sizeof(sendBuff));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
listen(listenfd, 10);
while(1)
{
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
newstruct.element1=1;
newstruct.element2=2;
if((n=send(connfd,(void *)&newstruct,sizeof(struct basestruct),0))<0)
perror("Write error");
printf("sent items :%d \n",n);
close(connfd);
sleep(1);
}}`
The client side code written in QT
#include "dialog.h"
#include "ui_dialog.h"
#include <QString>
#include <QDebug>
#include <QTextStream>
#include <QByteRef>
struct basestruct
{
int element1;
int element2;
};
basestruct newstruct;
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
ui->pushButton->setText("Connect");
ui->pushButton_2->setText("Ok");
ui->pushButton_3->setText("Close");
ui->pushButton_4->setText("Disconnect");
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::Read()
{
socket->waitForReadyRead(-1);
QByteArray byteArray;
byteArray=socket->readAll();
deserialize(byteArray);
qDebug()<<socket->readAll();
qDebug()<<"Read contents";
socket->flush();
}
void Dialog::on_pushButton_clicked()
{
socket=new QTcpSocket(this);
socket->connectToHost("127.0.0.1",5000);
qDebug()<<"Connected";
Read();
}
void Dialog::on_pushButton_4_clicked()
{
socket->close();
qDebug()<<"Disconnected";
}
void Dialog::deserialize(const QByteArray& byteArray)
{
QDataStream stream(byteArray);
stream.setVersion(QDataStream::Qt_4_0);
qDebug()<<"size received" <<byteArray.size();
stream >> newstruct.element1
>> newstruct.element2;
qDebug()<<"Element1"<<newstruct.element1<<"Element2"<<newstruct.element2;
}
When I receive the structure and print using qDebug() I am getting some garbage values. Kindly help me and point where I have gone wrong.Is there any easy alternative method to transfer structure in QT without serialising (similar to Non-GUI applications).
Thanks in advance
The Endianness problem can be overcome by specifying the Endianess as:
stream.setByteOrder(QDataStream::LittleEndian);
in the deserialise function after declaring Qdatastream.

Example code on using PACKET_QDISC_BYPASS option on ethernet raw socket on linux

According to http://man7.org/linux/man-pages/man7/packet.7.html, there is a new option
PACKET_QDISC_BYPASS since linux 3.14. This has the potential of sending and receiving packets a lot faster.
Saw a sample code: https://github.com/netoptimizer/network-testing/blob/master/src/raw_packet_send.c, but unfortunately it doesn't have the code on sending packets.
I added some code to send packets but it has a problem with sendto.
# ./raw_packet_send p6p1 64
Lame RAW/PF_PACKET socket TX test program
Enabled kernel qdisc bypass
error sendto : Invalid argument
Not sure why. Here is the code (original raw_packet_send.c with my silly code). Please let me know how could make it work or point me to some good simple sample code. Thanks.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_packet.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
//#include <stdio.h>
//#include <sys/types.h>
//#include <sys/stat.h>
//#include <sys/socket.h>
//#include <sys/mman.h>
//#include <linux/filter.h>
//#include <ctype.h>
//#include <fcntl.h>
//#include <unistd.h>
//#include <bits/wordsize.h>
//#include <net/ethernet.h>
//#include <netinet/ip.h>
//#include <arpa/inet.h>
//#include <stdint.h>
//#include <string.h>
//#include <assert.h>
//#include <net/if.h>
//#include <inttypes.h>
//#include <poll.h>
//#include <unistd.h>
#ifndef PACKET_QDISC_BYPASS
#define PACKET_QDISC_BYPASS 20
#endif
#include "common_socket.h"
char pkt[2000] = {0x00, 1,2,3,4,0, 0,1,2,3,4,1, 8, 0};;
int len = 96;
char intfName[100] = "em1";
/* Avail in kernel >= 3.14
* in commit d346a3fae3 (packet: introduce PACKET_QDISC_BYPASS socket option)
*/
void set_sock_qdisc_bypass(int fd, int verbose)
{
int ret, val = 1;
ret = setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS, &val, sizeof(val));
if (ret < 0) {
printf("[DEBUG] %s(): err:%d errno:%d\n", __func__, ret, errno);
if (errno == ENOPROTOOPT) {
if (verbose)
printf("No kernel support for PACKET_QDISC_BYPASS"
" (kernel < 3.14?)\n");
} else {
perror("Cannot set PACKET_QDISC_BYPASS");
}
} else
if (verbose) printf("Enabled kernel qdisc bypass\n");
}
int pf_tx_socket(int ver)
{
int ret, val = 1;
/* Don't use proto htons(ETH_P_ALL) as we only want to transmit */
int sock = socket(PF_PACKET, SOCK_RAW, 0);
//int sock = socket(AF_INET,SOCK_PACKET,htons(3));
if (sock == -1) {
perror("Creation of RAW PF_SOCKET failed!\n");
exit(1);
}
ret = Setsockopt(sock, SOL_PACKET, PACKET_VERSION, &ver, sizeof(ver));
return sock;
}
void mybind(int sock, char *intf) {
struct ifreq ifr;
int rc;
memset((char*)&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), intf);
if ((rc = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) < 0)
{
perror("Server-setsockopt() error for SO_BINDTODEVICE");
printf("%s\n", strerror(errno));
close(sock);
exit(-1);
}
}
int flood (int sock) {
struct sockaddr intfAddrs;
char cmd[100];
int tmp;
memset((char*)&intfAddrs, 0, sizeof (struct sockaddr));
intfAddrs.sa_family = PF_PACKET;
strcpy((char*)(intfAddrs.sa_data), intfName);
sprintf(cmd, "ifconfig %s promisc", intfName); system(cmd);
while (1) {
while (1) {
tmp = sendto(sock, pkt, len, 0, &intfAddrs, sizeof(intfAddrs));
if (tmp != len) {perror("error sendto "); exit(0); }
}
}
}
int main(int argc, char **argv)
{
if (argc > 1) { strcpy(intfName, argv[1]); }
if (argc > 2) { len = atoi(argv[2]); }
printf("Lame RAW/PF_PACKET socket TX test program\n");
int sock = pf_tx_socket(0);
set_sock_qdisc_bypass(sock, 1);
mybind(sock, intfName);
flood(sock);
return 0;
}
I think you need to change from PF_PACKET to AF_PACKET (although it looks like PF_PACKET is an alias to AF_PACKET) but I don't have any success when using PF_PACKET.
This code works for me:
// Create a raw socket
sock_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock_fd == -1) {
perror("Can't create AF_PACKET socket");
return EXIT_FAILURE;
}
// Check kernel version
static const int32_t sock_qdisc_bypass = 1;
int32_t sock_qdisc_ret = setsockopt(thd_opt->sock_fd, SOL_PACKET, PACKET_QDISC_BYPASS, &sock_qdisc_bypass, sizeof(sock_qdisc_bypass));
if (sock_qdisc_ret == -1) {
perror("Can't enable QDISC bypass on socket");
return EXIT_FAILURE;
}
This is from setup_socket_mmap() here

Using the Linux sysfs_notify call

I am trying to communicate asynchronously between a kernel driver and a user-space program (I know there are lots of questions here that ask for similar information, but I could find none that deal with sysfs_notify).
I am leaving Vilhelm's edit here, but adding the source to both a simple driver utilizing sysfs and a user-space program to poll it. The driver works fine (I got most of it from the net; it is missing the credits, but I couldn't find them when I went back to add them).
Unfortunately, the polling program does not work. It always returns success immediately. Interestingly, if I don't perform the two reads prior to the poll, the revents members are set to POLLERR | POLLIN instead of just POLLIN as seen in the program output.
Program output:
root#ubuntu:/home/wmulcahy/demo# ./readhello
triggered
Attribute file value: 74 (t) [0]
revents[0]: 00000001
revents[1]: 00000001
Here is the driver: hello.c (you can see where I started out...)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
struct my_attr {
struct attribute attr;
int value;
};
static struct my_attr notify = {
.attr.name="notify",
.attr.mode = 0644,
.value = 0,
};
static struct my_attr trigger = {
.attr.name="trigger",
.attr.mode = 0644,
.value = 0,
};
static struct attribute * myattr[] = {
&notify.attr,
&trigger.attr,
NULL
};
static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
printk( "hello: show called (%s)\n", a->attr.name );
return scnprintf(buf, PAGE_SIZE, "%s: %d\n", a->attr.name, a->value);
}
static struct kobject *mykobj;
static ssize_t store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
{
struct my_attr *a = container_of(attr, struct my_attr, attr);
sscanf(buf, "%d", &a->value);
notify.value = a->value;
printk("sysfs_notify store %s = %d\n", a->attr.name, a->value);
sysfs_notify(mykobj, NULL, "notify");
return sizeof(int);
}
static struct sysfs_ops myops = {
.show = show,
.store = store,
};
static struct kobj_type mytype = {
.sysfs_ops = &myops,
.default_attrs = myattr,
};
static struct kobject *mykobj;
static int __init hello_module_init(void)
{
int err = -1;
printk("Hello: init\n");
mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL);
if (mykobj) {
kobject_init(mykobj, &mytype);
if (kobject_add(mykobj, NULL, "%s", "hello")) {
err = -1;
printk("Hello: kobject_add() failed\n");
kobject_put(mykobj);
mykobj = NULL;
}
err = 0;
}
return err;
}
static void __exit hello_module_exit(void)
{
if (mykobj) {
kobject_put(mykobj);
kfree(mykobj);
}
printk("Hello: exit\n");
}
module_init(hello_module_init);
module_exit(hello_module_exit);
MODULE_LICENSE("GPL");
And here is the poll program: readhello.c
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <poll.h>
#define TEST_SYSFS_TRIGGER "/sys/hello/trigger"
#define TEST_SYSFS_NOTIFY "/sys/hello/notify"
int main(int argc, char **argv)
{
int cnt, notifyFd, triggerFd, rv;
char attrData[100];
struct pollfd ufds[2];
// Open a connection to the attribute file.
if ((notifyFd = open(TEST_SYSFS_NOTIFY, O_RDWR)) < 0)
{
perror("Unable to open notify");
exit(1);
}
// Open a connection to the attribute file.
if ((triggerFd = open(TEST_SYSFS_TRIGGER, O_RDWR)) < 0)
{
perror("Unable to open trigger");
exit(1);
}
ufds[0].fd = notifyFd;
ufds[0].events = POLLIN;
ufds[1].fd = triggerFd;
ufds[1].events = POLLIN;
// Someone suggested dummy reads before the poll() call
cnt = read( notifyFd, attrData, 100 );
cnt = read( triggerFd, attrData, 100 );
ufds[0].revents = 0;
ufds[1].revents = 0;
if (( rv = poll( ufds, 2, 10000)) < 0 )
{
perror("poll error");
}
else if (rv == 0)
{
printf("Timeout occurred!\n");
}
else if (ufds[0].revents & POLLIN)
{
printf("triggered\n");
cnt = read( notifyFd, attrData, 1 );
printf( "Attribute file value: %02X (%c) [%d]\n", attrData[0], attrData[0], cnt );
}
printf( "revents[0]: %08X\n", ufds[0].revents );
printf( "revents[1]: %08X\n", ufds[1].revents );
close( triggerFd );
close( notifyFd );
}
Some upcoming sysfs enhancements.
Internally, the patch adds a wait queue head to every kobject on the
system; that queue is inserted into a poll table in response to a
poll() call. The sysfs code has no way of knowing, however, when the
value of any given sysfs attribute has changed, so the subsystem
implementing a pollable attribute must make explicit calls to:
void sysfs_notify(struct kobject *kobj, char *dir, char *attr);
Thanks,
Lee
The blocking poll is from the user side. User code can simply tell the kernel which attributes it's interested in, then block in a poll() until one of them has changed.
The sysfs_notify() is a kernel-side call that releases the user-space poll(). After you adjust your kernel attribute value, just call sysfs_notify() to allow any user-space applications to respond to their outstanding poll().
Think of the poll() as "subscribing" to notices of a change in an attribute of interest, and sysfs_notify() as "publishing" the change to any subscribers.

"Device has no release() function" - what does this mean?

I am trying to write a simple linux kernel driver to turn a GPIO pin on while the module is loaded. Module loading works, but when I call rmmod to remove it I get this error:
sudo rmmod psctl
[13051.599199] ------------[ cut here ]------------
[13051.608758] WARNING: at drivers/base/core.c:196 device_release+0x78/0x84()
[13051.620581] Device 'psctl.0' does not have a release() function, it is broken and must be fixed.
[13051.637898] Modules linked in: psctl(O-) tmp102 hwmon nfsd rtc_ds1307 i2c_dev snd_bcm2835 snd_pcm snd_page_alloc snd_seq snd_seq_device snd_timer snd spidev leds_gpio led_class spi_bcm2708 i2c_bcm2708 [last unloaded: psctl]
[13051.670268] [<c0013f64>] (unwind_backtrace+0x0/0xf0) from [<c001e80c>] (warn_slowpath_common+0x4c/0x64)
[13051.688743] [<c001e80c>] (warn_slowpath_common+0x4c/0x64) from [<c001e8b8>] (warn_slowpath_fmt+0x30/0x40)
[13051.707217] [<c001e8b8>] (warn_slowpath_fmt+0x30/0x40) from [<c0239240>] (device_release+0x78/0x84)
[13051.725132] [<c0239240>] (device_release+0x78/0x84) from [<c01f4ad8>] (kobject_release+0x50/0x84)
[13051.742880] [<c01f4ad8>] (kobject_release+0x50/0x84) from [<bf0e6064>] (gpiotest_exit+0x10/0x2c [psctl])
[13051.761326] [<bf0e6064>] (gpiotest_exit+0x10/0x2c [psctl]) from [<c005e140>] (sys_delete_module+0x1b4/0x240)
[13051.780050] [<c005e140>] (sys_delete_module+0x1b4/0x240) from [<c000dae0>] (ret_fast_syscall+0x0/0x30)
[13051.798205] ---[ end trace 2eaa6df2dbf1f2e2 ]---
[13051.810083] psctl: Unloaded module
What does this mean? I have had a look at drivers/base/core.c and it seems that neither the kobject or the kobj_type have a release method associated with them (or there is no kobj_type set on the kobj). Am I supposed to set the release method myself, or is there something else that I haven't done that would set this for me?
The code for the module is below:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#define AUTHOR "Xian Stannard <xian#xianic.net>"
#define DESCRIPTION "Simple GPIO driver as proof of concept"
#define VERSION "0.1"
#define DRIVER_NAME "psctl"
#define OUT_GPIO 18
#define MPRINT(level, fmt, args...) printk(level DRIVER_NAME ": " fmt "\n", ## args)
struct ps_data_struct {
int out_gpio;
};
static int ps_probe(struct platform_device *pdev) {
struct ps_data_struct *data = pdev->dev.platform_data;
if(data == NULL)
MPRINT(KERN_DEBUG, "No platform data");
else {
MPRINT(KERN_DEBUG, "Has platform data: gpio %d", data->out_gpio);
}
return -ENODEV;
}
static int __exit ps_remove(struct platform_device *dev) {
MPRINT(KERN_DEBUG, "Remove");
return 0;
}
static void ps_release(struct device* dev) {
MPRINT(KERN_DEBUG, "Release");
}
static struct ps_data_struct ps_data = {
.out_gpio = OUT_GPIO
};
static struct platform_driver ps_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE
},
.probe = ps_probe,
.remove = __exit_p(ps_remove)
};
static struct platform_device ps_device = {
.name = DRIVER_NAME,
.id = 0,
.dev = {
.platform_data = &ps_data
}
};
static int __init gpiotest_init(void) {
int retval = 0;
retval = platform_driver_register(&ps_driver);
if(retval != 0) {
MPRINT(KERN_ERR, "Could not register driver");
goto drvreg;
}
retval = platform_device_register(&ps_device);
if(retval != 0) {
MPRINT(KERN_ERR, "Could not create device");
goto devreg;
}
MPRINT(KERN_INFO, "Loaded module");
return 0;
devreg:
platform_driver_unregister(&ps_driver);
drvreg:
MPRINT(KERN_ERR, "Module load failed with error code %d", retval);
return retval;
}
static void __exit gpiotest_exit(void) {
platform_device_unregister(&ps_device);
platform_driver_unregister(&ps_driver);
MPRINT(KERN_INFO, "Unloaded module");
}
module_init(gpiotest_init);
module_exit(gpiotest_exit);
MODULE_AUTHOR(AUTHOR);
MODULE_DESCRIPTION(DESCRIPTION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
I think using platform_device_register() to register a device, the pdev->dev->release is not initialized, so when you try to remove this device, the OOPS message print out.
There are two solutions to solve this problem:
Using platform_device_alloc() and platform_device_add() to register your device;
You init the ps_device.dev.release yourself, if you insist on using platform_device_register().
When you call platform_device_alloc() to get an instance of device, the release method initialization is done to the default function, as can be see here.
struct platform_device *platform_device_alloc(const char *name, int id)
{
...
...
pa->pdev.dev.release = platform_device_release;
...
...
}
This is the default implementation of platform_device_release .
However, when you avoid platform_device_alloc, you obviously miss this initialization and hence need to provide your own implementation of release method for device.

Resources