#define PCSPEAKER_VERSION "1.0" #define PCSPEAKER_MINOR 240 #include #include #include #include #if !defined(__i386__) && !defined(__x86_64__) #error Cannot build speaker driver for this machine configuration. #endif #include #include #include #include #include #include #include #include #include #include #include #include #include static int pcspeaker_open_cnt; /* #times opened */ static int pcspeaker_open_mode; /* special open modes */ #define PCSPEAKER_WRITE 1 /* opened for writing (exclusive) */ #define PCSPEAKER_EXCL 2 /* opened with O_EXCL */ /* I don't know how to link this module agains _kd_mktone() in drivers/char/vt.c -- Ilguiz Latypov */ /* * Generates sound of some frequency for some number of clock ticks * * If freq is 0, will turn off sound, else will turn it on for that time. * If msec is 0, will return immediately, else will sleep for msec time, then * turn sound off. * * We also return immediately, which is what was implied within the X * comments - KDMKTONE doesn't put the process to sleep. */ #if defined(__i386__) || defined(__alpha__) || defined(__powerpc__) \ || (defined(__mips__) && defined(CONFIG_ISA)) \ || (defined(__arm__) && defined(CONFIG_HOST_FOOTBRIDGE)) \ || defined(__x86_64__) static void kd_nosound(unsigned long ignored) { /* disable counter 2 */ outb(inb_p(0x61)&0xFC, 0x61); return; } void _kd_mksound2(unsigned int hz, unsigned int ticks) { static struct timer_list sound_timer = { function: kd_nosound }; unsigned int count = 0; unsigned long flags; if (hz > 20 && hz < 32767) count = 1193180 / hz; save_flags(flags); cli(); del_timer(&sound_timer); if (count) { /* enable counter 2 */ outb_p(inb_p(0x61)|3, 0x61); /* set command for counter 2, 2 byte write */ outb_p(0xB6, 0x43); /* select desired HZ */ outb_p(count & 0xff, 0x42); outb((count >> 8) & 0xff, 0x42); if (ticks) { sound_timer.expires = jiffies+ticks; add_timer(&sound_timer); } } else kd_nosound(0); restore_flags(flags); return; } #else void _kd_mksound2(unsigned int hz, unsigned int ticks) { } #endif static long long pcspeaker_llseek(struct file *file,loff_t offset, int origin ) { return 0; } static ssize_t pcspeaker_read(struct file * file, char * buf, size_t count, loff_t *ppos ) { return 0; } static ssize_t pcspeaker_write(struct file * file, const char * buf, size_t count, loff_t *ppos ) { return 0; } static int pcspeaker_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ) { int perm; perm = 0; if (suser()) perm = 1; switch( cmd ) { case KIOCSOUND: if (!perm) return -EPERM; if (arg) arg = CLOCK_TICK_RATE / arg; _kd_mksound2(arg, 0); return 0; case KDMKTONE: if (!perm) return -EPERM; { unsigned int ticks, count; /* * Generate the tone for the appropriate number of ticks. * If the time is zero, turn off sound ourselves. */ ticks = HZ * ((arg >> 16) & 0xffff) / 1000; count = ticks ? (arg & 0xffff) : 0; if (count) count = CLOCK_TICK_RATE / count; _kd_mksound2(count, ticks); return 0; } default: return( -EINVAL ); } } static int pcspeaker_open( struct inode *inode, struct file *file ) { if ((pcspeaker_open_cnt && (file->f_flags & O_EXCL)) || (pcspeaker_open_mode & PCSPEAKER_EXCL) || ((file->f_mode & 2) && (pcspeaker_open_mode & PCSPEAKER_WRITE))) return( -EBUSY ); if (file->f_flags & O_EXCL) pcspeaker_open_mode |= PCSPEAKER_EXCL; if (file->f_mode & 2) pcspeaker_open_mode |= PCSPEAKER_WRITE; pcspeaker_open_cnt++; return( 0 ); } static int pcspeaker_release( struct inode *inode, struct file *file ) { lock_kernel(); pcspeaker_open_cnt--; if (file->f_flags & O_EXCL) pcspeaker_open_mode &= ~PCSPEAKER_EXCL; if (file->f_mode & 2) pcspeaker_open_mode &= ~PCSPEAKER_WRITE; unlock_kernel(); return( 0 ); } static struct file_operations pcspeaker_fops = { owner: THIS_MODULE, llseek: pcspeaker_llseek, read: pcspeaker_read, write: pcspeaker_write, ioctl: pcspeaker_ioctl, open: pcspeaker_open, release: pcspeaker_release, }; static struct miscdevice pcspeaker_dev = { PCSPEAKER_MINOR, "pcspeaker", &pcspeaker_fops }; static int __init pcspeaker_init(void) { int ret; ret = misc_register( &pcspeaker_dev ); if (ret) { printk(KERN_ERR "pcspeaker: can't misc_register on minor=%d\n", PCSPEAKER_MINOR); goto out; } ret = 0; printk(KERN_INFO "PC speaker driver v" PCSPEAKER_VERSION "\n"); out: return( ret ); #if 0 outmisc: misc_deregister( &pcspeaker_dev ); goto out; #endif } static void __exit pcspeaker_cleanup_module (void) { misc_deregister( &pcspeaker_dev ); } module_init(pcspeaker_init); module_exit(pcspeaker_cleanup_module); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; /* * Local variables: * c-indent-level: 4 * tab-width: 4 * compile-command: "gcc -pipe -mpreferred-stack-boundary=2 -march=i386 -O2 -D__KERNEL__ -I../include -I/home/ilatypov/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strict-aliasing -DMODULE -DMODVERSIONS -include /home/ilatypov/linux/include/linux/modversions.h -c pcspeaker.c -o pcspeaker.o" * End: */