Errata und Ergänzungen zum Buch
"Linux Hardware Hackz"

Prof. Jürgen Plate

Linux Treiberprogrammierung

Die Art und Weise, wie ein Treiber compiliert wird, hat sich geändert. Hierzu folgt demnächst ein kleiner Artikel. Damit Sie schon einmal sehen können, wie es nun geht, hat der Kollege Prof. Dr. Erik Kamsties von der Fachhochschule Lübeck dankenswerter Weise ein Beispiel erstellt und dabei auch gleich einen dicken Fehler von mir beseitigt. Es handelt sich um Speicher-Treiber. Das Makefile lautet nun:

ifeq ($(KERNELRELEASE),)

    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    # called from kernel build system: just declare what our modules are
    obj-m := mymem.o
endif
Der Treiber wird also nicht mehr separat erstellt, sondern durch den Make-Mechanismus des Kernels erstellt, was vieles einfacher macht. Der Treiber selbst hat folgenden Code:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

#define MYMAJOR 250

static struct cdev *my_cdev;
static char driver_buffer[1024];
static int buffer_ptr;

static int driver_open(struct inode *geraete_datei, struct file *instanz)
{
	printk(KERN_INFO "Open wurde aufgerufen\n");
  if ((instanz->f_flags & O_ACCMODE) == O_WRONLY)
    buffer_ptr = 0;
	return 0;
}

static int driver_close(struct inode *geraete_datei, struct file *instanz)
{
	printk(KERN_INFO "Close wurde aufgerufen\n");
	return 0;
}

static ssize_t driver_read(struct file *File, char *user_buffer, size_t count, loff_t *offs)
{
  int to_copy = count, i;

  printk(KERN_INFO "Read wurde aufgerufen: %d\n", to_copy);
  if (to_copy > buffer_ptr)
    to_copy = buffer_ptr;
  if (to_copy == 0)
    return(0);
  if ((i = copy_to_user(user_buffer, driver_buffer, to_copy)) != 0)
  {
    printk(KERN_INFO "Not_Copied_To_User = %d\n", i);
    return(-EFAULT);
  }
  buffer_ptr -= to_copy;
  return(to_copy);
}

static ssize_t driver_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs)
{
  int to_copy = count;

  printk(KERN_INFO "Write wurde aufgerufen: %d\n", count); 
  if (to_copy > sizeof(driver_buffer))
    to_copy = sizeof(driver_buffer);
  if (copy_from_user(driver_buffer, user_buffer, to_copy) != 0)
  {
    return(-EFAULT);
  }
  printk(KERN_INFO "Copy_From_User = %s", driver_buffer);
  buffer_ptr = to_copy;
  return(to_copy);
}

static struct file_operations my_fops =
{
   .owner   = THIS_MODULE,
   .open    = driver_open,
   .release = driver_close,
   .read    = driver_read,
   .write   = driver_write,
};

static int __init mymem_init(void)
{
   int err, devno = MKDEV(MYMAJOR, 0);
   my_cdev = cdev_alloc();

   my_cdev->ops = &my_fops;
   my_cdev->owner = THIS_MODULE;
   printk(KERN_INFO "MyMemTreiber wird eingebunden\n");

   err = cdev_add(my_cdev, devno, 1);
   if (!err)
   {
      buffer_ptr = 0;
      printk("Device wurde belegt\n");
      return 0;
   }
   printk(KERN_INFO "Dummy: unable to get major %d\n",MYMAJOR);
   return(-EIO);
   return 0;
}

static void __exit mymem_exit(void)
{
   printk(KERN_INFO "Der Kernel ist nochmal davon gekommen\n");
   cdev_del(my_cdev);
}

module_init(mymem_init);
module_exit(mymem_exit);

MODULE_AUTHOR("Dein Name");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Dies ist ein Speichertreiber");