diff options
Diffstat (limited to 'kernel/drivers/isdn/hisax/st5481_init.c')
-rw-r--r-- | kernel/drivers/isdn/hisax/st5481_init.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/kernel/drivers/isdn/hisax/st5481_init.c b/kernel/drivers/isdn/hisax/st5481_init.c new file mode 100644 index 000000000..54ef9e4f8 --- /dev/null +++ b/kernel/drivers/isdn/hisax/st5481_init.c @@ -0,0 +1,221 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com> + * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * TODO: + * + * b layer1 delay? + * hotplug / unregister issues + * mod_inc/dec_use_count + * unify parts of d/b channel usb handling + * file header + * avoid copy to isoc buffer? + * improve usb delay? + * merge l1 state machines? + * clean up debug + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include "st5481.h" + +MODULE_DESCRIPTION("ISDN4Linux: driver for ST5481 USB ISDN adapter"); +MODULE_AUTHOR("Frode Isaksen"); +MODULE_LICENSE("GPL"); + +static int protocol = 2; /* EURO-ISDN Default */ +module_param(protocol, int, 0); + +static int number_of_leds = 2; /* 2 LEDs on the adpater default */ +module_param(number_of_leds, int, 0); + +#ifdef CONFIG_HISAX_DEBUG +static int debug = 0; +module_param(debug, int, 0); +#endif +int st5481_debug; + +/* ====================================================================== + * registration/deregistration with the USB layer + */ + +/* + * This function will be called when the adapter is plugged + * into the USB bus. + */ +static int probe_st5481(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct st5481_adapter *adapter; + struct hisax_b_if *b_if[2]; + int retval, i; + + printk(KERN_INFO "st541: found adapter VendorId %04x, ProductId %04x, LEDs %d\n", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct), + number_of_leds); + + adapter = kzalloc(sizeof(struct st5481_adapter), GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + adapter->number_of_leds = number_of_leds; + adapter->usb_dev = dev; + + adapter->hisax_d_if.owner = THIS_MODULE; + adapter->hisax_d_if.ifc.priv = adapter; + adapter->hisax_d_if.ifc.l2l1 = st5481_d_l2l1; + + for (i = 0; i < 2; i++) { + adapter->bcs[i].adapter = adapter; + adapter->bcs[i].channel = i; + adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i]; + adapter->bcs[i].b_if.ifc.l2l1 = st5481_b_l2l1; + } + + retval = st5481_setup_usb(adapter); + if (retval < 0) + goto err; + + retval = st5481_setup_d(adapter); + if (retval < 0) + goto err_usb; + + retval = st5481_setup_b(&adapter->bcs[0]); + if (retval < 0) + goto err_d; + + retval = st5481_setup_b(&adapter->bcs[1]); + if (retval < 0) + goto err_b; + + for (i = 0; i < 2; i++) + b_if[i] = &adapter->bcs[i].b_if; + + if (hisax_register(&adapter->hisax_d_if, b_if, "st5481_usb", + protocol) != 0) + goto err_b1; + + st5481_start(adapter); + + usb_set_intfdata(intf, adapter); + return 0; + +err_b1: + st5481_release_b(&adapter->bcs[1]); +err_b: + st5481_release_b(&adapter->bcs[0]); +err_d: + st5481_release_d(adapter); +err_usb: + st5481_release_usb(adapter); +err: + kfree(adapter); + return -EIO; +} + +/* + * This function will be called when the adapter is removed + * from the USB bus. + */ +static void disconnect_st5481(struct usb_interface *intf) +{ + struct st5481_adapter *adapter = usb_get_intfdata(intf); + + DBG(1, ""); + + usb_set_intfdata(intf, NULL); + if (!adapter) + return; + + st5481_stop(adapter); + st5481_release_b(&adapter->bcs[1]); + st5481_release_b(&adapter->bcs[0]); + st5481_release_d(adapter); + // we would actually better wait for completion of outstanding urbs + mdelay(2); + st5481_release_usb(adapter); + + hisax_unregister(&adapter->hisax_d_if); + + kfree(adapter); +} + +/* + * The last 4 bits in the Product Id is set with 4 pins on the chip. + */ +static struct usb_device_id st5481_ids[] = { + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x0) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x1) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x2) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x3) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x4) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x5) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x6) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x7) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x8) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0x9) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0xA) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0xB) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0xC) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0xD) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0xE) }, + { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID + 0xF) }, + { } +}; +MODULE_DEVICE_TABLE(usb, st5481_ids); + +static struct usb_driver st5481_usb_driver = { + .name = "st5481_usb", + .probe = probe_st5481, + .disconnect = disconnect_st5481, + .id_table = st5481_ids, + .disable_hub_initiated_lpm = 1, +}; + +static int __init st5481_usb_init(void) +{ + int retval; + +#ifdef CONFIG_HISAX_DEBUG + st5481_debug = debug; +#endif + + printk(KERN_INFO "hisax_st5481: ST5481 USB ISDN driver $Revision: 2.4.2.3 $\n"); + + retval = st5481_d_init(); + if (retval < 0) + goto out; + + retval = usb_register(&st5481_usb_driver); + if (retval < 0) + goto out_d_exit; + + return 0; + +out_d_exit: + st5481_d_exit(); +out: + return retval; +} + +static void __exit st5481_usb_exit(void) +{ + usb_deregister(&st5481_usb_driver); + st5481_d_exit(); +} + +module_init(st5481_usb_init); +module_exit(st5481_usb_exit); |