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

usbsevseg.c (9692B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * USB 7 Segment Driver
      4 *
      5 * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com>
      6 * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com)
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/errno.h>
     11#include <linux/slab.h>
     12#include <linux/module.h>
     13#include <linux/string.h>
     14#include <linux/usb.h>
     15
     16
     17#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>"
     18#define DRIVER_DESC "USB 7 Segment Driver"
     19
     20#define VENDOR_ID	0x0fc5
     21#define PRODUCT_ID	0x1227
     22#define MAXLEN		8
     23
     24/* table of devices that work with this driver */
     25static const struct usb_device_id id_table[] = {
     26	{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
     27	{ },
     28};
     29MODULE_DEVICE_TABLE(usb, id_table);
     30
     31/* the different text display modes the device is capable of */
     32static const char *display_textmodes[] = {"raw", "hex", "ascii"};
     33
     34struct usb_sevsegdev {
     35	struct usb_device *udev;
     36	struct usb_interface *intf;
     37
     38	u8 powered;
     39	u8 mode_msb;
     40	u8 mode_lsb;
     41	u8 decimals[MAXLEN];
     42	u8 textmode;
     43	u8 text[MAXLEN];
     44	u16 textlength;
     45
     46	u8 shadow_power; /* for PM */
     47	u8 has_interface_pm;
     48};
     49
     50/* sysfs_streq can't replace this completely
     51 * If the device was in hex mode, and the user wanted a 0,
     52 * if str commands are used, we would assume the end of string
     53 * so mem commands are used.
     54 */
     55static inline size_t my_memlen(const char *buf, size_t count)
     56{
     57	if (count > 0 && buf[count-1] == '\n')
     58		return count - 1;
     59	else
     60		return count;
     61}
     62
     63static void update_display_powered(struct usb_sevsegdev *mydev)
     64{
     65	int rc;
     66
     67	if (mydev->powered && !mydev->has_interface_pm) {
     68		rc = usb_autopm_get_interface(mydev->intf);
     69		if (rc < 0)
     70			return;
     71		mydev->has_interface_pm = 1;
     72	}
     73
     74	if (mydev->shadow_power != 1)
     75		return;
     76
     77	rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
     78				  (80 * 0x100) + 10, /*  (power mode) */
     79				  (0x00 * 0x100) + (mydev->powered ? 1 : 0),
     80				  NULL, 0, 2000, GFP_KERNEL);
     81	if (rc < 0)
     82		dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
     83
     84	if (!mydev->powered && mydev->has_interface_pm) {
     85		usb_autopm_put_interface(mydev->intf);
     86		mydev->has_interface_pm = 0;
     87	}
     88}
     89
     90static void update_display_mode(struct usb_sevsegdev *mydev)
     91{
     92	int rc;
     93
     94	if(mydev->shadow_power != 1)
     95		return;
     96
     97	rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
     98				  (82 * 0x100) + 10, /* (set mode) */
     99				  (mydev->mode_msb * 0x100) + mydev->mode_lsb,
    100				  NULL, 0, 2000, GFP_NOIO);
    101
    102	if (rc < 0)
    103		dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
    104}
    105
    106static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
    107{
    108	int rc;
    109	int i;
    110	unsigned char buffer[MAXLEN] = {0};
    111	u8 decimals = 0;
    112
    113	if(mydev->shadow_power != 1)
    114		return;
    115
    116	/* The device is right to left, where as you write left to right */
    117	for (i = 0; i < mydev->textlength; i++)
    118		buffer[i] = mydev->text[mydev->textlength-1-i];
    119
    120	rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
    121				  (85 * 0x100) + 10, /* (write text) */
    122				  (0 * 0x100) + mydev->textmode, /* mode  */
    123				  &buffer, mydev->textlength, 2000, mf);
    124
    125	if (rc < 0)
    126		dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
    127
    128	/* The device is right to left, where as you write left to right */
    129	for (i = 0; i < sizeof(mydev->decimals); i++)
    130		decimals |= mydev->decimals[i] << i;
    131
    132	rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
    133				  (86 * 0x100) + 10, /* (set decimal) */
    134				  (0 * 0x100) + decimals, /* decimals */
    135				  NULL, 0, 2000, mf);
    136
    137	if (rc < 0)
    138		dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
    139}
    140
    141#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)		\
    142static ssize_t name##_show(struct device *dev,			\
    143	struct device_attribute *attr, char *buf) 		\
    144{								\
    145	struct usb_interface *intf = to_usb_interface(dev);	\
    146	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
    147								\
    148	return sprintf(buf, "%u\n", mydev->name);		\
    149}								\
    150								\
    151static ssize_t name##_store(struct device *dev,			\
    152	struct device_attribute *attr, const char *buf, size_t count) \
    153{								\
    154	struct usb_interface *intf = to_usb_interface(dev);	\
    155	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
    156								\
    157	mydev->name = simple_strtoul(buf, NULL, 10);		\
    158	update_fcn(mydev); 					\
    159								\
    160	return count;						\
    161}								\
    162static DEVICE_ATTR_RW(name);
    163
    164static ssize_t text_show(struct device *dev,
    165	struct device_attribute *attr, char *buf)
    166{
    167	struct usb_interface *intf = to_usb_interface(dev);
    168	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
    169
    170	return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
    171}
    172
    173static ssize_t text_store(struct device *dev,
    174	struct device_attribute *attr, const char *buf, size_t count)
    175{
    176	struct usb_interface *intf = to_usb_interface(dev);
    177	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
    178	size_t end = my_memlen(buf, count);
    179
    180	if (end > sizeof(mydev->text))
    181		return -EINVAL;
    182
    183	memset(mydev->text, 0, sizeof(mydev->text));
    184	mydev->textlength = end;
    185
    186	if (end > 0)
    187		memcpy(mydev->text, buf, end);
    188
    189	update_display_visual(mydev, GFP_KERNEL);
    190	return count;
    191}
    192
    193static DEVICE_ATTR_RW(text);
    194
    195static ssize_t decimals_show(struct device *dev,
    196	struct device_attribute *attr, char *buf)
    197{
    198	struct usb_interface *intf = to_usb_interface(dev);
    199	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
    200	int i;
    201	int pos;
    202
    203	for (i = 0; i < sizeof(mydev->decimals); i++) {
    204		pos = sizeof(mydev->decimals) - 1 - i;
    205		if (mydev->decimals[i] == 0)
    206			buf[pos] = '0';
    207		else if (mydev->decimals[i] == 1)
    208			buf[pos] = '1';
    209		else
    210			buf[pos] = 'x';
    211	}
    212
    213	buf[sizeof(mydev->decimals)] = '\n';
    214	return sizeof(mydev->decimals) + 1;
    215}
    216
    217static ssize_t decimals_store(struct device *dev,
    218	struct device_attribute *attr, const char *buf, size_t count)
    219{
    220	struct usb_interface *intf = to_usb_interface(dev);
    221	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
    222	size_t end = my_memlen(buf, count);
    223	int i;
    224
    225	if (end > sizeof(mydev->decimals))
    226		return -EINVAL;
    227
    228	for (i = 0; i < end; i++)
    229		if (buf[i] != '0' && buf[i] != '1')
    230			return -EINVAL;
    231
    232	memset(mydev->decimals, 0, sizeof(mydev->decimals));
    233	for (i = 0; i < end; i++)
    234		if (buf[i] == '1')
    235			mydev->decimals[end-1-i] = 1;
    236
    237	update_display_visual(mydev, GFP_KERNEL);
    238
    239	return count;
    240}
    241
    242static DEVICE_ATTR_RW(decimals);
    243
    244static ssize_t textmode_show(struct device *dev,
    245	struct device_attribute *attr, char *buf)
    246{
    247	struct usb_interface *intf = to_usb_interface(dev);
    248	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
    249	int i;
    250
    251	buf[0] = 0;
    252
    253	for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) {
    254		if (mydev->textmode == i) {
    255			strcat(buf, " [");
    256			strcat(buf, display_textmodes[i]);
    257			strcat(buf, "] ");
    258		} else {
    259			strcat(buf, " ");
    260			strcat(buf, display_textmodes[i]);
    261			strcat(buf, " ");
    262		}
    263	}
    264	strcat(buf, "\n");
    265
    266
    267	return strlen(buf);
    268}
    269
    270static ssize_t textmode_store(struct device *dev,
    271	struct device_attribute *attr, const char *buf, size_t count)
    272{
    273	struct usb_interface *intf = to_usb_interface(dev);
    274	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
    275	int i;
    276
    277	i = sysfs_match_string(display_textmodes, buf);
    278	if (i < 0)
    279		return i;
    280
    281	mydev->textmode = i;
    282	update_display_visual(mydev, GFP_KERNEL);
    283	return count;
    284}
    285
    286static DEVICE_ATTR_RW(textmode);
    287
    288
    289MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
    290MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
    291MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
    292
    293static struct attribute *sevseg_attrs[] = {
    294	&dev_attr_powered.attr,
    295	&dev_attr_text.attr,
    296	&dev_attr_textmode.attr,
    297	&dev_attr_decimals.attr,
    298	&dev_attr_mode_msb.attr,
    299	&dev_attr_mode_lsb.attr,
    300	NULL
    301};
    302ATTRIBUTE_GROUPS(sevseg);
    303
    304static int sevseg_probe(struct usb_interface *interface,
    305	const struct usb_device_id *id)
    306{
    307	struct usb_device *udev = interface_to_usbdev(interface);
    308	struct usb_sevsegdev *mydev = NULL;
    309	int rc = -ENOMEM;
    310
    311	mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
    312	if (!mydev)
    313		goto error_mem;
    314
    315	mydev->udev = usb_get_dev(udev);
    316	mydev->intf = interface;
    317	usb_set_intfdata(interface, mydev);
    318
    319	/* PM */
    320	mydev->shadow_power = 1; /* currently active */
    321	mydev->has_interface_pm = 0; /* have not issued autopm_get */
    322
    323	/*set defaults */
    324	mydev->textmode = 0x02; /* ascii mode */
    325	mydev->mode_msb = 0x06; /* 6 characters */
    326	mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
    327
    328	dev_info(&interface->dev, "USB 7 Segment device now attached\n");
    329	return 0;
    330
    331error_mem:
    332	return rc;
    333}
    334
    335static void sevseg_disconnect(struct usb_interface *interface)
    336{
    337	struct usb_sevsegdev *mydev;
    338
    339	mydev = usb_get_intfdata(interface);
    340	usb_set_intfdata(interface, NULL);
    341	usb_put_dev(mydev->udev);
    342	kfree(mydev);
    343	dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
    344}
    345
    346static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
    347{
    348	struct usb_sevsegdev *mydev;
    349
    350	mydev = usb_get_intfdata(intf);
    351	mydev->shadow_power = 0;
    352
    353	return 0;
    354}
    355
    356static int sevseg_resume(struct usb_interface *intf)
    357{
    358	struct usb_sevsegdev *mydev;
    359
    360	mydev = usb_get_intfdata(intf);
    361	mydev->shadow_power = 1;
    362	update_display_mode(mydev);
    363	update_display_visual(mydev, GFP_NOIO);
    364
    365	return 0;
    366}
    367
    368static int sevseg_reset_resume(struct usb_interface *intf)
    369{
    370	struct usb_sevsegdev *mydev;
    371
    372	mydev = usb_get_intfdata(intf);
    373	mydev->shadow_power = 1;
    374	update_display_mode(mydev);
    375	update_display_visual(mydev, GFP_NOIO);
    376
    377	return 0;
    378}
    379
    380static struct usb_driver sevseg_driver = {
    381	.name =		"usbsevseg",
    382	.probe =	sevseg_probe,
    383	.disconnect =	sevseg_disconnect,
    384	.suspend =	sevseg_suspend,
    385	.resume =	sevseg_resume,
    386	.reset_resume =	sevseg_reset_resume,
    387	.id_table =	id_table,
    388	.dev_groups =	sevseg_groups,
    389	.supports_autosuspend = 1,
    390};
    391
    392module_usb_driver(sevseg_driver);
    393
    394MODULE_AUTHOR(DRIVER_AUTHOR);
    395MODULE_DESCRIPTION(DRIVER_DESC);
    396MODULE_LICENSE("GPL");