Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tools/labs/skels/device_drivers/extra/char-driver-lin/Kbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable

obj-m = modul.o
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
device="my_device"
major=42


rm -f /dev/${device}
mknod /dev/${device} c $major 0


124 changes: 124 additions & 0 deletions tools/labs/skels/device_drivers/extra/char-driver-lin/modul.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/ioctl.h>

#define MY_MAJOR 42
#define MY_MAX_MINORS 2
/* #define IOCTL_IN _IOC(_IOC_WRITE, 'k', 1, sizeof(my_ioctl_data)) */
#define MY_IOCTL_IN _IOC(_IOC_WRITE, 'k', 1, 0)

struct my_device_data {
struct cdev cdev;
/* my data starts here */
}devs[MY_MAX_MINORS];

MODULE_DESCRIPTION("My kernel module");
MODULE_AUTHOR("Me");
MODULE_LICENSE("GPL");

static int my_open(struct inode *inode, struct file *file) {
struct my_device_data *my_data =
container_of(inode->i_cdev, struct my_device_data, cdev);

printk( KERN_DEBUG "[my_open]\n" );
/* validate access to device */
file->private_data = my_data;
/* initialize device */

return 0;
}

static int my_close(struct inode *inode, struct file *file) {
printk( KERN_DEBUG "[my_close]\n" );
/* deinitialize device */
return 0;
}

static int my_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset) {
struct my_device_data *my_data =
(struct my_device_data*) file->private_data;
int sizeRead = 0;

printk( KERN_DEBUG "[my_read]\n" );
/* read data from device in my_data->buffer */
/* if(copy_to_user(user_buffer, my_data->buffer, my_data->size))
return -EFAULT; */

return sizeRead;
}

static int my_write(struct file *file, const char __user *user_buffer, size_t size, loff_t *offset) {
struct my_device_data *my_data =
(struct my_device_data*) file->private_data;
int sizeWritten = 0;

printk( KERN_DEBUG "[my_write]\n" );
/* copy_from_user */
/* write data to device from my_data->buffer */
sizeWritten = size; //only if sizeWritten == size !

return sizeWritten;
}

static long my_ioctl (struct file *file, unsigned int cmd, unsigned long arg) {
struct my_device_data *my_data =
(struct my_device_data*) file->private_data;
/* my_ioctl_data mid; */

printk( KERN_DEBUG "[my_ioctl]\n" );
switch(cmd) {
case MY_IOCTL_IN:
/* if( copy_from_user(&mid, (my_ioctl_data *) arg, sizeof(my_ioctl_data)) )
return -EFAULT; */

/* process data and execute command */
break;
default:
return -ENOTTY;
}
return 0;
}


struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = my_open,
.read = my_read,
.write = my_write,
.release = my_close,
.unlocked_ioctl = my_ioctl
};

int init_module(void) {
int i, err;

printk( KERN_DEBUG "[init_module]\n" );
err = register_chrdev_region(MKDEV(MY_MAJOR, 0), MY_MAX_MINORS,"my_device_driver");
if (err != 0) {
/* report error */
return err;
}

for(i = 0; i < MY_MAX_MINORS; i++) {
/* initialize devs[i] fields */
cdev_init(&devs[i].cdev, &my_fops);
cdev_add(&devs[i].cdev, MKDEV(MY_MAJOR, i), 1);
}

return 0;
}

void cleanup_module(void) {
int i;

printk( KERN_DEBUG "[cleanup_module]\n" );
for(i = 0; i < MY_MAX_MINORS; i++) {
/* release devs[i] fields */
cdev_del(&devs[i].cdev);
}
unregister_chrdev_region(MKDEV(MY_MAJOR, 0), MY_MAX_MINORS);
}

14 changes: 14 additions & 0 deletions tools/labs/skels/device_drivers/include/so2_cdev.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef __PSOCDEV_H__
#define __PSOCDEV_H__ 1

#include <asm/ioctl.h>

#define BUFFER_SIZE 256

#define MY_IOCTL_PRINT _IOC(_IOC_NONE, 'k', 1, 0)
#define MY_IOCTL_SET_BUFFER _IOC(_IOC_WRITE, 'k', 2, BUFFER_SIZE)
#define MY_IOCTL_GET_BUFFER _IOC(_IOC_READ, 'k', 3, BUFFER_SIZE)
#define MY_IOCTL_DOWN _IOC(_IOC_NONE, 'k', 4, 0)
#define MY_IOCTL_UP _IOC(_IOC_NONE, 'k', 5, 0)

#endif
3 changes: 3 additions & 0 deletions tools/labs/skels/device_drivers/kernel/Kbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable

obj-m = so2_cdev.o
228 changes: 228 additions & 0 deletions tools/labs/skels/device_drivers/kernel/so2_cdev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
* Character device drivers lab
*
* All tasks
*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/wait.h>

#include "../include/so2_cdev.h"

MODULE_DESCRIPTION("SO2 character device");
MODULE_AUTHOR("SO2");
MODULE_LICENSE("GPL");

#define LOG_LEVEL KERN_INFO

#define MY_MAJOR 42
#define MY_MINOR 0
#define NUM_MINORS 1
#define MODULE_NAME "so2_cdev"
#define MESSAGE "hello\n"
#define IOCTL_MESSAGE "Hello ioctl"

#ifndef BUFSIZ
#define BUFSIZ 4096
#endif


