ledclass {
compatible = "arrow,classleds";
reg = <0x7e200000 0xb4>;
pinctrl-names = "default";
pinctrl-0 = <&led_pins>;
red {
label = "red";
};
green {
label = "green";
};
blue {
label = "blue";
};
};
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/leds.h>
#define GPIO_27 27
#define GPIO_22 22
#define GPIO_26 26
#define GPFSEL2_offset 0x08
#define GPSET0_offset 0x1C
#define GPCLR0_offset 0x28
#define GPIO_27_INDEX 1 << (GPIO_27 % 32)
#define GPIO_22_INDEX 1 << (GPIO_22 % 32)
#define GPIO_26_INDEX 1 << (GPIO_26 % 32)
#define GPIO_27_FUNC 1 << ((GPIO_27 % 10) * 3)
#define GPIO_22_FUNC 1 << ((GPIO_22 % 10) * 3)
#define GPIO_26_FUNC 1 << ((GPIO_26 % 10) * 3)
#define FSEL_27_MASK 0b111 << ((GPIO_27 % 10) * 3)
#define FSEL_22_MASK 0b111 << ((GPIO_22 % 10) * 3)
#define FSEL_26_MASK 0b111 << ((GPIO_26 % 10) * 3)
#define GPIO_SET_FUNCTION_LEDS (GPIO_27_FUNC | GPIO_22_FUNC | GPIO_26_FUNC)
#define GPIO_MASK_ALL_LEDS (FSEL_27_MASK | FSEL_22_MASK | FSEL_26_MASK)
#define GPIO_SET_ALL_LEDS (GPIO_27_INDEX | GPIO_22_INDEX | GPIO_26_INDEX)
struct led_dev
{
u32 led_mask;
void __iomem *base;
struct led_classdev cdev;
};
static void led_control(struct led_classdev *led_cdev, enum led_brightness b)
{
struct led_dev *led = container_of(led_cdev, struct led_dev, cdev);
/*echo 1 > /sys/class/leds/blue/brightness 할 경우 write 하는 값이 b로 들어온다.*/
pr_info("led_brightness : [%d]\n",b);
if (b != LED_OFF)
iowrite32(led->led_mask, led->base + GPSET0_offset); /* LED ON */
else
iowrite32(led->led_mask, led->base + GPCLR0_offset); /* LED OFF */
}
static int probe(struct platform_device *pdev)
{
int ret = 0;
int count;
struct resource *r;
u32 GPFSEL_read, GPFSEL_write;
void __iomem *g_ioremap_addr;
struct device_node *child;
struct device *dev = &pdev->dev;
pr_info("probe enter\n");
/*
devoce tree에서 reg값을 return
address 값을 직접 사용하지 못함, 그러므로 ioremap 함수에 정보를 전달할 목적으로 사용
*/
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
pr_err("platform_get_resource fail\n");
return -EINVAL;
}
pr_info("r->start = 0x%08lx\n", (long unsigned int)r->start);
pr_info("r->end = 0x%08lx\n", (long unsigned int)r->end);
pr_info("r->name = %s\n", (char *)r->name);
/* devm_ 접두사는 Device Managed의 약자이며 device 해제 시 자동으로 iounmap을 호출 */
g_ioremap_addr = devm_ioremap(dev, r->start, resource_size(r));
if (!g_ioremap_addr) {
pr_err("ioremap failed \n");
return -ENOMEM;
}
/* node의 child를 알아온다*/
count = of_get_child_count(dev->of_node);
if (!count)
return -EINVAL;
pr_info("nodes num : [%d]\n", count);
GPFSEL_read = ioread32(g_ioremap_addr + GPFSEL2_offset);
GPFSEL_write = (GPFSEL_read & ~GPIO_MASK_ALL_LEDS) |
(GPIO_SET_FUNCTION_LEDS & GPIO_MASK_ALL_LEDS);
iowrite32(GPFSEL_write, g_ioremap_addr + GPFSEL2_offset);
iowrite32(GPIO_SET_ALL_LEDS, g_ioremap_addr + GPCLR0_offset);
/*node의 child node를 순회한다
ledclass node 아래로 3개의 노드가 존재한다.
device tree
ledclass {
compatible = "arrow,classleds";
reg = <0x7e200000 0xb4>;
pinctrl-names = "default";
pinctrl-0 = <&led_pins>;
red {
label = "red";
};
green {
label = "green";
};
blue {
label = "blue";
};
};
*/
for_each_child_of_node(dev->of_node, child){
struct led_dev *led_device;
struct led_classdev *cdev;
led_device = devm_kzalloc(dev, sizeof(*led_device), GFP_KERNEL);
if (!led_device)
return -ENOMEM;
cdev = &led_device->cdev;
led_device->base = g_ioremap_addr;
of_property_read_string(child, "label", &cdev->name);
if (strcmp(cdev->name,"red") == 0) {
led_device->led_mask = GPIO_27_INDEX;
}
else if (strcmp(cdev->name,"green") == 0) {
led_device->led_mask = GPIO_22_INDEX;
}
else if (strcmp(cdev->name,"blue") == 0) {
led_device->led_mask = GPIO_26_INDEX;
}
else {
pr_info("Bad device tree value\n");
return -EINVAL;
}
/*led 디폴트 값 설정*/
led_device->cdev.brightness = LED_OFF;
led_device->cdev.brightness_set = led_control;
/* struct led_classdev 등록 */
ret = devm_led_classdev_register(dev, &led_device->cdev);
if (ret) {
dev_err(dev, "fail to register %s\n", cdev->name);
of_node_put(child);
return ret;
}
}
pr_info("probe exit\n");
return 0;
}
static int remove(struct platform_device *pdev)
{
pr_info("remove enter\n");
pr_info("remove exit\n");
return 0;
}
static const struct of_device_id my_of_ids[] = {
{ .compatible = "arrow,classleds"},
{},
};
MODULE_DEVICE_TABLE(of, my_of_ids);
static struct platform_driver led_platform_driver = {
.probe = probe,
.remove = remove,
.driver = {
.name = "classled_name",
.of_match_table = my_of_ids,
.owner = THIS_MODULE,
}
};
static int led_class_init(void)
{
int ret;
pr_info("led_class_init enter\n");
ret = platform_driver_register(&led_platform_driver);
if (ret !=0)
{
pr_err("platform fail returned %d\n", ret);
return ret;
}
pr_info("led_class_init exit\n");
return 0;
}
static void led_class_exit(void)
{
pr_info("led_class_exit enter\n");
platform_driver_unregister(&led_platform_driver);
pr_info("led_class_exit exit\n");
}
module_init(led_class_init);
module_exit(led_class_exit);
MODULE_LICENSE("GPL");
Linux 커널은 다양한 디바이스와 시스템 기능을 모델링하고 관리하기 위해 많은 구조체를 제공합니다. struct led_classdev와 같은 디바이스 모델링 구조체에는 다음과 같은 것들이 있습니다.
1. struct device: 이는 Linux 디바이스 모델의 핵심 구조체로, 모든 시스템 디바이스를 대표합니다.
2. struct platform_device: 이 구조체는 플랫폼 디바이스를 대표하며, 메모리 매핑된 I/O와 같은 시스템 전체에 걸친 리소스를 사용하는 디바이스에 대한 정보를 담고 있습니다.
3. struct usb_device: 이 구조체는 USB 디바이스를 대표하며, USB 디바이스에 대한 정보와 동작을 담고 있습니다.
4. struct input_dev: 이 구조체는 입력 디바이스 (예: 키보드, 마우스)를 대표하며, 입력 이벤트를 생성하고 처리하는데 필요한 정보와 메서드를 담고 있습니다.
5. struct net_device: 이 구조체는 네트워크 인터페이스를 대표하며, 데이터 전송 및 수신과 같은 네트워킹 동작을 담당합니다.
6. struct i2c_client: 이 구조체는 I2C 디바이스를 대표하며, I2C 통신을 위한 정보와 메서드를 담고 있습니다.
7. struct spi_device: 이 구조체는 SPI 디바이스를 대표하며, SPI 통신을 위한 정보와 메서드를 담고 있습니다.
[ 80.109539] led_class_init enter
[ 80.110116] probe enter
[ 80.110129] r->start = 0x3f200000
[ 80.110140] r->end = 0x3f2000b3
[ 80.110150] r->name = ledclass
[ 80.110185] nodes num : [3]
[ 80.110642] probe exit
[ 80.110959] led_class_init exit
'devm_led_classdev_register' 등록으로 아래 경로에 파일 생성
root@raspberrypi:/home/board# ls /sys/class/leds/
blue default-on green input0::capslock input0::numlock input0::scrolllock led0 led1 mmc0 red
misc가 아닌 'devm_led_classdev_register'로 등록되었으므로 /sys/class 경로에 파일 생성
led on, off 방법
echo 1 > /sys/class/leds/red/brightness
[ 169.027422] led_brightness : [1]
echo 0 > /sys/class/leds/red/brightness
[ 174.299456] led_brightness : [0]
root@raspberrypi:/# find ./ -name *classled_name*
./sys/bus/platform/drivers/classled_name
'device driver' 카테고리의 다른 글
| charter device driver - 8. interrupt platform driver 만들기 (0) | 2023.05.30 |
|---|---|
| charter device driver - 7. i2c platform driver 만들기 (0) | 2023.05.28 |
| charter device driver - 5. platform_driver로 led control 해보자 (0) | 2023.05.22 |
| charter device driver - 4. platform driver module 만들기 (0) | 2023.05.21 |
| charter device driver - 3. misc driver 만들기 (0) | 2023.05.21 |
댓글