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
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);
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.
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
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[] = {
¬ify.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.
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.