RPi3でLEDを点灯するデバイスドライバを書く

結論から言うと、Raspberry piで学ぶARMデバイスドライバプログラミングの本の方法は、
最新のカーネルに対応していなかった。

# insmod leddriver.ko

すると、request_mem_region()で落ちてしまう。
最新の方法はこっち
http://d.hatena.ne.jp/seinzumtode/20171020/1508476422

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/uaccess.h>

#define BLOCK_SIZE 4096
#define REG_BASE 0x3F200000
#define GPIO_BASE (REG_BASE+0x200000)
#define FSEL0_INDEX 0
#define FSEL2_INDEX 0x02
#define SET0_INDEX 0x07
#define CLR0_INDEX 0x0A
#define GPF_OUTPUT 0x01

#define PINNUM 27

MODULE_AUTHOR("Shohei Aoki");
MODULE_LICENSE("Dual BSD/GPL");

#define NUM_DEVS 1
#define NMAJOR 0
#define NMINOR 0
#define DEVNAME "awesomeled"

static int major = NMAJOR;
static int minor = NMINOR;

static struct cdev *cdev_array = NULL;
static struct class *led_class = NULL;

#define GPIO_MAPNAME "gpio_map"

static void __iomem *gpio_map;
static volatile uint32_t *gpio_base;

static int led_gpio_map(void)
{
  if(!request_mem_region(GPIO_BASE,BLOCK_SIZE,GPIO_MAPNAME)){
    printk(KERN_ALERT "request mem region failed\n");
    return -EBUSY;
  }
  gpio_map = ioremap_nocache(GPIO_BASE,BLOCK_SIZE);
  gpio_base = (volatile uint32_t *) gpio_map;
}


static int gpio_unmap(void)
{
  iounmap(gpio_map);
  release_mem_region(GPIO_BASE,BLOCK_SIZE);
  gpio_map = NULL;
  gpio_base = NULL;
  return 0;
}

static int function_set(int pin, uint32_t func)
{
  //int index = FSEL0_INDEX + pin/10;
  int index = FSEL2_INDEX;
  uint32_t mask = ~(0x7 << ((pin % 10) * 3));
  uint32_t masked = (gpio_base[index] & mask);
  uint32_t added = (func & 0x7) << ((pin % 10) * 3);
  uint32_t final = masked | added;
  gpio_base[index] = final;
  return 1;
}


static int setpin(int pin)
{
  gpio_base[SET0_INDEX] = 1 << pin;
}

static int clearpin(int pin)
{
  gpio_base[CLR0_INDEX] = 1 << pin;
}

static int led_open(struct inode *inode, struct file *filep)
{
  int retval;
  if(gpio_base != NULL){
    printk(KERN_ERR "led is already open.\n");
    return -EIO;
  }
  retval = led_gpio_map();
  if(retval!=0){
    printk(KERN_ERR "Cannot open led.\n");
    return retval;
  }

  return 0;
}

static int led_release(struct inode *inode, struct file *filep)
{
  gpio_unmap();
  return 0;
}

static ssize_t led_write(
    struct file *filep,
    const char __user *buf,
    size_t count,
    loff_t *f_pos)
{
  char cvalue;
  int retval;

  if(count>0){
    if(copy_from_user(&cvalue, buf, sizeof(char))){
      return -EFAULT;
    }
    int i;
    int num = cvalue - '0'; //hack of ctoi()
    for(i=0;i<num;i++){
      setpin(PINNUM);
      msleep(100);
      clearpin(PINNUM);
      msleep(100);
    }
    return sizeof(char);
  }
  return 0;
}

struct file_operations led_fops = {
  .open = led_open,
  .release = led_release,
  .write = led_write,
};

static int led_register_dev(void)
{
  int retval;
  dev_t dev;
  size_t size;
  int i;
  retval = alloc_chrdev_region(
    &dev,
    NMINOR,
    NUM_DEVS,
    DEVNAME
  );

  if(retval<0){
    printk(KERN_ERR "alloc chrdev region failed\n");
    return retval;
  }
  major = MAJOR(dev);

  led_class = class_create(THIS_MODULE, DEVNAME);
  if(IS_ERR(led_class))
    return PTR_ERR(led_class);

  size = sizeof(struct cdev) * NUM_DEVS;
  cdev_array = (struct cdev*) kmalloc(size, GFP_KERNEL);

  for (i=0;i<NUM_DEVS;i++){
    dev_t devno = MKDEV(major,minor+i);
    cdev_init(&(cdev_array[i]),&led_fops);
    cdev_array[i].owner = THIS_MODULE;
    if(cdev_add(&(cdev_array[i]),devno,1) < 0){
      printk(KERN_ERR "cdev add failed minor = %d\n",minor+i);
    }
    else {
      device_create(
        led_class,
        NULL,
        devno,
        NULL,
        DEVNAME"%u",minor+i
      );
    }
  }
  return 0;
}


static int led_init(void)
{
  int retval;
  int i;

  printk(KERN_INFO "%s loading...\n", DEVNAME);

  retval = led_gpio_map();
  if(retval!=0){
    printk(KERN_ALERT "Can not use GPIO registers\n");
    return -EBUSY;
  }

  function_set(PINNUM, GPF_OUTPUT);

  for(i=0;i<10;i++){

    msleep(50);
  }


  return 0;
}

static void led_exit(void)
{
  int i;
  dev_t devno;

  for(i=0;i<NUM_DEVS;i++){
    cdev_del(&(cdev_array[i]));
    devno = MKDEV(major,minor+i);
    device_destroy(led_class,devno);
  }
  devno = MKDEV(major,minor);
  unregister_chrdev_region(devno, NUM_DEVS);
  class_destroy(led_class);
  kfree(cdev_array);
}

module_init(led_init);
module_exit(led_exit);