18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Garmin GPS driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2011 Hermann Kneissel herkne@gmx.de
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * The latest version of the driver can be found at
88c2ecf20Sopenharmony_ci * http://sourceforge.net/projects/garmin-gps/
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This driver has been derived from v2.1 of the visor driver.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/timer.h>
178c2ecf20Sopenharmony_ci#include <linux/tty.h>
188c2ecf20Sopenharmony_ci#include <linux/tty_driver.h>
198c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
238c2ecf20Sopenharmony_ci#include <linux/atomic.h>
248c2ecf20Sopenharmony_ci#include <linux/usb.h>
258c2ecf20Sopenharmony_ci#include <linux/usb/serial.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* the mode to be set when the port ist opened */
288c2ecf20Sopenharmony_cistatic int initial_mode = 1;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define GARMIN_VENDOR_ID             0x091E
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Version Information
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define VERSION_MAJOR	0
378c2ecf20Sopenharmony_ci#define VERSION_MINOR	36
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define _STR(s) #s
408c2ecf20Sopenharmony_ci#define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b)
418c2ecf20Sopenharmony_ci#define DRIVER_VERSION _DRIVER_VERSION(VERSION_MAJOR, VERSION_MINOR)
428c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "hermann kneissel"
438c2ecf20Sopenharmony_ci#define DRIVER_DESC "garmin gps driver"
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* error codes returned by the driver */
468c2ecf20Sopenharmony_ci#define EINVPKT	1000	/* invalid packet structure */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* size of the header of a packet using the usb protocol */
508c2ecf20Sopenharmony_ci#define GARMIN_PKTHDR_LENGTH	12
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* max. possible size of a packet using the serial protocol */
538c2ecf20Sopenharmony_ci#define MAX_SERIAL_PKT_SIZ (3 + 255 + 3)
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*  max. possible size of a packet with worst case stuffing */
568c2ecf20Sopenharmony_ci#define MAX_SERIAL_PKT_SIZ_STUFFED (MAX_SERIAL_PKT_SIZ + 256)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* size of a buffer able to hold a complete (no stuffing) packet
598c2ecf20Sopenharmony_ci * (the document protocol does not contain packets with a larger
608c2ecf20Sopenharmony_ci *  size, but in theory a packet may be 64k+12 bytes - if in
618c2ecf20Sopenharmony_ci *  later protocol versions larger packet sizes occur, this value
628c2ecf20Sopenharmony_ci *  should be increased accordingly, so the input buffer is always
638c2ecf20Sopenharmony_ci *  large enough the store a complete packet inclusive header) */
648c2ecf20Sopenharmony_ci#define GPS_IN_BUFSIZ  (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/* size of a buffer able to hold a complete (incl. stuffing) packet */
678c2ecf20Sopenharmony_ci#define GPS_OUT_BUFSIZ (GARMIN_PKTHDR_LENGTH+MAX_SERIAL_PKT_SIZ_STUFFED)
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* where to place the packet id of a serial packet, so we can
708c2ecf20Sopenharmony_ci * prepend the usb-packet header without the need to move the
718c2ecf20Sopenharmony_ci * packets data */
728c2ecf20Sopenharmony_ci#define GSP_INITIAL_OFFSET (GARMIN_PKTHDR_LENGTH-2)
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* max. size of incoming private packets (header+1 param) */
758c2ecf20Sopenharmony_ci#define PRIVPKTSIZ (GARMIN_PKTHDR_LENGTH+4)
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define GARMIN_LAYERID_TRANSPORT  0
788c2ecf20Sopenharmony_ci#define GARMIN_LAYERID_APPL      20
798c2ecf20Sopenharmony_ci/* our own layer-id to use for some control mechanisms */
808c2ecf20Sopenharmony_ci#define GARMIN_LAYERID_PRIVATE	0x01106E4B
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define GARMIN_PKTID_PVT_DATA	51
838c2ecf20Sopenharmony_ci#define GARMIN_PKTID_L001_COMMAND_DATA 10
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define CMND_ABORT_TRANSFER 0
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* packet ids used in private layer */
888c2ecf20Sopenharmony_ci#define PRIV_PKTID_SET_DEBUG	1
898c2ecf20Sopenharmony_ci#define PRIV_PKTID_SET_MODE	2
908c2ecf20Sopenharmony_ci#define PRIV_PKTID_INFO_REQ	3
918c2ecf20Sopenharmony_ci#define PRIV_PKTID_INFO_RESP	4
928c2ecf20Sopenharmony_ci#define PRIV_PKTID_RESET_REQ	5
938c2ecf20Sopenharmony_ci#define PRIV_PKTID_SET_DEF_MODE	6
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#define ETX	0x03
978c2ecf20Sopenharmony_ci#define DLE	0x10
988c2ecf20Sopenharmony_ci#define ACK	0x06
998c2ecf20Sopenharmony_ci#define NAK	0x15
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* structure used to queue incoming packets */
1028c2ecf20Sopenharmony_cistruct garmin_packet {
1038c2ecf20Sopenharmony_ci	struct list_head  list;
1048c2ecf20Sopenharmony_ci	int               seq;
1058c2ecf20Sopenharmony_ci	/* the real size of the data array, always > 0 */
1068c2ecf20Sopenharmony_ci	int               size;
1078c2ecf20Sopenharmony_ci	__u8              data[];
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* structure used to keep the current state of the driver */
1118c2ecf20Sopenharmony_cistruct garmin_data {
1128c2ecf20Sopenharmony_ci	__u8   state;
1138c2ecf20Sopenharmony_ci	__u16  flags;
1148c2ecf20Sopenharmony_ci	__u8   mode;
1158c2ecf20Sopenharmony_ci	__u8   count;
1168c2ecf20Sopenharmony_ci	__u8   pkt_id;
1178c2ecf20Sopenharmony_ci	__u32  serial_num;
1188c2ecf20Sopenharmony_ci	struct timer_list timer;
1198c2ecf20Sopenharmony_ci	struct usb_serial_port *port;
1208c2ecf20Sopenharmony_ci	int    seq_counter;
1218c2ecf20Sopenharmony_ci	int    insize;
1228c2ecf20Sopenharmony_ci	int    outsize;
1238c2ecf20Sopenharmony_ci	__u8   inbuffer [GPS_IN_BUFSIZ];  /* tty -> usb */
1248c2ecf20Sopenharmony_ci	__u8   outbuffer[GPS_OUT_BUFSIZ]; /* usb -> tty */
1258c2ecf20Sopenharmony_ci	__u8   privpkt[4*6];
1268c2ecf20Sopenharmony_ci	spinlock_t lock;
1278c2ecf20Sopenharmony_ci	struct list_head pktlist;
1288c2ecf20Sopenharmony_ci	struct usb_anchor write_urbs;
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#define STATE_NEW            0
1338c2ecf20Sopenharmony_ci#define STATE_INITIAL_DELAY  1
1348c2ecf20Sopenharmony_ci#define STATE_TIMEOUT        2
1358c2ecf20Sopenharmony_ci#define STATE_SESSION_REQ1   3
1368c2ecf20Sopenharmony_ci#define STATE_SESSION_REQ2   4
1378c2ecf20Sopenharmony_ci#define STATE_ACTIVE         5
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define STATE_RESET	     8
1408c2ecf20Sopenharmony_ci#define STATE_DISCONNECTED   9
1418c2ecf20Sopenharmony_ci#define STATE_WAIT_TTY_ACK  10
1428c2ecf20Sopenharmony_ci#define STATE_GSP_WAIT_DATA 11
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#define MODE_NATIVE          0
1458c2ecf20Sopenharmony_ci#define MODE_GARMIN_SERIAL   1
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci/* Flags used in garmin_data.flags: */
1488c2ecf20Sopenharmony_ci#define FLAGS_SESSION_REPLY_MASK  0x00C0
1498c2ecf20Sopenharmony_ci#define FLAGS_SESSION_REPLY1_SEEN 0x0080
1508c2ecf20Sopenharmony_ci#define FLAGS_SESSION_REPLY2_SEEN 0x0040
1518c2ecf20Sopenharmony_ci#define FLAGS_BULK_IN_ACTIVE      0x0020
1528c2ecf20Sopenharmony_ci#define FLAGS_BULK_IN_RESTART     0x0010
1538c2ecf20Sopenharmony_ci#define FLAGS_THROTTLED           0x0008
1548c2ecf20Sopenharmony_ci#define APP_REQ_SEEN              0x0004
1558c2ecf20Sopenharmony_ci#define APP_RESP_SEEN             0x0002
1568c2ecf20Sopenharmony_ci#define CLEAR_HALT_REQUIRED       0x0001
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci#define FLAGS_QUEUING             0x0100
1598c2ecf20Sopenharmony_ci#define FLAGS_DROP_DATA           0x0800
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#define FLAGS_GSP_SKIP            0x1000
1628c2ecf20Sopenharmony_ci#define FLAGS_GSP_DLESEEN         0x2000
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/* function prototypes */
1708c2ecf20Sopenharmony_cistatic int gsp_next_packet(struct garmin_data *garmin_data_p);
1718c2ecf20Sopenharmony_cistatic int garmin_write_bulk(struct usb_serial_port *port,
1728c2ecf20Sopenharmony_ci			     const unsigned char *buf, int count,
1738c2ecf20Sopenharmony_ci			     int dismiss_ack);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/* some special packets to be send or received */
1768c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_START_SESSION_REQ[]
1778c2ecf20Sopenharmony_ci	= { 0, 0, 0, 0,  5, 0, 0, 0, 0, 0, 0, 0 };
1788c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_START_SESSION_REPLY[]
1798c2ecf20Sopenharmony_ci	= { 0, 0, 0, 0,  6, 0, 0, 0, 4, 0, 0, 0 };
1808c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_BULK_IN_AVAIL_REPLY[]
1818c2ecf20Sopenharmony_ci	= { 0, 0, 0, 0,  2, 0, 0, 0, 0, 0, 0, 0 };
1828c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_STOP_TRANSFER_REQ[]
1838c2ecf20Sopenharmony_ci	= { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 0, 0 };
1848c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_STOP_TRANSFER_REQ_V2[]
1858c2ecf20Sopenharmony_ci	= { 20, 0, 0, 0,  10, 0, 0, 0, 1, 0, 0, 0, 0 };
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/* packets currently unused, left as documentation */
1888c2ecf20Sopenharmony_ci#if 0
1898c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_APP_LAYER_REPLY[]
1908c2ecf20Sopenharmony_ci	= { 0x14, 0, 0, 0 };
1918c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_START_PVT_REQ[]
1928c2ecf20Sopenharmony_ci	= { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 49, 0 };
1938c2ecf20Sopenharmony_cistatic unsigned char const GARMIN_STOP_PVT_REQ[]
1948c2ecf20Sopenharmony_ci	= { 20, 0, 0, 0,  10, 0, 0, 0, 2, 0, 0, 0, 50, 0 };
1958c2ecf20Sopenharmony_cistatic unsigned char const PRIVATE_REQ[]
1968c2ecf20Sopenharmony_ci	=    { 0x4B, 0x6E, 0x10, 0x01,  0xFF, 0, 0, 0, 0xFF, 0, 0, 0 };
1978c2ecf20Sopenharmony_ci#endif
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
2018c2ecf20Sopenharmony_ci	/* the same device id seems to be used by all
2028c2ecf20Sopenharmony_ci	   usb enabled GPS devices */
2038c2ecf20Sopenharmony_ci	{ USB_DEVICE(GARMIN_VENDOR_ID, 3) },
2048c2ecf20Sopenharmony_ci	{ }					/* Terminating entry */
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic inline int getLayerId(const __u8 *usbPacket)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	return __le32_to_cpup((__le32 *)(usbPacket));
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic inline int getPacketId(const __u8 *usbPacket)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return __le32_to_cpup((__le32 *)(usbPacket+4));
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic inline int getDataLength(const __u8 *usbPacket)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	return __le32_to_cpup((__le32 *)(usbPacket+8));
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/*
2268c2ecf20Sopenharmony_ci * check if the usb-packet in buf contains an abort-transfer command.
2278c2ecf20Sopenharmony_ci * (if yes, all queued data will be dropped)
2288c2ecf20Sopenharmony_ci */
2298c2ecf20Sopenharmony_cistatic inline int isAbortTrfCmnd(const unsigned char *buf)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	if (memcmp(buf, GARMIN_STOP_TRANSFER_REQ,
2328c2ecf20Sopenharmony_ci			sizeof(GARMIN_STOP_TRANSFER_REQ)) == 0 ||
2338c2ecf20Sopenharmony_ci	    memcmp(buf, GARMIN_STOP_TRANSFER_REQ_V2,
2348c2ecf20Sopenharmony_ci			sizeof(GARMIN_STOP_TRANSFER_REQ_V2)) == 0)
2358c2ecf20Sopenharmony_ci		return 1;
2368c2ecf20Sopenharmony_ci	else
2378c2ecf20Sopenharmony_ci		return 0;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void send_to_tty(struct usb_serial_port *port,
2438c2ecf20Sopenharmony_ci			char *data, unsigned int actual_length)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (actual_length) {
2468c2ecf20Sopenharmony_ci		usb_serial_debug_data(&port->dev, __func__, actual_length, data);
2478c2ecf20Sopenharmony_ci		tty_insert_flip_string(&port->port, data, actual_length);
2488c2ecf20Sopenharmony_ci		tty_flip_buffer_push(&port->port);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/******************************************************************************
2548c2ecf20Sopenharmony_ci * packet queue handling
2558c2ecf20Sopenharmony_ci ******************************************************************************/
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/*
2588c2ecf20Sopenharmony_ci * queue a received (usb-)packet for later processing
2598c2ecf20Sopenharmony_ci */
2608c2ecf20Sopenharmony_cistatic int pkt_add(struct garmin_data *garmin_data_p,
2618c2ecf20Sopenharmony_ci		   unsigned char *data, unsigned int data_length)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	int state = 0;
2648c2ecf20Sopenharmony_ci	int result = 0;
2658c2ecf20Sopenharmony_ci	unsigned long flags;
2668c2ecf20Sopenharmony_ci	struct garmin_packet *pkt;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* process only packets containing data ... */
2698c2ecf20Sopenharmony_ci	if (data_length) {
2708c2ecf20Sopenharmony_ci		pkt = kmalloc(struct_size(pkt, data, data_length), GFP_ATOMIC);
2718c2ecf20Sopenharmony_ci		if (!pkt)
2728c2ecf20Sopenharmony_ci			return 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		pkt->size = data_length;
2758c2ecf20Sopenharmony_ci		memcpy(pkt->data, data, data_length);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		spin_lock_irqsave(&garmin_data_p->lock, flags);
2788c2ecf20Sopenharmony_ci		garmin_data_p->flags |= FLAGS_QUEUING;
2798c2ecf20Sopenharmony_ci		result = list_empty(&garmin_data_p->pktlist);
2808c2ecf20Sopenharmony_ci		pkt->seq = garmin_data_p->seq_counter++;
2818c2ecf20Sopenharmony_ci		list_add_tail(&pkt->list, &garmin_data_p->pktlist);
2828c2ecf20Sopenharmony_ci		state = garmin_data_p->state;
2838c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		dev_dbg(&garmin_data_p->port->dev,
2868c2ecf20Sopenharmony_ci			"%s - added: pkt: %d - %d bytes\n", __func__,
2878c2ecf20Sopenharmony_ci			pkt->seq, data_length);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		/* in serial mode, if someone is waiting for data from
2908c2ecf20Sopenharmony_ci		   the device, convert and send the next packet to tty. */
2918c2ecf20Sopenharmony_ci		if (result && (state == STATE_GSP_WAIT_DATA))
2928c2ecf20Sopenharmony_ci			gsp_next_packet(garmin_data_p);
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci	return result;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci/* get the next pending packet */
2998c2ecf20Sopenharmony_cistatic struct garmin_packet *pkt_pop(struct garmin_data *garmin_data_p)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	unsigned long flags;
3028c2ecf20Sopenharmony_ci	struct garmin_packet *result = NULL;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
3058c2ecf20Sopenharmony_ci	if (!list_empty(&garmin_data_p->pktlist)) {
3068c2ecf20Sopenharmony_ci		result = (struct garmin_packet *)garmin_data_p->pktlist.next;
3078c2ecf20Sopenharmony_ci		list_del(&result->list);
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
3108c2ecf20Sopenharmony_ci	return result;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/* free up all queued data */
3158c2ecf20Sopenharmony_cistatic void pkt_clear(struct garmin_data *garmin_data_p)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	unsigned long flags;
3188c2ecf20Sopenharmony_ci	struct garmin_packet *result = NULL;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
3218c2ecf20Sopenharmony_ci	while (!list_empty(&garmin_data_p->pktlist)) {
3228c2ecf20Sopenharmony_ci		result = (struct garmin_packet *)garmin_data_p->pktlist.next;
3238c2ecf20Sopenharmony_ci		list_del(&result->list);
3248c2ecf20Sopenharmony_ci		kfree(result);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/******************************************************************************
3318c2ecf20Sopenharmony_ci * garmin serial protocol handling handling
3328c2ecf20Sopenharmony_ci ******************************************************************************/
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/* send an ack packet back to the tty */
3358c2ecf20Sopenharmony_cistatic int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	__u8 pkt[10];
3388c2ecf20Sopenharmony_ci	__u8 cksum = 0;
3398c2ecf20Sopenharmony_ci	__u8 *ptr = pkt;
3408c2ecf20Sopenharmony_ci	unsigned  l = 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	dev_dbg(&garmin_data_p->port->dev, "%s - pkt-id: 0x%X.\n", __func__,
3438c2ecf20Sopenharmony_ci			pkt_id);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	*ptr++ = DLE;
3468c2ecf20Sopenharmony_ci	*ptr++ = ACK;
3478c2ecf20Sopenharmony_ci	cksum += ACK;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	*ptr++ = 2;
3508c2ecf20Sopenharmony_ci	cksum += 2;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	*ptr++ = pkt_id;
3538c2ecf20Sopenharmony_ci	cksum += pkt_id;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (pkt_id == DLE)
3568c2ecf20Sopenharmony_ci		*ptr++ = DLE;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	*ptr++ = 0;
3598c2ecf20Sopenharmony_ci	*ptr++ = (-cksum) & 0xFF;
3608c2ecf20Sopenharmony_ci	*ptr++ = DLE;
3618c2ecf20Sopenharmony_ci	*ptr++ = ETX;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	l = ptr-pkt;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	send_to_tty(garmin_data_p->port, pkt, l);
3668c2ecf20Sopenharmony_ci	return 0;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci/*
3728c2ecf20Sopenharmony_ci * called for a complete packet received from tty layer
3738c2ecf20Sopenharmony_ci *
3748c2ecf20Sopenharmony_ci * the complete packet (pktid ... cksum) is in garmin_data_p->inbuf starting
3758c2ecf20Sopenharmony_ci * at GSP_INITIAL_OFFSET.
3768c2ecf20Sopenharmony_ci *
3778c2ecf20Sopenharmony_ci * count - number of bytes in the input buffer including space reserved for
3788c2ecf20Sopenharmony_ci *         the usb header: GSP_INITIAL_OFFSET + number of bytes in packet
3798c2ecf20Sopenharmony_ci *         (including pkt-id, data-length a. cksum)
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_cistatic int gsp_rec_packet(struct garmin_data *garmin_data_p, int count)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct device *dev = &garmin_data_p->port->dev;
3848c2ecf20Sopenharmony_ci	unsigned long flags;
3858c2ecf20Sopenharmony_ci	const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
3868c2ecf20Sopenharmony_ci	__le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
3878c2ecf20Sopenharmony_ci	int cksum = 0;
3888c2ecf20Sopenharmony_ci	int n = 0;
3898c2ecf20Sopenharmony_ci	int pktid = recpkt[0];
3908c2ecf20Sopenharmony_ci	int size = recpkt[1];
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	usb_serial_debug_data(&garmin_data_p->port->dev, __func__,
3938c2ecf20Sopenharmony_ci			      count-GSP_INITIAL_OFFSET, recpkt);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (size != (count-GSP_INITIAL_OFFSET-3)) {
3968c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - invalid size, expected %d bytes, got %d\n",
3978c2ecf20Sopenharmony_ci			__func__, size, (count-GSP_INITIAL_OFFSET-3));
3988c2ecf20Sopenharmony_ci		return -EINVPKT;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	cksum += *recpkt++;
4028c2ecf20Sopenharmony_ci	cksum += *recpkt++;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* sanity check, remove after test ... */
4058c2ecf20Sopenharmony_ci	if ((__u8 *)&(usbdata[3]) != recpkt) {
4068c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - ptr mismatch %p - %p\n", __func__,
4078c2ecf20Sopenharmony_ci			&(usbdata[4]), recpkt);
4088c2ecf20Sopenharmony_ci		return -EINVPKT;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	while (n < size) {
4128c2ecf20Sopenharmony_ci		cksum += *recpkt++;
4138c2ecf20Sopenharmony_ci		n++;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (((cksum + *recpkt) & 0xff) != 0) {
4178c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s - invalid checksum, expected %02x, got %02x\n",
4188c2ecf20Sopenharmony_ci			__func__, -cksum & 0xff, *recpkt);
4198c2ecf20Sopenharmony_ci		return -EINVPKT;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL);
4238c2ecf20Sopenharmony_ci	usbdata[1] = __cpu_to_le32(pktid);
4248c2ecf20Sopenharmony_ci	usbdata[2] = __cpu_to_le32(size);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	garmin_write_bulk(garmin_data_p->port, garmin_data_p->inbuffer,
4278c2ecf20Sopenharmony_ci			   GARMIN_PKTHDR_LENGTH+size, 0);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* if this was an abort-transfer command, flush all
4308c2ecf20Sopenharmony_ci	   queued data. */
4318c2ecf20Sopenharmony_ci	if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
4328c2ecf20Sopenharmony_ci		spin_lock_irqsave(&garmin_data_p->lock, flags);
4338c2ecf20Sopenharmony_ci		garmin_data_p->flags |= FLAGS_DROP_DATA;
4348c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
4358c2ecf20Sopenharmony_ci		pkt_clear(garmin_data_p);
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return count;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/*
4448c2ecf20Sopenharmony_ci * Called for data received from tty
4458c2ecf20Sopenharmony_ci *
4468c2ecf20Sopenharmony_ci * buf contains the data read, it may span more than one packet or even
4478c2ecf20Sopenharmony_ci * incomplete packets
4488c2ecf20Sopenharmony_ci *
4498c2ecf20Sopenharmony_ci * input record should be a serial-record, but it may not be complete.
4508c2ecf20Sopenharmony_ci * Copy it into our local buffer, until an etx is seen (or an error
4518c2ecf20Sopenharmony_ci * occurs).
4528c2ecf20Sopenharmony_ci * Once the record is complete, convert into a usb packet and send it
4538c2ecf20Sopenharmony_ci * to the bulk pipe, send an ack back to the tty.
4548c2ecf20Sopenharmony_ci *
4558c2ecf20Sopenharmony_ci * If the input is an ack, just send the last queued packet to the
4568c2ecf20Sopenharmony_ci * tty layer.
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * if the input is an abort command, drop all queued data.
4598c2ecf20Sopenharmony_ci */
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int gsp_receive(struct garmin_data *garmin_data_p,
4628c2ecf20Sopenharmony_ci		       const unsigned char *buf, int count)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct device *dev = &garmin_data_p->port->dev;
4658c2ecf20Sopenharmony_ci	unsigned long flags;
4668c2ecf20Sopenharmony_ci	int offs = 0;
4678c2ecf20Sopenharmony_ci	int ack_or_nak_seen = 0;
4688c2ecf20Sopenharmony_ci	__u8 *dest;
4698c2ecf20Sopenharmony_ci	int size;
4708c2ecf20Sopenharmony_ci	/* dleSeen: set if last byte read was a DLE */
4718c2ecf20Sopenharmony_ci	int dleSeen;
4728c2ecf20Sopenharmony_ci	/* skip: if set, skip incoming data until possible start of
4738c2ecf20Sopenharmony_ci	 *       new packet
4748c2ecf20Sopenharmony_ci	 */
4758c2ecf20Sopenharmony_ci	int skip;
4768c2ecf20Sopenharmony_ci	__u8 data;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
4798c2ecf20Sopenharmony_ci	dest = garmin_data_p->inbuffer;
4808c2ecf20Sopenharmony_ci	size = garmin_data_p->insize;
4818c2ecf20Sopenharmony_ci	dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
4828c2ecf20Sopenharmony_ci	skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
4838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* dev_dbg(dev, "%s - dle=%d skip=%d size=%d count=%d\n",
4868c2ecf20Sopenharmony_ci		__func__, dleSeen, skip, size, count); */
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (size == 0)
4898c2ecf20Sopenharmony_ci		size = GSP_INITIAL_OFFSET;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	while (offs < count) {
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		data = *(buf+offs);
4948c2ecf20Sopenharmony_ci		offs++;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		if (data == DLE) {
4978c2ecf20Sopenharmony_ci			if (skip) { /* start of a new pkt */
4988c2ecf20Sopenharmony_ci				skip = 0;
4998c2ecf20Sopenharmony_ci				size = GSP_INITIAL_OFFSET;
5008c2ecf20Sopenharmony_ci				dleSeen = 1;
5018c2ecf20Sopenharmony_ci			} else if (dleSeen) {
5028c2ecf20Sopenharmony_ci				dest[size++] = data;
5038c2ecf20Sopenharmony_ci				dleSeen = 0;
5048c2ecf20Sopenharmony_ci			} else {
5058c2ecf20Sopenharmony_ci				dleSeen = 1;
5068c2ecf20Sopenharmony_ci			}
5078c2ecf20Sopenharmony_ci		} else if (data == ETX) {
5088c2ecf20Sopenharmony_ci			if (dleSeen) {
5098c2ecf20Sopenharmony_ci				/* packet complete */
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci				data = dest[GSP_INITIAL_OFFSET];
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci				if (data == ACK) {
5148c2ecf20Sopenharmony_ci					ack_or_nak_seen = ACK;
5158c2ecf20Sopenharmony_ci					dev_dbg(dev, "ACK packet complete.\n");
5168c2ecf20Sopenharmony_ci				} else if (data == NAK) {
5178c2ecf20Sopenharmony_ci					ack_or_nak_seen = NAK;
5188c2ecf20Sopenharmony_ci					dev_dbg(dev, "NAK packet complete.\n");
5198c2ecf20Sopenharmony_ci				} else {
5208c2ecf20Sopenharmony_ci					dev_dbg(dev, "packet complete - id=0x%X.\n",
5218c2ecf20Sopenharmony_ci							data);
5228c2ecf20Sopenharmony_ci					gsp_rec_packet(garmin_data_p, size);
5238c2ecf20Sopenharmony_ci				}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci				skip = 1;
5268c2ecf20Sopenharmony_ci				size = GSP_INITIAL_OFFSET;
5278c2ecf20Sopenharmony_ci				dleSeen = 0;
5288c2ecf20Sopenharmony_ci			} else {
5298c2ecf20Sopenharmony_ci				dest[size++] = data;
5308c2ecf20Sopenharmony_ci			}
5318c2ecf20Sopenharmony_ci		} else if (!skip) {
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci			if (dleSeen) {
5348c2ecf20Sopenharmony_ci				size = GSP_INITIAL_OFFSET;
5358c2ecf20Sopenharmony_ci				dleSeen = 0;
5368c2ecf20Sopenharmony_ci			}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci			dest[size++] = data;
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		if (size >= GPS_IN_BUFSIZ) {
5428c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - packet too large.\n", __func__);
5438c2ecf20Sopenharmony_ci			skip = 1;
5448c2ecf20Sopenharmony_ci			size = GSP_INITIAL_OFFSET;
5458c2ecf20Sopenharmony_ci			dleSeen = 0;
5468c2ecf20Sopenharmony_ci		}
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	garmin_data_p->insize = size;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	/* copy flags back to structure */
5548c2ecf20Sopenharmony_ci	if (skip)
5558c2ecf20Sopenharmony_ci		garmin_data_p->flags |= FLAGS_GSP_SKIP;
5568c2ecf20Sopenharmony_ci	else
5578c2ecf20Sopenharmony_ci		garmin_data_p->flags &= ~FLAGS_GSP_SKIP;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (dleSeen)
5608c2ecf20Sopenharmony_ci		garmin_data_p->flags |= FLAGS_GSP_DLESEEN;
5618c2ecf20Sopenharmony_ci	else
5628c2ecf20Sopenharmony_ci		garmin_data_p->flags &= ~FLAGS_GSP_DLESEEN;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (ack_or_nak_seen) {
5678c2ecf20Sopenharmony_ci		if (gsp_next_packet(garmin_data_p) > 0)
5688c2ecf20Sopenharmony_ci			garmin_data_p->state = STATE_ACTIVE;
5698c2ecf20Sopenharmony_ci		else
5708c2ecf20Sopenharmony_ci			garmin_data_p->state = STATE_GSP_WAIT_DATA;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	return count;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/*
5788c2ecf20Sopenharmony_ci * Sends a usb packet to the tty
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * Assumes, that all packages and at an usb-packet boundary.
5818c2ecf20Sopenharmony_ci *
5828c2ecf20Sopenharmony_ci * return <0 on error, 0 if packet is incomplete or > 0 if packet was sent
5838c2ecf20Sopenharmony_ci */
5848c2ecf20Sopenharmony_cistatic int gsp_send(struct garmin_data *garmin_data_p,
5858c2ecf20Sopenharmony_ci		    const unsigned char *buf, int count)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct device *dev = &garmin_data_p->port->dev;
5888c2ecf20Sopenharmony_ci	const unsigned char *src;
5898c2ecf20Sopenharmony_ci	unsigned char *dst;
5908c2ecf20Sopenharmony_ci	int pktid = 0;
5918c2ecf20Sopenharmony_ci	int datalen = 0;
5928c2ecf20Sopenharmony_ci	int cksum = 0;
5938c2ecf20Sopenharmony_ci	int i = 0;
5948c2ecf20Sopenharmony_ci	int k;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - state %d - %d bytes.\n", __func__,
5978c2ecf20Sopenharmony_ci		garmin_data_p->state, count);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	k = garmin_data_p->outsize;
6008c2ecf20Sopenharmony_ci	if ((k+count) > GPS_OUT_BUFSIZ) {
6018c2ecf20Sopenharmony_ci		dev_dbg(dev, "packet too large\n");
6028c2ecf20Sopenharmony_ci		garmin_data_p->outsize = 0;
6038c2ecf20Sopenharmony_ci		return -4;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	memcpy(garmin_data_p->outbuffer+k, buf, count);
6078c2ecf20Sopenharmony_ci	k += count;
6088c2ecf20Sopenharmony_ci	garmin_data_p->outsize = k;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (k >= GARMIN_PKTHDR_LENGTH) {
6118c2ecf20Sopenharmony_ci		pktid  = getPacketId(garmin_data_p->outbuffer);
6128c2ecf20Sopenharmony_ci		datalen = getDataLength(garmin_data_p->outbuffer);
6138c2ecf20Sopenharmony_ci		i = GARMIN_PKTHDR_LENGTH + datalen;
6148c2ecf20Sopenharmony_ci		if (k < i)
6158c2ecf20Sopenharmony_ci			return 0;
6168c2ecf20Sopenharmony_ci	} else {
6178c2ecf20Sopenharmony_ci		return 0;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s - %d bytes in buffer, %d bytes in pkt.\n", __func__, k, i);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	/* garmin_data_p->outbuffer now contains a complete packet */
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	usb_serial_debug_data(&garmin_data_p->port->dev, __func__, k,
6258c2ecf20Sopenharmony_ci			      garmin_data_p->outbuffer);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	garmin_data_p->outsize = 0;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	if (getLayerId(garmin_data_p->outbuffer) != GARMIN_LAYERID_APPL) {
6308c2ecf20Sopenharmony_ci		dev_dbg(dev, "not an application packet (%d)\n",
6318c2ecf20Sopenharmony_ci				getLayerId(garmin_data_p->outbuffer));
6328c2ecf20Sopenharmony_ci		return -1;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (pktid > 255) {
6368c2ecf20Sopenharmony_ci		dev_dbg(dev, "packet-id %d too large\n", pktid);
6378c2ecf20Sopenharmony_ci		return -2;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (datalen > 255) {
6418c2ecf20Sopenharmony_ci		dev_dbg(dev, "packet-size %d too large\n", datalen);
6428c2ecf20Sopenharmony_ci		return -3;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	/* the serial protocol should be able to handle this packet */
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	k = 0;
6488c2ecf20Sopenharmony_ci	src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
6498c2ecf20Sopenharmony_ci	for (i = 0; i < datalen; i++) {
6508c2ecf20Sopenharmony_ci		if (*src++ == DLE)
6518c2ecf20Sopenharmony_ci			k++;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
6558c2ecf20Sopenharmony_ci	if (k > (GARMIN_PKTHDR_LENGTH-2)) {
6568c2ecf20Sopenharmony_ci		/* can't add stuffing DLEs in place, move data to end
6578c2ecf20Sopenharmony_ci		   of buffer ... */
6588c2ecf20Sopenharmony_ci		dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen;
6598c2ecf20Sopenharmony_ci		memcpy(dst, src, datalen);
6608c2ecf20Sopenharmony_ci		src = dst;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	dst = garmin_data_p->outbuffer;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	*dst++ = DLE;
6668c2ecf20Sopenharmony_ci	*dst++ = pktid;
6678c2ecf20Sopenharmony_ci	cksum += pktid;
6688c2ecf20Sopenharmony_ci	*dst++ = datalen;
6698c2ecf20Sopenharmony_ci	cksum += datalen;
6708c2ecf20Sopenharmony_ci	if (datalen == DLE)
6718c2ecf20Sopenharmony_ci		*dst++ = DLE;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	for (i = 0; i < datalen; i++) {
6748c2ecf20Sopenharmony_ci		__u8 c = *src++;
6758c2ecf20Sopenharmony_ci		*dst++ = c;
6768c2ecf20Sopenharmony_ci		cksum += c;
6778c2ecf20Sopenharmony_ci		if (c == DLE)
6788c2ecf20Sopenharmony_ci			*dst++ = DLE;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	cksum = -cksum & 0xFF;
6828c2ecf20Sopenharmony_ci	*dst++ = cksum;
6838c2ecf20Sopenharmony_ci	if (cksum == DLE)
6848c2ecf20Sopenharmony_ci		*dst++ = DLE;
6858c2ecf20Sopenharmony_ci	*dst++ = DLE;
6868c2ecf20Sopenharmony_ci	*dst++ = ETX;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	i = dst-garmin_data_p->outbuffer;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	send_to_tty(garmin_data_p->port, garmin_data_p->outbuffer, i);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	garmin_data_p->pkt_id = pktid;
6938c2ecf20Sopenharmony_ci	garmin_data_p->state  = STATE_WAIT_TTY_ACK;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	return i;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/*
7008c2ecf20Sopenharmony_ci * Process the next pending data packet - if there is one
7018c2ecf20Sopenharmony_ci */
7028c2ecf20Sopenharmony_cistatic int gsp_next_packet(struct garmin_data *garmin_data_p)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	int result = 0;
7058c2ecf20Sopenharmony_ci	struct garmin_packet *pkt = NULL;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	while ((pkt = pkt_pop(garmin_data_p)) != NULL) {
7088c2ecf20Sopenharmony_ci		dev_dbg(&garmin_data_p->port->dev, "%s - next pkt: %d\n", __func__, pkt->seq);
7098c2ecf20Sopenharmony_ci		result = gsp_send(garmin_data_p, pkt->data, pkt->size);
7108c2ecf20Sopenharmony_ci		if (result > 0) {
7118c2ecf20Sopenharmony_ci			kfree(pkt);
7128c2ecf20Sopenharmony_ci			return result;
7138c2ecf20Sopenharmony_ci		}
7148c2ecf20Sopenharmony_ci		kfree(pkt);
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci	return result;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/******************************************************************************
7228c2ecf20Sopenharmony_ci * garmin native mode
7238c2ecf20Sopenharmony_ci ******************************************************************************/
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci/*
7278c2ecf20Sopenharmony_ci * Called for data received from tty
7288c2ecf20Sopenharmony_ci *
7298c2ecf20Sopenharmony_ci * The input data is expected to be in garmin usb-packet format.
7308c2ecf20Sopenharmony_ci *
7318c2ecf20Sopenharmony_ci * buf contains the data read, it may span more than one packet
7328c2ecf20Sopenharmony_ci * or even incomplete packets
7338c2ecf20Sopenharmony_ci */
7348c2ecf20Sopenharmony_cistatic int nat_receive(struct garmin_data *garmin_data_p,
7358c2ecf20Sopenharmony_ci		       const unsigned char *buf, int count)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	unsigned long flags;
7388c2ecf20Sopenharmony_ci	__u8 *dest;
7398c2ecf20Sopenharmony_ci	int offs = 0;
7408c2ecf20Sopenharmony_ci	int result = count;
7418c2ecf20Sopenharmony_ci	int len;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	while (offs < count) {
7448c2ecf20Sopenharmony_ci		/* if buffer contains header, copy rest of data */
7458c2ecf20Sopenharmony_ci		if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH)
7468c2ecf20Sopenharmony_ci			len = GARMIN_PKTHDR_LENGTH
7478c2ecf20Sopenharmony_ci			      +getDataLength(garmin_data_p->inbuffer);
7488c2ecf20Sopenharmony_ci		else
7498c2ecf20Sopenharmony_ci			len = GARMIN_PKTHDR_LENGTH;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		if (len >= GPS_IN_BUFSIZ) {
7528c2ecf20Sopenharmony_ci			/* seems to be an invalid packet, ignore rest
7538c2ecf20Sopenharmony_ci			   of input */
7548c2ecf20Sopenharmony_ci			dev_dbg(&garmin_data_p->port->dev,
7558c2ecf20Sopenharmony_ci				"%s - packet size too large: %d\n",
7568c2ecf20Sopenharmony_ci				__func__, len);
7578c2ecf20Sopenharmony_ci			garmin_data_p->insize = 0;
7588c2ecf20Sopenharmony_ci			count = 0;
7598c2ecf20Sopenharmony_ci			result = -EINVPKT;
7608c2ecf20Sopenharmony_ci		} else {
7618c2ecf20Sopenharmony_ci			len -= garmin_data_p->insize;
7628c2ecf20Sopenharmony_ci			if (len > (count-offs))
7638c2ecf20Sopenharmony_ci				len = (count-offs);
7648c2ecf20Sopenharmony_ci			if (len > 0) {
7658c2ecf20Sopenharmony_ci				dest = garmin_data_p->inbuffer
7668c2ecf20Sopenharmony_ci						+ garmin_data_p->insize;
7678c2ecf20Sopenharmony_ci				memcpy(dest, buf+offs, len);
7688c2ecf20Sopenharmony_ci				garmin_data_p->insize += len;
7698c2ecf20Sopenharmony_ci				offs += len;
7708c2ecf20Sopenharmony_ci			}
7718c2ecf20Sopenharmony_ci		}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		/* do we have a complete packet ? */
7748c2ecf20Sopenharmony_ci		if (garmin_data_p->insize >= GARMIN_PKTHDR_LENGTH) {
7758c2ecf20Sopenharmony_ci			len = GARMIN_PKTHDR_LENGTH+
7768c2ecf20Sopenharmony_ci			   getDataLength(garmin_data_p->inbuffer);
7778c2ecf20Sopenharmony_ci			if (garmin_data_p->insize >= len) {
7788c2ecf20Sopenharmony_ci				garmin_write_bulk(garmin_data_p->port,
7798c2ecf20Sopenharmony_ci						   garmin_data_p->inbuffer,
7808c2ecf20Sopenharmony_ci						   len, 0);
7818c2ecf20Sopenharmony_ci				garmin_data_p->insize = 0;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci				/* if this was an abort-transfer command,
7848c2ecf20Sopenharmony_ci				   flush all queued data. */
7858c2ecf20Sopenharmony_ci				if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
7868c2ecf20Sopenharmony_ci					spin_lock_irqsave(&garmin_data_p->lock,
7878c2ecf20Sopenharmony_ci									flags);
7888c2ecf20Sopenharmony_ci					garmin_data_p->flags |= FLAGS_DROP_DATA;
7898c2ecf20Sopenharmony_ci					spin_unlock_irqrestore(
7908c2ecf20Sopenharmony_ci						&garmin_data_p->lock, flags);
7918c2ecf20Sopenharmony_ci					pkt_clear(garmin_data_p);
7928c2ecf20Sopenharmony_ci				}
7938c2ecf20Sopenharmony_ci			}
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci	return result;
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci/******************************************************************************
8018c2ecf20Sopenharmony_ci * private packets
8028c2ecf20Sopenharmony_ci ******************************************************************************/
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic void priv_status_resp(struct usb_serial_port *port)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
8078c2ecf20Sopenharmony_ci	__le32 *pkt = (__le32 *)garmin_data_p->privpkt;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	pkt[0] = __cpu_to_le32(GARMIN_LAYERID_PRIVATE);
8108c2ecf20Sopenharmony_ci	pkt[1] = __cpu_to_le32(PRIV_PKTID_INFO_RESP);
8118c2ecf20Sopenharmony_ci	pkt[2] = __cpu_to_le32(12);
8128c2ecf20Sopenharmony_ci	pkt[3] = __cpu_to_le32(VERSION_MAJOR << 16 | VERSION_MINOR);
8138c2ecf20Sopenharmony_ci	pkt[4] = __cpu_to_le32(garmin_data_p->mode);
8148c2ecf20Sopenharmony_ci	pkt[5] = __cpu_to_le32(garmin_data_p->serial_num);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	send_to_tty(port, (__u8 *)pkt, 6 * 4);
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci/******************************************************************************
8218c2ecf20Sopenharmony_ci * Garmin specific driver functions
8228c2ecf20Sopenharmony_ci ******************************************************************************/
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic int process_resetdev_request(struct usb_serial_port *port)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	unsigned long flags;
8278c2ecf20Sopenharmony_ci	int status;
8288c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
8318c2ecf20Sopenharmony_ci	garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED);
8328c2ecf20Sopenharmony_ci	garmin_data_p->state = STATE_RESET;
8338c2ecf20Sopenharmony_ci	garmin_data_p->serial_num = 0;
8348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
8378c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - usb_reset_device\n", __func__);
8388c2ecf20Sopenharmony_ci	status = usb_reset_device(port->serial->dev);
8398c2ecf20Sopenharmony_ci	if (status)
8408c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - usb_reset_device failed: %d\n",
8418c2ecf20Sopenharmony_ci			__func__, status);
8428c2ecf20Sopenharmony_ci	return status;
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci/*
8488c2ecf20Sopenharmony_ci * clear all cached data
8498c2ecf20Sopenharmony_ci */
8508c2ecf20Sopenharmony_cistatic int garmin_clear(struct garmin_data *garmin_data_p)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	unsigned long flags;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	/* flush all queued data */
8558c2ecf20Sopenharmony_ci	pkt_clear(garmin_data_p);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
8588c2ecf20Sopenharmony_ci	garmin_data_p->insize = 0;
8598c2ecf20Sopenharmony_ci	garmin_data_p->outsize = 0;
8608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	return 0;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic int garmin_init_session(struct usb_serial_port *port)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
8698c2ecf20Sopenharmony_ci	int status;
8708c2ecf20Sopenharmony_ci	int i;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
8758c2ecf20Sopenharmony_ci	if (status) {
8768c2ecf20Sopenharmony_ci		dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
8778c2ecf20Sopenharmony_ci				status);
8788c2ecf20Sopenharmony_ci		return status;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/*
8828c2ecf20Sopenharmony_ci	 * using the initialization method from gpsbabel. See comments in
8838c2ecf20Sopenharmony_ci	 * gpsbabel/jeeps/gpslibusb.c gusb_reset_toggles()
8848c2ecf20Sopenharmony_ci	 */
8858c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - starting session ...\n", __func__);
8868c2ecf20Sopenharmony_ci	garmin_data_p->state = STATE_ACTIVE;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
8898c2ecf20Sopenharmony_ci		status = garmin_write_bulk(port, GARMIN_START_SESSION_REQ,
8908c2ecf20Sopenharmony_ci				sizeof(GARMIN_START_SESSION_REQ), 0);
8918c2ecf20Sopenharmony_ci		if (status < 0)
8928c2ecf20Sopenharmony_ci			goto err_kill_urbs;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return 0;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cierr_kill_urbs:
8988c2ecf20Sopenharmony_ci	usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
8998c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	return status;
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int garmin_open(struct tty_struct *tty, struct usb_serial_port *port)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	unsigned long flags;
9098c2ecf20Sopenharmony_ci	int status = 0;
9108c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
9138c2ecf20Sopenharmony_ci	garmin_data_p->mode  = initial_mode;
9148c2ecf20Sopenharmony_ci	garmin_data_p->count = 0;
9158c2ecf20Sopenharmony_ci	garmin_data_p->flags &= FLAGS_SESSION_REPLY1_SEEN;
9168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	/* shutdown any bulk reads that might be going on */
9198c2ecf20Sopenharmony_ci	usb_kill_urb(port->read_urb);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (garmin_data_p->state == STATE_RESET)
9228c2ecf20Sopenharmony_ci		status = garmin_init_session(port);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	garmin_data_p->state = STATE_ACTIVE;
9258c2ecf20Sopenharmony_ci	return status;
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic void garmin_close(struct usb_serial_port *port)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	dev_dbg(&port->dev, "%s - mode=%d state=%d flags=0x%X\n",
9348c2ecf20Sopenharmony_ci		__func__, garmin_data_p->mode, garmin_data_p->state,
9358c2ecf20Sopenharmony_ci		garmin_data_p->flags);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	garmin_clear(garmin_data_p);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/* shutdown our urbs */
9408c2ecf20Sopenharmony_ci	usb_kill_urb(port->read_urb);
9418c2ecf20Sopenharmony_ci	usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* keep reset state so we know that we must start a new session */
9448c2ecf20Sopenharmony_ci	if (garmin_data_p->state != STATE_RESET)
9458c2ecf20Sopenharmony_ci		garmin_data_p->state = STATE_DISCONNECTED;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic void garmin_write_bulk_callback(struct urb *urb)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	if (port) {
9548c2ecf20Sopenharmony_ci		struct garmin_data *garmin_data_p =
9558c2ecf20Sopenharmony_ci					usb_get_serial_port_data(port);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci		if (getLayerId(urb->transfer_buffer) == GARMIN_LAYERID_APPL) {
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci			if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
9608c2ecf20Sopenharmony_ci				gsp_send_ack(garmin_data_p,
9618c2ecf20Sopenharmony_ci					((__u8 *)urb->transfer_buffer)[4]);
9628c2ecf20Sopenharmony_ci			}
9638c2ecf20Sopenharmony_ci		}
9648c2ecf20Sopenharmony_ci		usb_serial_port_softint(port);
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* Ignore errors that resulted from garmin_write_bulk with
9688c2ecf20Sopenharmony_ci	   dismiss_ack = 1 */
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	/* free up the transfer buffer, as usb_free_urb() does not do this */
9718c2ecf20Sopenharmony_ci	kfree(urb->transfer_buffer);
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic int garmin_write_bulk(struct usb_serial_port *port,
9768c2ecf20Sopenharmony_ci			      const unsigned char *buf, int count,
9778c2ecf20Sopenharmony_ci			      int dismiss_ack)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	unsigned long flags;
9808c2ecf20Sopenharmony_ci	struct usb_serial *serial = port->serial;
9818c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
9828c2ecf20Sopenharmony_ci	struct urb *urb;
9838c2ecf20Sopenharmony_ci	unsigned char *buffer;
9848c2ecf20Sopenharmony_ci	int status;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&garmin_data_p->lock, flags);
9878c2ecf20Sopenharmony_ci	garmin_data_p->flags &= ~FLAGS_DROP_DATA;
9888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&garmin_data_p->lock, flags);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	buffer = kmalloc(count, GFP_ATOMIC);
9918c2ecf20Sopenharmony_ci	if (!buffer)
9928c2ecf20Sopenharmony_ci		return -ENOMEM;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_ATOMIC);
9958c2ecf20Sopenharmony_ci	if (!urb) {
9968c2ecf20Sopenharmony_ci		kfree(buffer);
9978c2ecf20Sopenharmony_ci		return -ENOMEM;
9988c2ecf20Sopenharmony_ci	}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	memcpy(buffer, buf, count);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, count, buffer);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, serial->dev,
10058c2ecf20Sopenharmony_ci				usb_sndbulkpipe(serial->dev,
10068c2ecf20Sopenharmony_ci					port->bulk_out_endpointAddress),
10078c2ecf20Sopenharmony_ci				buffer, count,
10088c2ecf20Sopenharmony_ci				garmin_write_bulk_callback,
10098c2ecf20Sopenharmony_ci				dismiss_ack ? NULL : port);
10108c2ecf20Sopenharmony_ci	urb->transfer_flags |= URB_ZERO_PACKET;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (getLayerId(buffer) == GARMIN_LAYERID_APPL) {
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci		spin_lock_irqsave(&garmin_data_p->lock, flags);
10158c2ecf20Sopenharmony_ci		garmin_data_p->flags |= APP_REQ_SEEN;
10168c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci		if (garmin_data_p->mode == MODE_GARMIN_SERIAL)  {
10198c2ecf20Sopenharmony_ci			pkt_clear(garmin_data_p);
10208c2ecf20Sopenharmony_ci			garmin_data_p->state = STATE_GSP_WAIT_DATA;
10218c2ecf20Sopenharmony_ci		}
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* send it down the pipe */
10258c2ecf20Sopenharmony_ci	usb_anchor_urb(urb, &garmin_data_p->write_urbs);
10268c2ecf20Sopenharmony_ci	status = usb_submit_urb(urb, GFP_ATOMIC);
10278c2ecf20Sopenharmony_ci	if (status) {
10288c2ecf20Sopenharmony_ci		dev_err(&port->dev,
10298c2ecf20Sopenharmony_ci		   "%s - usb_submit_urb(write bulk) failed with status = %d\n",
10308c2ecf20Sopenharmony_ci				__func__, status);
10318c2ecf20Sopenharmony_ci		count = status;
10328c2ecf20Sopenharmony_ci		usb_unanchor_urb(urb);
10338c2ecf20Sopenharmony_ci		kfree(buffer);
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	/* we are done with this urb, so let the host driver
10378c2ecf20Sopenharmony_ci	 * really free it when it is finished with it */
10388c2ecf20Sopenharmony_ci	usb_free_urb(urb);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	return count;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
10448c2ecf20Sopenharmony_ci					 const unsigned char *buf, int count)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	struct device *dev = &port->dev;
10478c2ecf20Sopenharmony_ci	int pktid, pktsiz, len;
10488c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
10498c2ecf20Sopenharmony_ci	__le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	usb_serial_debug_data(dev, __func__, count, buf);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	if (garmin_data_p->state == STATE_RESET)
10548c2ecf20Sopenharmony_ci		return -EIO;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	/* check for our private packets */
10578c2ecf20Sopenharmony_ci	if (count >= GARMIN_PKTHDR_LENGTH) {
10588c2ecf20Sopenharmony_ci		len = PRIVPKTSIZ;
10598c2ecf20Sopenharmony_ci		if (count < len)
10608c2ecf20Sopenharmony_ci			len = count;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci		memcpy(garmin_data_p->privpkt, buf, len);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci		pktsiz = getDataLength(garmin_data_p->privpkt);
10658c2ecf20Sopenharmony_ci		pktid  = getPacketId(garmin_data_p->privpkt);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci		if (count == (GARMIN_PKTHDR_LENGTH + pktsiz) &&
10688c2ecf20Sopenharmony_ci				getLayerId(garmin_data_p->privpkt) ==
10698c2ecf20Sopenharmony_ci						GARMIN_LAYERID_PRIVATE) {
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci			dev_dbg(dev, "%s - processing private request %d\n",
10728c2ecf20Sopenharmony_ci				__func__, pktid);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci			/* drop all unfinished transfers */
10758c2ecf20Sopenharmony_ci			garmin_clear(garmin_data_p);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci			switch (pktid) {
10788c2ecf20Sopenharmony_ci			case PRIV_PKTID_SET_MODE:
10798c2ecf20Sopenharmony_ci				if (pktsiz != 4)
10808c2ecf20Sopenharmony_ci					return -EINVPKT;
10818c2ecf20Sopenharmony_ci				garmin_data_p->mode = __le32_to_cpu(privpkt[3]);
10828c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - mode set to %d\n",
10838c2ecf20Sopenharmony_ci					__func__, garmin_data_p->mode);
10848c2ecf20Sopenharmony_ci				break;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci			case PRIV_PKTID_INFO_REQ:
10878c2ecf20Sopenharmony_ci				priv_status_resp(port);
10888c2ecf20Sopenharmony_ci				break;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci			case PRIV_PKTID_RESET_REQ:
10918c2ecf20Sopenharmony_ci				process_resetdev_request(port);
10928c2ecf20Sopenharmony_ci				break;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci			case PRIV_PKTID_SET_DEF_MODE:
10958c2ecf20Sopenharmony_ci				if (pktsiz != 4)
10968c2ecf20Sopenharmony_ci					return -EINVPKT;
10978c2ecf20Sopenharmony_ci				initial_mode = __le32_to_cpu(privpkt[3]);
10988c2ecf20Sopenharmony_ci				dev_dbg(dev, "%s - initial_mode set to %d\n",
10998c2ecf20Sopenharmony_ci					__func__,
11008c2ecf20Sopenharmony_ci					garmin_data_p->mode);
11018c2ecf20Sopenharmony_ci				break;
11028c2ecf20Sopenharmony_ci			}
11038c2ecf20Sopenharmony_ci			return count;
11048c2ecf20Sopenharmony_ci		}
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
11088c2ecf20Sopenharmony_ci		return gsp_receive(garmin_data_p, buf, count);
11098c2ecf20Sopenharmony_ci	} else {	/* MODE_NATIVE */
11108c2ecf20Sopenharmony_ci		return nat_receive(garmin_data_p, buf, count);
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic int garmin_write_room(struct tty_struct *tty)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
11188c2ecf20Sopenharmony_ci	/*
11198c2ecf20Sopenharmony_ci	 * Report back the bytes currently available in the output buffer.
11208c2ecf20Sopenharmony_ci	 */
11218c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
11228c2ecf20Sopenharmony_ci	return GPS_OUT_BUFSIZ-garmin_data_p->outsize;
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_cistatic void garmin_read_process(struct garmin_data *garmin_data_p,
11278c2ecf20Sopenharmony_ci				 unsigned char *data, unsigned data_length,
11288c2ecf20Sopenharmony_ci				 int bulk_data)
11298c2ecf20Sopenharmony_ci{
11308c2ecf20Sopenharmony_ci	unsigned long flags;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	if (garmin_data_p->flags & FLAGS_DROP_DATA) {
11338c2ecf20Sopenharmony_ci		/* abort-transfer cmd is active */
11348c2ecf20Sopenharmony_ci		dev_dbg(&garmin_data_p->port->dev, "%s - pkt dropped\n", __func__);
11358c2ecf20Sopenharmony_ci	} else if (garmin_data_p->state != STATE_DISCONNECTED &&
11368c2ecf20Sopenharmony_ci		garmin_data_p->state != STATE_RESET) {
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci		/* if throttling is active or postprecessing is required
11398c2ecf20Sopenharmony_ci		   put the received data in the input queue, otherwise
11408c2ecf20Sopenharmony_ci		   send it directly to the tty port */
11418c2ecf20Sopenharmony_ci		if (garmin_data_p->flags & FLAGS_QUEUING) {
11428c2ecf20Sopenharmony_ci			pkt_add(garmin_data_p, data, data_length);
11438c2ecf20Sopenharmony_ci		} else if (bulk_data || (data_length >= sizeof(u32) &&
11448c2ecf20Sopenharmony_ci				getLayerId(data) == GARMIN_LAYERID_APPL)) {
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci			spin_lock_irqsave(&garmin_data_p->lock, flags);
11478c2ecf20Sopenharmony_ci			garmin_data_p->flags |= APP_RESP_SEEN;
11488c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&garmin_data_p->lock, flags);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci			if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
11518c2ecf20Sopenharmony_ci				pkt_add(garmin_data_p, data, data_length);
11528c2ecf20Sopenharmony_ci			} else {
11538c2ecf20Sopenharmony_ci				send_to_tty(garmin_data_p->port, data,
11548c2ecf20Sopenharmony_ci						data_length);
11558c2ecf20Sopenharmony_ci			}
11568c2ecf20Sopenharmony_ci		}
11578c2ecf20Sopenharmony_ci		/* ignore system layer packets ... */
11588c2ecf20Sopenharmony_ci	}
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic void garmin_read_bulk_callback(struct urb *urb)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	unsigned long flags;
11658c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
11668c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
11678c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
11688c2ecf20Sopenharmony_ci	int status = urb->status;
11698c2ecf20Sopenharmony_ci	int retval;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	if (status) {
11728c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
11738c2ecf20Sopenharmony_ci			__func__, status);
11748c2ecf20Sopenharmony_ci		return;
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	garmin_read_process(garmin_data_p, data, urb->actual_length, 1);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (urb->actual_length == 0 &&
11828c2ecf20Sopenharmony_ci			(garmin_data_p->flags & FLAGS_BULK_IN_RESTART) != 0) {
11838c2ecf20Sopenharmony_ci		spin_lock_irqsave(&garmin_data_p->lock, flags);
11848c2ecf20Sopenharmony_ci		garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART;
11858c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
11868c2ecf20Sopenharmony_ci		retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
11878c2ecf20Sopenharmony_ci		if (retval)
11888c2ecf20Sopenharmony_ci			dev_err(&port->dev,
11898c2ecf20Sopenharmony_ci				"%s - failed resubmitting read urb, error %d\n",
11908c2ecf20Sopenharmony_ci				__func__, retval);
11918c2ecf20Sopenharmony_ci	} else if (urb->actual_length > 0) {
11928c2ecf20Sopenharmony_ci		/* Continue trying to read until nothing more is received  */
11938c2ecf20Sopenharmony_ci		if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
11948c2ecf20Sopenharmony_ci			retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
11958c2ecf20Sopenharmony_ci			if (retval)
11968c2ecf20Sopenharmony_ci				dev_err(&port->dev,
11978c2ecf20Sopenharmony_ci					"%s - failed resubmitting read urb, error %d\n",
11988c2ecf20Sopenharmony_ci					__func__, retval);
11998c2ecf20Sopenharmony_ci		}
12008c2ecf20Sopenharmony_ci	} else {
12018c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - end of bulk data\n", __func__);
12028c2ecf20Sopenharmony_ci		spin_lock_irqsave(&garmin_data_p->lock, flags);
12038c2ecf20Sopenharmony_ci		garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE;
12048c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_cistatic void garmin_read_int_callback(struct urb *urb)
12108c2ecf20Sopenharmony_ci{
12118c2ecf20Sopenharmony_ci	unsigned long flags;
12128c2ecf20Sopenharmony_ci	int retval;
12138c2ecf20Sopenharmony_ci	struct usb_serial_port *port = urb->context;
12148c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
12158c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
12168c2ecf20Sopenharmony_ci	int status = urb->status;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	switch (status) {
12198c2ecf20Sopenharmony_ci	case 0:
12208c2ecf20Sopenharmony_ci		/* success */
12218c2ecf20Sopenharmony_ci		break;
12228c2ecf20Sopenharmony_ci	case -ECONNRESET:
12238c2ecf20Sopenharmony_ci	case -ENOENT:
12248c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
12258c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
12268c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
12278c2ecf20Sopenharmony_ci			__func__, status);
12288c2ecf20Sopenharmony_ci		return;
12298c2ecf20Sopenharmony_ci	default:
12308c2ecf20Sopenharmony_ci		dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
12318c2ecf20Sopenharmony_ci			__func__, status);
12328c2ecf20Sopenharmony_ci		return;
12338c2ecf20Sopenharmony_ci	}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
12368c2ecf20Sopenharmony_ci			      urb->transfer_buffer);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) &&
12398c2ecf20Sopenharmony_ci		memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY,
12408c2ecf20Sopenharmony_ci				sizeof(GARMIN_BULK_IN_AVAIL_REPLY)) == 0) {
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - bulk data available.\n", __func__);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci		if ((garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE) == 0) {
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci			/* bulk data available */
12478c2ecf20Sopenharmony_ci			retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
12488c2ecf20Sopenharmony_ci			if (retval) {
12498c2ecf20Sopenharmony_ci				dev_err(&port->dev,
12508c2ecf20Sopenharmony_ci				 "%s - failed submitting read urb, error %d\n",
12518c2ecf20Sopenharmony_ci							__func__, retval);
12528c2ecf20Sopenharmony_ci			} else {
12538c2ecf20Sopenharmony_ci				spin_lock_irqsave(&garmin_data_p->lock, flags);
12548c2ecf20Sopenharmony_ci				garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
12558c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&garmin_data_p->lock,
12568c2ecf20Sopenharmony_ci									flags);
12578c2ecf20Sopenharmony_ci			}
12588c2ecf20Sopenharmony_ci		} else {
12598c2ecf20Sopenharmony_ci			/* bulk-in transfer still active */
12608c2ecf20Sopenharmony_ci			spin_lock_irqsave(&garmin_data_p->lock, flags);
12618c2ecf20Sopenharmony_ci			garmin_data_p->flags |= FLAGS_BULK_IN_RESTART;
12628c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&garmin_data_p->lock, flags);
12638c2ecf20Sopenharmony_ci		}
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	} else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY))
12668c2ecf20Sopenharmony_ci			 && memcmp(data, GARMIN_START_SESSION_REPLY,
12678c2ecf20Sopenharmony_ci				 sizeof(GARMIN_START_SESSION_REPLY)) == 0) {
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci		spin_lock_irqsave(&garmin_data_p->lock, flags);
12708c2ecf20Sopenharmony_ci		garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN;
12718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&garmin_data_p->lock, flags);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci		/* save the serial number */
12748c2ecf20Sopenharmony_ci		garmin_data_p->serial_num = __le32_to_cpup(
12758c2ecf20Sopenharmony_ci					(__le32 *)(data+GARMIN_PKTHDR_LENGTH));
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci		dev_dbg(&port->dev, "%s - start-of-session reply seen - serial %u.\n",
12788c2ecf20Sopenharmony_ci			__func__, garmin_data_p->serial_num);
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	garmin_read_process(garmin_data_p, data, urb->actual_length, 0);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
12848c2ecf20Sopenharmony_ci	if (retval)
12858c2ecf20Sopenharmony_ci		dev_err(&urb->dev->dev,
12868c2ecf20Sopenharmony_ci			"%s - Error %d submitting interrupt urb\n",
12878c2ecf20Sopenharmony_ci			__func__, retval);
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci/*
12928c2ecf20Sopenharmony_ci * Sends the next queued packt to the tty port (garmin native mode only)
12938c2ecf20Sopenharmony_ci * and then sets a timer to call itself again until all queued data
12948c2ecf20Sopenharmony_ci * is sent.
12958c2ecf20Sopenharmony_ci */
12968c2ecf20Sopenharmony_cistatic int garmin_flush_queue(struct garmin_data *garmin_data_p)
12978c2ecf20Sopenharmony_ci{
12988c2ecf20Sopenharmony_ci	unsigned long flags;
12998c2ecf20Sopenharmony_ci	struct garmin_packet *pkt;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
13028c2ecf20Sopenharmony_ci		pkt = pkt_pop(garmin_data_p);
13038c2ecf20Sopenharmony_ci		if (pkt != NULL) {
13048c2ecf20Sopenharmony_ci			send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
13058c2ecf20Sopenharmony_ci			kfree(pkt);
13068c2ecf20Sopenharmony_ci			mod_timer(&garmin_data_p->timer, (1)+jiffies);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		} else {
13098c2ecf20Sopenharmony_ci			spin_lock_irqsave(&garmin_data_p->lock, flags);
13108c2ecf20Sopenharmony_ci			garmin_data_p->flags &= ~FLAGS_QUEUING;
13118c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&garmin_data_p->lock, flags);
13128c2ecf20Sopenharmony_ci		}
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci	return 0;
13158c2ecf20Sopenharmony_ci}
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_cistatic void garmin_throttle(struct tty_struct *tty)
13198c2ecf20Sopenharmony_ci{
13208c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
13218c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	/* set flag, data received will be put into a queue
13248c2ecf20Sopenharmony_ci	   for later processing */
13258c2ecf20Sopenharmony_ci	spin_lock_irq(&garmin_data_p->lock);
13268c2ecf20Sopenharmony_ci	garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED;
13278c2ecf20Sopenharmony_ci	spin_unlock_irq(&garmin_data_p->lock);
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_cistatic void garmin_unthrottle(struct tty_struct *tty)
13328c2ecf20Sopenharmony_ci{
13338c2ecf20Sopenharmony_ci	struct usb_serial_port *port = tty->driver_data;
13348c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
13358c2ecf20Sopenharmony_ci	int status;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	spin_lock_irq(&garmin_data_p->lock);
13388c2ecf20Sopenharmony_ci	garmin_data_p->flags &= ~FLAGS_THROTTLED;
13398c2ecf20Sopenharmony_ci	spin_unlock_irq(&garmin_data_p->lock);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	/* in native mode send queued data to tty, in
13428c2ecf20Sopenharmony_ci	   serial mode nothing needs to be done here */
13438c2ecf20Sopenharmony_ci	if (garmin_data_p->mode == MODE_NATIVE)
13448c2ecf20Sopenharmony_ci		garmin_flush_queue(garmin_data_p);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	if ((garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE) != 0) {
13478c2ecf20Sopenharmony_ci		status = usb_submit_urb(port->read_urb, GFP_KERNEL);
13488c2ecf20Sopenharmony_ci		if (status)
13498c2ecf20Sopenharmony_ci			dev_err(&port->dev,
13508c2ecf20Sopenharmony_ci				"%s - failed resubmitting read urb, error %d\n",
13518c2ecf20Sopenharmony_ci				__func__, status);
13528c2ecf20Sopenharmony_ci	}
13538c2ecf20Sopenharmony_ci}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci/*
13568c2ecf20Sopenharmony_ci * The timer is currently only used to send queued packets to
13578c2ecf20Sopenharmony_ci * the tty in cases where the protocol provides no own handshaking
13588c2ecf20Sopenharmony_ci * to initiate the transfer.
13598c2ecf20Sopenharmony_ci */
13608c2ecf20Sopenharmony_cistatic void timeout_handler(struct timer_list *t)
13618c2ecf20Sopenharmony_ci{
13628c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = from_timer(garmin_data_p, t, timer);
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	/* send the next queued packet to the tty port */
13658c2ecf20Sopenharmony_ci	if (garmin_data_p->mode == MODE_NATIVE)
13668c2ecf20Sopenharmony_ci		if (garmin_data_p->flags & FLAGS_QUEUING)
13678c2ecf20Sopenharmony_ci			garmin_flush_queue(garmin_data_p);
13688c2ecf20Sopenharmony_ci}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_cistatic int garmin_port_probe(struct usb_serial_port *port)
13738c2ecf20Sopenharmony_ci{
13748c2ecf20Sopenharmony_ci	int status;
13758c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
13788c2ecf20Sopenharmony_ci	if (!garmin_data_p)
13798c2ecf20Sopenharmony_ci		return -ENOMEM;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	timer_setup(&garmin_data_p->timer, timeout_handler, 0);
13828c2ecf20Sopenharmony_ci	spin_lock_init(&garmin_data_p->lock);
13838c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&garmin_data_p->pktlist);
13848c2ecf20Sopenharmony_ci	garmin_data_p->port = port;
13858c2ecf20Sopenharmony_ci	garmin_data_p->state = 0;
13868c2ecf20Sopenharmony_ci	garmin_data_p->flags = 0;
13878c2ecf20Sopenharmony_ci	garmin_data_p->count = 0;
13888c2ecf20Sopenharmony_ci	init_usb_anchor(&garmin_data_p->write_urbs);
13898c2ecf20Sopenharmony_ci	usb_set_serial_port_data(port, garmin_data_p);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	status = garmin_init_session(port);
13928c2ecf20Sopenharmony_ci	if (status)
13938c2ecf20Sopenharmony_ci		goto err_free;
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	return 0;
13968c2ecf20Sopenharmony_cierr_free:
13978c2ecf20Sopenharmony_ci	kfree(garmin_data_p);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	return status;
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_cistatic int garmin_port_remove(struct usb_serial_port *port)
14048c2ecf20Sopenharmony_ci{
14058c2ecf20Sopenharmony_ci	struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	usb_kill_anchored_urbs(&garmin_data_p->write_urbs);
14088c2ecf20Sopenharmony_ci	usb_kill_urb(port->interrupt_in_urb);
14098c2ecf20Sopenharmony_ci	del_timer_sync(&garmin_data_p->timer);
14108c2ecf20Sopenharmony_ci	kfree(garmin_data_p);
14118c2ecf20Sopenharmony_ci	return 0;
14128c2ecf20Sopenharmony_ci}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci/* All of the device info needed */
14168c2ecf20Sopenharmony_cistatic struct usb_serial_driver garmin_device = {
14178c2ecf20Sopenharmony_ci	.driver = {
14188c2ecf20Sopenharmony_ci		.owner       = THIS_MODULE,
14198c2ecf20Sopenharmony_ci		.name        = "garmin_gps",
14208c2ecf20Sopenharmony_ci	},
14218c2ecf20Sopenharmony_ci	.description         = "Garmin GPS usb/tty",
14228c2ecf20Sopenharmony_ci	.id_table            = id_table,
14238c2ecf20Sopenharmony_ci	.num_ports           = 1,
14248c2ecf20Sopenharmony_ci	.open                = garmin_open,
14258c2ecf20Sopenharmony_ci	.close               = garmin_close,
14268c2ecf20Sopenharmony_ci	.throttle            = garmin_throttle,
14278c2ecf20Sopenharmony_ci	.unthrottle          = garmin_unthrottle,
14288c2ecf20Sopenharmony_ci	.port_probe		= garmin_port_probe,
14298c2ecf20Sopenharmony_ci	.port_remove		= garmin_port_remove,
14308c2ecf20Sopenharmony_ci	.write               = garmin_write,
14318c2ecf20Sopenharmony_ci	.write_room          = garmin_write_room,
14328c2ecf20Sopenharmony_ci	.write_bulk_callback = garmin_write_bulk_callback,
14338c2ecf20Sopenharmony_ci	.read_bulk_callback  = garmin_read_bulk_callback,
14348c2ecf20Sopenharmony_ci	.read_int_callback   = garmin_read_int_callback,
14358c2ecf20Sopenharmony_ci};
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_cistatic struct usb_serial_driver * const serial_drivers[] = {
14388c2ecf20Sopenharmony_ci	&garmin_device, NULL
14398c2ecf20Sopenharmony_ci};
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_cimodule_usb_serial_driver(serial_drivers, id_table);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
14448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
14458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_cimodule_param(initial_mode, int, S_IRUGO);
14488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(initial_mode, "Initial mode");
1449