#include #include #include #include #include #include #include #include #include #include #include #include #include "gpioregs.h" #define DEVICE_NAME "buzzer" #define CLASS_NAME "buzzer_class" #define DRIVER_NAME "buzzer_driver" #define BUFFER_SIZE 1024 #define note(x) ((119318200+(x)/2)/(x)) // You can specify the major device number and minor device number through module parameters static int buzzer_major = 56; // Default main device number static int buzzer_minor = 100; // Default sub-device number module_param(buzzer_major, int, S_IRUGO); module_param(buzzer_minor, int, S_IRUGO); MODULE_PARM_DESC(buzzer_major, "Major device number"); MODULE_PARM_DESC(buzzer_minor, "Minor device number"); static struct class *char_class = NULL; static struct device *char_device = NULL; static struct cdev my_cdev; static dev_t dev_num; struct buzzer_dev { char *buffer; size_t size; struct mutex lock; struct cdev cdev; struct delayed_work delay_work1; }; static struct buzzer_dev *dev = NULL; void IoWrite8 (uint16_t addr, uint8_t data) { outb(data,addr); } uint8_t IoRead8 (uint16_t addr) { return inb(addr); } void BeepOff(void) { IoWrite8(BUZZER_PORT, IoRead8(BUZZER_PORT) & 0xfc); } void BeepOn(uint16_t freq) { uint16_t Frequency = note(freq); //set up channel 1 (used for delays) IoWrite8(BUZZER_CHANNEL, 0x54); IoWrite8(BUZZER_CHAN_1, 0x12); //set up channel 2 (used by speaker) IoWrite8(BUZZER_CHANNEL, 0xb6); IoWrite8(BUZZER_FREQ, (uint8_t)Frequency); IoWrite8(BUZZER_FREQ, (uint8_t)(Frequency >> 8)); //turn the speaker on IoWrite8(BUZZER_PORT, IoRead8(BUZZER_PORT) | 3); } static void delay_work_func(struct work_struct *work) { int status_flag = 0; int freq = 0; int duration = 0; if(dev->size < 4) { printk(KERN_INFO "buzzer: size < 4\n"); return; } // Close buzzer BeepOff(); printk(KERN_INFO "delay_work_func\n"); } static int buzzer_open(struct inode *inode, struct file *filp) { struct buzzer_dev *dev; // if (!request_region(PORT_80, 1, DRIVER_NAME)) // { // pr_err("Port 80 I/O region busy\n"); // return -EBUSY; // } dev = container_of(inode->i_cdev, struct buzzer_dev, cdev); filp->private_data = dev; printk(KERN_INFO "buzzer: Device opened (major=%d, minor=%d)\n", imajor(inode), iminor(inode)); return 0; } static int buzzer_release(struct inode *inode, struct file *filp) { // release_region(PORT_80, 1); printk(KERN_INFO "buzzer: Device closed\n"); return 0; } static ssize_t buzzer_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct buzzer_dev *dev = filp->private_data; ssize_t retval = 0; size_t available; int read_count = 0; int ret = 0; if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; // The read count here is not fixed at 4 bytes, in order to facilitate future expansion. if (count > dev->size) { read_count = dev->size; } else { read_count = count; } ret = copy_to_user(buf, dev->buffer, read_count); if (ret != 0) { printk(KERN_INFO "buzzer: copy_to_user failed\n"); goto out; } printk(KERN_INFO "buzzer: Read %zu bytes\n", count); out: mutex_unlock(&dev->lock); return ret; } static ssize_t buzzer_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct buzzer_dev *dev = filp->private_data; ssize_t retval = 0; size_t available; int ret = 0; int status_flag = 0; int freq = 0; int duration = 0; if (mutex_lock_interruptible(&dev->lock)) { return -ERESTARTSYS; } if (count > BUFFER_SIZE) { count = BUFFER_SIZE; } if(count < 4) { printk(KERN_INFO "buzzer: count < 4\n"); ret = -1; goto out; } ret = copy_from_user(dev->buffer, buf, count); if (ret != 0) { printk(KERN_INFO "buzzer: copy_from_user failed\n"); goto out; } dev->size = count; status_flag = dev->buffer[0]; freq = dev->buffer[2]<<8 + dev->buffer[1]; duration = dev->buffer[3]; if(status_flag == 1) { BeepOn(freq); } else { BeepOff(); } printk(KERN_INFO "buzzer: Written %zu bytes\n", count); out: mutex_unlock(&dev->lock); if (ret == 0) { schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(duration*1000)); } return count; } static struct file_operations fops = { .owner = THIS_MODULE, .open = buzzer_open, .release = buzzer_release, .read = buzzer_read, .write = buzzer_write, }; int buzzer_init(void) { int result; printk(KERN_INFO "buzzer: Initializing driver with major=%d, minor=%d\n", buzzer_major, buzzer_minor); if (buzzer_major <= 0) { printk(KERN_ALERT "buzzer: Invalid major number %d\n", buzzer_major); return -EINVAL; } dev_num = MKDEV(buzzer_major, buzzer_minor); result = register_chrdev_region(dev_num, 1, DEVICE_NAME); if (result < 0) { printk(KERN_ALERT "buzzer: Failed to register major number %d\n", buzzer_major); printk(KERN_ALERT "buzzer: Try using a different major number\n"); return result; } printk(KERN_INFO "buzzer: Registered with major=%d, minor=%d\n", MAJOR(dev_num), MINOR(dev_num)); dev = kmalloc(sizeof(struct buzzer_dev), GFP_KERNEL); if (!dev) { result = -ENOMEM; goto fail_malloc; } memset(dev, 0, sizeof(struct buzzer_dev)); dev->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); if (!dev->buffer) { result = -ENOMEM; goto fail_buffer; } mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func); cdev_init(&dev->cdev, &fops); dev->cdev.owner = THIS_MODULE; result = cdev_add(&dev->cdev, dev_num, 1); if (result) { printk(KERN_ALERT "buzzer: Failed to add cdev\n"); goto fail_cdev; } char_class = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(char_class)) { result = PTR_ERR(char_class); printk(KERN_ALERT "buzzer: Failed to create class\n"); goto fail_class; } char_device = device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME); if (IS_ERR(char_device)) { result = PTR_ERR(char_device); printk(KERN_ALERT "buzzer: Failed to create device\n"); goto fail_device; } printk(KERN_INFO "buzzer: Driver initialized successfully\n"); printk(KERN_INFO "buzzer: Device node: /dev/%s (major=%d, minor=%d)\n", DEVICE_NAME, buzzer_major, buzzer_minor); return 0; fail_device: class_destroy(char_class); fail_class: cdev_del(&dev->cdev); fail_cdev: kfree(dev->buffer); fail_buffer: kfree(dev); fail_malloc: unregister_chrdev_region(dev_num, 1); return result; } void buzzer_exit(void) { cancel_delayed_work_sync(&dev->delay_work1); device_destroy(char_class, dev_num); class_destroy(char_class); if (dev) { cdev_del(&dev->cdev); if (dev->buffer) kfree(dev->buffer); kfree(dev); } unregister_chrdev_region(dev_num, 1); printk(KERN_INFO "buzzer: Driver removed (major=%d, minor=%d)\n", buzzer_major, buzzer_minor); }