⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cxacru.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************** *  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) * *  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>	/* FIXME: linux/firmware.h should include it itself */#include <linux/firmware.h>#include "usbatm.h"#define DRIVER_AUTHOR	"Roman Kagan, David Woodhouse, Duncan Sands"#define DRIVER_VERSION	"0.2"#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	5000	/* msecs *//* 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,};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 work_struct poll_work;	/* contol handles */	struct semaphore cm_serialize;	u8 *rcv_buf;	u8 *snd_buf;	struct urb *rcv_urb;	struct urb *snd_urb;	struct completion rcv_done;	struct completion snd_done;};/* the following three functions are stolen from drivers/usb/core/message.c */static void cxacru_blocking_completion(struct urb *urb, struct pt_regs *regs){	complete((struct completion *)urb->context);}static void cxacru_timeout_kill(unsigned long data){	usb_unlink_urb((struct urb *) data);}static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,				 int* actual_length){	struct timer_list timer;	int status;	init_timer(&timer);	timer.expires = jiffies + msecs_to_jiffies(CMD_TIMEOUT);	timer.data = (unsigned long) urb;	timer.function = cxacru_timeout_kill;	add_timer(&timer);	wait_for_completion(done);	status = urb->status;	if (status == -ECONNRESET)		status = -ETIMEDOUT;	del_timer_sync(&timer);	if (actual_length)		*actual_length = urb->actual_length;	return status;}static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,		     u8 *wdata, int wsize, u8 *rdata, int rsize){	int ret, actlen;	int offb, offd;	const int stride = CMD_PACKET_SIZE - 4;	u8 *wbuf = instance->snd_buf;	u8 *rbuf = instance->rcv_buf;	int wbuflen = ((wsize - 1) / stride + 1) * CMD_PACKET_SIZE;	int rbuflen = ((rsize - 1) / stride + 1) * CMD_PACKET_SIZE;	if (wbuflen > PAGE_SIZE || rbuflen > PAGE_SIZE) {		dbg("too big transfer requested");		ret = -ENOMEM;		goto fail;	}	down(&instance->cm_serialize);	/* submit reading urb before the writing one */	init_completion(&instance->rcv_done);	ret = usb_submit_urb(instance->rcv_urb, GFP_KERNEL);	if (ret < 0) {		dbg("submitting read urb for cm %#x failed", cm);		ret = ret;		goto fail;	}	memset(wbuf, 0, wbuflen);	/* handle wsize == 0 */	wbuf[0] = cm;	for (offb = offd = 0; offd < wsize; offd += stride, offb += CMD_PACKET_SIZE) {		wbuf[offb] = cm;		memcpy(wbuf + offb + 4, wdata + offd, min_t(int, stride, wsize - offd));	}	instance->snd_urb->transfer_buffer_length = wbuflen;	init_completion(&instance->snd_done);	ret = usb_submit_urb(instance->snd_urb, GFP_KERNEL);	if (ret < 0) {		dbg("submitting write urb for cm %#x failed", cm);		ret = ret;		goto fail;	}	ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL);	if (ret < 0) {		dbg("sending cm %#x failed", cm);		ret = ret;		goto fail;	}	ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen);	if (ret < 0) {		dbg("receiving cm %#x failed", cm);		ret = ret;		goto fail;	}	if (actlen % CMD_PACKET_SIZE || !actlen) {		dbg("response is not a positive multiple of %d: %#x",				CMD_PACKET_SIZE, actlen);		ret = -EIO;		goto fail;	}	/* check the return status and copy the data to the output buffer, if needed */	for (offb = offd = 0; offd < rsize && offb < actlen; offb += CMD_PACKET_SIZE) {		if (rbuf[offb] != cm) {			dbg("wrong cm %#x in response", rbuf[offb]);			ret = -EIO;			goto fail;		}		if (rbuf[offb + 1] != CM_STATUS_SUCCESS) {			dbg("response failed: %#x", rbuf[offb + 1]);			ret = -EIO;			goto fail;		}		if (offd >= rsize)			break;		memcpy(rdata + offd, rbuf + offb + 4, min_t(int, stride, rsize - offd));		offd += stride;	}	ret = offd;	dbg("cm %#x", cm);fail:	up(&instance->cm_serialize);	return ret;}static int cxacru_cm_get_array(struct cxacru_data *instance, enum cxacru_cm_request cm,			       u32 *data, int size){	int ret, len;	u32 *buf;	int offb, offd;	const int stride = CMD_PACKET_SIZE / (4 * 2) - 1;	int buflen =  ((size - 1) / stride + 1 + size * 2) * 4;	buf = kmalloc(buflen, GFP_KERNEL);	if (!buf)		return -ENOMEM;	ret = cxacru_cm(instance, cm, NULL, 0, (u8 *) buf, buflen);	if (ret < 0)		goto cleanup;	/* len > 0 && len % 4 == 0 guaranteed by cxacru_cm() */	len = ret / 4;	for (offb = 0; offb < len; ) {		int l = le32_to_cpu(buf[offb++]);		if (l > stride || l > (len - offb) / 2) {			dbg("wrong data length %#x in response", l);			ret = -EIO;			goto cleanup;		}		while (l--) {			offd = le32_to_cpu(buf[offb++]);			if (offd >= size) {				dbg("wrong index %#x in response", offd);				ret = -EIO;				goto cleanup;			}			data[offd] = le32_to_cpu(buf[offb++]);		}	}	ret = 0;cleanup:	kfree(buf);	return ret;}static int cxacru_card_status(struct cxacru_data *instance){	int ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0);	if (ret < 0) {		/* firmware not loaded */		dbg("cxacru_adsl_start: CARD_GET_STATUS returned %d", ret);		return ret;	}	return 0;}static void cxacru_poll_status(struct cxacru_data *instance);static int cxacru_atm_start(struct usbatm_data *usbatm_instance,		struct atm_dev *atm_dev){	struct cxacru_data *instance = usbatm_instance->driver_data;	struct device *dev = &usbatm_instance->usb_intf->dev;	/*	struct atm_dev *atm_dev = usbatm_instance->atm_dev;	*/	int ret;	dbg("cxacru_atm_start");	/* Read MAC address */	ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_MAC_ADDRESS, NULL, 0,			atm_dev->esi, sizeof(atm_dev->esi));	if (ret < 0) {		dev_err(dev, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);		return ret;	}	/* start ADSL */	ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);	if (ret < 0) {		dev_err(dev, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);		return ret;	}	/* Start status polling */	cxacru_poll_status(instance);	return 0;}static void cxacru_poll_status(struct cxacru_data *instance){	u32 buf[CXINF_MAX] = {};	struct device *dev = &instance->usbatm->usb_intf->dev;	struct atm_dev *atm_dev = instance->usbatm->atm_dev;	int ret;	ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);	if (ret < 0) {		dev_warn(dev, "poll status: error %d\n", ret);		goto reschedule;	}	if (instance->line_status == buf[CXINF_LINE_STATUS])		goto reschedule;	instance->line_status = buf[CXINF_LINE_STATUS];	switch (instance->line_status) {	case 0:		atm_dev->signal = ATM_PHY_SIG_LOST;		dev_info(dev, "ADSL line: down\n");		break;	case 1:		atm_dev->signal = ATM_PHY_SIG_LOST;		dev_info(dev, "ADSL line: attemtping to activate\n");		break;	case 2:		atm_dev->signal = ATM_PHY_SIG_LOST;		dev_info(dev, "ADSL line: training\n");		break;	case 3:		atm_dev->signal = ATM_PHY_SIG_LOST;		dev_info(dev, "ADSL line: channel analysis\n");		break;	case 4:		atm_dev->signal = ATM_PHY_SIG_LOST;		dev_info(dev, "ADSL line: exchange\n");		break;	case 5:		atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424;		atm_dev->signal = ATM_PHY_SIG_FOUND;		dev_info(dev, "ADSL line: up (%d kb/s down | %d kb/s up)\n",		     buf[CXINF_DOWNSTREAM_RATE], buf[CXINF_UPSTREAM_RATE]);		break;	case 6:		atm_dev->signal = ATM_PHY_SIG_LOST;		dev_info(dev, "ADSL line: waiting\n");		break;	case 7:		atm_dev->signal = ATM_PHY_SIG_LOST;

⌨️ 快捷键说明

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