forked from mirrors/linux
		
	can: gs_usb: Added support for the GS_USB CAN devices
The Geschwister Schneider Family of devices are galvanically isolated USB2.0 to CAN2.0A/B adapters. Currently two form factors are available, a tethered dongle in a rugged enclosure, and mini-pci-e card. Signed-off-by: Maximilian Schneider <max@schneidersoft.net> Acked-by: Wolfgang Grandegger <wg@grandegger.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
		
							parent
							
								
									277bd320e7
								
							
						
					
					
						commit
						d08e973a77
					
				
					 3 changed files with 980 additions and 0 deletions
				
			
		|  | @ -13,6 +13,14 @@ config CAN_ESD_USB2 | ||||||
|           This driver supports the CAN-USB/2 interface |           This driver supports the CAN-USB/2 interface | ||||||
|           from esd electronic system design gmbh (http://www.esd.eu). |           from esd electronic system design gmbh (http://www.esd.eu). | ||||||
| 
 | 
 | ||||||
|  | config CAN_GS_USB | ||||||
|  | 	tristate "Geschwister Schneider UG interfaces" | ||||||
|  | 	---help--- | ||||||
|  | 	  This driver supports the Geschwister Schneider USB/CAN devices. | ||||||
|  | 	  If unsure choose N, | ||||||
|  | 	  choose Y for built in support, | ||||||
|  | 	  M to compile as module (module will be named: gs_usb). | ||||||
|  | 
 | ||||||
| config CAN_KVASER_USB | config CAN_KVASER_USB | ||||||
| 	tristate "Kvaser CAN/USB interface" | 	tristate "Kvaser CAN/USB interface" | ||||||
| 	---help--- | 	---help--- | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o | obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o | ||||||
| obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o | obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o | ||||||
|  | obj-$(CONFIG_CAN_GS_USB) += gs_usb.o | ||||||
| obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o | obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o | ||||||
| obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ | obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ | ||||||
| obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o | obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o | ||||||
|  |  | ||||||
							
								
								
									
										971
									
								
								drivers/net/can/usb/gs_usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										971
									
								
								drivers/net/can/usb/gs_usb.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,971 @@ | ||||||
|  | /* CAN driver for Geschwister Schneider USB/CAN devices.
 | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2013 Geschwister Schneider Technologie-, | ||||||
|  |  * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt). | ||||||
|  |  * | ||||||
|  |  * Many thanks to all socketcan devs! | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify it | ||||||
|  |  * under the terms of the GNU General Public License as published | ||||||
|  |  * by the Free Software Foundation; version 2 of the License. | ||||||
|  |  * | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/init.h> | ||||||
|  | #include <linux/signal.h> | ||||||
|  | #include <linux/module.h> | ||||||
|  | #include <linux/netdevice.h> | ||||||
|  | #include <linux/usb.h> | ||||||
|  | 
 | ||||||
|  | #include <linux/can.h> | ||||||
|  | #include <linux/can/dev.h> | ||||||
|  | #include <linux/can/error.h> | ||||||
|  | 
 | ||||||
|  | /* Device specific constants */ | ||||||
|  | #define USB_GSUSB_1_VENDOR_ID      0x1d50 | ||||||
|  | #define USB_GSUSB_1_PRODUCT_ID     0x606f | ||||||
|  | 
 | ||||||
|  | #define GSUSB_ENDPOINT_IN          1 | ||||||
|  | #define GSUSB_ENDPOINT_OUT         2 | ||||||
|  | 
 | ||||||
|  | /* Device specific constants */ | ||||||
|  | enum gs_usb_breq { | ||||||
|  | 	GS_USB_BREQ_HOST_FORMAT = 0, | ||||||
|  | 	GS_USB_BREQ_BITTIMING, | ||||||
|  | 	GS_USB_BREQ_MODE, | ||||||
|  | 	GS_USB_BREQ_BERR, | ||||||
|  | 	GS_USB_BREQ_BT_CONST, | ||||||
|  | 	GS_USB_BREQ_DEVICE_CONFIG | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum gs_can_mode { | ||||||
|  | 	/* reset a channel. turns it off */ | ||||||
|  | 	GS_CAN_MODE_RESET = 0, | ||||||
|  | 	/* starts a channel */ | ||||||
|  | 	GS_CAN_MODE_START | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum gs_can_state { | ||||||
|  | 	GS_CAN_STATE_ERROR_ACTIVE = 0, | ||||||
|  | 	GS_CAN_STATE_ERROR_WARNING, | ||||||
|  | 	GS_CAN_STATE_ERROR_PASSIVE, | ||||||
|  | 	GS_CAN_STATE_BUS_OFF, | ||||||
|  | 	GS_CAN_STATE_STOPPED, | ||||||
|  | 	GS_CAN_STATE_SLEEPING | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* data types passed between host and device */ | ||||||
|  | struct gs_host_config { | ||||||
|  | 	u32 byte_order; | ||||||
|  | } __packed; | ||||||
|  | /* All data exchanged between host and device is exchanged in host byte order,
 | ||||||
|  |  * thanks to the struct gs_host_config byte_order member, which is sent first | ||||||
|  |  * to indicate the desired byte order. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | struct gs_device_config { | ||||||
|  | 	u8 reserved1; | ||||||
|  | 	u8 reserved2; | ||||||
|  | 	u8 reserved3; | ||||||
|  | 	u8 icount; | ||||||
|  | 	u32 sw_version; | ||||||
|  | 	u32 hw_version; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define GS_CAN_MODE_NORMAL               0 | ||||||
|  | #define GS_CAN_MODE_LISTEN_ONLY          (1<<0) | ||||||
|  | #define GS_CAN_MODE_LOOP_BACK            (1<<1) | ||||||
|  | #define GS_CAN_MODE_TRIPLE_SAMPLE        (1<<2) | ||||||
|  | #define GS_CAN_MODE_ONE_SHOT             (1<<3) | ||||||
|  | 
 | ||||||
|  | struct gs_device_mode { | ||||||
|  | 	u32 mode; | ||||||
|  | 	u32 flags; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct gs_device_state { | ||||||
|  | 	u32 state; | ||||||
|  | 	u32 rxerr; | ||||||
|  | 	u32 txerr; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | struct gs_device_bittiming { | ||||||
|  | 	u32 prop_seg; | ||||||
|  | 	u32 phase_seg1; | ||||||
|  | 	u32 phase_seg2; | ||||||
|  | 	u32 sjw; | ||||||
|  | 	u32 brp; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define GS_CAN_FEATURE_LISTEN_ONLY      (1<<0) | ||||||
|  | #define GS_CAN_FEATURE_LOOP_BACK        (1<<1) | ||||||
|  | #define GS_CAN_FEATURE_TRIPLE_SAMPLE    (1<<2) | ||||||
|  | #define GS_CAN_FEATURE_ONE_SHOT         (1<<3) | ||||||
|  | 
 | ||||||
|  | struct gs_device_bt_const { | ||||||
|  | 	u32 feature; | ||||||
|  | 	u32 fclk_can; | ||||||
|  | 	u32 tseg1_min; | ||||||
|  | 	u32 tseg1_max; | ||||||
|  | 	u32 tseg2_min; | ||||||
|  | 	u32 tseg2_max; | ||||||
|  | 	u32 sjw_max; | ||||||
|  | 	u32 brp_min; | ||||||
|  | 	u32 brp_max; | ||||||
|  | 	u32 brp_inc; | ||||||
|  | } __packed; | ||||||
|  | 
 | ||||||
|  | #define GS_CAN_FLAG_OVERFLOW 1 | ||||||
|  | 
 | ||||||
|  | struct gs_host_frame { | ||||||
|  | 	u32 echo_id; | ||||||
|  | 	u32 can_id; | ||||||
|  | 
 | ||||||
|  | 	u8 can_dlc; | ||||||
|  | 	u8 channel; | ||||||
|  | 	u8 flags; | ||||||
|  | 	u8 reserved; | ||||||
|  | 
 | ||||||
|  | 	u8 data[8]; | ||||||
|  | } __packed; | ||||||
|  | /* The GS USB devices make use of the same flags and masks as in
 | ||||||
|  |  * linux/can.h and linux/can/error.h, and no additional mapping is necessary. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */ | ||||||
|  | #define GS_MAX_TX_URBS 10 | ||||||
|  | /* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */ | ||||||
|  | #define GS_MAX_RX_URBS 30 | ||||||
|  | /* Maximum number of interfaces the driver supports per device.
 | ||||||
|  |  * Current hardware only supports 2 interfaces. The future may vary. | ||||||
|  |  */ | ||||||
|  | #define GS_MAX_INTF 2 | ||||||
|  | 
 | ||||||
|  | struct gs_tx_context { | ||||||
|  | 	struct gs_can *dev; | ||||||
|  | 	unsigned int echo_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct gs_can { | ||||||
|  | 	struct can_priv can; /* must be the first member */ | ||||||
|  | 
 | ||||||
|  | 	struct gs_usb *parent; | ||||||
|  | 
 | ||||||
|  | 	struct net_device *netdev; | ||||||
|  | 	struct usb_device *udev; | ||||||
|  | 	struct usb_interface *iface; | ||||||
|  | 
 | ||||||
|  | 	struct can_bittiming_const bt_const; | ||||||
|  | 	unsigned int channel;	/* channel number */ | ||||||
|  | 
 | ||||||
|  | 	/* This lock prevents a race condition between xmit and recieve. */ | ||||||
|  | 	spinlock_t tx_ctx_lock; | ||||||
|  | 	struct gs_tx_context tx_context[GS_MAX_TX_URBS]; | ||||||
|  | 
 | ||||||
|  | 	struct usb_anchor tx_submitted; | ||||||
|  | 	atomic_t active_tx_urbs; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* usb interface struct */ | ||||||
|  | struct gs_usb { | ||||||
|  | 	struct gs_can *canch[GS_MAX_INTF]; | ||||||
|  | 	struct usb_anchor rx_submitted; | ||||||
|  | 	atomic_t active_channels; | ||||||
|  | 	struct usb_device *udev; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* 'allocate' a tx context.
 | ||||||
|  |  * returns a valid tx context or NULL if there is no space. | ||||||
|  |  */ | ||||||
|  | static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev) | ||||||
|  | { | ||||||
|  | 	int i = 0; | ||||||
|  | 	unsigned long flags; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&dev->tx_ctx_lock, flags); | ||||||
|  | 
 | ||||||
|  | 	for (; i < GS_MAX_TX_URBS; i++) { | ||||||
|  | 		if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) { | ||||||
|  | 			dev->tx_context[i].echo_id = i; | ||||||
|  | 			spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); | ||||||
|  | 			return &dev->tx_context[i]; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* releases a tx context
 | ||||||
|  |  */ | ||||||
|  | static void gs_free_tx_context(struct gs_tx_context *txc) | ||||||
|  | { | ||||||
|  | 	txc->echo_id = GS_MAX_TX_URBS; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Get a tx context by id.
 | ||||||
|  |  */ | ||||||
|  | static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, unsigned int id) | ||||||
|  | { | ||||||
|  | 	unsigned long flags; | ||||||
|  | 
 | ||||||
|  | 	if (id < GS_MAX_TX_URBS) { | ||||||
|  | 		spin_lock_irqsave(&dev->tx_ctx_lock, flags); | ||||||
|  | 		if (dev->tx_context[id].echo_id == id) { | ||||||
|  | 			spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); | ||||||
|  | 			return &dev->tx_context[id]; | ||||||
|  | 		} | ||||||
|  | 		spin_unlock_irqrestore(&dev->tx_ctx_lock, flags); | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev) | ||||||
|  | { | ||||||
|  | 	struct gs_device_mode *dm; | ||||||
|  | 	struct usb_interface *intf = gsdev->iface; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	dm = kzalloc(sizeof(*dm), GFP_KERNEL); | ||||||
|  | 	if (!dm) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	dm->mode = GS_CAN_MODE_RESET; | ||||||
|  | 
 | ||||||
|  | 	rc = usb_control_msg(interface_to_usbdev(intf), | ||||||
|  | 			     usb_sndctrlpipe(interface_to_usbdev(intf), 0), | ||||||
|  | 			     GS_USB_BREQ_MODE, | ||||||
|  | 			     USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||||||
|  | 			     gsdev->channel, | ||||||
|  | 			     0, | ||||||
|  | 			     dm, | ||||||
|  | 			     sizeof(*dm), | ||||||
|  | 			     1000); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gs_update_state(struct gs_can *dev, struct can_frame *cf) | ||||||
|  | { | ||||||
|  | 	struct can_device_stats *can_stats = &dev->can.can_stats; | ||||||
|  | 
 | ||||||
|  | 	if (cf->can_id & CAN_ERR_RESTARTED) { | ||||||
|  | 		dev->can.state = CAN_STATE_ERROR_ACTIVE; | ||||||
|  | 		can_stats->restarts++; | ||||||
|  | 	} else if (cf->can_id & CAN_ERR_BUSOFF) { | ||||||
|  | 		dev->can.state = CAN_STATE_BUS_OFF; | ||||||
|  | 		can_stats->bus_off++; | ||||||
|  | 	} else if (cf->can_id & CAN_ERR_CRTL) { | ||||||
|  | 		if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) || | ||||||
|  | 		    (cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) { | ||||||
|  | 			dev->can.state = CAN_STATE_ERROR_WARNING; | ||||||
|  | 			can_stats->error_warning++; | ||||||
|  | 		} else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) || | ||||||
|  | 			   (cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) { | ||||||
|  | 			dev->can.state = CAN_STATE_ERROR_PASSIVE; | ||||||
|  | 			can_stats->error_passive++; | ||||||
|  | 		} else { | ||||||
|  | 			dev->can.state = CAN_STATE_ERROR_ACTIVE; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gs_usb_recieve_bulk_callback(struct urb *urb) | ||||||
|  | { | ||||||
|  | 	struct gs_usb *usbcan = urb->context; | ||||||
|  | 	struct gs_can *dev; | ||||||
|  | 	struct net_device *netdev; | ||||||
|  | 	int rc; | ||||||
|  | 	struct net_device_stats *stats; | ||||||
|  | 	struct gs_host_frame *hf = urb->transfer_buffer; | ||||||
|  | 	struct gs_tx_context *txc; | ||||||
|  | 	struct can_frame *cf; | ||||||
|  | 	struct sk_buff *skb; | ||||||
|  | 
 | ||||||
|  | 	BUG_ON(!usbcan); | ||||||
|  | 
 | ||||||
|  | 	switch (urb->status) { | ||||||
|  | 	case 0: /* success */ | ||||||
|  | 		break; | ||||||
|  | 	case -ENOENT: | ||||||
|  | 	case -ESHUTDOWN: | ||||||
|  | 		return; | ||||||
|  | 	default: | ||||||
|  | 		/* do not resubmit aborted urbs. eg: when device goes down */ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* device reports out of range channel id */ | ||||||
|  | 	if (hf->channel >= GS_MAX_INTF) | ||||||
|  | 		goto resubmit_urb; | ||||||
|  | 
 | ||||||
|  | 	dev = usbcan->canch[hf->channel]; | ||||||
|  | 
 | ||||||
|  | 	netdev = dev->netdev; | ||||||
|  | 	stats = &netdev->stats; | ||||||
|  | 
 | ||||||
|  | 	if (!netif_device_present(netdev)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (hf->echo_id == -1) { /* normal rx */ | ||||||
|  | 		skb = alloc_can_skb(dev->netdev, &cf); | ||||||
|  | 		if (!skb) | ||||||
|  | 			return; | ||||||
|  | 
 | ||||||
|  | 		cf->can_id = hf->can_id; | ||||||
|  | 
 | ||||||
|  | 		cf->can_dlc = get_can_dlc(hf->can_dlc); | ||||||
|  | 		memcpy(cf->data, hf->data, 8); | ||||||
|  | 
 | ||||||
|  | 		/* ERROR frames tell us information about the controller */ | ||||||
|  | 		if (hf->can_id & CAN_ERR_FLAG) | ||||||
|  | 			gs_update_state(dev, cf); | ||||||
|  | 
 | ||||||
|  | 		netdev->stats.rx_packets++; | ||||||
|  | 		netdev->stats.rx_bytes += hf->can_dlc; | ||||||
|  | 
 | ||||||
|  | 		netif_rx(skb); | ||||||
|  | 	} else { /* echo_id == hf->echo_id */ | ||||||
|  | 		if (hf->echo_id >= GS_MAX_TX_URBS) { | ||||||
|  | 			netdev_err(netdev, | ||||||
|  | 				   "Unexpected out of range echo id %d\n", | ||||||
|  | 				   hf->echo_id); | ||||||
|  | 			goto resubmit_urb; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		netdev->stats.tx_packets++; | ||||||
|  | 		netdev->stats.tx_bytes += hf->can_dlc; | ||||||
|  | 
 | ||||||
|  | 		txc = gs_get_tx_context(dev, hf->echo_id); | ||||||
|  | 
 | ||||||
|  | 		/* bad devices send bad echo_ids. */ | ||||||
|  | 		if (!txc) { | ||||||
|  | 			netdev_err(netdev, | ||||||
|  | 				   "Unexpected unused echo id %d\n", | ||||||
|  | 				   hf->echo_id); | ||||||
|  | 			goto resubmit_urb; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		can_get_echo_skb(netdev, hf->echo_id); | ||||||
|  | 
 | ||||||
|  | 		gs_free_tx_context(txc); | ||||||
|  | 
 | ||||||
|  | 		netif_wake_queue(netdev); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (hf->flags & GS_CAN_FLAG_OVERFLOW) { | ||||||
|  | 		skb = alloc_can_err_skb(netdev, &cf); | ||||||
|  | 		if (!skb) | ||||||
|  | 			goto resubmit_urb; | ||||||
|  | 
 | ||||||
|  | 		cf->can_id |= CAN_ERR_CRTL; | ||||||
|  | 		cf->can_dlc = CAN_ERR_DLC; | ||||||
|  | 		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; | ||||||
|  | 		stats->rx_over_errors++; | ||||||
|  | 		stats->rx_errors++; | ||||||
|  | 		netif_rx(skb); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  |  resubmit_urb: | ||||||
|  | 	usb_fill_bulk_urb(urb, | ||||||
|  | 			  usbcan->udev, | ||||||
|  | 			  usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN), | ||||||
|  | 			  hf, | ||||||
|  | 			  sizeof(struct gs_host_frame), | ||||||
|  | 			  gs_usb_recieve_bulk_callback, | ||||||
|  | 			  usbcan | ||||||
|  | 			  ); | ||||||
|  | 
 | ||||||
|  | 	rc = usb_submit_urb(urb, GFP_ATOMIC); | ||||||
|  | 
 | ||||||
|  | 	/* USB failure take down all interfaces */ | ||||||
|  | 	if (rc == -ENODEV) { | ||||||
|  | 		for (rc = 0; rc < GS_MAX_INTF; rc++) { | ||||||
|  | 			if (usbcan->canch[rc]) | ||||||
|  | 				netif_device_detach(usbcan->canch[rc]->netdev); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gs_usb_set_bittiming(struct net_device *netdev) | ||||||
|  | { | ||||||
|  | 	struct gs_can *dev = netdev_priv(netdev); | ||||||
|  | 	struct can_bittiming *bt = &dev->can.bittiming; | ||||||
|  | 	struct usb_interface *intf = dev->iface; | ||||||
|  | 	int rc; | ||||||
|  | 	struct gs_device_bittiming *dbt; | ||||||
|  | 
 | ||||||
|  | 	dbt = kmalloc(sizeof(*dbt), GFP_KERNEL); | ||||||
|  | 	if (!dbt) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	dbt->prop_seg = bt->prop_seg; | ||||||
|  | 	dbt->phase_seg1 = bt->phase_seg1; | ||||||
|  | 	dbt->phase_seg2 = bt->phase_seg2; | ||||||
|  | 	dbt->sjw = bt->sjw; | ||||||
|  | 	dbt->brp = bt->brp; | ||||||
|  | 
 | ||||||
|  | 	/* request bit timings */ | ||||||
|  | 	rc = usb_control_msg(interface_to_usbdev(intf), | ||||||
|  | 			     usb_sndctrlpipe(interface_to_usbdev(intf), 0), | ||||||
|  | 			     GS_USB_BREQ_BITTIMING, | ||||||
|  | 			     USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||||||
|  | 			     dev->channel, | ||||||
|  | 			     0, | ||||||
|  | 			     dbt, | ||||||
|  | 			     sizeof(*dbt), | ||||||
|  | 			     1000); | ||||||
|  | 
 | ||||||
|  | 	kfree(dbt); | ||||||
|  | 
 | ||||||
|  | 	if (rc < 0) | ||||||
|  | 		dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)", | ||||||
|  | 			rc); | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gs_usb_xmit_callback(struct urb *urb) | ||||||
|  | { | ||||||
|  | 	struct gs_tx_context *txc = urb->context; | ||||||
|  | 	struct gs_can *dev = txc->dev; | ||||||
|  | 	struct net_device *netdev = dev->netdev; | ||||||
|  | 
 | ||||||
|  | 	if (urb->status) | ||||||
|  | 		netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id); | ||||||
|  | 
 | ||||||
|  | 	usb_free_coherent(urb->dev, | ||||||
|  | 			  urb->transfer_buffer_length, | ||||||
|  | 			  urb->transfer_buffer, | ||||||
|  | 			  urb->transfer_dma); | ||||||
|  | 
 | ||||||
|  | 	atomic_dec(&dev->active_tx_urbs); | ||||||
|  | 
 | ||||||
|  | 	if (!netif_device_present(netdev)) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	if (netif_queue_stopped(netdev)) | ||||||
|  | 		netif_wake_queue(netdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct net_device *netdev) | ||||||
|  | { | ||||||
|  | 	struct gs_can *dev = netdev_priv(netdev); | ||||||
|  | 	struct net_device_stats *stats = &dev->netdev->stats; | ||||||
|  | 	struct urb *urb; | ||||||
|  | 	struct gs_host_frame *hf; | ||||||
|  | 	struct can_frame *cf; | ||||||
|  | 	int rc; | ||||||
|  | 	unsigned int idx; | ||||||
|  | 	struct gs_tx_context *txc; | ||||||
|  | 
 | ||||||
|  | 	if (can_dropped_invalid_skb(netdev, skb)) | ||||||
|  | 		return NETDEV_TX_OK; | ||||||
|  | 
 | ||||||
|  | 	/* find an empty context to keep track of transmission */ | ||||||
|  | 	txc = gs_alloc_tx_context(dev); | ||||||
|  | 	if (!txc) | ||||||
|  | 		return NETDEV_TX_BUSY; | ||||||
|  | 
 | ||||||
|  | 	/* create a URB, and a buffer for it */ | ||||||
|  | 	urb = usb_alloc_urb(0, GFP_ATOMIC); | ||||||
|  | 	if (!urb) { | ||||||
|  | 		netdev_err(netdev, "No memory left for URB\n"); | ||||||
|  | 		goto nomem_urb; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC, | ||||||
|  | 				&urb->transfer_dma); | ||||||
|  | 	if (!hf) { | ||||||
|  | 		netdev_err(netdev, "No memory left for USB buffer\n"); | ||||||
|  | 		goto nomem_hf; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	idx = txc->echo_id; | ||||||
|  | 
 | ||||||
|  | 	if (idx >= GS_MAX_TX_URBS) { | ||||||
|  | 		netdev_err(netdev, "Invalid tx context %d\n", idx); | ||||||
|  | 		goto badidx; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	hf->echo_id = idx; | ||||||
|  | 	hf->channel = dev->channel; | ||||||
|  | 
 | ||||||
|  | 	cf = (struct can_frame *)skb->data; | ||||||
|  | 
 | ||||||
|  | 	hf->can_id = cf->can_id; | ||||||
|  | 	hf->can_dlc = cf->can_dlc; | ||||||
|  | 	memcpy(hf->data, cf->data, cf->can_dlc); | ||||||
|  | 
 | ||||||
|  | 	usb_fill_bulk_urb(urb, dev->udev, | ||||||
|  | 			  usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT), | ||||||
|  | 			  hf, | ||||||
|  | 			  sizeof(*hf), | ||||||
|  | 			  gs_usb_xmit_callback, | ||||||
|  | 			  txc); | ||||||
|  | 
 | ||||||
|  | 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||||||
|  | 	usb_anchor_urb(urb, &dev->tx_submitted); | ||||||
|  | 
 | ||||||
|  | 	can_put_echo_skb(skb, netdev, idx); | ||||||
|  | 
 | ||||||
|  | 	atomic_inc(&dev->active_tx_urbs); | ||||||
|  | 
 | ||||||
|  | 	rc = usb_submit_urb(urb, GFP_ATOMIC); | ||||||
|  | 	if (unlikely(rc)) {			/* usb send failed */ | ||||||
|  | 		atomic_dec(&dev->active_tx_urbs); | ||||||
|  | 
 | ||||||
|  | 		can_free_echo_skb(netdev, idx); | ||||||
|  | 		gs_free_tx_context(txc); | ||||||
|  | 
 | ||||||
|  | 		usb_unanchor_urb(urb); | ||||||
|  | 		usb_free_coherent(dev->udev, | ||||||
|  | 				  sizeof(*hf), | ||||||
|  | 				  hf, | ||||||
|  | 				  urb->transfer_dma); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		if (rc == -ENODEV) { | ||||||
|  | 			netif_device_detach(netdev); | ||||||
|  | 		} else { | ||||||
|  | 			netdev_err(netdev, "usb_submit failed (err=%d)\n", rc); | ||||||
|  | 			stats->tx_dropped++; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		/* Slow down tx path */ | ||||||
|  | 		if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS) | ||||||
|  | 			netif_stop_queue(netdev); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* let usb core take care of this urb */ | ||||||
|  | 	usb_free_urb(urb); | ||||||
|  | 
 | ||||||
|  | 	return NETDEV_TX_OK; | ||||||
|  | 
 | ||||||
|  |  badidx: | ||||||
|  | 	usb_free_coherent(dev->udev, | ||||||
|  | 			  sizeof(*hf), | ||||||
|  | 			  hf, | ||||||
|  | 			  urb->transfer_dma); | ||||||
|  |  nomem_hf: | ||||||
|  | 	usb_free_urb(urb); | ||||||
|  | 
 | ||||||
|  |  nomem_urb: | ||||||
|  | 	gs_free_tx_context(txc); | ||||||
|  | 	dev_kfree_skb(skb); | ||||||
|  | 	stats->tx_dropped++; | ||||||
|  | 	return NETDEV_TX_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gs_can_open(struct net_device *netdev) | ||||||
|  | { | ||||||
|  | 	struct gs_can *dev = netdev_priv(netdev); | ||||||
|  | 	struct gs_usb *parent = dev->parent; | ||||||
|  | 	int rc, i; | ||||||
|  | 	struct gs_device_mode *dm; | ||||||
|  | 	u32 ctrlmode; | ||||||
|  | 
 | ||||||
|  | 	rc = open_candev(netdev); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
|  | 
 | ||||||
|  | 	if (atomic_add_return(1, &parent->active_channels) == 1) { | ||||||
|  | 		for (i = 0; i < GS_MAX_RX_URBS; i++) { | ||||||
|  | 			struct urb *urb; | ||||||
|  | 			u8 *buf; | ||||||
|  | 
 | ||||||
|  | 			/* alloc rx urb */ | ||||||
|  | 			urb = usb_alloc_urb(0, GFP_KERNEL); | ||||||
|  | 			if (!urb) { | ||||||
|  | 				netdev_err(netdev, | ||||||
|  | 					   "No memory left for URB\n"); | ||||||
|  | 				return -ENOMEM; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			/* alloc rx buffer */ | ||||||
|  | 			buf = usb_alloc_coherent(dev->udev, | ||||||
|  | 						 sizeof(struct gs_host_frame), | ||||||
|  | 						 GFP_KERNEL, | ||||||
|  | 						 &urb->transfer_dma); | ||||||
|  | 			if (!buf) { | ||||||
|  | 				netdev_err(netdev, | ||||||
|  | 					   "No memory left for USB buffer\n"); | ||||||
|  | 				usb_free_urb(urb); | ||||||
|  | 				return -ENOMEM; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			/* fill, anchor, and submit rx urb */ | ||||||
|  | 			usb_fill_bulk_urb(urb, | ||||||
|  | 					  dev->udev, | ||||||
|  | 					  usb_rcvbulkpipe(dev->udev, | ||||||
|  | 							  GSUSB_ENDPOINT_IN), | ||||||
|  | 					  buf, | ||||||
|  | 					  sizeof(struct gs_host_frame), | ||||||
|  | 					  gs_usb_recieve_bulk_callback, | ||||||
|  | 					  parent); | ||||||
|  | 			urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||||||
|  | 
 | ||||||
|  | 			usb_anchor_urb(urb, &parent->rx_submitted); | ||||||
|  | 
 | ||||||
|  | 			rc = usb_submit_urb(urb, GFP_KERNEL); | ||||||
|  | 			if (rc) { | ||||||
|  | 				if (rc == -ENODEV) | ||||||
|  | 					netif_device_detach(dev->netdev); | ||||||
|  | 
 | ||||||
|  | 				netdev_err(netdev, | ||||||
|  | 					   "usb_submit failed (err=%d)\n", | ||||||
|  | 					   rc); | ||||||
|  | 
 | ||||||
|  | 				usb_unanchor_urb(urb); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			/* Drop reference,
 | ||||||
|  | 			 * USB core will take care of freeing it | ||||||
|  | 			 */ | ||||||
|  | 			usb_free_urb(urb); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dm = kmalloc(sizeof(*dm), GFP_KERNEL); | ||||||
|  | 	if (!dm) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	/* flags */ | ||||||
|  | 	ctrlmode = dev->can.ctrlmode; | ||||||
|  | 	dm->flags = 0; | ||||||
|  | 
 | ||||||
|  | 	if (ctrlmode & CAN_CTRLMODE_LOOPBACK) | ||||||
|  | 		dm->flags |= GS_CAN_MODE_LOOP_BACK; | ||||||
|  | 	else if (ctrlmode & CAN_CTRLMODE_LISTENONLY) | ||||||
|  | 		dm->flags |= GS_CAN_MODE_LISTEN_ONLY; | ||||||
|  | 
 | ||||||
|  | 	/* Controller is not allowed to retry TX
 | ||||||
|  | 	 * this mode is unavailable on atmels uc3c hardware | ||||||
|  | 	 */ | ||||||
|  | 	if (ctrlmode & CAN_CTRLMODE_ONE_SHOT) | ||||||
|  | 		dm->flags |= GS_CAN_MODE_ONE_SHOT; | ||||||
|  | 
 | ||||||
|  | 	if (ctrlmode & CAN_CTRLMODE_3_SAMPLES) | ||||||
|  | 		dm->flags |= GS_CAN_MODE_TRIPLE_SAMPLE; | ||||||
|  | 
 | ||||||
|  | 	/* finally start device */ | ||||||
|  | 	dm->mode = GS_CAN_MODE_START; | ||||||
|  | 	rc = usb_control_msg(interface_to_usbdev(dev->iface), | ||||||
|  | 			     usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0), | ||||||
|  | 			     GS_USB_BREQ_MODE, | ||||||
|  | 			     USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||||||
|  | 			     dev->channel, | ||||||
|  | 			     0, | ||||||
|  | 			     dm, | ||||||
|  | 			     sizeof(*dm), | ||||||
|  | 			     1000); | ||||||
|  | 
 | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		netdev_err(netdev, "Couldn't start device (err=%d)\n", rc); | ||||||
|  | 		kfree(dm); | ||||||
|  | 		return rc; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	kfree(dm); | ||||||
|  | 
 | ||||||
|  | 	dev->can.state = CAN_STATE_ERROR_ACTIVE; | ||||||
|  | 
 | ||||||
|  | 	if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)) | ||||||
|  | 		netif_start_queue(netdev); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gs_can_close(struct net_device *netdev) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	struct gs_can *dev = netdev_priv(netdev); | ||||||
|  | 	struct gs_usb *parent = dev->parent; | ||||||
|  | 
 | ||||||
|  | 	netif_stop_queue(netdev); | ||||||
|  | 
 | ||||||
|  | 	/* Stop polling */ | ||||||
|  | 	if (atomic_dec_and_test(&parent->active_channels)) | ||||||
|  | 		usb_kill_anchored_urbs(&parent->rx_submitted); | ||||||
|  | 
 | ||||||
|  | 	/* Stop sending URBs */ | ||||||
|  | 	usb_kill_anchored_urbs(&dev->tx_submitted); | ||||||
|  | 	atomic_set(&dev->active_tx_urbs, 0); | ||||||
|  | 
 | ||||||
|  | 	/* reset the device */ | ||||||
|  | 	rc = gs_cmd_reset(parent, dev); | ||||||
|  | 	if (rc < 0) | ||||||
|  | 		netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc); | ||||||
|  | 
 | ||||||
|  | 	/* reset tx contexts */ | ||||||
|  | 	for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { | ||||||
|  | 		dev->tx_context[rc].dev = dev; | ||||||
|  | 		dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* close the netdev */ | ||||||
|  | 	close_candev(netdev); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct net_device_ops gs_usb_netdev_ops = { | ||||||
|  | 	.ndo_open = gs_can_open, | ||||||
|  | 	.ndo_stop = gs_can_close, | ||||||
|  | 	.ndo_start_xmit = gs_can_start_xmit, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf) | ||||||
|  | { | ||||||
|  | 	struct gs_can *dev; | ||||||
|  | 	struct net_device *netdev; | ||||||
|  | 	int rc; | ||||||
|  | 	struct gs_device_bt_const *bt_const; | ||||||
|  | 
 | ||||||
|  | 	bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL); | ||||||
|  | 	if (!bt_const) | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 
 | ||||||
|  | 	/* fetch bit timing constants */ | ||||||
|  | 	rc = usb_control_msg(interface_to_usbdev(intf), | ||||||
|  | 			     usb_rcvctrlpipe(interface_to_usbdev(intf), 0), | ||||||
|  | 			     GS_USB_BREQ_BT_CONST, | ||||||
|  | 			     USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||||||
|  | 			     channel, | ||||||
|  | 			     0, | ||||||
|  | 			     bt_const, | ||||||
|  | 			     sizeof(*bt_const), | ||||||
|  | 			     1000); | ||||||
|  | 
 | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		dev_err(&intf->dev, | ||||||
|  | 			"Couldn't get bit timing const for channel (err=%d)\n", | ||||||
|  | 			rc); | ||||||
|  | 		kfree(bt_const); | ||||||
|  | 		return ERR_PTR(rc); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* create netdev */ | ||||||
|  | 	netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS); | ||||||
|  | 	if (!netdev) { | ||||||
|  | 		dev_err(&intf->dev, "Couldn't allocate candev\n"); | ||||||
|  | 		kfree(bt_const); | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dev = netdev_priv(netdev); | ||||||
|  | 
 | ||||||
|  | 	netdev->netdev_ops = &gs_usb_netdev_ops; | ||||||
|  | 
 | ||||||
|  | 	netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */ | ||||||
|  | 
 | ||||||
|  | 	/* dev settup */ | ||||||
|  | 	strcpy(dev->bt_const.name, "gs_usb"); | ||||||
|  | 	dev->bt_const.tseg1_min = bt_const->tseg1_min; | ||||||
|  | 	dev->bt_const.tseg1_max = bt_const->tseg1_max; | ||||||
|  | 	dev->bt_const.tseg2_min = bt_const->tseg2_min; | ||||||
|  | 	dev->bt_const.tseg2_max = bt_const->tseg2_max; | ||||||
|  | 	dev->bt_const.sjw_max = bt_const->sjw_max; | ||||||
|  | 	dev->bt_const.brp_min = bt_const->brp_min; | ||||||
|  | 	dev->bt_const.brp_max = bt_const->brp_max; | ||||||
|  | 	dev->bt_const.brp_inc = bt_const->brp_inc; | ||||||
|  | 
 | ||||||
|  | 	dev->udev = interface_to_usbdev(intf); | ||||||
|  | 	dev->iface = intf; | ||||||
|  | 	dev->netdev = netdev; | ||||||
|  | 	dev->channel = channel; | ||||||
|  | 
 | ||||||
|  | 	init_usb_anchor(&dev->tx_submitted); | ||||||
|  | 	atomic_set(&dev->active_tx_urbs, 0); | ||||||
|  | 	spin_lock_init(&dev->tx_ctx_lock); | ||||||
|  | 	for (rc = 0; rc < GS_MAX_TX_URBS; rc++) { | ||||||
|  | 		dev->tx_context[rc].dev = dev; | ||||||
|  | 		dev->tx_context[rc].echo_id = GS_MAX_TX_URBS; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* can settup */ | ||||||
|  | 	dev->can.state = CAN_STATE_STOPPED; | ||||||
|  | 	dev->can.clock.freq = bt_const->fclk_can; | ||||||
|  | 	dev->can.bittiming_const = &dev->bt_const; | ||||||
|  | 	dev->can.do_set_bittiming = gs_usb_set_bittiming; | ||||||
|  | 
 | ||||||
|  | 	dev->can.ctrlmode_supported = 0; | ||||||
|  | 
 | ||||||
|  | 	if (bt_const->feature & GS_CAN_FEATURE_LISTEN_ONLY) | ||||||
|  | 		dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; | ||||||
|  | 
 | ||||||
|  | 	if (bt_const->feature & GS_CAN_FEATURE_LOOP_BACK) | ||||||
|  | 		dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK; | ||||||
|  | 
 | ||||||
|  | 	if (bt_const->feature & GS_CAN_FEATURE_TRIPLE_SAMPLE) | ||||||
|  | 		dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; | ||||||
|  | 
 | ||||||
|  | 	if (bt_const->feature & GS_CAN_FEATURE_ONE_SHOT) | ||||||
|  | 		dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; | ||||||
|  | 
 | ||||||
|  | 	kfree(bt_const); | ||||||
|  | 
 | ||||||
|  | 	SET_NETDEV_DEV(netdev, &intf->dev); | ||||||
|  | 
 | ||||||
|  | 	rc = register_candev(dev->netdev); | ||||||
|  | 	if (rc) { | ||||||
|  | 		free_candev(dev->netdev); | ||||||
|  | 		dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc); | ||||||
|  | 		return ERR_PTR(rc); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return dev; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gs_destroy_candev(struct gs_can *dev) | ||||||
|  | { | ||||||
|  | 	unregister_candev(dev->netdev); | ||||||
|  | 	free_candev(dev->netdev); | ||||||
|  | 	kfree(dev); | ||||||
|  | 	usb_kill_anchored_urbs(&dev->tx_submitted); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) | ||||||
|  | { | ||||||
|  | 	struct gs_usb *dev; | ||||||
|  | 	int rc = -ENOMEM; | ||||||
|  | 	unsigned int icount, i; | ||||||
|  | 	struct gs_host_config *hconf; | ||||||
|  | 	struct gs_device_config *dconf; | ||||||
|  | 
 | ||||||
|  | 	hconf = kmalloc(sizeof(*hconf), GFP_KERNEL); | ||||||
|  | 	if (!hconf) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	hconf->byte_order = 0x0000beef; | ||||||
|  | 
 | ||||||
|  | 	/* send host config */ | ||||||
|  | 	rc = usb_control_msg(interface_to_usbdev(intf), | ||||||
|  | 			     usb_sndctrlpipe(interface_to_usbdev(intf), 0), | ||||||
|  | 			     GS_USB_BREQ_HOST_FORMAT, | ||||||
|  | 			     USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||||||
|  | 			     1, | ||||||
|  | 			     intf->altsetting[0].desc.bInterfaceNumber, | ||||||
|  | 			     hconf, | ||||||
|  | 			     sizeof(*hconf), | ||||||
|  | 			     1000); | ||||||
|  | 
 | ||||||
|  | 	kfree(hconf); | ||||||
|  | 
 | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		dev_err(&intf->dev, "Couldn't send data format (err=%d)\n", | ||||||
|  | 			rc); | ||||||
|  | 		return rc; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dconf = kmalloc(sizeof(*dconf), GFP_KERNEL); | ||||||
|  | 	if (!dconf) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	/* read device config */ | ||||||
|  | 	rc = usb_control_msg(interface_to_usbdev(intf), | ||||||
|  | 			     usb_rcvctrlpipe(interface_to_usbdev(intf), 0), | ||||||
|  | 			     GS_USB_BREQ_DEVICE_CONFIG, | ||||||
|  | 			     USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, | ||||||
|  | 			     1, | ||||||
|  | 			     intf->altsetting[0].desc.bInterfaceNumber, | ||||||
|  | 			     dconf, | ||||||
|  | 			     sizeof(*dconf), | ||||||
|  | 			     1000); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n", | ||||||
|  | 			rc); | ||||||
|  | 
 | ||||||
|  | 		kfree(dconf); | ||||||
|  | 
 | ||||||
|  | 		return rc; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	icount = dconf->icount+1; | ||||||
|  | 
 | ||||||
|  | 	kfree(dconf); | ||||||
|  | 
 | ||||||
|  | 	dev_info(&intf->dev, "Configuring for %d interfaces\n", icount); | ||||||
|  | 
 | ||||||
|  | 	if (icount > GS_MAX_INTF) { | ||||||
|  | 		dev_err(&intf->dev, | ||||||
|  | 			"Driver cannot handle more that %d CAN interfaces\n", | ||||||
|  | 			GS_MAX_INTF); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||||||
|  | 	init_usb_anchor(&dev->rx_submitted); | ||||||
|  | 
 | ||||||
|  | 	atomic_set(&dev->active_channels, 0); | ||||||
|  | 
 | ||||||
|  | 	usb_set_intfdata(intf, dev); | ||||||
|  | 	dev->udev = interface_to_usbdev(intf); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < icount; i++) { | ||||||
|  | 		dev->canch[i] = gs_make_candev(i, intf); | ||||||
|  | 		if (IS_ERR_OR_NULL(dev->canch[i])) { | ||||||
|  | 			/* on failure destroy previously created candevs */ | ||||||
|  | 			icount = i; | ||||||
|  | 			for (i = 0; i < icount; i++) { | ||||||
|  | 				gs_destroy_candev(dev->canch[i]); | ||||||
|  | 				dev->canch[i] = NULL; | ||||||
|  | 			} | ||||||
|  | 			kfree(dev); | ||||||
|  | 			return rc; | ||||||
|  | 		} | ||||||
|  | 		dev->canch[i]->parent = dev; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gs_usb_disconnect(struct usb_interface *intf) | ||||||
|  | { | ||||||
|  | 	unsigned i; | ||||||
|  | 	struct gs_usb *dev = usb_get_intfdata(intf); | ||||||
|  | 	usb_set_intfdata(intf, NULL); | ||||||
|  | 
 | ||||||
|  | 	if (!dev) { | ||||||
|  | 		dev_err(&intf->dev, "Disconnect (nodata)\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < GS_MAX_INTF; i++) { | ||||||
|  | 		struct gs_can *can = dev->canch[i]; | ||||||
|  | 
 | ||||||
|  | 		if (!can) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		gs_destroy_candev(can); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	usb_kill_anchored_urbs(&dev->rx_submitted); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct usb_device_id gs_usb_table[] = { | ||||||
|  | 	{USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)}, | ||||||
|  | 	{} /* Terminating entry */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | MODULE_DEVICE_TABLE(usb, gs_usb_table); | ||||||
|  | 
 | ||||||
|  | static struct usb_driver gs_usb_driver = { | ||||||
|  | 	.name       = "gs_usb", | ||||||
|  | 	.probe      = gs_usb_probe, | ||||||
|  | 	.disconnect = gs_usb_disconnect, | ||||||
|  | 	.id_table   = gs_usb_table, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | module_usb_driver(gs_usb_driver); | ||||||
|  | 
 | ||||||
|  | MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>"); | ||||||
|  | MODULE_DESCRIPTION( | ||||||
|  | "Socket CAN device driver for Geschwister Schneider Technologie-, " | ||||||
|  | "Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces."); | ||||||
|  | MODULE_LICENSE("GPL v2"); | ||||||
		Loading…
	
		Reference in a new issue
	
	 Maximilian Schneider
						Maximilian Schneider