Skip to content
ems_usb.c 26.7 KiB
Newer Older
 */
static int ems_usb_probe(struct usb_interface *intf,
			 const struct usb_device_id *id)
{
	struct net_device *netdev;
	struct ems_usb *dev;
	int i, err = -ENOMEM;

	netdev = alloc_candev(sizeof(struct ems_usb), MAX_TX_URBS);
	if (!netdev) {
		dev_err(netdev->dev.parent, "Couldn't alloc candev\n");
		return -ENOMEM;
	}

	dev = netdev_priv(netdev);

	dev->udev = interface_to_usbdev(intf);
	dev->netdev = netdev;

	dev->can.state = CAN_STATE_STOPPED;
	dev->can.clock.freq = EMS_USB_ARM7_CLOCK;
	dev->can.bittiming_const = &ems_usb_bittiming_const;
	dev->can.do_set_bittiming = ems_usb_set_bittiming;
	dev->can.do_set_mode = ems_usb_set_mode;
	dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;

	netdev->netdev_ops = &ems_usb_netdev_ops;

	netdev->flags |= IFF_ECHO; /* we support local echo */

	init_usb_anchor(&dev->rx_submitted);

	init_usb_anchor(&dev->tx_submitted);
	atomic_set(&dev->active_tx_urbs, 0);

	for (i = 0; i < MAX_TX_URBS; i++)
		dev->tx_contexts[i].echo_index = MAX_TX_URBS;

	dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!dev->intr_urb) {
		dev_err(netdev->dev.parent, "Couldn't alloc intr URB\n");
		goto cleanup_candev;
	}

	dev->intr_in_buffer = kzalloc(INTR_IN_BUFFER_SIZE, GFP_KERNEL);
	if (!dev->intr_in_buffer) {
		dev_err(netdev->dev.parent, "Couldn't alloc Intr buffer\n");
		goto cleanup_intr_urb;
	}

	dev->tx_msg_buffer = kzalloc(CPC_HEADER_SIZE +
				     sizeof(struct ems_cpc_msg), GFP_KERNEL);
	if (!dev->tx_msg_buffer) {
		dev_err(netdev->dev.parent, "Couldn't alloc Tx buffer\n");
		goto cleanup_intr_in_buffer;
	}

	usb_set_intfdata(intf, dev);

	SET_NETDEV_DEV(netdev, &intf->dev);

	init_params_sja1000(&dev->active_params);

	err = ems_usb_command_msg(dev, &dev->active_params);
	if (err) {
		dev_err(netdev->dev.parent,
			"couldn't initialize controller: %d\n", err);
		goto cleanup_tx_msg_buffer;
	}

	err = register_candev(netdev);
	if (err) {
		dev_err(netdev->dev.parent,
			"couldn't register CAN device: %d\n", err);
		goto cleanup_tx_msg_buffer;
	}

	return 0;

cleanup_tx_msg_buffer:
	kfree(dev->tx_msg_buffer);

cleanup_intr_in_buffer:
	kfree(dev->intr_in_buffer);

cleanup_intr_urb:
	usb_free_urb(dev->intr_urb);

cleanup_candev:
	free_candev(netdev);

	return err;
}

/*
 * called by the usb core when the device is removed from the system
 */
static void ems_usb_disconnect(struct usb_interface *intf)
{
	struct ems_usb *dev = usb_get_intfdata(intf);

	usb_set_intfdata(intf, NULL);

	if (dev) {
		unregister_netdev(dev->netdev);
		free_candev(dev->netdev);

		unlink_all_urbs(dev);

		usb_free_urb(dev->intr_urb);

		kfree(dev->intr_in_buffer);
	}
}

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver ems_usb_driver = {
	.name = "ems_usb",
	.probe = ems_usb_probe,
	.disconnect = ems_usb_disconnect,
	.id_table = ems_usb_table,
};

static int __init ems_usb_init(void)
{
	int err;

	printk(KERN_INFO "CPC-USB kernel driver loaded\n");

	/* register this driver with the USB subsystem */
	err = usb_register(&ems_usb_driver);

	if (err) {
		err("usb_register failed. Error number %d\n", err);
		return err;
	}

	return 0;
}

static void __exit ems_usb_exit(void)
{
	/* deregister this driver with the USB subsystem */
	usb_deregister(&ems_usb_driver);
}

module_init(ems_usb_init);
module_exit(ems_usb_exit);