08 - Urządzenie znakowe

hello_dev.c

hello_dev.c
#include <linux/module.h>
#include <linux/kernel.h>
/* Character devices */
#include <linux/cdev.h>
#include <linux/fs.h>
/* Przekazywanie danych do/z przestrzeni użytkownika */
#include <linux/uaccess.h>
#include <asm/io.h>
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marcin Bis");
MODULE_DESCRIPTION("Przykladowe urzadzenie blokowe");
 
#ifdef CONFIG_HELLO_CDEV_IOMAP
/* Jeżeli została ustawiona odpowiednia flaga kompilacji, to
urzadzenie zapewnia dostep do pamieci w fizycznej przestrzeni adresowej
o rozmiarze DEV_SIZE, od offsetu DEV_PHYS_MEM */
#define DEV_PHYS_MEM 0x000a0000
#endif
/* W przeciwnym wypadku pamięć alokowana jest standardowymi metodami,
DEV_SIZE określa jej ilość. */
#define DEV_SIZE 0x20000
 
static void *my_buf;
static int my_buf_size = DEV_SIZE;
 
static dev_t my_dev = MKDEV(202,128);
 
static struct cdev my_cdev;
 
/* Funkcje implementujące operacje na pliku */
static ssize_t my_read_file(struct file *file, char __user *userbuf,
                                   size_t count, loff_t *ppos)
{
  int remaining_size, transfer_size;
 
  remaining_size = my_buf_size - (int) (*ppos);
  if (remaining_size == 0) return 0;
 
  transfer_size = min(remaining_size, (int) count);
 
  if (copy_to_user(userbuf, my_buf + *ppos, transfer_size)) {
    return -EFAULT;
  } else {
    *ppos += transfer_size;
    return transfer_size;
  }
}
 
static ssize_t my_write_file(struct file *file, const char __user *userbuf,
                                   size_t count, loff_t *ppos)
{
  int remaining_bytes;
 
  remaining_bytes = my_buf_size - (int) (*ppos);
 
  if (count > remaining_bytes) return -EIO;
 
  if (copy_from_user(my_buf + *ppos, userbuf, count)) {
    return -EFAULT;
  } else {
    *ppos += count;
    return count;
  }
}
 
/* Struktura opisująca operacje na pliku
   uwaga na przecinek po ostatniej funkcji */
static struct file_operations my_fops = {
  .read = my_read_file,
  .write = my_write_file,
};
 
int __init my_init(void)
{
  int res;
 
  printk(KERN_INFO "Character device test.\n");
 
#ifdef CONFIG_HELLO_CDEV_IOMAP
  my_buf = ioremap(DEV_PHYS_MEM, my_buf_size);
#else
  my_buf = kmalloc(my_buf_size, GFP_KERNEL | __GFP_ZERO);
#endif
  if (!my_buf) goto eexit;
 
  res = register_chrdev_region(my_dev, 1, "mydev");
  if (res) goto efree;
 
  cdev_init(&my_cdev, &my_fops);
  res = cdev_add(&my_cdev, my_dev, 1);
  if (res) goto edev;
 
  return 0;
 
  edev:
    unregister_chrdev_region(my_dev, 1);
  efree:
#ifdef CONFIG_HELLO_CDEV_IOMAP
    iounmap(my_buf);
#else
    kfree(my_buf);
#endif
  eexit:
    return -ENODEV;
}
module_init(my_init);
 
void __exit my_exit(void)
{
  cdev_del(&my_cdev);
  unregister_chrdev_region(my_dev, 1);
#ifdef CONFIG_HELLO_CDEV_IOMAP
    iounmap(my_buf);
#else
    kfree(my_buf);
#endif
  printk(KERN_INFO "Character device stop.\n");
}
module_exit(my_exit);

hello_dev_s1.c

hello_dev_s1.c
/* Dodatkowo implementowane jest blokowanie, plik urządzenia może być otwarty
   tylko przez jeden proces na raz.
 
   UWAGA! To jest antyprzykład - nie należy tego _nigdy_ stosować.
*/
#include <linux/module.h>
#include <linux/kernel.h>
/* Character devices */
#include <linux/cdev.h>
#include <linux/fs.h>
/* Przekazywanie danych do/z przestrzeni użytkownika */
#include <linux/uaccess.h>
#include <asm/io.h>
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marcin Bis");
MODULE_DESCRIPTION("Przykladowe urzadzenie blokowe");
 
