linux kernel Module programming #2 Device driver (리눅스 커널 모듈 프로그래밍 #2 디바이스 드라이버)



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

#define DEVICE_NAME "Minibuf"
#define BUFFER_LEN 1024
#define DEV_MAJOR 254
#define DEV_MINOR 5

static int s_bDeviceOpen = 0;
static char s_strBuf[BUFFER_LEN];
static int s_nBufPos = 0, s_nBufEnd = 0;

static int device_open(struct inode *inode, struct file *filp);
static int device_release(struct inode *inode, struct file *filp);
static ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset);
static ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset);

static int is_buffer_empty(void);
static int is_buffer_full(void);
static int read_buffer_char(char *buffer);
static int write_buffer_char(char *buffer);

struct file_operations device_fops = {
 read: device_read,
 write: device_write,
 open: device_open,
 release: device_release
};

static struct cdev minibuf_cdev = {
 .owner = THIS_MODULE,
 .ops = &device_fops,
};

dev_t dev_num = -1;
struct cdev *dev_ptr = NULL;

int __init init_minibuf(void)
{
 printk("Loading MiniBuffer Module\n");

 dev_num = MKDEV(DEV_MAJOR, DEV_MINOR);
 register_chrdev_region(dev_num, 1, DEVICE_NAME);
 dev_ptr = cdev_alloc();
 cdev_init(dev_ptr, &device_fops);
 cdev_add(dev_ptr, dev_num, 1);

 strcpy(s_strBuf, "Hello, World\n");
 s_nBufEnd = strlen(s_strBuf)+1;
 return 0;
}

void __exit exit_minibuf(void)
{
 printk("Unloading Minibuffer Module\n");

 unregister_chrdev_region(dev_num, 1);
}

int device_open(struct inode *inode, struct file *filp)
{
 printk(DEVICE_NAME ": Device open (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
 if(s_bDeviceOpen)
 {
  printk(DEVICE_NAME ": Device already open\n");
  return -EBUSY;
 }

 ++s_bDeviceOpen;
 return 0;
}

int device_release(struct inode *inode, struct file *filp)
{
 printk(DEVICE_NAME ": Device release (%d, %d)\n", MAJOR(inode->i_rdev),MINOR(inode->i_rdev));

 if(!s_bDeviceOpen)
 {
  printk(DEVICE_NAME ": Device has not opened\n");
  return -EINVAL;
 }

 --s_bDeviceOpen;
 return 0;
}

ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
 int count = 0;
 if(is_buffer_empty())
 {
  printk(DEVICE_NAME ": Read return EOF\n");
  return 0;
 }

 while(!is_buffer_empty() && length > 1)
 {
  read_buffer_char(buffer);
  ++buffer;
  --length;
  ++count;
 }

 put_user(0, buffer);
 ++count;

 printk(DEVICE_NAME ": Read %d bytes\n", count);
 return count;
}

ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset)
{
 return -ENOSYS;
}

int is_buffer_empty(void)
{
 return (s_nBufPos == s_nBufEnd) ? 1 : 0;
}

int is_buffer_full(void)
{
 int pos = s_nBufEnd + 1;
 if(pos == BUFFER_LEN)
  pos = 0;
 return (pos == s_nBufPos) ? 1 : 0;
}

int read_buffer_char(char *buffer)
{
 if(is_buffer_empty())
  return -1;

 put_user(s_strBuf[s_nBufPos], buffer);

 if(++s_nBufPos == BUFFER_LEN)
  s_nBufPos = 0;

 return 0;
}

int write_buffer_char(char *buffer)
{
 if(is_buffer_full())
  return -1;

 get_user(s_strBuf[s_nBufEnd], buffer);
 if(++s_nBufEnd == BUFFER_LEN)
  s_nBufEnd = 0;

 return 0;
}

module_init(init_minibuf);
module_exit(exit_minibuf);
MODULE_LICENSE("GPL");