cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bcm203x.c (6117B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *
      4 *  Broadcom Blutonium firmware driver
      5 *
      6 *  Copyright (C) 2003  Maxim Krasnyansky <maxk@qualcomm.com>
      7 *  Copyright (C) 2003  Marcel Holtmann <marcel@holtmann.org>
      8 */
      9
     10#include <linux/module.h>
     11
     12#include <linux/kernel.h>
     13#include <linux/init.h>
     14#include <linux/slab.h>
     15#include <linux/types.h>
     16#include <linux/errno.h>
     17
     18#include <linux/device.h>
     19#include <linux/firmware.h>
     20
     21#include <linux/usb.h>
     22
     23#include <net/bluetooth/bluetooth.h>
     24
     25#define VERSION "1.2"
     26
     27static const struct usb_device_id bcm203x_table[] = {
     28	/* Broadcom Blutonium (BCM2033) */
     29	{ USB_DEVICE(0x0a5c, 0x2033) },
     30
     31	{ }	/* Terminating entry */
     32};
     33
     34MODULE_DEVICE_TABLE(usb, bcm203x_table);
     35
     36#define BCM203X_ERROR		0
     37#define BCM203X_RESET		1
     38#define BCM203X_LOAD_MINIDRV	2
     39#define BCM203X_SELECT_MEMORY	3
     40#define BCM203X_CHECK_MEMORY	4
     41#define BCM203X_LOAD_FIRMWARE	5
     42#define BCM203X_CHECK_FIRMWARE	6
     43
     44#define BCM203X_IN_EP		0x81
     45#define BCM203X_OUT_EP		0x02
     46
     47struct bcm203x_data {
     48	struct usb_device	*udev;
     49
     50	unsigned long		state;
     51
     52	struct work_struct	work;
     53	atomic_t		shutdown;
     54
     55	struct urb		*urb;
     56	unsigned char		*buffer;
     57
     58	unsigned char		*fw_data;
     59	unsigned int		fw_size;
     60	unsigned int		fw_sent;
     61};
     62
     63static void bcm203x_complete(struct urb *urb)
     64{
     65	struct bcm203x_data *data = urb->context;
     66	struct usb_device *udev = urb->dev;
     67	int len;
     68
     69	BT_DBG("udev %p urb %p", udev, urb);
     70
     71	if (urb->status) {
     72		BT_ERR("URB failed with status %d", urb->status);
     73		data->state = BCM203X_ERROR;
     74		return;
     75	}
     76
     77	switch (data->state) {
     78	case BCM203X_LOAD_MINIDRV:
     79		memcpy(data->buffer, "#", 1);
     80
     81		usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
     82				data->buffer, 1, bcm203x_complete, data);
     83
     84		data->state = BCM203X_SELECT_MEMORY;
     85
     86		/* use workqueue to have a small delay */
     87		schedule_work(&data->work);
     88		break;
     89
     90	case BCM203X_SELECT_MEMORY:
     91		usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
     92				data->buffer, 32, bcm203x_complete, data, 1);
     93
     94		data->state = BCM203X_CHECK_MEMORY;
     95
     96		if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
     97			BT_ERR("Can't submit URB");
     98		break;
     99
    100	case BCM203X_CHECK_MEMORY:
    101		if (data->buffer[0] != '#') {
    102			BT_ERR("Memory select failed");
    103			data->state = BCM203X_ERROR;
    104			break;
    105		}
    106
    107		data->state = BCM203X_LOAD_FIRMWARE;
    108		fallthrough;
    109	case BCM203X_LOAD_FIRMWARE:
    110		if (data->fw_sent == data->fw_size) {
    111			usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
    112				data->buffer, 32, bcm203x_complete, data, 1);
    113
    114			data->state = BCM203X_CHECK_FIRMWARE;
    115		} else {
    116			len = min_t(uint, data->fw_size - data->fw_sent, 4096);
    117
    118			usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
    119				data->fw_data + data->fw_sent, len, bcm203x_complete, data);
    120
    121			data->fw_sent += len;
    122		}
    123
    124		if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
    125			BT_ERR("Can't submit URB");
    126		break;
    127
    128	case BCM203X_CHECK_FIRMWARE:
    129		if (data->buffer[0] != '.') {
    130			BT_ERR("Firmware loading failed");
    131			data->state = BCM203X_ERROR;
    132			break;
    133		}
    134
    135		data->state = BCM203X_RESET;
    136		break;
    137	}
    138}
    139
    140static void bcm203x_work(struct work_struct *work)
    141{
    142	struct bcm203x_data *data =
    143		container_of(work, struct bcm203x_data, work);
    144
    145	if (atomic_read(&data->shutdown))
    146		return;
    147
    148	if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
    149		BT_ERR("Can't submit URB");
    150}
    151
    152static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
    153{
    154	const struct firmware *firmware;
    155	struct usb_device *udev = interface_to_usbdev(intf);
    156	struct bcm203x_data *data;
    157	int size;
    158
    159	BT_DBG("intf %p id %p", intf, id);
    160
    161	if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
    162		return -ENODEV;
    163
    164	data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
    165	if (!data)
    166		return -ENOMEM;
    167
    168	data->udev  = udev;
    169	data->state = BCM203X_LOAD_MINIDRV;
    170
    171	data->urb = usb_alloc_urb(0, GFP_KERNEL);
    172	if (!data->urb)
    173		return -ENOMEM;
    174
    175	if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
    176		BT_ERR("Mini driver request failed");
    177		usb_free_urb(data->urb);
    178		return -EIO;
    179	}
    180
    181	BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
    182
    183	size = max_t(uint, firmware->size, 4096);
    184
    185	data->buffer = kmalloc(size, GFP_KERNEL);
    186	if (!data->buffer) {
    187		BT_ERR("Can't allocate memory for mini driver");
    188		release_firmware(firmware);
    189		usb_free_urb(data->urb);
    190		return -ENOMEM;
    191	}
    192
    193	memcpy(data->buffer, firmware->data, firmware->size);
    194
    195	usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
    196			data->buffer, firmware->size, bcm203x_complete, data);
    197
    198	release_firmware(firmware);
    199
    200	if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
    201		BT_ERR("Firmware request failed");
    202		usb_free_urb(data->urb);
    203		kfree(data->buffer);
    204		return -EIO;
    205	}
    206
    207	BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
    208
    209	data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
    210	if (!data->fw_data) {
    211		BT_ERR("Can't allocate memory for firmware image");
    212		release_firmware(firmware);
    213		usb_free_urb(data->urb);
    214		kfree(data->buffer);
    215		return -ENOMEM;
    216	}
    217
    218	data->fw_size = firmware->size;
    219	data->fw_sent = 0;
    220
    221	release_firmware(firmware);
    222
    223	INIT_WORK(&data->work, bcm203x_work);
    224
    225	usb_set_intfdata(intf, data);
    226
    227	/* use workqueue to have a small delay */
    228	schedule_work(&data->work);
    229
    230	return 0;
    231}
    232
    233static void bcm203x_disconnect(struct usb_interface *intf)
    234{
    235	struct bcm203x_data *data = usb_get_intfdata(intf);
    236
    237	BT_DBG("intf %p", intf);
    238
    239	atomic_inc(&data->shutdown);
    240	cancel_work_sync(&data->work);
    241
    242	usb_kill_urb(data->urb);
    243
    244	usb_set_intfdata(intf, NULL);
    245
    246	usb_free_urb(data->urb);
    247	kfree(data->fw_data);
    248	kfree(data->buffer);
    249}
    250
    251static struct usb_driver bcm203x_driver = {
    252	.name		= "bcm203x",
    253	.probe		= bcm203x_probe,
    254	.disconnect	= bcm203x_disconnect,
    255	.id_table	= bcm203x_table,
    256	.disable_hub_initiated_lpm = 1,
    257};
    258
    259module_usb_driver(bcm203x_driver);
    260
    261MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
    262MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
    263MODULE_VERSION(VERSION);
    264MODULE_LICENSE("GPL");
    265MODULE_FIRMWARE("BCM2033-MD.hex");
    266MODULE_FIRMWARE("BCM2033-FW.bin");