aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-03-12 12:13:31 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-03-12 12:13:31 -0400
commitf7a0d426f3e7ec321b8037238b6426566df36edb (patch)
tree505e7d0264500db94534f1226684f78af7757289 /drivers/net/usb
parentfde7d9049e55ab85a390be7f415d74c9f62dd0f9 (diff)
parentb790f5d1260b4c962bd066cd34ae982943c27fe1 (diff)
Merge 3.3-rc7 into usb-next
This resolves the conflict with drivers/usb/host/ehci-fsl.h that happened with changes in Linus's and this branch at the same time. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r--drivers/net/usb/Kconfig22
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/qmi_wwan.c444
3 files changed, 467 insertions, 0 deletions
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 233576127934..4bad899fb38f 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -398,6 +398,27 @@ config USB_NET_KALMIA
398 To compile this driver as a module, choose M here: the 398 To compile this driver as a module, choose M here: the
399 module will be called kalmia. 399 module will be called kalmia.
400 400
401config USB_NET_QMI_WWAN
402 tristate "QMI WWAN driver for Qualcomm MSM based 3G and LTE modems"
403 depends on USB_USBNET
404 help
405 Support WWAN LTE/3G devices based on Qualcomm Mobile Data Modem
406 (MDM) chipsets. Examples of such devices are
407 * Huawei E392/E398
408
409 This driver will only drive the ethernet part of the chips.
410 The devices require additional configuration to be usable.
411 Multiple management interfaces with linux drivers are
412 available:
413
414 * option: AT commands on /dev/ttyUSBx
415 * cdc-wdm: Qualcomm MSM Interface (QMI) protocol on /dev/cdc-wdmx
416
417 A modem manager with support for QMI is recommended.
418
419 To compile this driver as a module, choose M here: the
420 module will be called qmi_wwan.
421
401config USB_HSO 422config USB_HSO
402 tristate "Option USB High Speed Mobile Devices" 423 tristate "Option USB High Speed Mobile Devices"
403 depends on USB && RFKILL 424 depends on USB && RFKILL
@@ -461,4 +482,5 @@ config USB_VL600
461 482
462 http://ubuntuforums.org/showpost.php?p=10589647&postcount=17 483 http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
463 484
485
464endmenu 486endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index c203fa21f6b1..a2e2d72c52a0 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -29,4 +29,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
29obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o 29obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
30obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o 30obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
31obj-$(CONFIG_USB_VL600) += lg-vl600.o 31obj-$(CONFIG_USB_VL600) += lg-vl600.o
32obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o
32 33
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
new file mode 100644
index 000000000000..e14479dd2438
--- /dev/null
+++ b/drivers/net/usb/qmi_wwan.c
@@ -0,0 +1,444 @@
1/*
2 * Copyright (c) 2012 Bjørn Mork <bjorn@mork.no>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * version 2 as published by the Free Software Foundation.
7 */
8
9#include <linux/module.h>
10#include <linux/netdevice.h>
11#include <linux/ethtool.h>
12#include <linux/mii.h>
13#include <linux/usb.h>
14#include <linux/usb/cdc.h>
15#include <linux/usb/usbnet.h>
16#include <linux/usb/cdc-wdm.h>
17
18/* The name of the CDC Device Management driver */
19#define DM_DRIVER "cdc_wdm"
20
21/*
22 * This driver supports wwan (3G/LTE/?) devices using a vendor
23 * specific management protocol called Qualcomm MSM Interface (QMI) -
24 * in addition to the more common AT commands over serial interface
25 * management
26 *
27 * QMI is wrapped in CDC, using CDC encapsulated commands on the
28 * control ("master") interface of a two-interface CDC Union
29 * resembling standard CDC ECM. The devices do not use the control
30 * interface for any other CDC messages. Most likely because the
31 * management protocol is used in place of the standard CDC
32 * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE
33 *
34 * Handling a protocol like QMI is out of the scope for any driver.
35 * It can be exported as a character device using the cdc-wdm driver,
36 * which will enable userspace applications ("modem managers") to
37 * handle it. This may be required to use the network interface
38 * provided by the driver.
39 *
40 * These devices may alternatively/additionally be configured using AT
41 * commands on any of the serial interfaces driven by the option driver
42 *
43 * This driver binds only to the data ("slave") interface to enable
44 * the cdc-wdm driver to bind to the control interface. It still
45 * parses the CDC functional descriptors on the control interface to
46 * a) verify that this is indeed a handled interface (CDC Union
47 * header lists it as slave)
48 * b) get MAC address and other ethernet config from the CDC Ethernet
49 * header
50 * c) enable user bind requests against the control interface, which
51 * is the common way to bind to CDC Ethernet Control Model type
52 * interfaces
53 * d) provide a hint to the user about which interface is the
54 * corresponding management interface
55 */
56
57static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
58{
59 int status = -1;
60 struct usb_interface *control = NULL;
61 u8 *buf = intf->cur_altsetting->extra;
62 int len = intf->cur_altsetting->extralen;
63 struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc;
64 struct usb_cdc_union_desc *cdc_union = NULL;
65 struct usb_cdc_ether_desc *cdc_ether = NULL;
66 u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE;
67 u32 found = 0;
68 atomic_t *pmcount = (void *)&dev->data[1];
69
70 atomic_set(pmcount, 0);
71
72 /*
73 * assume a data interface has no additional descriptors and
74 * that the control and data interface are numbered
75 * consecutively - this holds for the Huawei device at least
76 */
77 if (len == 0 && desc->bInterfaceNumber > 0) {
78 control = usb_ifnum_to_if(dev->udev, desc->bInterfaceNumber - 1);
79 if (!control)
80 goto err;
81
82 buf = control->cur_altsetting->extra;
83 len = control->cur_altsetting->extralen;
84 dev_dbg(&intf->dev, "guessing \"control\" => %s, \"data\" => this\n",
85 dev_name(&control->dev));
86 }
87
88 while (len > 3) {
89 struct usb_descriptor_header *h = (void *)buf;
90
91 /* ignore any misplaced descriptors */
92 if (h->bDescriptorType != USB_DT_CS_INTERFACE)
93 goto next_desc;
94
95 /* buf[2] is CDC descriptor subtype */
96 switch (buf[2]) {
97 case USB_CDC_HEADER_TYPE:
98 if (found & 1 << USB_CDC_HEADER_TYPE) {
99 dev_dbg(&intf->dev, "extra CDC header\n");
100 goto err;
101 }
102 if (h->bLength != sizeof(struct usb_cdc_header_desc)) {
103 dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength);
104 goto err;
105 }
106 break;
107 case USB_CDC_UNION_TYPE:
108 if (found & 1 << USB_CDC_UNION_TYPE) {
109 dev_dbg(&intf->dev, "extra CDC union\n");
110 goto err;
111 }
112 if (h->bLength != sizeof(struct usb_cdc_union_desc)) {
113 dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength);
114 goto err;
115 }
116 cdc_union = (struct usb_cdc_union_desc *)buf;
117 break;
118 case USB_CDC_ETHERNET_TYPE:
119 if (found & 1 << USB_CDC_ETHERNET_TYPE) {
120 dev_dbg(&intf->dev, "extra CDC ether\n");
121 goto err;
122 }
123 if (h->bLength != sizeof(struct usb_cdc_ether_desc)) {
124 dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength);
125 goto err;
126 }
127 cdc_ether = (struct usb_cdc_ether_desc *)buf;
128 break;
129 }
130
131 /*
132 * Remember which CDC functional descriptors we've seen. Works
133 * for all types we care about, of which USB_CDC_ETHERNET_TYPE
134 * (0x0f) is the highest numbered
135 */
136 if (buf[2] < 32)
137 found |= 1 << buf[2];
138
139next_desc:
140 len -= h->bLength;
141 buf += h->bLength;
142 }
143
144 /* did we find all the required ones? */
145 if ((found & required) != required) {
146 dev_err(&intf->dev, "CDC functional descriptors missing\n");
147 goto err;
148 }
149
150 /* give the user a helpful hint if trying to bind to the wrong interface */
151 if (cdc_union && desc->bInterfaceNumber == cdc_union->bMasterInterface0) {
152 dev_err(&intf->dev, "leaving \"control\" interface for " DM_DRIVER " - try binding to %s instead!\n",
153 dev_name(&usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0)->dev));
154 goto err;
155 }
156
157 /* errors aren't fatal - we can live with the dynamic address */
158 if (cdc_ether) {
159 dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize);
160 usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress);
161 }
162
163 /* success! point the user to the management interface */
164 if (control)
165 dev_info(&intf->dev, "Use \"" DM_DRIVER "\" for QMI interface %s\n",
166 dev_name(&control->dev));
167
168 /* XXX: add a sysfs symlink somewhere to help management applications find it? */
169
170 /* collect bulk endpoints now that we know intf == "data" interface */
171 status = usbnet_get_endpoints(dev, intf);
172
173err:
174 return status;
175}
176
177/* using a counter to merge subdriver requests with our own into a combined state */
178static int qmi_wwan_manage_power(struct usbnet *dev, int on)
179{
180 atomic_t *pmcount = (void *)&dev->data[1];
181 int rv = 0;
182
183 dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(pmcount), on);
184
185 if ((on && atomic_add_return(1, pmcount) == 1) || (!on && atomic_dec_and_test(pmcount))) {
186 /* need autopm_get/put here to ensure the usbcore sees the new value */
187 rv = usb_autopm_get_interface(dev->intf);
188 if (rv < 0)
189 goto err;
190 dev->intf->needs_remote_wakeup = on;
191 usb_autopm_put_interface(dev->intf);
192 }
193err:
194 return rv;
195}
196
197static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on)
198{
199 struct usbnet *dev = usb_get_intfdata(intf);
200 return qmi_wwan_manage_power(dev, on);
201}
202
203/* Some devices combine the "control" and "data" functions into a
204 * single interface with all three endpoints: interrupt + bulk in and
205 * out
206 *
207 * Setting up cdc-wdm as a subdriver owning the interrupt endpoint
208 * will let it provide userspace access to the encapsulated QMI
209 * protocol without interfering with the usbnet operations.
210 */
211static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
212{
213 int rv;
214 struct usb_driver *subdriver = NULL;
215 atomic_t *pmcount = (void *)&dev->data[1];
216
217 atomic_set(pmcount, 0);
218
219 /* collect all three endpoints */
220 rv = usbnet_get_endpoints(dev, intf);
221 if (rv < 0)
222 goto err;
223
224 /* require interrupt endpoint for subdriver */
225 if (!dev->status) {
226 rv = -EINVAL;
227 goto err;
228 }
229
230 subdriver = usb_cdc_wdm_register(intf, &dev->status->desc, 512, &qmi_wwan_cdc_wdm_manage_power);
231 if (IS_ERR(subdriver)) {
232 rv = PTR_ERR(subdriver);
233 goto err;
234 }
235
236 /* can't let usbnet use the interrupt endpoint */
237 dev->status = NULL;
238
239 /* save subdriver struct for suspend/resume wrappers */
240 dev->data[0] = (unsigned long)subdriver;
241
242err:
243 return rv;
244}
245
246/* Gobi devices uses identical class/protocol codes for all interfaces regardless
247 * of function. Some of these are CDC ACM like and have the exact same endpoints
248 * we are looking for. This leaves two possible strategies for identifying the
249 * correct interface:
250 * a) hardcoding interface number, or
251 * b) use the fact that the wwan interface is the only one lacking additional
252 * (CDC functional) descriptors
253 *
254 * Let's see if we can get away with the generic b) solution.
255 */
256static int qmi_wwan_bind_gobi(struct usbnet *dev, struct usb_interface *intf)
257{
258 int rv = -EINVAL;
259
260 /* ignore any interface with additional descriptors */
261 if (intf->cur_altsetting->extralen)
262 goto err;
263
264 rv = qmi_wwan_bind_shared(dev, intf);
265err:
266 return rv;
267}
268
269static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf)
270{
271 struct usb_driver *subdriver = (void *)dev->data[0];
272
273 if (subdriver && subdriver->disconnect)
274 subdriver->disconnect(intf);
275
276 dev->data[0] = (unsigned long)NULL;
277}
278
279/* suspend/resume wrappers calling both usbnet and the cdc-wdm
280 * subdriver if present.
281 *
282 * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide
283 * wrappers for those without adding usbnet reset support first.
284 */
285static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
286{
287 struct usbnet *dev = usb_get_intfdata(intf);
288 struct usb_driver *subdriver = (void *)dev->data[0];
289 int ret;
290
291 ret = usbnet_suspend(intf, message);
292 if (ret < 0)
293 goto err;
294
295 if (subdriver && subdriver->suspend)
296 ret = subdriver->suspend(intf, message);
297 if (ret < 0)
298 usbnet_resume(intf);
299err:
300 return ret;
301}
302
303static int qmi_wwan_resume(struct usb_interface *intf)
304{
305 struct usbnet *dev = usb_get_intfdata(intf);
306 struct usb_driver *subdriver = (void *)dev->data[0];
307 int ret = 0;
308
309 if (subdriver && subdriver->resume)
310 ret = subdriver->resume(intf);
311 if (ret < 0)
312 goto err;
313 ret = usbnet_resume(intf);
314 if (ret < 0 && subdriver && subdriver->resume && subdriver->suspend)
315 subdriver->suspend(intf, PMSG_SUSPEND);
316err:
317 return ret;
318}
319
320
321static const struct driver_info qmi_wwan_info = {
322 .description = "QMI speaking wwan device",
323 .flags = FLAG_WWAN,
324 .bind = qmi_wwan_bind,
325 .manage_power = qmi_wwan_manage_power,
326};
327
328static const struct driver_info qmi_wwan_shared = {
329 .description = "QMI speaking wwan device with combined interface",
330 .flags = FLAG_WWAN,
331 .bind = qmi_wwan_bind_shared,
332 .unbind = qmi_wwan_unbind_shared,
333 .manage_power = qmi_wwan_manage_power,
334};
335
336static const struct driver_info qmi_wwan_gobi = {
337 .description = "Qualcomm Gobi wwan/QMI device",
338 .flags = FLAG_WWAN,
339 .bind = qmi_wwan_bind_gobi,
340 .unbind = qmi_wwan_unbind_shared,
341 .manage_power = qmi_wwan_manage_power,
342};
343
344#define HUAWEI_VENDOR_ID 0x12D1
345#define QMI_GOBI_DEVICE(vend, prod) \
346 USB_DEVICE(vend, prod), \
347 .driver_info = (unsigned long)&qmi_wwan_gobi
348
349static const struct usb_device_id products[] = {
350 { /* Huawei E392, E398 and possibly others sharing both device id and more... */
351 .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
352 .idVendor = HUAWEI_VENDOR_ID,
353 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
354 .bInterfaceSubClass = 1,
355 .bInterfaceProtocol = 8, /* NOTE: This is the *slave* interface of the CDC Union! */
356 .driver_info = (unsigned long)&qmi_wwan_info,
357 },
358 { /* Huawei E392, E398 and possibly others in "Windows mode"
359 * using a combined control and data interface without any CDC
360 * functional descriptors
361 */
362 .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
363 .idVendor = HUAWEI_VENDOR_ID,
364 .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
365 .bInterfaceSubClass = 1,
366 .bInterfaceProtocol = 17,
367 .driver_info = (unsigned long)&qmi_wwan_shared,
368 },
369 { /* Pantech UML290 */
370 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
371 .idVendor = 0x106c,
372 .idProduct = 0x3718,
373 .bInterfaceClass = 0xff,
374 .bInterfaceSubClass = 0xf0,
375 .bInterfaceProtocol = 0xff,
376 .driver_info = (unsigned long)&qmi_wwan_shared,
377 },
378 {QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
379 {QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
380 {QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
381 {QMI_GOBI_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
382 {QMI_GOBI_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */
383 {QMI_GOBI_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */
384 {QMI_GOBI_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */
385 {QMI_GOBI_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */
386 {QMI_GOBI_DEVICE(0x05c6, 0x9001)}, /* Generic Gobi Modem device */
387 {QMI_GOBI_DEVICE(0x05c6, 0x9002)}, /* Generic Gobi Modem device */
388 {QMI_GOBI_DEVICE(0x05c6, 0x9202)}, /* Generic Gobi Modem device */
389 {QMI_GOBI_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */
390 {QMI_GOBI_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
391 {QMI_GOBI_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
392 {QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
393 {QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
394 {QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
395 {QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
396 {QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
397 {QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
398 {QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
399 {QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
400 {QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
401 {QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
402 {QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
403 {QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
404 {QMI_GOBI_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
405 {QMI_GOBI_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
406 {QMI_GOBI_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
407 {QMI_GOBI_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
408 {QMI_GOBI_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
409 {QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
410 {QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
411 {QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
412 {QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
413 {QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
414 {QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
415 { } /* END */
416};
417MODULE_DEVICE_TABLE(usb, products);
418
419static struct usb_driver qmi_wwan_driver = {
420 .name = "qmi_wwan",
421 .id_table = products,
422 .probe = usbnet_probe,
423 .disconnect = usbnet_disconnect,
424 .suspend = qmi_wwan_suspend,
425 .resume = qmi_wwan_resume,
426 .reset_resume = qmi_wwan_resume,
427 .supports_autosuspend = 1,
428};
429
430static int __init qmi_wwan_init(void)
431{
432 return usb_register(&qmi_wwan_driver);
433}
434module_init(qmi_wwan_init);
435
436static void __exit qmi_wwan_exit(void)
437{
438 usb_deregister(&qmi_wwan_driver);
439}
440module_exit(qmi_wwan_exit);
441
442MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>");
443MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver");
444MODULE_LICENSE("GPL");