MISCデバイスドライバの基本概念 MISC(Miscellaneous)は「雑多な」ことを意味し、特定の分類に当てはまらないハードウェアデバイスに対して使用されます。このドライバは通常、platformバスドライバのサブシステムとして実装されます。
MISCデバイスはすべて主デバイス番号10を使用し、異なるデバイスごとに独自のサブデバイス番号を割り当てます。cdev構造体の自動生成により、従来の手動登録プロセスが不要になります。
以下はmiscdevice構造体の定義です:
struct miscdevice {
int minor; /* サブデバイス番号 */
const char *name; /* デバイス名 */
const struct file_operations *fops; /* ファイル操作関数ポインタ */
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
ドライバ開発では、minor、name、fopsの3つのメンバ変数を初期化する必要があります。misc_register()関数でデバイスを登録すると、以下の処理が自動的に実行されます:
alloc_chrdev_region();
cdev_init();
cdev_add();
class_create();
device_create();
アンロード時にはmisc_deregister()関数を使用します:
cdev_del();
unregister_chrdev_region();
device_destroy();
class_destroy();
実装例:Beepデバイスドライバ
- デバイスツリーの設定
stm32mp15-pinctrl.dtsiにピンコンフィギュレーションを追加:
beep_pins_a: beep-pins {
pins {
pinmux = <STM32_PINMUX('C', 7, GPIO)>;
drive-push-pull;
bias-pull-up;
output-high;
slew-rate = <0>;
};
};
stm32mp157d-atk.dtsにデバイスノードを追加:
beep {
compatible = "alientek,beep";
status = "okay";
pinctrl-0 = <&beep_pins_a>;
pinctrl-names = "default";
beep-gpio = <&gpioc 7 GPIO_ACTIVE_LOW>;
};
- ドライバコード 以下はリファクタリング後の実装例です:
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#define DRIVER_NAME "gpio_beep"
#define MINOR_NUM 144
struct gpio_beep_data {
int gpio_pin;
};
static int gpio_beep_init(struct device_node *np)
{
int ret;
struct gpio_beep_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
data->gpio_pin = of_get_named_gpio(np, "beep-gpio", 0);
if (!gpio_is_valid(data->gpio_pin)) {
return -EINVAL;
}
ret = gpio_request(data->gpio_pin, "beep");
if (ret) return ret;
gpio_direction_output(data->gpio_pin, 1);
return 0;
}
static ssize_t gpio_beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
char val;
if (copy_from_user(&val, buf, 1)) return -EFAULT;
gpio_set_value(gpio_beep_data->gpio_pin, val ? 0 : 1);
return 1;
}
static const struct file_operations gpio_beep_fops = {
.owner = THIS_MODULE,
.write = gpio_beep_write,
};
static struct miscdevice gpio_beep_dev = {
.minor = MINOR_NUM,
.name = DRIVER_NAME,
.fops = &gpio_beep_fops,
};
static int gpio_beep_probe(struct platform_device *pdev)
{
int ret;
ret = gpio_beep_init(pdev->dev.of_node);
if (ret) return ret;
return misc_register(&gpio_beep_dev);
}
static int gpio_beep_remove(struct platform_device *pdev)
{
misc_deregister(&gpio_beep_dev);
gpio_free(gpio_beep_data->gpio_pin);
return 0;
}
static const struct of_device_id gpio_beep_of_match[] = {
{ .compatible = "alientek,beep" },
{}
};
static struct platform_driver gpio_beep_driver = {
.probe = gpio_beep_probe,
.remove = gpio_beep_remove,
.driver = {
.name = "gpio_beep_driver",
.of_match_table = gpio_beep_of_match,
},
};
module_platform_driver(gpio_beep_driver);
MODULE_LICENSE("GPL");
- テストアプリケーション
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd, val;
char cmd;
if (argc != 3) {
printf("Usage: %s <device> <0|1>\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("open");
return -1;
}
cmd = atoi(argv[2]);
write(fd, &cmd, 1);
close(fd);
return 0;
}
実行手順
- Makefile作成:
obj-m := gpio_beep.o
- コンパイル:
make -C /lib/modules/$(uname -r)/build M=$(PWD) modules
arm-linux-gnueabihf-gcc beep_test.c -o beep_test
- テスト:
insmod gpio_beep.ko
./beep_test /dev/gpio_beep 1
./beep_test /dev/gpio_beep 0
rmmod gpio_beep