--- cdc-acm.c.orig 2011-10-16 14:24:53.424670474 +0900 +++ cdc-acm.c 2011-10-16 14:25:22.204670724 +0900 @@ -101,6 +101,44 @@ #endif /* + * Functions for set modem mode. + */ + +static int acm_set_modem_mode(struct acm *acm) +{ + int retval; + unsigned char modeTable[3]; + + /* SET_LINK */ + modeTable[0]=2; /* bTableLength */ + modeTable[1]=FOMA_BMODE_MODEM; + modeTable[2]=FOMA_BMODE_VENDOR1; + retval = usb_control_msg(acm->dev, + usb_sndctrlpipe(acm->dev, 0), + USB_REQ_SET_LINK, /* Set Link */ + USB_RT_VENDORSPEC, /* VendorSpecificRequest */ + 0x0000, /* ACM */ + acm->control->altsetting[0].desc.bInterfaceNumber, + modeTable, /* Data mode table */ + 2, /* Data Length 2 */ + 5000); + if( retval < 0 ) { + return retval < 0 ? retval : 0; + } + /* ACTIVATE_MODE */ + retval = usb_control_msg(acm->dev, + usb_sndctrlpipe(acm->dev, 0), + USB_REQ_ACTIVATE_MODE, /* Activate Mode */ + USB_RT_VENDORSPEC, /* VendorSpecificRequest */ + 0x0001, /* modem */ + acm->control->altsetting[0].desc.bInterfaceNumber, + NULL, /* Data none */ + 0, /* Data Length 0 */ + 5000); + return retval < 0 ? retval : 0; +} + +/* * Functions for ACM control messages. */ @@ -931,6 +969,7 @@ { struct usb_cdc_union_desc *union_header = NULL; struct usb_cdc_country_functional_desc *cfd = NULL; + struct usb_host_interface *host = NULL; unsigned char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -940,6 +979,7 @@ struct usb_endpoint_descriptor *epwrite = NULL; struct usb_device *usb_dev = interface_to_usbdev(intf); struct acm *acm; + struct foma_function_desc *foma_desc = NULL; int minor; int ctrlsize, readsize; u8 *buf; @@ -986,7 +1026,11 @@ while (buflen > 0) { if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); + if (buffer [1] == FOMA_SPECIFIC_INTERFACE) { + foma_desc = (struct foma_function_desc *) buffer; + } else { + dev_err(&intf->dev, "skipping garbage\n"); + } goto next_desc; } @@ -1044,6 +1088,21 @@ } } } else { + if (foma_desc) { + dev_dbg(&intf->dev, "btype %d\n", foma_desc->bType); + if (foma_desc->bType != FOMA_BTYPE_AB_1 && foma_desc->bType != FOMA_BTYPE_AB_2) { + return -ENODEV; + } else { + buf = (u8 *)foma_desc + 4; + for (i = 0; i < foma_desc->bFunctionLength - 4;i++) { + dev_dbg(&intf->dev, "bMode_%d %d\n", i, buf[i]); + if (buf[i] == FOMA_BMODE_MODEM) + goto check_end; + } + return -ENODEV; + } + } +check_end: control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0); data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0)); if (!control_interface || !data_interface) { @@ -1295,6 +1354,15 @@ acm_table[minor] = acm; + host = acm->control->altsetting; + if (host->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_ACM_FOMA && + host->desc.bInterfaceProtocol == USB_CDC_ACM_PROTO_AT_V25TER) { + acm_set_modem_mode(acm); + printk(KERN_INFO "this device is FOMA Mobile Phone\n"); + } else { + printk(KERN_INFO "this device is NOT FOMA Mobile Phone\n"); + } + return 0; alloc_fail8: for (i = 0; i < ACM_NW; i++) @@ -1640,6 +1708,30 @@ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_CDMA) }, + /* control interfaces with various FOMA sets */ +// { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, +// USB_CDC_ACM_PROTO_AT_V25TER) }, + + /* control interfaces with various FOMA sets */ + { USB_DEVICE_FOMA(0x0a3c,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* Legacy */ + { USB_DEVICE_FOMA(0x04da,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* Panasonic */ + { USB_DEVICE_FOMA(0x0409,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* NEC */ + { USB_DEVICE_FOMA(0x22b8,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* Motorola */ + { USB_DEVICE_FOMA(0x06d3,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* Mitsubishi */ + { USB_DEVICE_FOMA(0x04c5,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* Fujitsu */ + { USB_DEVICE_FOMA(0x04dd,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* SHARP */ + { USB_DEVICE_FOMA(0x0474,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* SANYO */ + { USB_DEVICE_FOMA(0x0fce,USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM_FOMA, + USB_CDC_ACM_PROTO_AT_V25TER) }, /* SONY */ + { } };