struct so2_device_data {
/* TODO 2: add cdev member */
struct cdev cdev;

/* TODO 4: add buffer with BUFSIZ elements */
char buffer[BUFSIZ];
size_t size;

/* TODO 7: extra members for home */
/* TODO 3: add atomic_t access variable to keep track if file is opened */
atomic_t access;
};

struct so2_device_data devs[NUM_MINORS];

static int so2_cdev_open(struct inode *inode, struct file *file)
{
struct so2_device_data *data;

/* TODO 2: print message when the device file is open. */
printk(LOG_LEVEL "open device\n");

/* TODO 3: inode->i_cdev contains our cdev struct, use container_of to obtain a pointer to so2_device_data */
data = container_of(inode->i_cdev, struct so2_device_data, cdev);

file->private_data = data;

/* TODO 3: return immediately if access is != 0, use atomic_cmpxchg */
if (atomic_cmpxchg(&data->access, 0, 1) != 0)
return -EBUSY;

set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10 * HZ);

return 0;
}

static int
so2_cdev_release(struct inode *inode, struct file *file)
{
/* TODO 2: print message when the device file is closed. */
printk(LOG_LEVEL "device close\n");

#ifndef EXTRA
struct so2_device_data *data =
(struct so2_device_data *) file->private_data;

/* TODO 3: reset access variable to 0, use atomic_set */
atomic_set(&data->access, 0);

#endif
return 0;
}

static ssize_t
so2_cdev_read(struct file *file,
char __user *user_buffer,
size_t size, loff_t *offset)
{
struct so2_device_data *data =
(struct so2_device_data *) file->private_data;
size_t to_read;

#ifdef EXTRA
/* TODO 7: extra tasks for home */
#endif

/* TODO 4: Copy data->buffer to user_buffer, use copy_to_user */
if (size > data->size - *offset) {
to_read = data->size - *offset;
} else {
to_read = size;
}
if (copy_to_user(user_buffer, data->buffer + *offset, to_read) != 0) {
return -EFAULT;
}
*offset += to_read;

return to_read;
}

static ssize_t
so2_cdev_write(struct file *file,
const char __user *user_buffer,
size_t size, loff_t *offset)
{
struct so2_device_data *data =
(struct so2_device_data *) file->private_data;


/* TODO 5: copy user_buffer to data->buffer, use copy_from_user */
if (*offset + size > BUFSIZ) {
size = BUFSIZ - *offset;
} else {
size = size;
}
if (copy_from_user(data->buffer + *offset, user_buffer, size) != 0)
return -EFAULT;
*offset += size;
data->size = *offset;

/* TODO 7: extra tasks for home */

return size;
}

static long
so2_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct so2_device_data *data =
(struct so2_device_data *) file->private_data;
int ret = 0;
int remains;

switch (cmd) {
/* TODO 6: if cmd = MY_IOCTL_PRINT, display IOCTL_MESSAGE */
case MY_IOCTL_PRINT:
printk(LOG_LEVEL "%s\n", IOCTL_MESSAGE);
break;

/* TODO 7: extra tasks, for home */
default:
ret = -EINVAL;
}

return ret;
}

static const struct file_operations so2_fops = {
.owner = THIS_MODULE,
/* TODO 2: add open and release functions */
.open = so2_cdev_open,
.release = so2_cdev_release,

/* TODO 4: add read function */
.read = so2_cdev_read,

/* TODO 5: add write function */
.write = so2_cdev_write,

/* TODO 6: add ioctl function */
.unlocked_ioctl = so2_cdev_ioctl
};

static int so2_cdev_init(void)
{
int err;
int i;

/* TODO 1: register char device region for MY_MAJOR and NUM_MINORS starting at MY_MINOR */
err = register_chrdev_region(MKDEV(MY_MAJOR, MY_MINOR), NUM_MINORS, MODULE_NAME);
if (err != 0) {
pr_err("registration of module %s failed", MODULE_NAME);
return err;
}


for (i = 0; i < NUM_MINORS; i++) {
#ifdef EXTRA
/* TODO 7: extra tasks, for home */
#else
/*TODO 4: initialize buffer with MESSAGE string */
memcpy(devs[i].buffer, MESSAGE, sizeof(MESSAGE));
devs[i].size = sizeof(MESSAGE);

#endif
/* TODO 7: extra tasks for home */
/* TODO 3: set access variable to 0, use atomic_set */
atomic_set(&devs[i].access, 0);

/* TODO 2: init and add cdev to kernel core */
cdev_init(&devs[i].cdev, &so2_fops);
cdev_add(&devs[i].cdev, MKDEV(MY_MAJOR, i), 1);
}

return 0;
}

static void so2_cdev_exit(void)
{
int i;

for (i = 0; i < NUM_MINORS; i++) {
/* TODO 2: delete cdev from kernel core */
cdev_del(&devs[i].cdev);
}

/* TODO 1: unregister char device region, for MY_MAJOR and NUM_MINORS starting at MY_MINOR */
unregister_chrdev_region(MKDEV(MY_MAJOR, MY_MINOR), NUM_MINORS);
}

module_init(so2_cdev_init);
module_exit(so2_cdev_exit);
7 changes: 7 additions & 0 deletions tools/labs/skels/device_drivers/user/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
all: so2_cdev_test.c
gcc -m32 -static -o so2_cdev_test so2_cdev_test.c

.PHONY: clean

clean:
-rm -f *~ *.o so2_cdev_test
Binary file not shown.
Loading