123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- /*
- * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/usb.h>
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/tty.h>
- #include <linux/tty_driver.h>
- #include <linux/tty_flip.h>
- #include <linux/slab.h>
- #include <linux/usb/cdc.h>
- #include "gdm_mux.h"
- static struct workqueue_struct *mux_rx_wq;
- static u16 packet_type[TTY_MAX_COUNT] = {0xF011, 0xF010};
- #define USB_DEVICE_CDC_DATA(vid, pid) \
- .match_flags = \
- USB_DEVICE_ID_MATCH_DEVICE |\
- USB_DEVICE_ID_MATCH_INT_CLASS |\
- USB_DEVICE_ID_MATCH_INT_SUBCLASS,\
- .idVendor = vid,\
- .idProduct = pid,\
- .bInterfaceClass = USB_CLASS_COMM,\
- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM
- static const struct usb_device_id id_table[] = {
- { USB_DEVICE_CDC_DATA(0x1076, 0x8000) }, /* GCT GDM7240 */
- { USB_DEVICE_CDC_DATA(0x1076, 0x8f00) }, /* GCT GDM7243 */
- { USB_DEVICE_CDC_DATA(0x1076, 0x9000) }, /* GCT GDM7243 */
- { USB_DEVICE_CDC_DATA(0x1d74, 0x2300) }, /* LGIT Phoenix */
- {}
- };
- MODULE_DEVICE_TABLE(usb, id_table);
- static int packet_type_to_index(u16 packetType)
- {
- int i;
- for (i = 0; i < TTY_MAX_COUNT; i++) {
- if (packet_type[i] == packetType)
- return i;
- }
- return -1;
- }
- static struct mux_tx *alloc_mux_tx(int len)
- {
- struct mux_tx *t = NULL;
- t = kzalloc(sizeof(*t), GFP_ATOMIC);
- if (!t)
- return NULL;
- t->urb = usb_alloc_urb(0, GFP_ATOMIC);
- t->buf = kmalloc(MUX_TX_MAX_SIZE, GFP_ATOMIC);
- if (!t->urb || !t->buf) {
- usb_free_urb(t->urb);
- kfree(t->buf);
- kfree(t);
- return NULL;
- }
- return t;
- }
- static void free_mux_tx(struct mux_tx *t)
- {
- if (t) {
- usb_free_urb(t->urb);
- kfree(t->buf);
- kfree(t);
- }
- }
- static struct mux_rx *alloc_mux_rx(void)
- {
- struct mux_rx *r = NULL;
- r = kzalloc(sizeof(*r), GFP_KERNEL);
- if (!r)
- return NULL;
- r->urb = usb_alloc_urb(0, GFP_KERNEL);
- r->buf = kmalloc(MUX_RX_MAX_SIZE, GFP_KERNEL);
- if (!r->urb || !r->buf) {
- usb_free_urb(r->urb);
- kfree(r->buf);
- kfree(r);
- return NULL;
- }
- return r;
- }
- static void free_mux_rx(struct mux_rx *r)
- {
- if (r) {
- usb_free_urb(r->urb);
- kfree(r->buf);
- kfree(r);
- }
- }
- static struct mux_rx *get_rx_struct(struct rx_cxt *rx)
- {
- struct mux_rx *r;
- unsigned long flags;
- spin_lock_irqsave(&rx->free_list_lock, flags);
- if (list_empty(&rx->rx_free_list)) {
- spin_unlock_irqrestore(&rx->free_list_lock, flags);
- return NULL;
- }
- r = list_entry(rx->rx_free_list.prev, struct mux_rx, free_list);
- list_del(&r->free_list);
- spin_unlock_irqrestore(&rx->free_list_lock, flags);
- return r;
- }
- static void put_rx_struct(struct rx_cxt *rx, struct mux_rx *r)
- {
- unsigned long flags;
- spin_lock_irqsave(&rx->free_list_lock, flags);
- list_add_tail(&r->free_list, &rx->rx_free_list);
- spin_unlock_irqrestore(&rx->free_list_lock, flags);
- }
- static int up_to_host(struct mux_rx *r)
- {
- struct mux_dev *mux_dev = r->mux_dev;
- struct mux_pkt_header *mux_header;
- unsigned int start_flag;
- unsigned int payload_size;
- unsigned short packet_type;
- int total_len;
- u32 packet_size_sum = r->offset;
- int index;
- int ret = TO_HOST_INVALID_PACKET;
- int len = r->len;
- while (1) {
- mux_header = (struct mux_pkt_header *)(r->buf +
- packet_size_sum);
- start_flag = __le32_to_cpu(mux_header->start_flag);
- payload_size = __le32_to_cpu(mux_header->payload_size);
- packet_type = __le16_to_cpu(mux_header->packet_type);
- if (start_flag != START_FLAG) {
- pr_err("invalid START_FLAG %x\n", start_flag);
- break;
- }
- total_len = ALIGN(MUX_HEADER_SIZE + payload_size, 4);
- if (len - packet_size_sum <
- total_len) {
- pr_err("invalid payload : %d %d %04x\n",
- payload_size, len, packet_type);
- break;
- }
- index = packet_type_to_index(packet_type);
- if (index < 0) {
- pr_err("invalid index %d\n", index);
- break;
- }
- ret = r->callback(mux_header->data,
- payload_size,
- index,
- mux_dev->tty_dev,
- RECV_PACKET_PROCESS_CONTINUE
- );
- if (ret == TO_HOST_BUFFER_REQUEST_FAIL) {
- r->offset += packet_size_sum;
- break;
- }
- packet_size_sum += total_len;
- if (len - packet_size_sum <= MUX_HEADER_SIZE + 2) {
- ret = r->callback(NULL,
- 0,
- index,
- mux_dev->tty_dev,
- RECV_PACKET_PROCESS_COMPLETE
- );
- break;
- }
- }
- return ret;
- }
- static void do_rx(struct work_struct *work)
- {
- struct mux_dev *mux_dev =
- container_of(work, struct mux_dev, work_rx.work);
- struct mux_rx *r;
- struct rx_cxt *rx = &mux_dev->rx;
- unsigned long flags;
- int ret = 0;
- while (1) {
- spin_lock_irqsave(&rx->to_host_lock, flags);
- if (list_empty(&rx->to_host_list)) {
- spin_unlock_irqrestore(&rx->to_host_lock, flags);
- break;
- }
- r = list_entry(rx->to_host_list.next, struct mux_rx,
- to_host_list);
- list_del(&r->to_host_list);
- spin_unlock_irqrestore(&rx->to_host_lock, flags);
- ret = up_to_host(r);
- if (ret == TO_HOST_BUFFER_REQUEST_FAIL)
- pr_err("failed to send mux data to host\n");
- else
- put_rx_struct(rx, r);
- }
- }
- static void remove_rx_submit_list(struct mux_rx *r, struct rx_cxt *rx)
- {
- unsigned long flags;
- struct mux_rx *r_remove, *r_remove_next;
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- list_for_each_entry_safe(r_remove, r_remove_next, &rx->rx_submit_list,
- rx_submit_list) {
- if (r == r_remove)
- list_del(&r->rx_submit_list);
- }
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- }
- static void gdm_mux_rcv_complete(struct urb *urb)
- {
- struct mux_rx *r = urb->context;
- struct mux_dev *mux_dev = r->mux_dev;
- struct rx_cxt *rx = &mux_dev->rx;
- unsigned long flags;
- remove_rx_submit_list(r, rx);
- if (urb->status) {
- if (mux_dev->usb_state == PM_NORMAL)
- dev_err(&urb->dev->dev, "%s: urb status error %d\n",
- __func__, urb->status);
- put_rx_struct(rx, r);
- } else {
- r->len = r->urb->actual_length;
- spin_lock_irqsave(&rx->to_host_lock, flags);
- list_add_tail(&r->to_host_list, &rx->to_host_list);
- queue_work(mux_rx_wq, &mux_dev->work_rx.work);
- spin_unlock_irqrestore(&rx->to_host_lock, flags);
- }
- }
- static int gdm_mux_recv(void *priv_dev, int (*cb)(void *data, int len,
- int tty_index, struct tty_dev *tty_dev, int complete))
- {
- struct mux_dev *mux_dev = priv_dev;
- struct usb_device *usbdev = mux_dev->usbdev;
- struct mux_rx *r;
- struct rx_cxt *rx = &mux_dev->rx;
- unsigned long flags;
- int ret;
- if (!usbdev) {
- pr_err("device is disconnected\n");
- return -ENODEV;
- }
- r = get_rx_struct(rx);
- if (!r) {
- pr_err("get_rx_struct fail\n");
- return -ENOMEM;
- }
- r->offset = 0;
- r->mux_dev = (void *)mux_dev;
- r->callback = cb;
- mux_dev->rx_cb = cb;
- usb_fill_bulk_urb(r->urb,
- usbdev,
- usb_rcvbulkpipe(usbdev, 0x86),
- r->buf,
- MUX_RX_MAX_SIZE,
- gdm_mux_rcv_complete,
- r);
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- list_add_tail(&r->rx_submit_list, &rx->rx_submit_list);
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- ret = usb_submit_urb(r->urb, GFP_KERNEL);
- if (ret) {
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- list_del(&r->rx_submit_list);
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- put_rx_struct(rx, r);
- pr_err("usb_submit_urb ret=%d\n", ret);
- }
- usb_mark_last_busy(usbdev);
- return ret;
- }
- static void gdm_mux_send_complete(struct urb *urb)
- {
- struct mux_tx *t = urb->context;
- if (urb->status == -ECONNRESET) {
- dev_info(&urb->dev->dev, "CONNRESET\n");
- free_mux_tx(t);
- return;
- }
- if (t->callback)
- t->callback(t->cb_data);
- free_mux_tx(t);
- }
- static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
- void (*cb)(void *data), void *cb_data)
- {
- struct mux_dev *mux_dev = priv_dev;
- struct usb_device *usbdev = mux_dev->usbdev;
- struct mux_pkt_header *mux_header;
- struct mux_tx *t = NULL;
- static u32 seq_num = 1;
- int total_len;
- int ret;
- unsigned long flags;
- if (mux_dev->usb_state == PM_SUSPEND) {
- ret = usb_autopm_get_interface(mux_dev->intf);
- if (!ret)
- usb_autopm_put_interface(mux_dev->intf);
- }
- spin_lock_irqsave(&mux_dev->write_lock, flags);
- total_len = ALIGN(MUX_HEADER_SIZE + len, 4);
- t = alloc_mux_tx(total_len);
- if (!t) {
- pr_err("alloc_mux_tx fail\n");
- spin_unlock_irqrestore(&mux_dev->write_lock, flags);
- return -ENOMEM;
- }
- mux_header = (struct mux_pkt_header *)t->buf;
- mux_header->start_flag = __cpu_to_le32(START_FLAG);
- mux_header->seq_num = __cpu_to_le32(seq_num++);
- mux_header->payload_size = __cpu_to_le32((u32)len);
- mux_header->packet_type = __cpu_to_le16(packet_type[tty_index]);
- memcpy(t->buf + MUX_HEADER_SIZE, data, len);
- memset(t->buf + MUX_HEADER_SIZE + len, 0, total_len - MUX_HEADER_SIZE -
- len);
- t->len = total_len;
- t->callback = cb;
- t->cb_data = cb_data;
- usb_fill_bulk_urb(t->urb,
- usbdev,
- usb_sndbulkpipe(usbdev, 5),
- t->buf,
- total_len,
- gdm_mux_send_complete,
- t);
- ret = usb_submit_urb(t->urb, GFP_ATOMIC);
- spin_unlock_irqrestore(&mux_dev->write_lock, flags);
- if (ret)
- pr_err("usb_submit_urb Error: %d\n", ret);
- usb_mark_last_busy(usbdev);
- return ret;
- }
- static int gdm_mux_send_control(void *priv_dev, int request, int value,
- void *buf, int len)
- {
- struct mux_dev *mux_dev = priv_dev;
- struct usb_device *usbdev = mux_dev->usbdev;
- int ret;
- ret = usb_control_msg(usbdev,
- usb_sndctrlpipe(usbdev, 0),
- request,
- USB_RT_ACM,
- value,
- 2,
- buf,
- len,
- 5000
- );
- if (ret < 0)
- pr_err("usb_control_msg error: %d\n", ret);
- return ret < 0 ? ret : 0;
- }
- static void release_usb(struct mux_dev *mux_dev)
- {
- struct rx_cxt *rx = &mux_dev->rx;
- struct mux_rx *r, *r_next;
- unsigned long flags;
- cancel_delayed_work(&mux_dev->work_rx);
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
- rx_submit_list) {
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- usb_kill_urb(r->urb);
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- }
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- spin_lock_irqsave(&rx->free_list_lock, flags);
- list_for_each_entry_safe(r, r_next, &rx->rx_free_list, free_list) {
- list_del(&r->free_list);
- free_mux_rx(r);
- }
- spin_unlock_irqrestore(&rx->free_list_lock, flags);
- spin_lock_irqsave(&rx->to_host_lock, flags);
- list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) {
- if (r->mux_dev == (void *)mux_dev) {
- list_del(&r->to_host_list);
- free_mux_rx(r);
- }
- }
- spin_unlock_irqrestore(&rx->to_host_lock, flags);
- }
- static int init_usb(struct mux_dev *mux_dev)
- {
- struct mux_rx *r;
- struct rx_cxt *rx = &mux_dev->rx;
- int ret = 0;
- int i;
- spin_lock_init(&mux_dev->write_lock);
- INIT_LIST_HEAD(&rx->to_host_list);
- INIT_LIST_HEAD(&rx->rx_submit_list);
- INIT_LIST_HEAD(&rx->rx_free_list);
- spin_lock_init(&rx->to_host_lock);
- spin_lock_init(&rx->submit_list_lock);
- spin_lock_init(&rx->free_list_lock);
- for (i = 0; i < MAX_ISSUE_NUM * 2; i++) {
- r = alloc_mux_rx();
- if (!r) {
- ret = -ENOMEM;
- break;
- }
- list_add(&r->free_list, &rx->rx_free_list);
- }
- INIT_DELAYED_WORK(&mux_dev->work_rx, do_rx);
- return ret;
- }
- static int gdm_mux_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
- {
- struct mux_dev *mux_dev;
- struct tty_dev *tty_dev;
- u16 idVendor, idProduct;
- int bInterfaceNumber;
- int ret;
- int i;
- struct usb_device *usbdev = interface_to_usbdev(intf);
- bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber;
- idVendor = __le16_to_cpu(usbdev->descriptor.idVendor);
- idProduct = __le16_to_cpu(usbdev->descriptor.idProduct);
- pr_info("mux vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct);
- if (bInterfaceNumber != 2)
- return -ENODEV;
- mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
- if (!mux_dev)
- return -ENOMEM;
- tty_dev = kzalloc(sizeof(*tty_dev), GFP_KERNEL);
- if (!tty_dev) {
- ret = -ENOMEM;
- goto err_free_mux;
- }
- mux_dev->usbdev = usbdev;
- mux_dev->control_intf = intf;
- ret = init_usb(mux_dev);
- if (ret)
- goto err_free_usb;
- tty_dev->priv_dev = (void *)mux_dev;
- tty_dev->send_func = gdm_mux_send;
- tty_dev->recv_func = gdm_mux_recv;
- tty_dev->send_control = gdm_mux_send_control;
- ret = register_lte_tty_device(tty_dev, &intf->dev);
- if (ret)
- goto err_unregister_tty;
- for (i = 0; i < TTY_MAX_COUNT; i++)
- mux_dev->tty_dev = tty_dev;
- mux_dev->intf = intf;
- mux_dev->usb_state = PM_NORMAL;
- usb_get_dev(usbdev);
- usb_set_intfdata(intf, tty_dev);
- return 0;
- err_unregister_tty:
- unregister_lte_tty_device(tty_dev);
- err_free_usb:
- release_usb(mux_dev);
- kfree(tty_dev);
- err_free_mux:
- kfree(mux_dev);
- return ret;
- }
- static void gdm_mux_disconnect(struct usb_interface *intf)
- {
- struct tty_dev *tty_dev;
- struct mux_dev *mux_dev;
- struct usb_device *usbdev = interface_to_usbdev(intf);
- tty_dev = usb_get_intfdata(intf);
- mux_dev = tty_dev->priv_dev;
- release_usb(mux_dev);
- unregister_lte_tty_device(tty_dev);
- kfree(mux_dev);
- kfree(tty_dev);
- usb_put_dev(usbdev);
- }
- static int gdm_mux_suspend(struct usb_interface *intf, pm_message_t pm_msg)
- {
- struct tty_dev *tty_dev;
- struct mux_dev *mux_dev;
- struct rx_cxt *rx;
- struct mux_rx *r, *r_next;
- unsigned long flags;
- tty_dev = usb_get_intfdata(intf);
- mux_dev = tty_dev->priv_dev;
- rx = &mux_dev->rx;
- if (mux_dev->usb_state != PM_NORMAL) {
- dev_err(intf->usb_dev, "usb suspend - invalid state\n");
- return -1;
- }
- mux_dev->usb_state = PM_SUSPEND;
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
- rx_submit_list) {
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- usb_kill_urb(r->urb);
- spin_lock_irqsave(&rx->submit_list_lock, flags);
- }
- spin_unlock_irqrestore(&rx->submit_list_lock, flags);
- return 0;
- }
- static int gdm_mux_resume(struct usb_interface *intf)
- {
- struct tty_dev *tty_dev;
- struct mux_dev *mux_dev;
- u8 i;
- tty_dev = usb_get_intfdata(intf);
- mux_dev = tty_dev->priv_dev;
- if (mux_dev->usb_state != PM_SUSPEND) {
- dev_err(intf->usb_dev, "usb resume - invalid state\n");
- return -1;
- }
- mux_dev->usb_state = PM_NORMAL;
- for (i = 0; i < MAX_ISSUE_NUM; i++)
- gdm_mux_recv(mux_dev, mux_dev->rx_cb);
- return 0;
- }
- static struct usb_driver gdm_mux_driver = {
- .name = "gdm_mux",
- .probe = gdm_mux_probe,
- .disconnect = gdm_mux_disconnect,
- .id_table = id_table,
- .supports_autosuspend = 1,
- .suspend = gdm_mux_suspend,
- .resume = gdm_mux_resume,
- .reset_resume = gdm_mux_resume,
- };
- static int __init gdm_usb_mux_init(void)
- {
- mux_rx_wq = create_workqueue("mux_rx_wq");
- if (!mux_rx_wq) {
- pr_err("work queue create fail\n");
- return -1;
- }
- register_lte_tty_driver();
- return usb_register(&gdm_mux_driver);
- }
- static void __exit gdm_usb_mux_exit(void)
- {
- if (mux_rx_wq) {
- flush_workqueue(mux_rx_wq);
- destroy_workqueue(mux_rx_wq);
- }
- usb_deregister(&gdm_mux_driver);
- unregister_lte_tty_driver();
- }
- module_init(gdm_usb_mux_init);
- module_exit(gdm_usb_mux_exit);
- MODULE_DESCRIPTION("GCT LTE TTY Device Driver");
- MODULE_LICENSE("GPL");
|