1 /* 2 * PC Speaker beeper driver for Linux 3 * 4 * Copyright (c) 2002 Vojtech Pavlik 5 * Copyright (c) 1992 Orest Zborowski 6 * 7 */ 8 9 /* 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License version 2 as published by 12 * the Free Software Foundation 13 */ 14 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/init.h> 18 #include <linux/input.h> 19 #include <linux/platform_device.h> 20 #include <asm/8253pit.h> 21 #include <asm/io.h> 22 23 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 24 MODULE_DESCRIPTION("PC Speaker beeper driver"); 25 MODULE_LICENSE("GPL"); 26 27 static struct platform_device *pcspkr_platform_device; 28 static DEFINE_SPINLOCK(i8253_beep_lock); 29 30 static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 31 { 32 unsigned int count = 0; 33 unsigned long flags; 34 35 if (type != EV_SND) 36 return -1; 37 38 switch (code) { 39 case SND_BELL: if (value) value = 1000; 40 case SND_TONE: break; 41 default: return -1; 42 } 43 44 if (value > 20 && value < 32767) 45 count = PIT_TICK_RATE / value; 46 47 spin_lock_irqsave(&i8253_beep_lock, flags); 48 49 if (count) { 50 /* enable counter 2 */ 51 outb_p(inb_p(0x61) | 3, 0x61); 52 /* set command for counter 2, 2 byte write */ 53 outb_p(0xB6, 0x43); 54 /* select desired HZ */ 55 outb_p(count & 0xff, 0x42); 56 outb((count >> 8) & 0xff, 0x42); 57 } else { 58 /* disable counter 2 */ 59 outb(inb_p(0x61) & 0xFC, 0x61); 60 } 61 62 spin_unlock_irqrestore(&i8253_beep_lock, flags); 63 64 return 0; 65 } 66 67 static int __devinit pcspkr_probe(struct platform_device *dev) 68 { 69 struct input_dev *pcspkr_dev; 70 int err; 71 72 pcspkr_dev = input_allocate_device(); 73 if (!pcspkr_dev) 74 return -ENOMEM; 75 76 pcspkr_dev->name = "PC Speaker"; 77 pcspkr_dev->phys = "isa0061/input0"; 78 pcspkr_dev->id.bustype = BUS_ISA; 79 pcspkr_dev->id.vendor = 0x001f; 80 pcspkr_dev->id.product = 0x0001; 81 pcspkr_dev->id.version = 0x0100; 82 pcspkr_dev->cdev.dev = &dev->dev; 83 84 pcspkr_dev->evbit[0] = BIT(EV_SND); 85 pcspkr_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); 86 pcspkr_dev->event = pcspkr_event; 87 88 err = input_register_device(pcspkr_dev); 89 if (err) { 90 input_free_device(pcspkr_dev); 91 return err; 92 } 93 94 platform_set_drvdata(dev, pcspkr_dev); 95 96 return 0; 97 } 98 99 static int __devexit pcspkr_remove(struct platform_device *dev) 100 { 101 struct input_dev *pcspkr_dev = platform_get_drvdata(dev); 102 103 input_unregister_device(pcspkr_dev); 104 platform_set_drvdata(dev, NULL); 105 /* turn off the speaker */ 106 pcspkr_event(NULL, EV_SND, SND_BELL, 0); 107 108 return 0; 109 } 110 111 static int pcspkr_suspend(struct platform_device *dev, pm_message_t state) 112 { 113 pcspkr_event(NULL, EV_SND, SND_BELL, 0); 114 115 return 0; 116 } 117 118 static void pcspkr_shutdown(struct platform_device *dev) 119 { 120 /* turn off the speaker */ 121 pcspkr_event(NULL, EV_SND, SND_BELL, 0); 122 } 123 124 static struct platform_driver pcspkr_platform_driver = { 125 .driver = { 126 .name = "pcspkr", 127 .owner = THIS_MODULE, 128 }, 129 .probe = pcspkr_probe, 130 .remove = __devexit_p(pcspkr_remove), 131 .suspend = pcspkr_suspend, 132 .shutdown = pcspkr_shutdown, 133 }; 134 135 136 static int __init pcspkr_init(void) 137 { 138 int err; 139 140 err = platform_driver_register(&pcspkr_platform_driver); 141 if (err) 142 return err; 143 144 pcspkr_platform_device = platform_device_alloc("pcspkr", -1); 145 if (!pcspkr_platform_device) { 146 err = -ENOMEM; 147 goto err_unregister_driver; 148 } 149 150 err = platform_device_add(pcspkr_platform_device); 151 if (err) 152 goto err_free_device; 153 154 return 0; 155 156 err_free_device: 157 platform_device_put(pcspkr_platform_device); 158 err_unregister_driver: 159 platform_driver_unregister(&pcspkr_platform_driver); 160 161 return err; 162 } 163 164 static void __exit pcspkr_exit(void) 165 { 166 platform_device_unregister(pcspkr_platform_device); 167 platform_driver_unregister(&pcspkr_platform_driver); 168 } 169 170 module_init(pcspkr_init); 171 module_exit(pcspkr_exit); 172