1
0

led.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * led.c - Using GPIO to control the LED on/off
  3. */
  4. #include <linux/cdev.h>
  5. #include <linux/delay.h>
  6. #include <linux/device.h>
  7. #include <linux/fs.h>
  8. #include <linux/gpio.h>
  9. #include <linux/init.h>
  10. #include <linux/module.h>
  11. #include <linux/printk.h>
  12. #include <linux/types.h>
  13. #include <linux/uaccess.h>
  14. #include <linux/version.h>
  15. #include <asm/errno.h>
  16. #define DEVICE_NAME "gpio_led"
  17. #define DEVICE_CNT 1
  18. #define BUF_LEN 2
  19. static char control_signal[BUF_LEN];
  20. static unsigned long device_buffer_size = 0;
  21. struct LED_dev {
  22. dev_t dev_num;
  23. int major_num, minor_num;
  24. struct cdev cdev;
  25. struct class *cls;
  26. struct device *dev;
  27. };
  28. static struct LED_dev led_device;
  29. /* Define GPIOs for LEDs.
  30. * TODO: According to the requirements, search /sys/kernel/debug/gpio to
  31. * find the corresponding GPIO location.
  32. */
  33. static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };
  34. /* This is called whenever a process attempts to open the device file */
  35. static int device_open(struct inode *inode, struct file *file)
  36. {
  37. return 0;
  38. }
  39. static int device_release(struct inode *inode, struct file *file)
  40. {
  41. return 0;
  42. }
  43. static ssize_t device_write(struct file *file, const char __user *buffer,
  44. size_t length, loff_t *offset)
  45. {
  46. device_buffer_size = min(BUF_LEN, length);
  47. if (copy_from_user(control_signal, buffer, device_buffer_size)) {
  48. return -EFAULT;
  49. }
  50. /* Determine the received signal to decide the LED on/off state. */
  51. switch (control_signal[0]) {
  52. case '0':
  53. gpio_set_value(leds[0].gpio, 0);
  54. pr_info("LED OFF");
  55. break;
  56. case '1':
  57. gpio_set_value(leds[0].gpio, 1);
  58. pr_info("LED ON");
  59. break;
  60. default:
  61. pr_warn("Invalid value!\n");
  62. break;
  63. }
  64. *offset += device_buffer_size;
  65. /* Again, return the number of input characters used. */
  66. return device_buffer_size;
  67. }
  68. static struct file_operations fops = {
  69. .owner = THIS_MODULE,
  70. .write = device_write,
  71. .open = device_open,
  72. .release = device_release,
  73. };
  74. /* Initialize the module - Register the character device */
  75. static int __init led_init(void)
  76. {
  77. int ret = 0;
  78. /* Determine whether dynamic allocation of the device number is needed. */
  79. if (led_device.major_num) {
  80. led_device.dev_num = MKDEV(led_device.major_num, led_device.minor_num);
  81. ret =
  82. register_chrdev_region(led_device.dev_num, DEVICE_CNT, DEVICE_NAME);
  83. } else {
  84. ret = alloc_chrdev_region(&led_device.dev_num, 0, DEVICE_CNT,
  85. DEVICE_NAME);
  86. }
  87. /* Negative values signify an error */
  88. if (ret < 0) {
  89. pr_alert("Failed to register character device, error: %d\n", ret);
  90. return ret;
  91. }
  92. pr_info("Major = %d, Minor = %d\n", MAJOR(led_device.dev_num),
  93. MINOR(led_device.dev_num));
  94. /* Prevents module unloading while operations are in use */
  95. led_device.cdev.owner = THIS_MODULE;
  96. cdev_init(&led_device.cdev, &fops);
  97. ret = cdev_add(&led_device.cdev, led_device.dev_num, 1);
  98. if (ret) {
  99. pr_err("Failed to add the device to the system\n");
  100. goto fail1;
  101. }
  102. #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
  103. led_device.cls = class_create(DEVICE_NAME);
  104. #else
  105. led_device.cls = class_create(THIS_MODULE, DEVICE_NAME);
  106. #endif
  107. if (IS_ERR(led_device.cls)) {
  108. pr_err("Failed to create class for device\n");
  109. ret = PTR_ERR(led_device.cls);
  110. goto fail2;
  111. }
  112. led_device.dev = device_create(led_device.cls, NULL, led_device.dev_num,
  113. NULL, DEVICE_NAME);
  114. if (IS_ERR(led_device.dev)) {
  115. pr_err("Failed to create the device file\n");
  116. ret = PTR_ERR(led_device.dev);
  117. goto fail3;
  118. }
  119. pr_info("Device created on /dev/%s\n", DEVICE_NAME);
  120. ret = gpio_request(leds[0].gpio, leds[0].label);
  121. if (ret) {
  122. pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
  123. goto fail4;
  124. }
  125. ret = gpio_direction_output(leds[0].gpio, leds[0].flags);
  126. if (ret) {
  127. pr_err("Failed to set GPIO %d direction\n", leds[0].gpio);
  128. goto fail5;
  129. }
  130. return 0;
  131. fail5:
  132. gpio_free(leds[0].gpio);
  133. fail4:
  134. device_destroy(led_device.cls, led_device.dev_num);
  135. fail3:
  136. class_destroy(led_device.cls);
  137. fail2:
  138. cdev_del(&led_device.cdev);
  139. fail1:
  140. unregister_chrdev_region(led_device.dev_num, DEVICE_CNT);
  141. return ret;
  142. }
  143. static void __exit led_exit(void)
  144. {
  145. gpio_set_value(leds[0].gpio, 0);
  146. gpio_free(leds[0].gpio);
  147. device_destroy(led_device.cls, led_device.dev_num);
  148. class_destroy(led_device.cls);
  149. cdev_del(&led_device.cdev);
  150. unregister_chrdev_region(led_device.dev_num, DEVICE_CNT);
  151. }
  152. module_init(led_init);
  153. module_exit(led_exit);
  154. MODULE_LICENSE("GPL");