Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ac37208 authored by Simon Arlott's avatar Simon Arlott Committed by Greg Kroah-Hartman
Browse files

USB: cxacru: add write-only sysfs attribute for modem configuration

The modem can be configured using CM_REQUEST_CARD_DATA_SET,
although CM_REQUEST_CARD_DATA_GET does not return any data.

Tested by setting the modulation (0x0a) option.

There is a list of parameters in the following archive,
but the meaning of many of them is not well documented:
  http://sourceforge.net/project/shownotes.php?release_id=301825



This source also indicates that the highest parameter set
is 0x4a but this varies by model so an arbitrary limit of
0x7f has been used (the index is a 32-bit integer).

Signed-off-by: default avatarSimon Arlott <simon@fire.lp0.eu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 885582c4
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -15,6 +15,15 @@ several sysfs attribute files for retrieving device statistics:
* adsl_headend_environment
	Information about the remote headend.

* adsl_config
	Configuration writing interface.
	Write parameters in hexadecimal format <index>=<value>,
	separated by whitespace, e.g.:
		"1=0 a=5"
	Up to 7 parameters at a time will be sent and the modem will restart
	the ADSL connection when any value is set. These are logged for future
	reference.

* downstream_attenuation (dB)
* downstream_bits_per_frame
* downstream_rate (kbps)
+75 −1
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ static const char cxacru_driver_name[] = "cxacru";
#define CXACRU_EP_DATA		0x02	/* Bulk in/out */

#define CMD_PACKET_SIZE		64	/* Should be maxpacket(ep)? */
#define CMD_MAX_CONFIG		((CMD_PACKET_SIZE / 4 - 1) / 2)

/* Addresses */
#define PLLFCLK_ADDR	0x00350068
@@ -216,6 +217,10 @@ static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
	cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)

#define CXACRU_SET_INIT(_name) \
static DEVICE_ATTR(_name, S_IWUSR, \
	NULL, cxacru_sysfs_store_##_name)

#define CXACRU_ATTR_INIT(_value, _type, _name) \
static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
	struct device_attribute *attr, char *buf) \
@@ -232,10 +237,12 @@ CXACRU__ATTR_INIT(_name)

#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
#define CXACRU_CMD_CREATE(_name)          CXACRU_DEVICE_CREATE_FILE(_name)
#define CXACRU_SET_CREATE(_name)          CXACRU_DEVICE_CREATE_FILE(_name)
#define CXACRU__ATTR_CREATE(_name)        CXACRU_DEVICE_CREATE_FILE(_name)

#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
#define CXACRU_CMD_REMOVE(_name)          CXACRU_DEVICE_REMOVE_FILE(_name)
#define CXACRU_SET_REMOVE(_name)          CXACRU_DEVICE_REMOVE_FILE(_name)
#define CXACRU__ATTR_REMOVE(_name)        CXACRU_DEVICE_REMOVE_FILE(_name)

static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
@@ -438,6 +445,72 @@ static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
	return ret;
}

/* CM_REQUEST_CARD_DATA_GET times out, so no show attribute */

static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
	struct device_attribute *attr, const char *buf, size_t count)
{
	struct cxacru_data *instance = to_usbatm_driver_data(
			to_usb_interface(dev));
	int len = strlen(buf);
	int ret, pos, num;
	__le32 data[CMD_PACKET_SIZE / 4];

	if (!capable(CAP_NET_ADMIN))
		return -EACCES;

	if (instance == NULL)
		return -ENODEV;

	pos = 0;
	num = 0;
	while (pos < len) {
		int tmp;
		u32 index;
		u32 value;

		ret = sscanf(buf + pos, "%x=%x%n", &index, &value, &tmp);
		if (ret < 2)
			return -EINVAL;
		if (index < 0 || index > 0x7f)
			return -EINVAL;
		pos += tmp;

		/* skip trailing newline */
		if (buf[pos] == '\n' && pos == len-1)
			pos++;

		data[num * 2 + 1] = cpu_to_le32(index);
		data[num * 2 + 2] = cpu_to_le32(value);
		num++;

		/* send config values when data buffer is full
		 * or no more data
		 */
		if (pos >= len || num >= CMD_MAX_CONFIG) {
			char log[CMD_MAX_CONFIG * 12 + 1]; /* %02x=%08x */

			data[0] = cpu_to_le32(num);
			ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
				(u8 *) data, 4 + num * 8, NULL, 0);
			if (ret < 0) {
				atm_err(instance->usbatm,
					"set card data returned %d\n", ret);
				return -EIO;
			}

			for (tmp = 0; tmp < num; tmp++)
				snprintf(log + tmp*12, 13, " %02x=%08x",
					le32_to_cpu(data[tmp * 2 + 1]),
					le32_to_cpu(data[tmp * 2 + 2]));
			atm_info(instance->usbatm, "config%s\n", log);
			num = 0;
		}
	}

	return len;
}

/*
 * All device attributes are included in CXACRU_ALL_FILES
 * so that the same list can be used multiple times:
@@ -473,7 +546,8 @@ CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND,              u32,  adsl_headend); \
CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT,  u32,  adsl_headend_environment); \
CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION,        u32,  adsl_controller_version); \
CXACRU_CMD_##_action(                                        adsl_state);
CXACRU_CMD_##_action(                                        adsl_state); \
CXACRU_SET_##_action(                                        adsl_config);

CXACRU_ALL_FILES(INIT);