#ifdef CONFIG_HELLO_CDEV_IOMAP
/* Jeżeli została ustawiona odpowiednia flaga kompilacji, to
urzadzenie zapewnia dostep do pamieci w fizycznej przestrzeni adresowej
o rozmiarze DEV_SIZE, od offsetu DEV_PHYS_MEM */
#define DEV_PHYS_MEM 0x000a0000
#endif
/* W przeciwnym wypadku pamięć alokowana jest standardowymi metodami,
DEV_SIZE określa jej ilość. */
#define DEV_SIZE 0x20000
 
static void *my_buf;
static int my_buf_size = DEV_SIZE;
 
static dev_t my_dev = MKDEV(202,128);
 
static struct cdev my_cdev;
 
/* Funkcje implementujące operacje na pliku */
static ssize_t my_read_file(struct file *file, char __user *userbuf,
                                   size_t count, loff_t *ppos)
{
  int remaining_size, transfer_size;
 
  remaining_size = my_buf_size - (int) (*ppos);
  if (remaining_size == 0) return 0;
 
  transfer_size = min(remaining_size, (int) count);
 
  if (copy_to_user(userbuf, my_buf + *ppos, transfer_size)) {
    return -EFAULT;
  } else {
    *ppos += transfer_size;
    return transfer_size;
  }
}
 
static ssize_t my_write_file(struct file *file, const char __user *userbuf,
                                   size_t count, loff_t *ppos)
{
  int remaining_bytes;
 
  remaining_bytes = my_buf_size - (int) (*ppos);
 
  if (count > remaining_bytes) return -EIO;
 
  if (copy_from_user(my_buf + *ppos, userbuf, count)) {
    return -EFAULT;
  } else {
    *ppos += count;
    return count;
  }
}
 
int count = 0;
int my_open_file(struct inode *inode, struct file *file)
{
  if (count > 0) return -EBUSY;
  /* W tym miejscu proces może zostać wywłaszczony (przez przerwanie), następnie
     planista uruchomi drugi proces, który również będzie chciał uzyskać dostęp do
     pliku urządzenia - i uda mu się ! */
  count++;
  return 0;
}
int my_close_file (struct inode *inode, struct file *file)
{
  count--;
  return 0;
}
 
/* Struktura opisująca operacje na pliku
   uwaga na przecinek po ostatniej funkcji */
static struct file_operations my_fops = {
  .read = my_read_file,
  .write = my_write_file,
  .open = my_open_file,
  .release = my_close_file,
};
 
int __init my_init(void)
{
  int res;
 
  printk(KERN_INFO "Character device test.\n");
 
#ifdef CONFIG_HELLO_CDEV_IOMAP
  my_buf = ioremap(DEV_PHYS_MEM, my_buf_size);
#else
  my_buf = kmalloc(my_buf_size, GFP_KERNEL | __GFP_ZERO);
#endif
  if (!my_buf) goto eexit;
 
  res = register_chrdev_region(my_dev, 1, "mydev");
  if (res) goto efree;
 
  cdev_init(&my_cdev, &my_fops);
  res = cdev_add(&my_cdev, my_dev, 1);
  if (res) goto edev;
 
  return 0;
 
  edev:
    unregister_chrdev_region(my_dev, 1);
  efree:
#ifdef CONFIG_HELLO_CDEV_IOMAP
    iounmap(my_buf);
#else
    kfree(my_buf);
#endif
  eexit:
    return -ENODEV;
}
module_init(my_init);
 
void __exit my_exit(void)
{
  cdev_del(&my_cdev);
  unregister_chrdev_region(my_dev, 1);
#ifdef CONFIG_HELLO_CDEV_IOMAP
    iounmap(my_buf);
#else
    kfree(my_buf);
#endif
  printk(KERN_INFO "Character device stop.\n");
}
module_exit(my_exit);

hello_dev_s2.c

hello_dev_s2.c
/* Dodatkowo implementowane jest blokowanie, plik urządzenia może być otwarty
   tylko przez jeden proces na raz. Do implementacji blokowania używamy semafora.
   Inne procesy chcące uzyskać dostęp, oczekują na zwolnienie zasobu.
 
   UWAGA! To jest poprawne rozwiązanie.
*/
#include <linux/module.h>
#include <linux/kernel.h>
/* Character devices */
#include <linux/cdev.h>
#include <linux/fs.h>
/* Przekazywanie danych do/z przestrzeni użytkownika */
#include <linux/uaccess.h>
#include <asm/io.h>
/* Blokady */
#include <linux/mutex.h>
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marcin Bis");
MODULE_DESCRIPTION("Przykladowe urzadzenie blokowe");
 
