cxacru.c

来自「linux 内核源代码」· C语言 代码 · 共 1,269 行 · 第 1/3 页

C
1,269
字号
/****************************************************************************** *  cxacru.c  -  driver for USB ADSL modems based on *               Conexant AccessRunner chipset * *  Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan *  Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) *  Copyright (C) 2007 Simon Arlott * *  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. * ******************************************************************************//* *  Credit is due for Josep Comas, who created the original patch to speedtch.c *  to support the different padding used by the AccessRunner (now generalized *  into usbatm), and the userspace firmware loading utility. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/device.h>#include <linux/firmware.h>#include <linux/mutex.h>#include "usbatm.h"#define DRIVER_AUTHOR	"Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"#define DRIVER_VERSION	"0.3"#define DRIVER_DESC	"Conexant AccessRunner ADSL USB modem driver"static const char cxacru_driver_name[] = "cxacru";#define CXACRU_EP_CMD		0x01	/* Bulk/interrupt in/out */#define CXACRU_EP_DATA		0x02	/* Bulk in/out */#define CMD_PACKET_SIZE		64	/* Should be maxpacket(ep)? *//* Addresses */#define PLLFCLK_ADDR	0x00350068#define PLLBCLK_ADDR	0x0035006c#define SDRAMEN_ADDR	0x00350010#define FW_ADDR		0x00801000#define BR_ADDR		0x00180600#define SIG_ADDR	0x00180500#define BR_STACK_ADDR	0x00187f10/* Values */#define SDRAM_ENA	0x1#define CMD_TIMEOUT	2000	/* msecs */#define POLL_INTERVAL	1	/* secs *//* commands for interaction with the modem through the control channel before * firmware is loaded  */enum cxacru_fw_request {	FW_CMD_ERR,	FW_GET_VER,	FW_READ_MEM,	FW_WRITE_MEM,	FW_RMW_MEM,	FW_CHECKSUM_MEM,	FW_GOTO_MEM,};/* commands for interaction with the modem through the control channel once * firmware is loaded  */enum cxacru_cm_request {	CM_REQUEST_UNDEFINED = 0x80,	CM_REQUEST_TEST,	CM_REQUEST_CHIP_GET_MAC_ADDRESS,	CM_REQUEST_CHIP_GET_DP_VERSIONS,	CM_REQUEST_CHIP_ADSL_LINE_START,	CM_REQUEST_CHIP_ADSL_LINE_STOP,	CM_REQUEST_CHIP_ADSL_LINE_GET_STATUS,	CM_REQUEST_CHIP_ADSL_LINE_GET_SPEED,	CM_REQUEST_CARD_INFO_GET,	CM_REQUEST_CARD_DATA_GET,	CM_REQUEST_CARD_DATA_SET,	CM_REQUEST_COMMAND_HW_IO,	CM_REQUEST_INTERFACE_HW_IO,	CM_REQUEST_CARD_SERIAL_DATA_PATH_GET,	CM_REQUEST_CARD_SERIAL_DATA_PATH_SET,	CM_REQUEST_CARD_CONTROLLER_VERSION_GET,	CM_REQUEST_CARD_GET_STATUS,	CM_REQUEST_CARD_GET_MAC_ADDRESS,	CM_REQUEST_CARD_GET_DATA_LINK_STATUS,	CM_REQUEST_MAX,};/* reply codes to the commands above */enum cxacru_cm_status {	CM_STATUS_UNDEFINED,	CM_STATUS_SUCCESS,	CM_STATUS_ERROR,	CM_STATUS_UNSUPPORTED,	CM_STATUS_UNIMPLEMENTED,	CM_STATUS_PARAMETER_ERROR,	CM_STATUS_DBG_LOOPBACK,	CM_STATUS_MAX,};/* indices into CARD_INFO_GET return array */enum cxacru_info_idx {	CXINF_DOWNSTREAM_RATE,	CXINF_UPSTREAM_RATE,	CXINF_LINK_STATUS,	CXINF_LINE_STATUS,	CXINF_MAC_ADDRESS_HIGH,	CXINF_MAC_ADDRESS_LOW,	CXINF_UPSTREAM_SNR_MARGIN,	CXINF_DOWNSTREAM_SNR_MARGIN,	CXINF_UPSTREAM_ATTENUATION,	CXINF_DOWNSTREAM_ATTENUATION,	CXINF_TRANSMITTER_POWER,	CXINF_UPSTREAM_BITS_PER_FRAME,	CXINF_DOWNSTREAM_BITS_PER_FRAME,	CXINF_STARTUP_ATTEMPTS,	CXINF_UPSTREAM_CRC_ERRORS,	CXINF_DOWNSTREAM_CRC_ERRORS,	CXINF_UPSTREAM_FEC_ERRORS,	CXINF_DOWNSTREAM_FEC_ERRORS,	CXINF_UPSTREAM_HEC_ERRORS,	CXINF_DOWNSTREAM_HEC_ERRORS,	CXINF_LINE_STARTABLE,	CXINF_MODULATION,	CXINF_ADSL_HEADEND,	CXINF_ADSL_HEADEND_ENVIRONMENT,	CXINF_CONTROLLER_VERSION,	/* dunno what the missing two mean */	CXINF_MAX = 0x1c,};enum cxacru_poll_state {	CXPOLL_STOPPING,	CXPOLL_STOPPED,	CXPOLL_POLLING,	CXPOLL_SHUTDOWN};struct cxacru_modem_type {	u32 pll_f_clk;	u32 pll_b_clk;	int boot_rom_patch;};struct cxacru_data {	struct usbatm_data *usbatm;	const struct cxacru_modem_type *modem_type;	int line_status;	struct mutex adsl_state_serialize;	int adsl_status;	struct delayed_work poll_work;	u32 card_info[CXINF_MAX];	struct mutex poll_state_serialize;	enum cxacru_poll_state poll_state;	/* contol handles */	struct mutex cm_serialize;	u8 *rcv_buf;	u8 *snd_buf;	struct urb *rcv_urb;	struct urb *snd_urb;	struct completion rcv_done;	struct completion snd_done;};static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,	u8 *wdata, int wsize, u8 *rdata, int rsize);static void cxacru_poll_status(struct work_struct *work);/* Card info exported through sysfs */#define CXACRU__ATTR_INIT(_name) \static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)#define CXACRU_CMD_INIT(_name) \static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \	cxacru_sysfs_show_##_name, 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) \{ \	struct usb_interface *intf = to_usb_interface(dev); \	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \	struct cxacru_data *instance = usbatm_instance->driver_data; \	return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \} \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__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__ATTR_REMOVE(_name)        CXACRU_DEVICE_REMOVE_FILE(_name)static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf){	return snprintf(buf, PAGE_SIZE, "%u\n", value);}static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf){	return snprintf(buf, PAGE_SIZE, "%d\n", value);}static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf){	return snprintf(buf, PAGE_SIZE, "%d.%02u\n",					value / 100, abs(value) % 100);}static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf){	static char *str[] = { "no", "yes" };	if (unlikely(value >= ARRAY_SIZE(str)))		return snprintf(buf, PAGE_SIZE, "%u\n", value);	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);}static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf){	static char *str[] = { NULL, "not connected", "connected", "lost" };	if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))		return snprintf(buf, PAGE_SIZE, "%u\n", value);	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);}static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf){	static char *str[] = { "down", "attempting to activate",		"training", "channel analysis", "exchange", "up",		"waiting", "initialising"	};	if (unlikely(value >= ARRAY_SIZE(str)))		return snprintf(buf, PAGE_SIZE, "%u\n", value);	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);}static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf){	static char *str[] = {			NULL,			"ANSI T1.413",			"ITU-T G.992.1 (G.DMT)",			"ITU-T G.992.2 (G.LITE)"	};	if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))		return snprintf(buf, PAGE_SIZE, "%u\n", value);	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);}/* * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since * this data is already in atm_dev there's no point. * * MAC_ADDRESS_HIGH = 0x????5544 * MAC_ADDRESS_LOW  = 0x33221100 * Where 00-55 are bytes 0-5 of the MAC. */static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,	struct device_attribute *attr, char *buf){	struct usb_interface *intf = to_usb_interface(dev);	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);	struct atm_dev *atm_dev = usbatm_instance->atm_dev;	return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",			atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2],			atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);}static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,	struct device_attribute *attr, char *buf){	struct usb_interface *intf = to_usb_interface(dev);	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);	struct cxacru_data *instance = usbatm_instance->driver_data;	u32 value = instance->card_info[CXINF_LINE_STARTABLE];	static char *str[] = { "running", "stopped" };	if (unlikely(value >= ARRAY_SIZE(str)))		return snprintf(buf, PAGE_SIZE, "%u\n", value);	return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);}static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,	struct device_attribute *attr, const char *buf, size_t count){	struct usb_interface *intf = to_usb_interface(dev);	struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);	struct cxacru_data *instance = usbatm_instance->driver_data;	int ret;	int poll = -1;	char str_cmd[8];	int len = strlen(buf);	if (!capable(CAP_NET_ADMIN))		return -EACCES;	ret = sscanf(buf, "%7s", str_cmd);	if (ret != 1)		return -EINVAL;	ret = 0;	if (mutex_lock_interruptible(&instance->adsl_state_serialize))		return -ERESTARTSYS;	if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {		ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);		if (ret < 0) {			atm_err(usbatm_instance, "change adsl state:"				" CHIP_ADSL_LINE_STOP returned %d\n", ret);			ret = -EIO;		} else {			ret = len;			poll = CXPOLL_STOPPED;		}	}	/* Line status is only updated every second	 * and the device appears to only react to	 * START/STOP every second too. Wait 1.5s to	 * be sure that restart will have an effect. */	if (!strcmp(str_cmd, "restart"))		msleep(1500);	if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {		ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);		if (ret < 0) {			atm_err(usbatm_instance, "change adsl state:"				" CHIP_ADSL_LINE_START returned %d\n", ret);			ret = -EIO;		} else {			ret = len;			poll = CXPOLL_POLLING;		}	}	if (!strcmp(str_cmd, "poll")) {		ret = len;		poll = CXPOLL_POLLING;	}	if (ret == 0) {		ret = -EINVAL;		poll = -1;	}	if (poll == CXPOLL_POLLING) {		mutex_lock(&instance->poll_state_serialize);		switch (instance->poll_state) {		case CXPOLL_STOPPED:			/* start polling */			instance->poll_state = CXPOLL_POLLING;			break;		case CXPOLL_STOPPING:			/* abort stop request */			instance->poll_state = CXPOLL_POLLING;		case CXPOLL_POLLING:		case CXPOLL_SHUTDOWN:			/* don't start polling */			poll = -1;		}		mutex_unlock(&instance->poll_state_serialize);	} else if (poll == CXPOLL_STOPPED) {		mutex_lock(&instance->poll_state_serialize);		/* request stop */		if (instance->poll_state == CXPOLL_POLLING)			instance->poll_state = CXPOLL_STOPPING;		mutex_unlock(&instance->poll_state_serialize);	}	mutex_unlock(&instance->adsl_state_serialize);	if (poll == CXPOLL_POLLING)		cxacru_poll_status(&instance->poll_work.work);	return ret;}/* * All device attributes are included in CXACRU_ALL_FILES * so that the same list can be used multiple times: *     INIT   (define the device attributes) *     CREATE (create all the device files) *     REMOVE (remove all the device files) * * With the last two being defined as needed in the functions * they are used in before calling CXACRU_ALL_FILES() */#define CXACRU_ALL_FILES(_action) \CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE,           u32,  downstream_rate); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE,             u32,  upstream_rate); \CXACRU_ATTR_##_action(CXINF_LINK_STATUS,               LINK, link_status); \CXACRU_ATTR_##_action(CXINF_LINE_STATUS,               LINE, line_status); \CXACRU__ATTR_##_action(                                      mac_address); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN,       dB,   upstream_snr_margin); \CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN,     dB,   downstream_snr_margin); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION,      dB,   upstream_attenuation); \

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?