Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  PC Speaker beeper driver for Linux
0004  *
0005  *  Copyright (c) 2002 Vojtech Pavlik
0006  *  Copyright (c) 1992 Orest Zborowski
0007  */
0008 
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/i8253.h>
0013 #include <linux/input.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/timex.h>
0016 #include <linux/io.h>
0017 
0018 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
0019 MODULE_DESCRIPTION("PC Speaker beeper driver");
0020 MODULE_LICENSE("GPL");
0021 MODULE_ALIAS("platform:pcspkr");
0022 
0023 static int pcspkr_event(struct input_dev *dev, unsigned int type,
0024             unsigned int code, int value)
0025 {
0026     unsigned int count = 0;
0027     unsigned long flags;
0028 
0029     if (type != EV_SND)
0030         return -EINVAL;
0031 
0032     switch (code) {
0033     case SND_BELL:
0034         if (value)
0035             value = 1000;
0036         break;
0037     case SND_TONE:
0038         break;
0039     default:
0040         return -EINVAL;
0041     }
0042 
0043     if (value > 20 && value < 32767)
0044         count = PIT_TICK_RATE / value;
0045 
0046     raw_spin_lock_irqsave(&i8253_lock, flags);
0047 
0048     if (count) {
0049         /* set command for counter 2, 2 byte write */
0050         outb_p(0xB6, 0x43);
0051         /* select desired HZ */
0052         outb_p(count & 0xff, 0x42);
0053         outb((count >> 8) & 0xff, 0x42);
0054         /* enable counter 2 */
0055         outb_p(inb_p(0x61) | 3, 0x61);
0056     } else {
0057         /* disable counter 2 */
0058         outb(inb_p(0x61) & 0xFC, 0x61);
0059     }
0060 
0061     raw_spin_unlock_irqrestore(&i8253_lock, flags);
0062 
0063     return 0;
0064 }
0065 
0066 static int pcspkr_probe(struct platform_device *dev)
0067 {
0068     struct input_dev *pcspkr_dev;
0069     int err;
0070 
0071     pcspkr_dev = input_allocate_device();
0072     if (!pcspkr_dev)
0073         return -ENOMEM;
0074 
0075     pcspkr_dev->name = "PC Speaker";
0076     pcspkr_dev->phys = "isa0061/input0";
0077     pcspkr_dev->id.bustype = BUS_ISA;
0078     pcspkr_dev->id.vendor = 0x001f;
0079     pcspkr_dev->id.product = 0x0001;
0080     pcspkr_dev->id.version = 0x0100;
0081     pcspkr_dev->dev.parent = &dev->dev;
0082 
0083     pcspkr_dev->evbit[0] = BIT_MASK(EV_SND);
0084     pcspkr_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
0085     pcspkr_dev->event = pcspkr_event;
0086 
0087     err = input_register_device(pcspkr_dev);
0088     if (err) {
0089         input_free_device(pcspkr_dev);
0090         return err;
0091     }
0092 
0093     platform_set_drvdata(dev, pcspkr_dev);
0094 
0095     return 0;
0096 }
0097 
0098 static int pcspkr_remove(struct platform_device *dev)
0099 {
0100     struct input_dev *pcspkr_dev = platform_get_drvdata(dev);
0101 
0102     input_unregister_device(pcspkr_dev);
0103     /* turn off the speaker */
0104     pcspkr_event(NULL, EV_SND, SND_BELL, 0);
0105 
0106     return 0;
0107 }
0108 
0109 static int pcspkr_suspend(struct device *dev)
0110 {
0111     pcspkr_event(NULL, EV_SND, SND_BELL, 0);
0112 
0113     return 0;
0114 }
0115 
0116 static void pcspkr_shutdown(struct platform_device *dev)
0117 {
0118     /* turn off the speaker */
0119     pcspkr_event(NULL, EV_SND, SND_BELL, 0);
0120 }
0121 
0122 static const struct dev_pm_ops pcspkr_pm_ops = {
0123     .suspend = pcspkr_suspend,
0124 };
0125 
0126 static struct platform_driver pcspkr_platform_driver = {
0127     .driver     = {
0128         .name   = "pcspkr",
0129         .pm = &pcspkr_pm_ops,
0130     },
0131     .probe      = pcspkr_probe,
0132     .remove     = pcspkr_remove,
0133     .shutdown   = pcspkr_shutdown,
0134 };
0135 module_platform_driver(pcspkr_platform_driver);
0136