option.c revision 7d28e74b97c8eb859fd9f5eb018bb1c75627bd55
158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* 214f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs USB Driver for GSM modems 358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de> 558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs This driver is free software; you can redistribute it and/or modify 758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs it under the terms of Version 2 of the GNU General Public License as 858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs published by the Free Software Foundation. 958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 1058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org> 1158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 12b3fdab59b8f5d8e42fa339be74cd015dc1a3192fMatthias Urlichs History: see the git log. 13ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs 14ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs Work sponsored by: Sigos GmbH, Germany <info@sigos.de> 15ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs 1614f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs This driver exists because the "normal" serial driver doesn't work too well 1714f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs with GSM modems. Issues: 1814f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs - data loss -- one single Receive URB is not nearly enough 197c1c2f73e00b5d0413399a14b7ab9e80db94926fMatthias Urlichs - nonstandard flow (Option devices) control 2014f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs - controlling the baud rate doesn't make sense 2114f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs 2214f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs This driver is named "option" because the most common device it's 2314f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs used for is a PC-Card (with an internal OHCI-USB interface, behind 2414f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs which the GSM interface sits), made by Option Inc. 2514f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs 2614f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs Some of the "one port" devices actually exhibit multiple USB instances 2714f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs on the USB bus. This is not a bug, these ports are used for different 2814f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs device features. 2958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs*/ 30ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs 31e37de9e0d6591706a76cff63582cbc721c317333Matthias Urlichs#define DRIVER_VERSION "v0.7.1" 3258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>" 3314f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs#define DRIVER_DESC "USB Driver for GSM modems" 3458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 3558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/kernel.h> 3658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/jiffies.h> 3758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/errno.h> 3858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/tty.h> 3958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/tty_flip.h> 4058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/module.h> 4158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs#include <linux/usb.h> 42a969888ce91673c7f4b86520d851a6f0d5a5fa7dGreg Kroah-Hartman#include <linux/usb/serial.h> 4358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 4458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* Function prototypes */ 457bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_open(struct usb_serial_port *port, struct file *filp); 467bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_close(struct usb_serial_port *port, struct file *filp); 477bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_startup(struct usb_serial *serial); 487bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_shutdown(struct usb_serial *serial); 497bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_rx_throttle(struct usb_serial_port *port); 507bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_rx_unthrottle(struct usb_serial_port *port); 517bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_write_room(struct usb_serial_port *port); 5258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 537d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void option_instat_callback(struct urb *urb); 5458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 557bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_write(struct usb_serial_port *port, 567bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton const unsigned char *buf, int count); 5758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 587bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_chars_in_buffer(struct usb_serial_port *port); 597bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_ioctl(struct usb_serial_port *port, struct file *file, 607bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton unsigned int cmd, unsigned long arg); 617bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_set_termios(struct usb_serial_port *port, 62606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *old); 637bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_break_ctl(struct usb_serial_port *port, int break_state); 647bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_tiocmget(struct usb_serial_port *port, struct file *file); 657bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_tiocmset(struct usb_serial_port *port, struct file *file, 667bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton unsigned int set, unsigned int clear); 677bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_send_setup(struct usb_serial_port *port); 6858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 6958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* Vendor and product IDs */ 70fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_VENDOR_ID 0x0AF0 71fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_COLT 0x5000 72fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA 0x6000 73fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_LIGHT 0x6100 74fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_QUAD 0x6200 75fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT 0x6300 76fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_NDIS 0x6050 77fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT 0x6150 78fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_NDIS_QUAD 0x6250 79fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT 0x6350 80fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_COBRA 0x6500 81fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_COBRA_BUS 0x6501 82fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_VIPER 0x6600 83fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_VIPER_BUS 0x6601 84fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_GT_MAX_READY 0x6701 85fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_GT_MAX 0x6711 86fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_FUJI_MODEM_LIGHT 0x6721 87fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_FUJI_MODEM_GT 0x6741 88fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_FUJI_MODEM_EX 0x6761 89fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT 0x6731 90fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_FUJI_NETWORK_GT 0x6751 91fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_FUJI_NETWORK_EX 0x6771 92fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_KOI_MODEM 0x6800 93fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_KOI_NETWORK 0x6811 94fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_SCORPION_MODEM 0x6901 95fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_SCORPION_NETWORK 0x6911 96fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_MODEM 0x7001 97fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_NETWORK 0x7011 98fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_MODEM_LITE 0x7021 99fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_MODEM_GT 0x7041 100fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_MODEM_EX 0x7061 101fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_NETWORK_LITE 0x7031 102fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_NETWORK_GT 0x7051 103fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_NETWORK_EX 0x7071 104fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_KOI_MODEM 0x7100 105fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define OPTION_PRODUCT_ETNA_KOI_NETWORK 0x7111 106fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman 107fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define HUAWEI_VENDOR_ID 0x12D1 108fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define HUAWEI_PRODUCT_E600 0x1001 109fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define HUAWEI_PRODUCT_E220 0x1003 110fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman 111fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define NOVATELWIRELESS_VENDOR_ID 0x1410 112fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman 113fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define ANYDATA_VENDOR_ID 0x16d5 114fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman#define ANYDATA_PRODUCT_ID 0x6501 11558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 11658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsstatic struct usb_device_id option_ids[] = { 117fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, 118fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, 119fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) }, 120fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) }, 121fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) }, 122fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) }, 123fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) }, 124fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) }, 125fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) }, 12614f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, 127fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) }, 128fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) }, 129fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) }, 130fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) }, 131fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) }, 132fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) }, 133fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) }, 134fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) }, 135fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) }, 136fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) }, 137fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) }, 138fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) }, 139fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) }, 140fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) }, 141fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) }, 142fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) }, 143fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) }, 144fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) }, 145fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) }, 146fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) }, 147fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) }, 148fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) }, 149fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) }, 150fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, 151fd978bfa127a0b8c0bdbbbc9d64f3c73bf080f61Greg Kroah-Hartman { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) }, 152b6137383bda844a433d65e027502df7b20ba45c2Matthias Urlichs { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, 153ab1958905514da3b6c06d61523ebed142a16cc72Johann Wilhelm { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) }, 15469806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1100) }, /* Novatel Merlin XS620/S640 */ 15569806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1110) }, /* Novatel Merlin S620 */ 15669806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1120) }, /* Novatel Merlin EX720 */ 15769806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1130) }, /* Novatel Merlin S720 */ 15869806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1400) }, /* Novatel U730 */ 15969806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1410) }, /* Novatel U740 */ 16069806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1420) }, /* Novatel EU870 */ 16169806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel Merlin XU870 HSDPA/3G */ 16269806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel XU870 */ 16369806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2100) }, /* Novatel EV620 CDMA/EV-DO */ 16469806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2110) }, /* Novatel Merlin ES620 / Merlin ES720 / Ovation U720 */ 16569806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */ 16669806d5631b79ed0c442ae5b15c46bcfd8662476Greg Kroah-Hartman { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */ 16731fcbb733812bca52e8bee96d62ba56df0fc408bMatthias Urlichs { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, 16814f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs { } /* Terminating entry */ 16914f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs}; 17058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias UrlichsMODULE_DEVICE_TABLE(usb, option_ids); 17158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 17258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsstatic struct usb_driver option_driver = { 17358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs .name = "option", 17458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs .probe = usb_serial_probe, 17558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs .disconnect = usb_serial_disconnect, 17658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs .id_table = option_ids, 177ba9dc657af86d05d2971633e57d1f6f94ed60472Greg Kroah-Hartman .no_dynamic_id = 1, 17858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs}; 17958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 180c30fe7f73194650148b58ee80908c1bc38246397Uwe Zeisberger/* The card has three separate interfaces, which the serial driver 18158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs * recognizes separately, thus num_port=1. 18258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs */ 18314f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs 18414f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichsstatic struct usb_serial_driver option_1port_device = { 18514f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs .driver = { 18614f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs .owner = THIS_MODULE, 18702b2ac5b0370b1157a5a99f2fdf006644b9b86d5Matthias Urlichs .name = "option1", 18814f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs }, 18914f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs .description = "GSM modem (1-port)", 190d9b1b787736852f462dbf277b3ca708cbbf693aeJohannes Hölzl .usb_driver = &option_driver, 191b656b2cbd74fb591d46e07c7c291791b280ad5b4Greg Kroah-Hartman .id_table = option_ids, 192ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .num_interrupt_in = NUM_DONT_CARE, 193ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .num_bulk_in = NUM_DONT_CARE, 194ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .num_bulk_out = NUM_DONT_CARE, 19514f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs .num_ports = 1, 196ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .open = option_open, 197ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .close = option_close, 198ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .write = option_write, 199ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .write_room = option_write_room, 200ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .chars_in_buffer = option_chars_in_buffer, 201ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .throttle = option_rx_throttle, 202ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .unthrottle = option_rx_unthrottle, 203ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .ioctl = option_ioctl, 204ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .set_termios = option_set_termios, 205ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .break_ctl = option_break_ctl, 206ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .tiocmget = option_tiocmget, 207ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .tiocmset = option_tiocmset, 208ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .attach = option_startup, 209ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .shutdown = option_shutdown, 210ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs .read_int_callback = option_instat_callback, 21158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs}; 21258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 213ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#ifdef CONFIG_USB_DEBUG 21458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsstatic int debug; 215ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#else 216ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#define debug 0 217ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#endif 218ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs 21958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* per port private data */ 22058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 221ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#define N_IN_URB 4 222ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#define N_OUT_URB 1 223b27c73dcab61826e5f1228d69d56f469b0abfc05Matthias Urlichs#define IN_BUFLEN 4096 224ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#define OUT_BUFLEN 128 22558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 22658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsstruct option_port_private { 22758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Input endpoints and buffer for this port */ 228ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct urb *in_urbs[N_IN_URB]; 229ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs char in_buffer[N_IN_URB][IN_BUFLEN]; 23058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Output endpoints and buffer for this port */ 231ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct urb *out_urbs[N_OUT_URB]; 232ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs char out_buffer[N_OUT_URB][OUT_BUFLEN]; 23358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 23458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Settings for the port */ 235ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int rts_state; /* Handshaking pins (outputs) */ 236ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int dtr_state; 237ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int cts_state; /* Handshaking pins (inputs) */ 238ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int dsr_state; 239ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int dcd_state; 240ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int ri_state; 241ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs 242ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs unsigned long tx_start_time[N_OUT_URB]; 24358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs}; 24458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 24558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* Functions used by new usb-serial code. */ 2467bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int __init option_init(void) 24758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 24858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int retval; 24914f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs retval = usb_serial_register(&option_1port_device); 25014f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs if (retval) 25114f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs goto failed_1port_device_register; 25258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs retval = usb_register(&option_driver); 25358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (retval) 25458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs goto failed_driver_register; 25558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 25658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs info(DRIVER_DESC ": " DRIVER_VERSION); 25758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 25858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return 0; 25958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 26058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsfailed_driver_register: 26114f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs usb_serial_deregister (&option_1port_device); 26214f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichsfailed_1port_device_register: 26358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return retval; 26458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 26558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 2667bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void __exit option_exit(void) 26758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 26858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_deregister (&option_driver); 26914f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs usb_serial_deregister (&option_1port_device); 27058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 27158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 27258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsmodule_init(option_init); 27358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsmodule_exit(option_exit); 27458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 2757bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_rx_throttle(struct usb_serial_port *port) 27658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 27758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 27858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 27958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 2807bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_rx_unthrottle(struct usb_serial_port *port) 28158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 28258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 28358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 28458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 2857bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_break_ctl(struct usb_serial_port *port, int break_state) 28658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 28758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Unfortunately, I don't know how to send a break */ 288ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs dbg("%s", __FUNCTION__); 28958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 29058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 2917bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_set_termios(struct usb_serial_port *port, 292606d099cdd1080bbb50ea50dc52d98252f8f10a1Alan Cox struct ktermios *old_termios) 29358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 29458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 29558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 29658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs option_send_setup(port); 29758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 29858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 2997bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_tiocmget(struct usb_serial_port *port, struct file *file) 30058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 301ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs unsigned int value; 302ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 30358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 30458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 30558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 30658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs value = ((portdata->rts_state) ? TIOCM_RTS : 0) | 30758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs ((portdata->dtr_state) ? TIOCM_DTR : 0) | 30858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs ((portdata->cts_state) ? TIOCM_CTS : 0) | 30958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs ((portdata->dsr_state) ? TIOCM_DSR : 0) | 31058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs ((portdata->dcd_state) ? TIOCM_CAR : 0) | 31158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs ((portdata->ri_state) ? TIOCM_RNG : 0); 31258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 31358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return value; 31458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 31558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 3167bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_tiocmset(struct usb_serial_port *port, struct file *file, 3177bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton unsigned int set, unsigned int clear) 31858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 319ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 32058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 32158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 32258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 32358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (set & TIOCM_RTS) 32458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->rts_state = 1; 32558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (set & TIOCM_DTR) 32658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->dtr_state = 1; 32758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 32858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (clear & TIOCM_RTS) 32958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->rts_state = 0; 33058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (clear & TIOCM_DTR) 33158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->dtr_state = 0; 33258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return option_send_setup(port); 33358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 33458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 3357bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_ioctl(struct usb_serial_port *port, struct file *file, 3367bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton unsigned int cmd, unsigned long arg) 33758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 33858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return -ENOIOCTLCMD; 33958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 34058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 34158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* Write */ 3427bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_write(struct usb_serial_port *port, 3437bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton const unsigned char *buf, int count) 34458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 345ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 346ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int i; 347ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int left, todo; 348ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct urb *this_urb = NULL; /* spurious */ 349ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int err; 35058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 35158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 35258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 35358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: write (%d chars)", __FUNCTION__, count); 35458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 35558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs i = 0; 35658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs left = count; 357ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs for (i=0; left > 0 && i < N_OUT_URB; i++) { 35858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs todo = left; 35958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (todo > OUT_BUFLEN) 36058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs todo = OUT_BUFLEN; 36158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 362ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs this_urb = portdata->out_urbs[i]; 363ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs if (this_urb->status == -EINPROGRESS) { 3647bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton if (time_before(jiffies, 3657bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton portdata->tx_start_time[i] + 10 * HZ)) 36658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs continue; 36758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_unlink_urb(this_urb); 368ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs continue; 36958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 370ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs if (this_urb->status != 0) 3717bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("usb_write %p failed (err=%d)", 3727bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton this_urb, this_urb->status); 37358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 3747bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: endpoint %d buf %d", __FUNCTION__, 3757bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton usb_pipeendpoint(this_urb->pipe), i); 37658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 377ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs /* send the data */ 37858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs memcpy (this_urb->transfer_buffer, buf, todo); 37958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs this_urb->transfer_buffer_length = todo; 38058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 38158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs this_urb->dev = port->serial->dev; 38258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs err = usb_submit_urb(this_urb, GFP_ATOMIC); 38358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (err) { 3847bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("usb_submit_urb %p (write bulk) failed " 3857bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton "(%d, has %d)", this_urb, 3867bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton err, this_urb->status); 38758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs continue; 38858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 38958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->tx_start_time[i] = jiffies; 39058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs buf += todo; 39158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs left -= todo; 39258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 39358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 39458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs count -= left; 39558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: wrote (did %d)", __FUNCTION__, count); 39658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return count; 39758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 39858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 3997d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void option_indat_callback(struct urb *urb) 40058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 40133f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox int err; 40258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int endpoint; 40358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct usb_serial_port *port; 40458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct tty_struct *tty; 40558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs unsigned char *data = urb->transfer_buffer; 40658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 40758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: %p", __FUNCTION__, urb); 40858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 40958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs endpoint = usb_pipeendpoint(urb->pipe); 41058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port = (struct usb_serial_port *) urb->context; 41158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 41258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (urb->status) { 41358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: nonzero status: %d on endpoint %02x.", 41458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs __FUNCTION__, urb->status, endpoint); 41558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } else { 41658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs tty = port->tty; 41758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (urb->actual_length) { 41833f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_buffer_request_room(tty, urb->actual_length); 41933f0f88f1c51ae5c2d593d26960c760ea154c2e2Alan Cox tty_insert_flip_string(tty, data, urb->actual_length); 42058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs tty_flip_buffer_push(tty); 42158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } else { 42258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: empty read urb received", __FUNCTION__); 42358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 42458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 42558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Resubmit urb so we continue receiving */ 42658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (port->open_count && urb->status != -ESHUTDOWN) { 42758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs err = usb_submit_urb(urb, GFP_ATOMIC); 42858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (err) 4297bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton printk(KERN_ERR "%s: resubmit read urb failed. " 4307bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton "(%d)", __FUNCTION__, err); 43158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 43258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 43358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return; 43458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 43558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 4367d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void option_outdat_callback(struct urb *urb) 43758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 43858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct usb_serial_port *port; 43958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 44058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 44158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 44258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port = (struct usb_serial_port *) urb->context; 44358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 444cf2c7481d2ff7f0c266de873b2fe93883e9782f9Pete Zaitcev usb_serial_port_softint(port); 44558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 44658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 4477d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void option_instat_callback(struct urb *urb) 44858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 44958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int err; 45058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct usb_serial_port *port = (struct usb_serial_port *) urb->context; 45158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct option_port_private *portdata = usb_get_serial_port_data(port); 45258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct usb_serial *serial = port->serial; 45358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 45458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 45558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata); 45658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 45758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (urb->status == 0) { 45858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct usb_ctrlrequest *req_pkt = 45958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs (struct usb_ctrlrequest *)urb->transfer_buffer; 46058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 46158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (!req_pkt) { 46258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: NULL req_pkt\n", __FUNCTION__); 46358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return; 46458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 4657bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton if ((req_pkt->bRequestType == 0xA1) && 4667bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton (req_pkt->bRequest == 0x20)) { 46758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int old_dcd_state; 46858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs unsigned char signals = *((unsigned char *) 4697bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton urb->transfer_buffer + 4707bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton sizeof(struct usb_ctrlrequest)); 47158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 47258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: signal x%x", __FUNCTION__, signals); 47358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 47458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs old_dcd_state = portdata->dcd_state; 47558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->cts_state = 1; 47658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->dcd_state = ((signals & 0x01) ? 1 : 0); 47758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->dsr_state = ((signals & 0x02) ? 1 : 0); 47858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->ri_state = ((signals & 0x08) ? 1 : 0); 47958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 4807bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton if (port->tty && !C_CLOCAL(port->tty) && 4817bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton old_dcd_state && !portdata->dcd_state) 48258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs tty_hangup(port->tty); 4837bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton } else { 4847bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: type %x req %x", __FUNCTION__, 4857bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton req_pkt->bRequestType,req_pkt->bRequest); 4867bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton } 48758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } else 48858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: error %d", __FUNCTION__, urb->status); 48958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 49058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Resubmit urb so we continue receiving IRQ data */ 49158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (urb->status != -ESHUTDOWN) { 49258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs urb->dev = serial->dev; 49358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs err = usb_submit_urb(urb, GFP_ATOMIC); 49458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (err) 4957bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: resubmit intr urb failed. (%d)", 4967bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton __FUNCTION__, err); 49758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 49858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 49958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 5007bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_write_room(struct usb_serial_port *port) 50158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 50258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct option_port_private *portdata; 50358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int i; 50458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int data_len = 0; 50558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct urb *this_urb; 50658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 50758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 50858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 509ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs for (i=0; i < N_OUT_URB; i++) { 51058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs this_urb = portdata->out_urbs[i]; 51158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (this_urb && this_urb->status != -EINPROGRESS) 51258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs data_len += OUT_BUFLEN; 513ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs } 51458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 51558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: %d", __FUNCTION__, data_len); 51658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return data_len; 51758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 51858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 5197bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_chars_in_buffer(struct usb_serial_port *port) 52058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 52158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct option_port_private *portdata; 52258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int i; 52358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int data_len = 0; 52458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct urb *this_urb; 52558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 52658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 52758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 528ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs for (i=0; i < N_OUT_URB; i++) { 52958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs this_urb = portdata->out_urbs[i]; 53058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (this_urb && this_urb->status == -EINPROGRESS) 53158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs data_len += this_urb->transfer_buffer_length; 532ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs } 53358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: %d", __FUNCTION__, data_len); 53458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return data_len; 53558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 53658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 5377bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_open(struct usb_serial_port *port, struct file *filp) 53858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 539ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 540ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct usb_serial *serial = port->serial; 541ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int i, err; 542ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct urb *urb; 54358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 54458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 54558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 54658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 54758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 54858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Set some sane defaults */ 54958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->rts_state = 1; 55058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->dtr_state = 1; 55158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 55258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Reset low level data toggle and start reading from endpoints */ 55358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < N_IN_URB; i++) { 55458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs urb = portdata->in_urbs[i]; 55558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (! urb) 55658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs continue; 55758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (urb->dev != serial->dev) { 5587bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: dev %p != %p", __FUNCTION__, 5597bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton urb->dev, serial->dev); 56058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs continue; 56158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 56258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 5637bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton /* 5647bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton * make sure endpoint data toggle is synchronized with the 5657bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton * device 5667bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton */ 56758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_clear_halt(urb->dev, urb->pipe); 56858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 56958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs err = usb_submit_urb(urb, GFP_KERNEL); 57058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (err) { 5717bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: submit urb %d failed (%d) %d", 5727bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton __FUNCTION__, i, err, 57358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs urb->transfer_buffer_length); 57458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 57558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 57658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 57758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Reset low level data toggle on out endpoints */ 57858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < N_OUT_URB; i++) { 57958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs urb = portdata->out_urbs[i]; 58058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (! urb) 58158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs continue; 58258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs urb->dev = serial->dev; 5837bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), 5847bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton usb_pipeout(urb->pipe), 0); */ 58558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 58658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 58758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port->tty->low_latency = 1; 58858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 58958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs option_send_setup(port); 59058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 59158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return (0); 59258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 59358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 5947bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_close(struct usb_serial_port *port, struct file *filp) 59558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 596ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int i; 597ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct usb_serial *serial = port->serial; 598ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 59958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 60058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 60158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 60258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 60358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->rts_state = 0; 60458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->dtr_state = 0; 60558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 60658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (serial->dev) { 60758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs option_send_setup(port); 60858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 60958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Stop reading/writing urbs */ 61058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < N_IN_URB; i++) 6117d28e74b97c8eb859fd9f5eb018bb1c75627bd55Oliver Neukum usb_kill_urb(portdata->in_urbs[i]); 61258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < N_OUT_URB; i++) 6137d28e74b97c8eb859fd9f5eb018bb1c75627bd55Oliver Neukum usb_kill_urb(portdata->out_urbs[i]); 61458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 61558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port->tty = NULL; 61658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 61758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 61858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* Helper functions used by option_setup_urbs */ 6197bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic struct urb *option_setup_urb(struct usb_serial *serial, int endpoint, 6207bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton int dir, void *ctx, char *buf, int len, 6217d12e780e003f93433d49ce78cfedf4b4c52adc5David Howells void (*callback)(struct urb *)) 62258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 62358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct urb *urb; 62458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 62558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (endpoint == -1) 62658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return NULL; /* endpoint not needed */ 62758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 62858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ 62958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (urb == NULL) { 63058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint); 63158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return NULL; 63258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 63358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 63458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Fill URB using supplied data. */ 63558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_fill_bulk_urb(urb, serial->dev, 63658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_sndbulkpipe(serial->dev, endpoint) | dir, 63758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs buf, len, callback, ctx); 63858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 63958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return urb; 64058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 64158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 64258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs/* Setup urbs */ 6437bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_setup_urbs(struct usb_serial *serial) 64458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 64514f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs int i,j; 646ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct usb_serial_port *port; 647ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 64858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 64958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 65058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 65114f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs for (i = 0; i < serial->num_ports; i++) { 65214f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs port = serial->port[i]; 65314f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs portdata = usb_get_serial_port_data(port); 65458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 65558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Do indat endpoints first */ 65614f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs for (j = 0; j < N_IN_URB; ++j) { 65714f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs portdata->in_urbs[j] = option_setup_urb (serial, 65814f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs port->bulk_in_endpointAddress, USB_DIR_IN, port, 65914f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); 66014f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs } 66158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 66214f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs /* outdat endpoints */ 66314f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs for (j = 0; j < N_OUT_URB; ++j) { 66414f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs portdata->out_urbs[j] = option_setup_urb (serial, 66514f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs port->bulk_out_endpointAddress, USB_DIR_OUT, port, 66614f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); 66714f76cc7ab75b1c9db036dcd6b247e0dcc8952beMatthias Urlichs } 66858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 66958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 67058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 6717bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_send_setup(struct usb_serial_port *port) 67258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 67358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct usb_serial *serial = port->serial; 67458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs struct option_port_private *portdata; 67558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 67658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 67758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 6788c1527132c25512563b197b35453c7da22b4d699Miguel Angel Alvarez if (port->number != 0) 6798c1527132c25512563b197b35453c7da22b4d699Miguel Angel Alvarez return 0; 6808c1527132c25512563b197b35453c7da22b4d699Miguel Angel Alvarez 68158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 68258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 68358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (port->tty) { 68458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs int val = 0; 68558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (portdata->dtr_state) 68658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs val |= 0x01; 68758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (portdata->rts_state) 68858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs val |= 0x02; 68958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 6907bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton return usb_control_msg(serial->dev, 6917bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton usb_rcvctrlpipe(serial->dev, 0), 6927bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT); 69358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 69458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 69558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return 0; 69658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 69758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 6987bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic int option_startup(struct usb_serial *serial) 69958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 700ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int i, err; 701ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct usb_serial_port *port; 702ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 70358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 70458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 70558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 70658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Now setup per port private data */ 70758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < serial->num_ports; i++) { 70858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port = serial->port[i]; 70980b6ca48321974a6566a1c9048ba34f60420bca6Eric Sesterhenn portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); 71058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (!portdata) { 7117bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: kmalloc for option_port_private (%d) failed!.", 7127bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton __FUNCTION__, i); 71358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return (1); 71458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 71558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 71658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_set_serial_port_data(port, portdata); 71758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 71858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (! port->interrupt_in_urb) 71958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs continue; 72058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); 72158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (err) 7227bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton dbg("%s: submit irq_in urb failed %d", 7237bb75aeeeec7417a961920b3f63a83007475260fAndrew Morton __FUNCTION__, err); 72458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 72558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 72658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs option_setup_urbs(serial); 72758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 72858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs return (0); 72958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 73058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 7317bb75aeeeec7417a961920b3f63a83007475260fAndrew Mortonstatic void option_shutdown(struct usb_serial *serial) 73258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs{ 733ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs int i, j; 734ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct usb_serial_port *port; 735ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs struct option_port_private *portdata; 73658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 73758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs dbg("%s", __FUNCTION__); 73858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 73958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Stop reading/writing urbs */ 74058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < serial->num_ports; ++i) { 74158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port = serial->port[i]; 74258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 74358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (j = 0; j < N_IN_URB; j++) 7447d28e74b97c8eb859fd9f5eb018bb1c75627bd55Oliver Neukum usb_kill_urb(portdata->in_urbs[j]); 74558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (j = 0; j < N_OUT_URB; j++) 7467d28e74b97c8eb859fd9f5eb018bb1c75627bd55Oliver Neukum usb_kill_urb(portdata->out_urbs[j]); 74758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 74858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 74958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Now free them */ 75058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < serial->num_ports; ++i) { 75158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port = serial->port[i]; 75258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata = usb_get_serial_port_data(port); 75358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 75458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (j = 0; j < N_IN_URB; j++) { 75558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (portdata->in_urbs[j]) { 75658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_free_urb(portdata->in_urbs[j]); 75758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->in_urbs[j] = NULL; 75858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 75958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 76058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (j = 0; j < N_OUT_URB; j++) { 76158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs if (portdata->out_urbs[j]) { 76258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs usb_free_urb(portdata->out_urbs[j]); 76358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs portdata->out_urbs[j] = NULL; 76458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 76558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 76658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 76758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 76858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs /* Now free per port private data */ 76958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs for (i = 0; i < serial->num_ports; i++) { 77058cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs port = serial->port[i]; 77158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs kfree(usb_get_serial_port_data(port)); 77258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs } 77358cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs} 77458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 77558cfe9113e485f7e04bd0eac4fc4251b330af501Matthias UrlichsMODULE_AUTHOR(DRIVER_AUTHOR); 77658cfe9113e485f7e04bd0eac4fc4251b330af501Matthias UrlichsMODULE_DESCRIPTION(DRIVER_DESC); 77758cfe9113e485f7e04bd0eac4fc4251b330af501Matthias UrlichsMODULE_VERSION(DRIVER_VERSION); 77858cfe9113e485f7e04bd0eac4fc4251b330af501Matthias UrlichsMODULE_LICENSE("GPL"); 77958cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 780ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#ifdef CONFIG_USB_DEBUG 78158cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichsmodule_param(debug, bool, S_IRUGO | S_IWUSR); 78258cfe9113e485f7e04bd0eac4fc4251b330af501Matthias UrlichsMODULE_PARM_DESC(debug, "Debug messages"); 783ba460e48064edeb57e3398eb8972c58de33f11eaMatthias Urlichs#endif 78458cfe9113e485f7e04bd0eac4fc4251b330af501Matthias Urlichs 785