LDD学习笔记系列之二

基础知识


主设备号和次设备号

#include 

dev_t /* 主设备号:12bit,次设备号:20bit */
MAJOR(dev_t dev);/* 获取主设备号 */
MINOR(dev_t dev);/* 获取次设备号 */
MKDEV(int major, int minor);/* 组合设备编号 */

#include 

int register_chrdev_region(dev_t first, unsigned int count, char *name);
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
void unregitster_chrdev_region(dev_t first, unsigned int count);

if (scull_major) {
    dev = MKDEV(scull_major, scull_minor);
    result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
    result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull");
    scull_major = MAJOR(dev);
}
if (result < 0) {
    printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
    return result;
}

重要数据结构

#include 

inode 
dev_t i_rdev;
struct cdev *i_cdev;
- 内核用inode结构在内部表示文件;
- 和file结构不同,file结构表示打开的文件描述符;
- 对单个文件,可能会有许多个表示打开的文件描述符的file结构,但他们都指向单个inode结构

unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);

fops → struct file_operations 

sturct module *owner; /* THIS_MODULE  */
int (*open)(struct inode *, struct file *);
- 检查设备特定错误;
- 初始化首次打开的设备;
- 如有必要,更新`f_op`指针;
- 分配并填写置于`filp->private_data`里的数据结构;

#include 
container_of(pointer, container_type, container_field);

int (*release)(struct inode *, struct file *);
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, char __user *, size_t, loff_t *);

#include 
#include 
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long count);

filp → struct file
mode_t f_mode;
lofft_t f_pos;
unsigned int f_flags;
struct file_operations *f_op;
void *private_data;
struct dentry *f_dentry;

字符设备的注册及移除

方式一

#include 
void cdev_init(struct cdev *cdev, struct file_operation *fops);
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
void cdev_del(struct cdev *dev);

方式二

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
int unregister_chrdev(unsigned int major, const char *name);

字符设备驱动编写要点


  1. 获取设备编号;
/*
 * Get a range of minor numbers to work with, asking for a dynamic
 * major unless directed otherwise at load time.
 */
if (scull_major) {
    dev = MKDEV(scull_major, scull_minor);
    result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
    result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
            "scull");
    scull_major = MAJOR(dev);
}
if (result < 0) {
    printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
    return result;
}
  1. 注册字符设备;
/*
 * Set up the char_dev structure for this device.
 */
