1/* 2 * Telephony registration for Linux 3 * 4 * (c) Copyright 1999 Red Hat Software Inc. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 * 11 * Author: Alan Cox, <alan@redhat.com> 12 * 13 * Fixes: Mar 01 2000 Thomas Sparr, <thomas.l.sparr@telia.com> 14 * phone_register_device now works with unit!=PHONE_UNIT_ANY 15 */ 16 17#include <linux/module.h> 18#include <linux/types.h> 19#include <linux/kernel.h> 20#include <linux/fs.h> 21#include <linux/mm.h> 22#include <linux/string.h> 23#include <linux/errno.h> 24#include <linux/phonedev.h> 25#include <linux/init.h> 26#include <asm/uaccess.h> 27#include <asm/system.h> 28 29#include <linux/kmod.h> 30#include <linux/sem.h> 31#include <linux/mutex.h> 32 33#define PHONE_NUM_DEVICES 256 34 35/* 36 * Active devices 37 */ 38 39static struct phone_device *phone_device[PHONE_NUM_DEVICES]; 40static DEFINE_MUTEX(phone_lock); 41 42/* 43 * Open a phone device. 44 */ 45 46static int phone_open(struct inode *inode, struct file *file) 47{ 48 unsigned int minor = iminor(inode); 49 int err = 0; 50 struct phone_device *p; 51 const struct file_operations *old_fops, *new_fops = NULL; 52 53 if (minor >= PHONE_NUM_DEVICES) 54 return -ENODEV; 55 56 mutex_lock(&phone_lock); 57 p = phone_device[minor]; 58 if (p) 59 new_fops = fops_get(p->f_op); 60 if (!new_fops) { 61 mutex_unlock(&phone_lock); 62 request_module("char-major-%d-%d", PHONE_MAJOR, minor); 63 mutex_lock(&phone_lock); 64 p = phone_device[minor]; 65 if (p == NULL || (new_fops = fops_get(p->f_op)) == NULL) 66 { 67 err=-ENODEV; 68 goto end; 69 } 70 } 71 old_fops = file->f_op; 72 file->f_op = new_fops; 73 if (p->open) 74 err = p->open(p, file); /* Tell the device it is open */ 75 if (err) { 76 fops_put(file->f_op); 77 file->f_op = fops_get(old_fops); 78 } 79 fops_put(old_fops); 80end: 81 mutex_unlock(&phone_lock); 82 return err; 83} 84 85/* 86 * Telephony For Linux device drivers request registration here. 87 */ 88 89int phone_register_device(struct phone_device *p, int unit) 90{ 91 int base; 92 int end; 93 int i; 94 95 base = 0; 96 end = PHONE_NUM_DEVICES - 1; 97 98 if (unit != PHONE_UNIT_ANY) { 99 base = unit; 100 end = unit + 1; /* enter the loop at least one time */ 101 } 102 103 mutex_lock(&phone_lock); 104 for (i = base; i < end; i++) { 105 if (phone_device[i] == NULL) { 106 phone_device[i] = p; 107 p->minor = i; 108 mutex_unlock(&phone_lock); 109 return 0; 110 } 111 } 112 mutex_unlock(&phone_lock); 113 return -ENFILE; 114} 115 116/* 117 * Unregister an unused Telephony for linux device 118 */ 119 120void phone_unregister_device(struct phone_device *pfd) 121{ 122 mutex_lock(&phone_lock); 123 if (phone_device[pfd->minor] != pfd) 124 panic("phone: bad unregister"); 125 phone_device[pfd->minor] = NULL; 126 mutex_unlock(&phone_lock); 127} 128 129 130static const struct file_operations phone_fops = 131{ 132 .owner = THIS_MODULE, 133 .open = phone_open, 134}; 135 136/* 137 * Board init functions 138 */ 139 140 141/* 142 * Initialise Telephony for linux 143 */ 144 145static int __init telephony_init(void) 146{ 147 printk(KERN_INFO "Linux telephony interface: v1.00\n"); 148 if (register_chrdev(PHONE_MAJOR, "telephony", &phone_fops)) { 149 printk("phonedev: unable to get major %d\n", PHONE_MAJOR); 150 return -EIO; 151 } 152 153 return 0; 154} 155 156static void __exit telephony_exit(void) 157{ 158 unregister_chrdev(PHONE_MAJOR, "telephony"); 159} 160 161module_init(telephony_init); 162module_exit(telephony_exit); 163 164MODULE_LICENSE("GPL"); 165 166EXPORT_SYMBOL(phone_register_device); 167EXPORT_SYMBOL(phone_unregister_device); 168