cxacru.c

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

C
1,269
字号
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION,    dB,   downstream_attenuation); \CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER,         s8,   transmitter_power); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME,   u32,  upstream_bits_per_frame); \CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32,  downstream_bits_per_frame); \CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS,          u32,  startup_attempts); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS,       u32,  upstream_crc_errors); \CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS,     u32,  downstream_crc_errors); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS,       u32,  upstream_fec_errors); \CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS,     u32,  downstream_fec_errors); \CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS,       u32,  upstream_hec_errors); \CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS,     u32,  downstream_hec_errors); \CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE,            bool, line_startable); \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_ALL_FILES(INIT);/* the following three functions are stolen from drivers/usb/core/message.c */static void cxacru_blocking_completion(struct urb *urb){	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;	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);	del_timer_sync(&timer);	if (actual_length)		*actual_length = urb->actual_length;	return urb->status; /* must read status after completion */}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) {		if (printk_ratelimit())			usb_err(instance->usbatm, "requested transfer size too large (%d, %d)\n",				wbuflen, rbuflen);		ret = -ENOMEM;		goto fail;	}	mutex_lock(&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) {		if (printk_ratelimit())			usb_err(instance->usbatm, "submit of read urb for cm %#x failed (%d)\n",				cm, 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) {		if (printk_ratelimit())			usb_err(instance->usbatm, "submit of write urb for cm %#x failed (%d)\n",				cm, ret);		goto fail;	}	ret = cxacru_start_wait_urb(instance->snd_urb, &instance->snd_done, NULL);	if (ret < 0) {		if (printk_ratelimit())			usb_err(instance->usbatm, "send of cm %#x failed (%d)\n", cm, ret);		goto fail;	}	ret = cxacru_start_wait_urb(instance->rcv_urb, &instance->rcv_done, &actlen);	if (ret < 0) {		if (printk_ratelimit())			usb_err(instance->usbatm, "receive of cm %#x failed (%d)\n", cm, ret);		goto fail;	}	if (actlen % CMD_PACKET_SIZE || !actlen) {		if (printk_ratelimit())			usb_err(instance->usbatm, "invalid response length to cm %#x: %d\n",				cm, 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) {			if (printk_ratelimit())				usb_err(instance->usbatm, "wrong cm %#x in response to cm %#x\n",					rbuf[offb], cm);			ret = -EIO;			goto fail;		}		if (rbuf[offb + 1] != CM_STATUS_SUCCESS) {			if (printk_ratelimit())				usb_err(instance->usbatm, "response to cm %#x failed: %#x\n",					cm, 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:	mutex_unlock(&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) {			if (printk_ratelimit())				usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n",					cm, l);			ret = -EIO;			goto cleanup;		}		while (l--) {			offd = le32_to_cpu(buf[offb++]);			if (offd >= size) {				if (printk_ratelimit())					usb_err(instance->usbatm, "wrong index #%x in response to cm #%x\n",						offd, cm);				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_remove_device_files(struct usbatm_data *usbatm_instance,		struct atm_dev *atm_dev){	struct usb_interface *intf = usbatm_instance->usb_intf;	#define CXACRU_DEVICE_REMOVE_FILE(_name) \		device_remove_file(&intf->dev, &dev_attr_##_name);	CXACRU_ALL_FILES(REMOVE);	#undef CXACRU_DEVICE_REMOVE_FILE}static int cxacru_atm_start(struct usbatm_data *usbatm_instance,		struct atm_dev *atm_dev){	struct cxacru_data *instance = usbatm_instance->driver_data;	struct usb_interface *intf = usbatm_instance->usb_intf;	/*	struct atm_dev *atm_dev = usbatm_instance->atm_dev;	*/	int ret;	int start_polling = 1;	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) {		atm_err(usbatm_instance, "cxacru_atm_start: CARD_GET_MAC_ADDRESS returned %d\n", ret);		return ret;	}	#define CXACRU_DEVICE_CREATE_FILE(_name) \		ret = device_create_file(&intf->dev, &dev_attr_##_name); \		if (unlikely(ret)) \			goto fail_sysfs;	CXACRU_ALL_FILES(CREATE);	#undef CXACRU_DEVICE_CREATE_FILE	/* start ADSL */	mutex_lock(&instance->adsl_state_serialize);	ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);	if (ret < 0)		atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);	/* Start status 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 */		start_polling = 0;	}	mutex_unlock(&instance->poll_state_serialize);	mutex_unlock(&instance->adsl_state_serialize);	if (start_polling)		cxacru_poll_status(&instance->poll_work.work);	return 0;fail_sysfs:	usb_err(usbatm_instance, "cxacru_atm_start: device_create_file failed (%d)\n", ret);	cxacru_remove_device_files(usbatm_instance, atm_dev);	return ret;}static void cxacru_poll_status(struct work_struct *work){	struct cxacru_data *instance =		container_of(work, struct cxacru_data, poll_work.work);	u32 buf[CXINF_MAX] = {};	struct usbatm_data *usbatm = instance->usbatm;	struct atm_dev *atm_dev = usbatm->atm_dev;	int keep_polling = 1;	int ret;	ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);	if (ret < 0) {		if (ret != -ESHUTDOWN)			atm_warn(usbatm, "poll status: error %d\n", ret);		mutex_lock(&instance->poll_state_serialize);		if (instance->poll_state != CXPOLL_SHUTDOWN) {			instance->poll_state = CXPOLL_STOPPED;			if (ret != -ESHUTDOWN)				atm_warn(usbatm, "polling disabled, set adsl_state"						" to 'start' or 'poll' to resume\n");		}		mutex_unlock(&instance->poll_state_serialize);		goto reschedule;	}	memcpy(instance->card_info, buf, sizeof(instance->card_info));	if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {		instance->adsl_status = buf[CXINF_LINE_STARTABLE];		switch (instance->adsl_status) {		case 0:			atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");			break;		case 1:			atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");			break;		default:			atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);			break;		}	}	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;		atm_info(usbatm, "ADSL line: down\n");		break;	case 1:		atm_dev->signal = ATM_PHY_SIG_LOST;		atm_info(usbatm, "ADSL line: attempting to activate\n");		break;	case 2:		atm_dev->signal = ATM_PHY_SIG_LOST;		atm_info(usbatm, "ADSL line: training\n");		break;	case 3:		atm_dev->signal = ATM_PHY_SIG_LOST;		atm_info(usbatm, "ADSL line: channel analysis\n");		break;	case 4:		atm_dev->signal = ATM_PHY_SIG_LOST;		atm_info(usbatm, "ADSL line: exchange\n");		break;	case 5:		atm_dev->link_rate = buf[CXINF_DOWNSTREAM_RATE] * 1000 / 424;		atm_dev->signal = ATM_PHY_SIG_FOUND;		atm_info(usbatm, "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;		atm_info(usbatm, "ADSL line: waiting\n");		break;	case 7:		atm_dev->signal = ATM_PHY_SIG_LOST;		atm_info(usbatm, "ADSL line: initializing\n");		break;	default:		atm_dev->signal = ATM_PHY_SIG_UNKNOWN;		atm_info(usbatm, "Unknown line state %02x\n", instance->line_status);		break;	}reschedule:	mutex_lock(&instance->poll_state_serialize);	if (instance->poll_state == CXPOLL_STOPPING &&				instance->adsl_status == 1 && /* stopped */				instance->line_status == 0) /* down */		instance->poll_state = CXPOLL_STOPPED;	if (instance->poll_state == CXPOLL_STOPPED)		keep_polling = 0;	mutex_unlock(&instance->poll_state_serialize);	if (keep_polling)		schedule_delayed_work(&instance->poll_work,				round_jiffies_relative(POLL_INTERVAL*HZ));}static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,		     u8 code1, u8 code2, u32 addr, u8 *data, int size){	int ret;	u8 *buf;	int offd, offb;	const int stride = CMD_PACKET_SIZE - 8;	buf = (u8 *) __get_free_page(GFP_KERNEL);	if (!buf)		return -ENOMEM;	offb = offd = 0;	do {		int l = min_t(int, stride, size - offd);		buf[offb++] = fw;		buf[offb++] = l;		buf[offb++] = code1;		buf[offb++] = code2;		*((u32 *) (buf + offb)) = cpu_to_le32(addr);		offb += 4;		addr += l;		if(l)			memcpy(buf + offb, data + offd, l);		if (l < stride)			memset(buf + offb + l, 0, stride - l);

⌨️ 快捷键说明

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