#ifdef CONFIG_HELLO_CDEV_IOMAP
/* Jeżeli została ustawiona odpowiednia flaga kompilacji, to
urzadzenie zapewnia dostep do pamieci w fizycznej przestrzeni adresowej
o rozmiarze DEV_SIZE, od offsetu DEV_PHYS_MEM */
#define DEV_PHYS_MEM 0x000a0000
#endif
/* W przeciwnym wypadku pamięć alokowana jest standardowymi metodami,
DEV_SIZE określa jej ilość. */
#define DEV_SIZE 0x20000
 
static void *my_buf;
static int my_buf_size = DEV_SIZE;
 
static dev_t my_dev = MKDEV(202,128);
 
static struct cdev my_cdev;
 
/* Funkcje implementujące operacje na pliku */
static ssize_t my_read_file(struct file *file, char __user *userbuf,
                                   size_t count, loff_t *ppos)
{
  int remaining_size, transfer_size;
 
  remaining_size = my_buf_size - (int) (*ppos);
  if (remaining_size == 0) return 0;
 
  transfer_size = min(remaining_size, (int) count);
 
  if (copy_to_user(userbuf, my_buf + *ppos, transfer_size)) {
    return -EFAULT;
  } else {
    *ppos += transfer_size;
    return transfer_size;
  }
}
 
static ssize_t my_write_file(struct file *file, const char __user *userbuf,
                                   size_t count, loff_t *ppos)
{
  int remaining_bytes;
 
  remaining_bytes = my_buf_size - (int) (*ppos);
 
  if (count > remaining_bytes) return -EIO;
 
  if (copy_from_user(my_buf + *ppos, userbuf, count)) {
    return -EFAULT;
  } else {
    *ppos += count;
    return count;
  }
}
 
DEFINE_MUTEX(blokada);
int my_open_file(struct inode *inode, struct file *file)
{
  mutex_lock(&blokada);
  return 0;
}
int my_close_file (struct inode *inode, struct file *file)
{
  mutex_unlock(&blokada);
  return 0;
}
 
/* Struktura opisująca operacje na pliku
   uwaga na przecinek po ostatniej funkcji */
static struct file_operations my_fops = {
  .read = my_read_file,
  .write = my_write_file,
  .open = my_open_file,
  .release = my_close_file,
};
 
int __init my_init(void)
{
  int res;
 
  printk(KERN_INFO "Character device test.\n");
 
#ifdef CONFIG_HELLO_CDEV_IOMAP
  my_buf = ioremap(DEV_PHYS_MEM, my_buf_size);
#else
  my_buf = kmalloc(my_buf_size, GFP_KERNEL | __GFP_ZERO);
#endif
  if (!my_buf) goto eexit;
 
  res = register_chrdev_region(my_dev, 1, "mydev");
  if (res) goto efree;
 
  cdev_init(&my_cdev, &my_fops);
  res = cdev_add(&my_cdev, my_dev, 1);
  if (res) goto edev;
 
  return 0;
 
  edev:
    unregister_chrdev_region(my_dev, 1);
  efree:
#ifdef CONFIG_HELLO_CDEV_IOMAP
    iounmap(my_buf);
#else
    kfree(my_buf);
#endif
  eexit:
    return -ENODEV;
}
module_init(my_init);
 
void __exit my_exit(void)
{
  cdev_del(&my_cdev);
  unregister_chrdev_region(my_dev, 1);
#ifdef CONFIG_HELLO_CDEV_IOMAP
    iounmap(my_buf);
#else
    kfree(my_buf);
#endif
  printk(KERN_INFO "Character device stop.\n");
}
module_exit(my_exit);

Makefile

Makefile
obj-m += hello_dev.o
obj-m += hello_dev_s1.o
obj-m += hello_dev_s2.o
 
all:
	make -C /lib/modules/$(shell uname -r)/build \
		SUBDIRS=$(shell pwd) modules
 
clean:
	make -C /lib/modules/$(shell uname -r)/build \
		SUBDIRS=$(shell pwd) clean
ostatnio zmienione: 2011/06/16 15:53