The qcserial module is quite new. So new that it is part of the 2.6.30 Linux kernel, but not available as part of any previous releases. Since I wanted to use Ubuntu's regular stock kernel it was necessary to get just one file: qcserial.c
The best way is to go to http://kernel.org/ and grab the most recent kernel source code and find qcserial.c among the usb/serial kernel modules. As an alternative I pasted the sourcecode plus the makefile at the end of this post.
To install the module, copy both files, qcserial.c and Makefile to an empty directory. Build the module with make and install it with make install. The module is loaded by modprobe qcserial. After a reboot the module is loaded automatically.
If the module is loaded successfully, a serial device /dev/ttyUSB0 appears. This will be used by the firmware loader.
### BEGIN qcserial.c ##################################
/*
* Qualcomm Serial USB driver
*
* Copyright (c) 2008 QUALCOMM Incorporated.
* Copyright (c) 2009 Greg Kroah-Hartman
* Copyright (c) 2009 Novell Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
*/
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
#define DRIVER_VERSION "v0.4"
#define DRIVER_AUTHOR "Qualcomm Inc"
#define DRIVER_DESC "Qualcomm USB Serial driver"
#define NUM_BULK_EPS 1
#define MAX_BULK_EPS 6
static int debug;
static struct usb_device_id id_table[] = {
{USB_DEVICE(0x05c6, 0x9211)}, /* Acer Gobi QDL device */
{USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
{USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */
{USB_DEVICE(0x05c6, 0x9221)}, /* Sony Vaio Z31 Gobi QDL Device */
{USB_DEVICE(0x05c6, 0x9222)}, /* Sony Vaio Z31 Gobi Modem Device */
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver qcdriver = {
.name = "qcserial",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
.suspend = usb_serial_suspend,
.resume = usb_serial_resume,
.supports_autosuspend = true,
};
static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
{
int retval = -ENODEV;
__u8 nintf;
__u8 ifnum;
dbg("%s", __func__);
nintf = serial->dev->actconfig->desc.bNumInterfaces;
dbg("Num Interfaces = %d", nintf);
ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
dbg("This Interface = %d", ifnum);
switch (nintf) {
case 1:
/* QDL mode */
if (serial->interface->num_altsetting == 2) {
struct usb_host_interface *intf;
intf = &serial->interface->altsetting[1];
if (intf->desc.bNumEndpoints == 2) {
if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
dbg("QDL port found");
retval = usb_set_interface(serial->dev, ifnum, 1);
if (retval < 0) {
dev_err(&serial->dev->dev,
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
}
return retval;
}
}
}
break;
case 4:
/* Composite mode */
if (ifnum == 2) {
dbg("Modem port found");
retval = usb_set_interface(serial->dev, ifnum, 0);
if (retval < 0) {
dev_err(&serial->dev->dev,
"Could not set interface, error %d\n",
retval);
retval = -ENODEV;
}
return retval;
}
break;
default:
dev_err(&serial->dev->dev,
"unknown number of interfaces: %d\n", nintf);
return -ENODEV;
}
return retval;
}
static struct usb_serial_driver qcdevice = {
.driver = {
.owner = THIS_MODULE,
.name = "qcserial",
},
.description = "Qualcomm USB modem",
.id_table = id_table,
.usb_driver = &qcdriver,
.num_ports = 1,
.probe = qcprobe,
};
static int __init qcinit(void)
{
int retval;
retval = usb_serial_register(&qcdevice);
if (retval)
return retval;
retval = usb_register(&qcdriver);
if (retval) {
usb_serial_deregister(&qcdevice);
return retval;
}
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION "\n");
return 0;
}
static void __exit qcexit(void)
{
usb_deregister(&qcdriver);
usb_serial_deregister(&qcdevice);
}
module_init(qcinit);
module_exit(qcexit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRIVER_VERSION);
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
### EOF qcserial.c #########################################
### BEGIN Makefile #######################################
obj-m += qcserial.o
KDIR := /lib/modules/$(shell uname -r)
PWD := $(shell pwd)
all: default
default:
$(MAKE) -C $(KDIR)/build SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR)/build SUBDIRS=$(PWD) clean
@rm -f modules.order
@rm -f Module.markers
@rm -f *~
install:
cp qcserial.ko $(KDIR)/kernel/drivers/usb/serial/
depmod -a
uninstall:
rm $(KDIR)/kernel/drivers/usb/serial/qcserial.ko
rmmod qcserial
### EOF Makefile ##########################################