static void scull_setup_cdev(struct scull_dev *dev, int index)
{
    int err, devno = MKDEV(scull_major, scull_minor + index);
    
    cdev_init(&dev->cdev, &scull_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &scull_fops;
    err = cdev_add (&dev->cdev, devno, 1);
    /* Fail gracefully if need be */
    if (err)
        printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}

example


#include 
#include 
#include 
#include     /* printk() */
#include         /* everything... */
#include     /* error codes */
#include     /* size_t */
#include 
#include     /* copy_*_user */

int example_major = 0;
int example_minor = 0;
static unsigned int example_nr_devs = 1;
typedef struct EXAMPLE_CDEV
{
     struct cdev cdev;            
}EXAMPLE_CDEV_STRU;
static EXAMPLE_CDEV_STRU example_cdev;

module_param(example_major, int, S_IRUGO);
module_param(example_minor, int, S_IRUGO);

MODULE_LICENSE("Dual BSD/GPL");

static int example_open(struct inode *inode, struct file *filp) 
{
    printk("example: open\n");
    return 0;
}

static int example_close(struct inode *inode, struct file *filp) 
{
    printk("example: close\n");
    return 0;
}

static ssize_t example_read(struct file *filp, char __user *buf, size_t size, loff_t *f_pos)
{
    size_t count;
    uint8_t byte;
    char data[] = "0123456789\r\n";
    
    printk("example: read (size=%zu)\n", size);
    for(count = 0; (count < size) && (*f_pos) < strlen(data); ++(*f_pos), ++count) {
        byte = data[*f_pos];
        if(copy_to_user(buf + count, &byte, 1) != 0) {
            break;
        }
        printk("example: read (buf[%zu]=%c)\n", count, (unsigned)byte);
    }

    return count;
}

static ssize_t example_write(struct file *filp, const char __user *buf, size_t size, loff_t *f_pos) 
{
    size_t count;
    uint8_t byte;

    printk("example: write (size=%zu)\n", size);
    for(count = 0; count < size; ++count) {
        if(copy_from_user(&byte, buf + count, 1) != 0) {
            break;
        }
        printk("example: write (buf[%zu]=%c)\n", count, (unsigned)byte);
    }

    return count;
}

static struct file_operations example_fops = {
    .open = example_open,
    .release = example_close,
    .read = example_read,
    .write = example_write,
};

static void example_setup_cdev(EXAMPLE_CDEV_STRU *dev, int index)
{
    int err, devno = MKDEV(example_major, example_minor + index);
    
    cdev_init(&dev->cdev, &example_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &example_fops;
    err = cdev_add (&dev->cdev, devno, 1);
    /* Fail gracefully if need be */
    if (err)
        printk(KERN_NOTICE "Error %d adding example-%d", err, index);
}


static int example_init(void) 
{
    dev_t dev;
    int result;

    printk("example: init\n");

    if (example_major) {
        dev = MKDEV(example_major, example_minor);
        result = register_chrdev_region(dev, example_nr_devs, "example");
    } else {
        result = alloc_chrdev_region(&dev, example_minor, example_nr_devs,
                "example");
        example_major = MAJOR(dev);
    }
    if (result < 0) {
        printk(KERN_WARNING "example: can't get major %d\n", example_major);
        return result;
    }
    example_setup_cdev(&example_cdev, 0);
    printk("example: %s driver(major %d) installed.\n", "example", example_major);

    return 0;
}

static void example_exit(void) 
{
    dev_t dev = MKDEV(example_major, example_minor);

    printk("example: exit\n");
    /* Delete the character device driver from system. */
    cdev_del(&example_cdev.cdev);
    /* Unregister the allocated character device. */
    unregister_chrdev_region(dev, example_nr_devs);
    printk("example: %s driver removed.\n", "example"); 
}

module_init(example_init);
module_exit(example_exit);

#!/bin/sh
# $Id: example_load,v 0.1 2018/11/15 $

module="example"
echo "-------begin--------"
insmod ./$module.ko
dmesg | tail -2
major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
#echo $major
sudo rm -f /dev/$module
sudo mknod /dev/$module c $major 0
echo -n "hello world" > /dev/$module
dmesg | tail -n 14
cat /dev/$module
dmesg | tail -n 17
echo "-------end--------"

其他

tail

tail [ -f ] [ -c Number | -n Number | -m Number | -b Number | -k Number ] [ File ]
参数解释:
-f 该参数用于监视File文件增长。
-c Number 从 Number 字节位置读取指定文件
-n Number 从 Number 行位置读取指定文件。
-m Number 从 Number 多字节字符位置读取指定文件,比方你的文件假设包括中文字,假设指定-c参数,可能导致截断,但使用-m则会避免该问题。
-b Number 从 Number 表示的512字节块位置读取指定文件。
-k Number 从 Number 表示的1KB块位置读取指定文件。
File 指定操作的目标文件名称
上述命令中,都涉及到number,假设不指定,默认显示10行。Number前面可使用正负号,表示该偏移从顶部还是从尾部開始计算。
tail可运行文件一般在/usr/bin/以下。

dmesg

3.png

echo

4.png

mknode

mknod [options] name {bc} major minor
mknod [options] name p

awk

awk 'Pattern {Action}' filename
-其中Pattern用来指定判断条件
-{}中包含的是awk的动作,也就是awk对记录的操作
major=$(awk "\$2==\"example\" {print \$1}" /proc/devices)

你可能感兴趣的:(LDD学习笔记系列之二)