cregit-Linux how code gets into the kernel

Release 4.7 drivers/bluetooth/bcm203x.c

/*
 *
 *  Broadcom Blutonium firmware driver
 *
 *  Copyright (C) 2003  Maxim Krasnyansky <maxk@qualcomm.com>
 *  Copyright (C) 2003  Marcel Holtmann <marcel@holtmann.org>
 *
 *
 *  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; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <linux/module.h>

#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>

#include <linux/device.h>
#include <linux/firmware.h>

#include <linux/usb.h>

#include <net/bluetooth/bluetooth.h>


#define VERSION "1.2"


static const struct usb_device_id bcm203x_table[] = {
	/* Broadcom Blutonium (BCM2033) */
	{ USB_DEVICE(0x0a5c, 0x2033) },

	{ }	/* Terminating entry */
};

MODULE_DEVICE_TABLE(usb, bcm203x_table);


#define BCM203X_ERROR		0

#define BCM203X_RESET		1

#define BCM203X_LOAD_MINIDRV	2

#define BCM203X_SELECT_MEMORY	3

#define BCM203X_CHECK_MEMORY	4

#define BCM203X_LOAD_FIRMWARE	5

#define BCM203X_CHECK_FIRMWARE	6


#define BCM203X_IN_EP		0x81

#define BCM203X_OUT_EP		0x02


struct bcm203x_data {
	
struct usb_device	*udev;

	
unsigned long		state;

	
struct work_struct	work;
	
atomic_t		shutdown;

	
struct urb		*urb;
	
unsigned char		*buffer;

	
unsigned char		*fw_data;
	
unsigned int		fw_size;
	
unsigned int		fw_sent;
};


static void bcm203x_complete(struct urb *urb) { struct bcm203x_data *data = urb->context; struct usb_device *udev = urb->dev; int len; BT_DBG("udev %p urb %p", udev, urb); if (urb->status) { BT_ERR("URB failed with status %d", urb->status); data->state = BCM203X_ERROR; return; } switch (data->state) { case BCM203X_LOAD_MINIDRV: memcpy(data->buffer, "#", 1); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP), data->buffer, 1, bcm203x_complete, data); data->state = BCM203X_SELECT_MEMORY; /* use workqueue to have a small delay */ schedule_work(&data->work); break; case BCM203X_SELECT_MEMORY: usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP), data->buffer, 32, bcm203x_complete, data, 1); data->state = BCM203X_CHECK_MEMORY; if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) BT_ERR("Can't submit URB"); break; case BCM203X_CHECK_MEMORY: if (data->buffer[0] != '#') { BT_ERR("Memory select failed"); data->state = BCM203X_ERROR; break; } data->state = BCM203X_LOAD_FIRMWARE; case BCM203X_LOAD_FIRMWARE: if (data->fw_sent == data->fw_size) { usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP), data->buffer, 32, bcm203x_complete, data, 1); data->state = BCM203X_CHECK_FIRMWARE; } else { len = min_t(uint, data->fw_size - data->fw_sent, 4096); usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP), data->fw_data + data->fw_sent, len, bcm203x_complete, data); data->fw_sent += len; } if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) BT_ERR("Can't submit URB"); break; case BCM203X_CHECK_FIRMWARE: if (data->buffer[0] != '.') { BT_ERR("Firmware loading failed"); data->state = BCM203X_ERROR; break; } data->state = BCM203X_RESET; break; } }

Contributors

PersonTokensPropCommitsCommitProp
marcel holtmannmarcel holtmann36999.73%375.00%
david herrmanndavid herrmann10.27%125.00%
Total370100.00%4100.00%


static void bcm203x_work(struct work_struct *work) { struct bcm203x_data *data = container_of(work, struct bcm203x_data, work); if (atomic_read(&data->shutdown)) return; if (usb_submit_urb(data->urb, GFP_KERNEL) < 0) BT_ERR("Can't submit URB"); }

Contributors

PersonTokensPropCommitsCommitProp
marcel holtmannmarcel holtmann3156.36%240.00%
david howellsdavid howells1221.82%120.00%
david herrmanndavid herrmann1221.82%240.00%
Total55100.00%5100.00%


static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id) { const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); struct bcm203x_data *data; int size; BT_DBG("intf %p id %p", intf, id); if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->udev = udev; data->state = BCM203X_LOAD_MINIDRV; data->urb = usb_alloc_urb(0, GFP_KERNEL); if (!data->urb) { BT_ERR("Can't allocate URB"); return -ENOMEM; } if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) { BT_ERR("Mini driver request failed"); usb_free_urb(data->urb); return -EIO; } BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size); size = max_t(uint, firmware->size, 4096); data->buffer = kmalloc(size, GFP_KERNEL); if (!data->buffer) { BT_ERR("Can't allocate memory for mini driver"); release_firmware(firmware); usb_free_urb(data->urb); return -ENOMEM; } memcpy(data->buffer, firmware->data, firmware->size); usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP), data->buffer, firmware->size, bcm203x_complete, data); release_firmware(firmware); if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) { BT_ERR("Firmware request failed"); usb_free_urb(data->urb); kfree(data->buffer); return -EIO; } BT_DBG("firmware data %p size %zu", firmware->data, firmware->size); data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL); if (!data->fw_data) { BT_ERR("Can't allocate memory for firmware image"); release_firmware(firmware); usb_free_urb(data->urb); kfree(data->buffer); return -ENOMEM; } data->fw_size = firmware->size; data->fw_sent = 0; release_firmware(firmware); INIT_WORK(&data->work, bcm203x_work); usb_set_intfdata(intf, data); /* use workqueue to have a small delay */ schedule_work(&data->work); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
marcel holtmannmarcel holtmann42496.15%555.56%
sachin kamatsachin kamat61.36%111.11%
magnus dammmagnus damm51.13%111.11%
julia lawalljulia lawall51.13%111.11%
david herrmanndavid herrmann10.23%111.11%
Total441100.00%9100.00%


static void bcm203x_disconnect(struct usb_interface *intf) { struct bcm203x_data *data = usb_get_intfdata(intf); BT_DBG("intf %p", intf); atomic_inc(&data->shutdown); cancel_work_sync(&data->work); usb_kill_urb(data->urb); usb_set_intfdata(intf, NULL); usb_free_urb(data->urb); kfree(data->fw_data); kfree(data->buffer); }

Contributors

PersonTokensPropCommitsCommitProp
marcel holtmannmarcel holtmann6379.75%375.00%
david herrmanndavid herrmann1620.25%125.00%
Total79100.00%4100.00%

static struct usb_driver bcm203x_driver = { .name = "bcm203x", .probe = bcm203x_probe, .disconnect = bcm203x_disconnect, .id_table = bcm203x_table, .disable_hub_initiated_lpm = 1, }; module_usb_driver(bcm203x_driver); MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); MODULE_FIRMWARE("BCM2033-MD.hex"); MODULE_FIRMWARE("BCM2033-FW.bin");

Overall Contributors

PersonTokensPropCommitsCommitProp
marcel holtmannmarcel holtmann108893.79%1052.63%
david herrmanndavid herrmann363.10%210.53%
david howellsdavid howells121.03%15.26%
sachin kamatsachin kamat60.52%15.26%
sarah sharpsarah sharp50.43%15.26%
julia lawalljulia lawall50.43%15.26%
magnus dammmagnus damm50.43%15.26%
greg kroah-hartmangreg kroah-hartman20.17%15.26%
marton nemethmarton nemeth10.09%15.26%
Total1160100.00%19100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}