2448 lines
54 KiB
C
2448 lines
54 KiB
C
/*
|
|
*
|
|
* HiSax ISDN driver - chip specific routines for ST5481 USB ISDN modem
|
|
*
|
|
* Author Frode Isaksen (fisaksen@bewan.com)
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/init.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/string.h>
|
|
#define __NO_VERSION__
|
|
#include "hisax.h"
|
|
#include "isdnl1.h"
|
|
#include "st5481_hdlc.h"
|
|
#include "st5481.h"
|
|
#include "st5481-debug.h"
|
|
|
|
#define ERR(format, arg...) \
|
|
printk(KERN_ERR __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg)
|
|
|
|
#define WARN(format, arg...) \
|
|
printk(KERN_WARNING __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg)
|
|
|
|
#define INFO(format, arg...) \
|
|
printk(KERN_INFO __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg)
|
|
|
|
static const char *st5481_revision = "$Revision$";
|
|
|
|
/* Internal event, make sure it does not overlap with the events in isdnl1.h!!! */
|
|
#define D_OUT_EVENT 10
|
|
|
|
/* Generic FIFO structure */
|
|
struct fifo {
|
|
u_char r,w,count,size;
|
|
spinlock_t lock;
|
|
};
|
|
|
|
#define MAX_EVT_FIFO 16
|
|
struct evt_fifo {
|
|
struct fifo f;
|
|
unsigned int data[MAX_EVT_FIFO];
|
|
};
|
|
|
|
typedef void (*ctrl_complete_t)(void *);
|
|
|
|
typedef struct ctrl_msg {
|
|
devrequest dr;
|
|
ctrl_complete_t complete;
|
|
void * context;
|
|
} ctrl_msg;
|
|
|
|
#define MAX_EP0_MSG 16
|
|
struct ctrl_msg_fifo {
|
|
struct fifo f;
|
|
struct ctrl_msg data[MAX_EP0_MSG];
|
|
};
|
|
|
|
|
|
/*
|
|
FIFO utility functions.
|
|
*/
|
|
|
|
/*
|
|
Get the index to the next entry to write to the FIFO without updating write index.
|
|
*/
|
|
static int
|
|
fifo_get(struct fifo *fifo)
|
|
{
|
|
unsigned long flags;
|
|
int index;
|
|
|
|
if (!fifo) {
|
|
return -1;
|
|
}
|
|
|
|
spin_lock_irqsave(&fifo->lock, flags);
|
|
if (fifo->count==fifo->size) {
|
|
// FIFO full
|
|
index = -1;
|
|
} else {
|
|
// Return index where to get the next data to add to the FIFO
|
|
index = fifo->w & (fifo->size-1);
|
|
}
|
|
spin_unlock_irqrestore(&fifo->lock, flags);
|
|
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
Add an entry to the FIFO by incrementing write index.
|
|
*/
|
|
static void
|
|
fifo_add(struct fifo *fifo)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&fifo->lock, flags);
|
|
fifo->w++;
|
|
fifo->count++;
|
|
spin_unlock_irqrestore(&fifo->lock, flags);
|
|
}
|
|
|
|
/*
|
|
Remove an entry from the FIFO with the index returned.
|
|
*/
|
|
static int
|
|
fifo_remove(struct fifo *fifo)
|
|
{
|
|
unsigned long flags;
|
|
int index;
|
|
|
|
if (!fifo) {
|
|
return -1;
|
|
}
|
|
|
|
spin_lock_irqsave(&fifo->lock, flags);
|
|
if (!fifo->count) {
|
|
// FIFO empty
|
|
index = -1;
|
|
} else {
|
|
// Return index where to get the next data from the FIFO
|
|
index = fifo->r++ & (fifo->size-1);
|
|
fifo->count--;
|
|
}
|
|
spin_unlock_irqrestore(&fifo->lock, flags);
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
/*
|
|
USB utility functions.
|
|
*/
|
|
|
|
/*
|
|
USB double buffering, return the URB index (0 or 1).
|
|
*/
|
|
static inline int
|
|
get_buf_nr(struct urb *urbs[], struct urb *urb)
|
|
{
|
|
return (urbs[0]==urb ? 0 : 1);
|
|
}
|
|
|
|
/*
|
|
Submit an URB with error reporting. This is a macro so
|
|
the __FUNCTION__ returns the caller function name.
|
|
*/
|
|
#define submit_urb(urb) \
|
|
{ \
|
|
int status; \
|
|
if ((status = usb_submit_urb(urb)) < 0) { \
|
|
WARN("usb_submit_urb failed,status=%d",status); \
|
|
} \
|
|
}
|
|
|
|
static inline unsigned int
|
|
usb_b_sndisocpipe(struct usb_device *dev, int channel)
|
|
{
|
|
return usb_sndisocpipe(dev, EP_B1_OUT + (channel*2));
|
|
}
|
|
|
|
static inline unsigned int
|
|
usb_b_rcvisocpipe(struct usb_device *dev, int channel)
|
|
{
|
|
return usb_rcvisocpipe(dev, (EP_B1_IN + (channel*2)) | USB_DIR_IN);
|
|
}
|
|
|
|
/*
|
|
Fill the ISOC URB.
|
|
*/
|
|
static void
|
|
fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,
|
|
void *buf, int num_packets, int packet_size,
|
|
usb_complete_t complete, void *context)
|
|
{
|
|
int k;
|
|
|
|
spin_lock_init(&urb->lock);
|
|
urb->dev=dev;
|
|
urb->pipe=pipe;
|
|
urb->transfer_buffer=buf;
|
|
urb->number_of_packets = num_packets;
|
|
urb->transfer_buffer_length=num_packets*packet_size;
|
|
urb->actual_length = 0;
|
|
urb->complete=complete;
|
|
urb->context=context;
|
|
urb->transfer_flags=USB_ISO_ASAP;
|
|
for (k = 0; k < num_packets; k++) {
|
|
urb->iso_frame_desc[k].offset = packet_size * k;
|
|
urb->iso_frame_desc[k].length = packet_size;
|
|
urb->iso_frame_desc[k].actual_length = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Make the transfer_buffer contiguous by
|
|
copying from the iso descriptors if necessary.
|
|
*/
|
|
static int
|
|
isoc_flatten(struct urb *urb)
|
|
{
|
|
piso_packet_descriptor_t pipd,pend;
|
|
unsigned char *src,*dst;
|
|
unsigned int len;
|
|
|
|
if (urb->status < 0) {
|
|
return urb->status;
|
|
}
|
|
for (pipd = &urb->iso_frame_desc[0],
|
|
pend = &urb->iso_frame_desc[urb->number_of_packets],
|
|
dst = urb->transfer_buffer;
|
|
pipd < pend;
|
|
pipd++) {
|
|
|
|
if (pipd->status < 0) {
|
|
return (pipd->status);
|
|
}
|
|
|
|
len = pipd->actual_length;
|
|
pipd->actual_length = 0;
|
|
src = urb->transfer_buffer+pipd->offset;
|
|
|
|
if (src != dst) {
|
|
// Need to copy since isoc buffers not full
|
|
while (len--) {
|
|
*dst++ = *src++;
|
|
}
|
|
} else {
|
|
// No need to copy, just update destination buffer
|
|
dst += len;
|
|
}
|
|
}
|
|
// Return size of flattened buffer
|
|
return (dst - (unsigned char *)urb->transfer_buffer);
|
|
}
|
|
|
|
|
|
/*
|
|
USB control endpoint functions
|
|
*/
|
|
|
|
/*
|
|
Send the next endpoint 0 request stored in the FIFO.
|
|
Called either by the completion or by usb_ctrl_msg.
|
|
*/
|
|
static void
|
|
usb_next_ctrl_msg(struct urb *urb, struct st5481_hw *hw)
|
|
{
|
|
int r_index;
|
|
|
|
if (test_and_set_bit(0,&hw->ctrl_busy)) {
|
|
return;
|
|
}
|
|
|
|
if (hw->ctrl_msg_fifo == NULL) {
|
|
test_and_clear_bit(0,&hw->ctrl_busy);
|
|
return;
|
|
}
|
|
|
|
if ((r_index = fifo_remove(&hw->ctrl_msg_fifo->f)) < 0) {
|
|
test_and_clear_bit(0,&hw->ctrl_busy);
|
|
return;
|
|
}
|
|
urb->setup_packet =
|
|
(unsigned char *)&hw->ctrl_msg_fifo->data[r_index];
|
|
|
|
|
|
DBG(1,"request=0x%02x,value=0x%04x,index=%x",
|
|
((struct ctrl_msg *)urb->setup_packet)->dr.request,
|
|
((struct ctrl_msg *)urb->setup_packet)->dr.value,
|
|
((struct ctrl_msg *)urb->setup_packet)->dr.index);
|
|
|
|
// Prepare the URB
|
|
urb->dev = hw->dev;
|
|
|
|
submit_urb(urb);
|
|
}
|
|
|
|
/*
|
|
The request on endpoint 0 has completed.
|
|
Call the user provided completion routine and try
|
|
to send the next request.
|
|
*/
|
|
static void
|
|
usb_ctrl_complete(struct urb *urb)
|
|
{
|
|
struct IsdnCardState *cs = (struct IsdnCardState *)urb->context;
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
struct ctrl_msg *ctrl_msg;
|
|
|
|
if (urb->status < 0) {
|
|
if (urb->status != USB_ST_URB_KILLED) {
|
|
WARN("urb status %d",urb->status);
|
|
} else {
|
|
DBG(1,"urb killed");
|
|
return; // Give up
|
|
}
|
|
}
|
|
|
|
ctrl_msg = (struct ctrl_msg *)urb->setup_packet;
|
|
|
|
if (ctrl_msg->dr.request == USB_REQ_CLEAR_FEATURE) {
|
|
/* Special case handling for pipe reset */
|
|
le16_to_cpus(&ctrl_msg->dr.index);
|
|
usb_endpoint_running(hw->dev,
|
|
ctrl_msg->dr.index & ~USB_DIR_IN,
|
|
(ctrl_msg->dr.index & USB_DIR_IN) == 0);
|
|
|
|
/* toggle is reset on clear */
|
|
usb_settoggle(hw->dev,
|
|
ctrl_msg->dr.index & ~USB_DIR_IN,
|
|
(ctrl_msg->dr.index & USB_DIR_IN) == 0,
|
|
0);
|
|
|
|
|
|
}
|
|
|
|
if (ctrl_msg->complete) {
|
|
ctrl_msg->complete(ctrl_msg->context);
|
|
}
|
|
|
|
test_and_clear_bit(0, &hw->ctrl_busy);
|
|
|
|
// Try to send next control message
|
|
usb_next_ctrl_msg(urb, hw);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
Asynchronous endpoint 0 request (async version of usb_control_msg).
|
|
The request will be queued up in a FIFO if the endpoint is busy.
|
|
*/
|
|
static void
|
|
usb_ctrl_msg(struct IsdnCardState *cs,
|
|
__u8 request, __u8 requesttype, __u16 value, __u16 index,
|
|
ctrl_complete_t complete, void *context)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
int w_index;
|
|
struct ctrl_msg *ctrl_msg;
|
|
|
|
if (hw->ctrl_msg_fifo == NULL) {
|
|
return;
|
|
}
|
|
|
|
if ((w_index = fifo_get(&hw->ctrl_msg_fifo->f)) < 0) {
|
|
WARN("control msg FIFO full");
|
|
return;
|
|
}
|
|
ctrl_msg = &hw->ctrl_msg_fifo->data[w_index];
|
|
|
|
ctrl_msg->dr.requesttype = requesttype;
|
|
ctrl_msg->dr.request = request;
|
|
ctrl_msg->dr.value = cpu_to_le16p(&value);
|
|
ctrl_msg->dr.index = cpu_to_le16p(&index);
|
|
ctrl_msg->dr.length = 0;
|
|
ctrl_msg->complete = complete;
|
|
ctrl_msg->context = context;
|
|
|
|
// Add this msg to the FIFO
|
|
fifo_add(&hw->ctrl_msg_fifo->f);
|
|
|
|
usb_next_ctrl_msg(hw->ctrl_urb, hw);
|
|
}
|
|
|
|
/*
|
|
Asynchronous endpoint 0 device request.
|
|
*/
|
|
static void
|
|
usb_device_ctrl_msg(struct IsdnCardState *cs,
|
|
__u8 request, __u16 value,
|
|
ctrl_complete_t complete,void *context)
|
|
{
|
|
usb_ctrl_msg(cs,
|
|
request, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, 0,
|
|
complete, context);
|
|
}
|
|
|
|
/*
|
|
Asynchronous pipe reset (async version of usb_clear_halt).
|
|
*/
|
|
static void
|
|
usb_pipe_reset(struct IsdnCardState *cs,
|
|
u_char pipe,
|
|
ctrl_complete_t complete,void *context)
|
|
{
|
|
DBG(1,"pipe=%02x",pipe);
|
|
|
|
usb_ctrl_msg(cs,
|
|
USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RECIP_ENDPOINT, 0, pipe,
|
|
complete, context);
|
|
}
|
|
|
|
|
|
/*
|
|
B channel functions
|
|
*/
|
|
|
|
static void
|
|
st5481B_sched_event(struct BCState *bcs, int event)
|
|
{
|
|
bcs->event |= 1 << event;
|
|
queue_task(&bcs->tqueue, &tq_immediate);
|
|
mark_bh(IMMEDIATE_BH);
|
|
}
|
|
|
|
/*
|
|
Encode and transmit next frame.
|
|
*/
|
|
static void
|
|
usb_b_out(struct BCState *bcs,int buf_nr)
|
|
{
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
struct urb *urb;
|
|
unsigned int packet_size,offset;
|
|
int len,buf_size,bytes_sent;
|
|
int i;
|
|
struct sk_buff *skb;
|
|
|
|
if (test_and_set_bit(buf_nr,&hw->b_out_busy)) {
|
|
DBG(4,"ep %d urb %d busy",(bcs->channel+1)*2,buf_nr);
|
|
return;
|
|
}
|
|
urb = hw->b_out_urb[buf_nr];
|
|
|
|
// Adjust isoc buffer size according to flow state
|
|
#define B_FLOW_ADJUST 2
|
|
if(hw->b_flow_event & (OUT_DOWN | OUT_UNDERRUN)) {
|
|
buf_size = NUM_ISO_PACKETS_B*8+B_FLOW_ADJUST;
|
|
packet_size = 8+B_FLOW_ADJUST;
|
|
DBG(4,"B%d,adjust flow,add %d bytes",bcs->channel+1,B_FLOW_ADJUST);
|
|
} else if(hw->b_flow_event & OUT_UP){
|
|
buf_size = NUM_ISO_PACKETS_B*8-B_FLOW_ADJUST;
|
|
packet_size = 8-B_FLOW_ADJUST;
|
|
DBG(4,"B%d,adjust flow,remove %d bytes",bcs->channel+1,B_FLOW_ADJUST);
|
|
} else {
|
|
buf_size = NUM_ISO_PACKETS_B*8;
|
|
packet_size = 8;
|
|
}
|
|
hw->b_flow_event = 0;
|
|
#undef B_FLOW_ADJUST
|
|
|
|
len = 0;
|
|
while (len < buf_size) {
|
|
if ((skb = bcs->tx_skb)) {
|
|
DUMP_SKB(0x10, skb);
|
|
DBG(4,"B%d,len=%d",bcs->channel+1,skb->len);
|
|
|
|
if (bcs->mode == L1_MODE_TRANS) {
|
|
bytes_sent = (buf_size-len) > skb->len ? skb->len : (buf_size-len);
|
|
|
|
memcpy(skb->data, urb->transfer_buffer+len, buf_size);
|
|
|
|
len += bytes_sent;
|
|
} else {
|
|
len += hdlc_encode(hw->hdlc_state_out,
|
|
skb->data, skb->len, &bytes_sent,
|
|
urb->transfer_buffer+len, buf_size-len);
|
|
}
|
|
|
|
skb_pull(skb, bytes_sent);
|
|
|
|
if (!skb->len) {
|
|
// Frame sent
|
|
int tmp = bcs->tx_cnt;
|
|
bcs->tx_cnt = 0;
|
|
|
|
if (bcs->st->lli.l1writewakeup &&
|
|
(PACKET_NOACK != skb->pkt_type))
|
|
bcs->st->lli.l1writewakeup(bcs->st, tmp);
|
|
dev_kfree_skb_any(skb);
|
|
if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
|
|
st5481B_sched_event(bcs, B_XMTBUFREADY);
|
|
}
|
|
}
|
|
} else {
|
|
// Send flags
|
|
len += hdlc_encode(hw->hdlc_state_out,
|
|
NULL, 0, &bytes_sent,
|
|
urb->transfer_buffer+len, buf_size-len);
|
|
}
|
|
}
|
|
|
|
// Prepare the URB
|
|
offset = 0;
|
|
for(i=0,offset=0;i<NUM_ISO_PACKETS_B;i++){
|
|
urb->iso_frame_desc[i].offset = offset;
|
|
urb->iso_frame_desc[i].length = packet_size;
|
|
offset += packet_size;
|
|
packet_size = 8;
|
|
if (offset >= buf_size){
|
|
packet_size = 0;
|
|
offset = buf_size;
|
|
}
|
|
}
|
|
urb->transfer_buffer_length = buf_size;
|
|
urb->number_of_packets = NUM_ISO_PACKETS_B;
|
|
urb->dev = bcs->cs->hw.st5481.dev;
|
|
|
|
DUMP_ISO_PACKET(0x10,urb);
|
|
|
|
submit_urb(urb);
|
|
}
|
|
|
|
|
|
static void
|
|
usb_b_out_complete(struct urb *urb)
|
|
{
|
|
struct BCState *bcs = (struct BCState *)urb->context;
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
int buf_nr;
|
|
|
|
buf_nr = get_buf_nr(hw->b_out_urb, urb);
|
|
test_and_clear_bit(buf_nr, &hw->b_out_busy);
|
|
|
|
if (urb->status < 0) {
|
|
if (urb->status != USB_ST_URB_KILLED) {
|
|
WARN("urb status %d",urb->status);
|
|
if (hw->b_out_busy == 0) {
|
|
usb_pipe_reset(bcs->cs, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL);
|
|
}
|
|
} else {
|
|
DBG(1,"urb killed");
|
|
return; // Give up
|
|
}
|
|
}
|
|
|
|
usb_b_out(bcs,buf_nr);
|
|
}
|
|
|
|
/*
|
|
Start transfering (flags or data) on the B channel, since
|
|
FIFO counters has been set to a non-zero value.
|
|
*/
|
|
static void
|
|
st5481B_start_xfer(void *context)
|
|
{
|
|
struct BCState *bcs = context;
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
unsigned int pipe;
|
|
|
|
DBG(4,"B%d",bcs->channel+1);
|
|
|
|
// Start receiving from B channel
|
|
pipe = usb_b_rcvisocpipe(bcs->cs->hw.st5481.dev,bcs->channel);
|
|
|
|
hw->b_in_urb[0]->pipe = pipe;
|
|
hw->b_in_urb[0]->dev = bcs->cs->hw.st5481.dev;
|
|
submit_urb(hw->b_in_urb[0]);
|
|
|
|
hw->b_in_urb[1]->pipe = pipe;
|
|
hw->b_in_urb[1]->dev = bcs->cs->hw.st5481.dev;
|
|
submit_urb(hw->b_in_urb[1]);
|
|
|
|
// Start transmitting (flags or data) on B channel
|
|
pipe = usb_b_sndisocpipe(bcs->cs->hw.st5481.dev,bcs->channel);
|
|
|
|
hw->b_out_urb[0]->pipe = pipe;
|
|
usb_b_out(bcs,0);
|
|
|
|
hw->b_out_urb[1]->pipe = pipe;
|
|
usb_b_out(bcs,1);
|
|
}
|
|
|
|
/*
|
|
Start or stop the transfer on the B channel.
|
|
*/
|
|
static void
|
|
st5481B_mode(struct BCState *bcs, int mode, int bc)
|
|
{
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
|
|
DBG(4,"B%d,mode=%d,bcs=%d",bc+1,mode,bcs==&bcs->cs->bcs[0] ? 0 : 1);
|
|
|
|
if (bcs->cs->debug & L1_DEB_HSCX)
|
|
debugl1(bcs->cs, "ST5481 bchannel mode %d bchan %d/%d",
|
|
mode, bc, bcs->channel);
|
|
|
|
if ((bcs->mode == mode) && (bcs->channel == bc)) {
|
|
return;
|
|
}
|
|
|
|
bcs->mode = mode;
|
|
bcs->channel = bc;
|
|
|
|
// Cancel all USB transfers on this B channel
|
|
usb_unlink_urb(hw->b_in_urb[0]);
|
|
usb_unlink_urb(hw->b_in_urb[1]);
|
|
usb_unlink_urb(hw->b_out_urb[0]);
|
|
usb_unlink_urb(hw->b_out_urb[1]);
|
|
hw->b_out_busy = 0;
|
|
|
|
if (bcs->mode) {
|
|
// Open the B channel
|
|
if (bcs->mode != L1_MODE_TRANS) {
|
|
hdlc_rcv_init(hw->hdlc_state_in, bcs->mode == L1_MODE_HDLC_56K);
|
|
hdlc_out_init(hw->hdlc_state_out, 0, bcs->mode == L1_MODE_HDLC_56K);
|
|
}
|
|
usb_pipe_reset(bcs->cs, (bcs->channel+1)*2, NULL, NULL);
|
|
usb_pipe_reset(bcs->cs, (bcs->channel+1)*2+1, NULL, NULL);
|
|
|
|
// Enable B channel interrupts
|
|
usb_device_ctrl_msg(bcs->cs, FFMSK_B1+(bcs->channel*2),
|
|
OUT_UP+OUT_DOWN+OUT_UNDERRUN, NULL, NULL);
|
|
|
|
// Enable B channel FIFOs
|
|
usb_device_ctrl_msg(bcs->cs, OUT_B1_COUNTER+(bcs->channel*2), 32, NULL, NULL);
|
|
usb_device_ctrl_msg(bcs->cs, IN_B1_COUNTER+(bcs->channel*2), 32,
|
|
st5481B_start_xfer, bcs);
|
|
#if NUMBER_OF_LEDS == 4
|
|
if (bc == 0) {
|
|
bcs->cs->hw.st5481.leds |= B1_LED;
|
|
} else {
|
|
bcs->cs->hw.st5481.leds |= B2_LED;
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
// Disble B channel interrupts
|
|
usb_device_ctrl_msg(bcs->cs, FFMSK_B1+(bcs->channel*2), 0, NULL, NULL);
|
|
|
|
// Disable B channel FIFOs
|
|
usb_device_ctrl_msg(bcs->cs, OUT_B1_COUNTER+(bcs->channel*2), 0, NULL, NULL);
|
|
usb_device_ctrl_msg(bcs->cs, IN_B1_COUNTER+(bcs->channel*2), 0, NULL, NULL);
|
|
|
|
#if NUMBER_OF_LEDS == 4
|
|
if (bc == 0) {
|
|
bcs->cs->hw.st5481.leds &= ~B1_LED;
|
|
} else {
|
|
bcs->cs->hw.st5481.leds &= ~B2_LED;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* st5481B_l2l1 is the entry point for upper layer routines that want to
|
|
* transmit on the B channel. PH_DATA | REQUEST is a normal packet that
|
|
* we either start transmitting (if idle) or queue (if busy).
|
|
* PH_PULL | REQUEST can be called to request a callback message
|
|
* (PH_PULL | CONFIRM)
|
|
* once the link is idle. After a "pull" callback, the upper layer
|
|
* routines can use PH_PULL | INDICATION to send data.
|
|
*/
|
|
static void
|
|
st5481B_l2l1(struct PStack *st, int pr, void *arg)
|
|
{
|
|
struct sk_buff *skb = arg;
|
|
long flags;
|
|
|
|
switch (pr) {
|
|
case (PH_DATA | REQUEST):
|
|
save_flags(flags);
|
|
cli();
|
|
if (st->l1.bcs->tx_skb) {
|
|
skb_queue_tail(&st->l1.bcs->squeue, skb);
|
|
} else {
|
|
st->l1.bcs->tx_skb = skb;
|
|
}
|
|
restore_flags(flags);
|
|
break;
|
|
case (PH_PULL | INDICATION):
|
|
if (st->l1.bcs->tx_skb) {
|
|
WARN("st->l1.bcs->tx_skb not NULL");
|
|
break;
|
|
}
|
|
save_flags(flags);
|
|
cli();
|
|
st->l1.bcs->tx_skb = skb;
|
|
restore_flags(flags);
|
|
break;
|
|
case (PH_PULL | REQUEST):
|
|
if (!st->l1.bcs->tx_skb) {
|
|
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
|
st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
|
|
} else
|
|
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
|
break;
|
|
case (PH_ACTIVATE | REQUEST):
|
|
DBG(4,"B%d,PH_ACTIVATE_REQUEST",st->l1.bc+1);
|
|
test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
|
|
st5481B_mode(st->l1.bcs, st->l1.mode, st->l1.bc);
|
|
l1_msg_b(st, pr, arg);
|
|
break;
|
|
case (PH_DEACTIVATE | REQUEST):
|
|
DBG(4,"B%d,PH_DEACTIVATE_REQUEST",st->l1.bc+1);
|
|
l1_msg_b(st, pr, arg);
|
|
break;
|
|
case (PH_DEACTIVATE | CONFIRM):
|
|
DBG(4,"B%d,PH_DEACTIVATE_CONFIRM",st->l1.bc+1);
|
|
test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
|
|
test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
|
|
st5481B_mode(st->l1.bcs, 0, st->l1.bc);
|
|
st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Called after B channel closed to release buffers.
|
|
*/
|
|
static void
|
|
BC_Close_st5481(struct BCState *bcs)
|
|
{
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
|
|
DBG(4,"B%d,bcs=%d",bcs->channel+1,bcs==&bcs->cs->bcs[0] ? 0 : 1);
|
|
|
|
st5481B_mode(bcs, 0, bcs->channel);
|
|
|
|
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
|
|
if (hw->rcvbuf) {
|
|
kfree(hw->rcvbuf);
|
|
hw->rcvbuf = NULL;
|
|
}
|
|
skb_queue_purge(&bcs->rqueue);
|
|
skb_queue_purge(&bcs->squeue);
|
|
if (bcs->tx_skb) {
|
|
dev_kfree_skb_any(bcs->tx_skb);
|
|
bcs->tx_skb = NULL;
|
|
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Allocate receive buffers
|
|
*/
|
|
static int
|
|
st5481B_open(struct BCState *bcs)
|
|
{
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
|
|
DBG(4,"B%d",bcs->channel+1);
|
|
|
|
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
|
|
if (!(hw->rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
|
|
WARN("No memory for rcvbuf");
|
|
test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
|
|
return (1);
|
|
}
|
|
skb_queue_head_init(&bcs->rqueue);
|
|
skb_queue_head_init(&bcs->squeue);
|
|
}
|
|
bcs->tx_skb = NULL;
|
|
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
|
bcs->event = 0;
|
|
hw->rcvidx = 0;
|
|
bcs->tx_cnt = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
BC_SetStack_st5481(struct PStack *st, struct BCState *bcs)
|
|
{
|
|
DBG(4,"B%d",st->l1.bc+1);
|
|
|
|
bcs->channel = st->l1.bc;
|
|
if (st5481B_open(bcs)) {
|
|
return (-1);
|
|
}
|
|
st->l1.bcs = bcs;
|
|
st->l2.l2l1 = st5481B_l2l1;
|
|
setstack_manager(st);
|
|
bcs->st = st;
|
|
setstack_l1_B(st);
|
|
return (0);
|
|
}
|
|
|
|
#if NUMBER_OF_LEDS==2
|
|
/*
|
|
If the adapter has only 2 LEDs, the green
|
|
LED will blink with a rate depending
|
|
on the number of channels opened.
|
|
*/
|
|
static void
|
|
led_blink(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
u_char leds = hw->leds;
|
|
|
|
// 50 frames/sec for each channel
|
|
if (++hw->led_counter % 50) {
|
|
return;
|
|
}
|
|
|
|
if (hw->led_counter % 100) {
|
|
leds |= GREEN_LED;
|
|
} else {
|
|
leds &= ~GREEN_LED;
|
|
}
|
|
|
|
usb_device_ctrl_msg(cs, GPIO_OUT, leds, NULL, NULL);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Decode frames received on this B channel.
|
|
Note that this function will be called continously
|
|
with 64Kb/s of data and hence it will be called 50 times
|
|
per second with 20 ISOC descriptors.
|
|
Called at interrupt.
|
|
*/
|
|
static void
|
|
usb_b_in_complete(struct urb *urb)
|
|
{
|
|
struct BCState *bcs = (struct BCState *)urb->context;
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
unsigned char *ptr;
|
|
struct sk_buff *skb;
|
|
int len,count;
|
|
unsigned short status;
|
|
|
|
if (urb->status < 0) {
|
|
if (urb->status != USB_ST_URB_KILLED) {
|
|
WARN("urb status %d",urb->status);
|
|
} else {
|
|
DBG(1,"urb killed");
|
|
return; // Give up
|
|
}
|
|
}
|
|
|
|
DUMP_ISO_PACKET(0x10,urb);
|
|
|
|
len = isoc_flatten(urb);
|
|
ptr = urb->transfer_buffer;
|
|
while (len > 0) {
|
|
if (bcs->mode == L1_MODE_TRANS) {
|
|
memcpy(hw->rcvbuf, ptr, len);
|
|
status = HDLC_END_OF_FRAME | (unsigned short)len;
|
|
len = 0;
|
|
} else {
|
|
status = hdlc_decode(hw->hdlc_state_in, ptr, len, &count,
|
|
hw->rcvbuf, HSCX_BUFMAX);
|
|
ptr += count;
|
|
len -= count;
|
|
}
|
|
|
|
if (status & HDLC_END_OF_FRAME) {
|
|
// Good frame received
|
|
count = status & 0x0FFF;
|
|
DBG(4,"B%d,count=%d",bcs->channel+1,count);
|
|
DUMP_PACKET(0x10, hw->rcvbuf, count);
|
|
if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
|
|
debugl1(bcs->cs, "st5481 Bchan Frame %d", count);
|
|
if (!(skb = dev_alloc_skb(count)))
|
|
WARN("Bchan receive out of memory\n");
|
|
else {
|
|
memcpy(skb_put(skb, count), hw->rcvbuf, count);
|
|
skb_queue_tail(&bcs->rqueue, skb);
|
|
}
|
|
st5481B_sched_event(bcs, B_RCVBUFREADY);
|
|
} else if (status & HDLC_CRC_ERROR) {
|
|
WARN("CRC error");
|
|
#ifdef ERROR_STATISTIC
|
|
++bcs->err_crc;
|
|
#endif
|
|
} else if (status & (HDLC_FRAMING_ERROR | HDLC_LENGTH_ERROR)) {
|
|
WARN("framing/length error");
|
|
#ifdef ERROR_STATISTIC
|
|
++bcs->err_inv;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Prepare URB for next transfer
|
|
urb->dev = bcs->cs->hw.st5481.dev;
|
|
urb->actual_length = 0;
|
|
|
|
submit_urb(urb);
|
|
|
|
#if NUMBER_OF_LEDS==2
|
|
led_blink(bcs->cs);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
D channel functions
|
|
*/
|
|
|
|
static void
|
|
st5481_sched_event(struct IsdnCardState *cs, int event)
|
|
{
|
|
test_and_set_bit(event, &cs->event);
|
|
queue_task(&cs->tqueue, &tq_immediate);
|
|
mark_bh(IMMEDIATE_BH);
|
|
}
|
|
|
|
/*
|
|
Schedule the private D_OUT_EVENT.
|
|
The actual event is stored in a FIFO.
|
|
*/
|
|
static void
|
|
st5481_sched_d_out_event(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
int w_index;
|
|
|
|
DBG(2,"event=%s",D_EVENT_string(event));
|
|
|
|
// Add this event to the FIFO
|
|
if (hw->xmt_evt_fifo == NULL) {
|
|
return;
|
|
}
|
|
|
|
if ((w_index = fifo_get(&hw->xmt_evt_fifo->f)) < 0) {
|
|
WARN("D_OUT event FIFO full");
|
|
return;
|
|
}
|
|
hw->xmt_evt_fifo->data[w_index] = event;
|
|
fifo_add(&hw->xmt_evt_fifo->f);
|
|
|
|
// Schedule to tell that an event has been added
|
|
test_and_set_bit(D_OUT_EVENT, &cs->event);
|
|
queue_task(&cs->tqueue, &tq_immediate);
|
|
mark_bh(IMMEDIATE_BH);
|
|
}
|
|
|
|
/*
|
|
The interrupt endpoint will be called when any
|
|
of the 6 registers changes state (depending on masks).
|
|
Decode the register values and schedule a private event.
|
|
Called at interrupt.
|
|
*/
|
|
static void
|
|
usb_int_complete(struct urb *urb)
|
|
{
|
|
u_char *data = urb->transfer_buffer;
|
|
u_char irqbyte;
|
|
struct IsdnCardState *cs = (struct IsdnCardState *)urb->context;
|
|
struct BCState *bcs;
|
|
|
|
if (urb->status < 0) {
|
|
if (urb->status != USB_ST_URB_KILLED) {
|
|
WARN("urb status %d",urb->status);
|
|
urb->actual_length = 0;
|
|
} else {
|
|
DBG(1,"urb killed");
|
|
return; // Give up
|
|
}
|
|
}
|
|
|
|
DUMP_PACKET(1, data, INT_PKT_SIZE);
|
|
|
|
if (urb->actual_length == 0) {
|
|
return;
|
|
}
|
|
|
|
irqbyte = data[MPINT];
|
|
if (irqbyte & DEN_INT) {
|
|
st5481_sched_d_out_event(cs, DEN_EVENT);
|
|
}
|
|
|
|
if (irqbyte & DCOLL_INT) {
|
|
st5481_sched_d_out_event(cs, DCOLL_EVENT);
|
|
}
|
|
|
|
irqbyte = data[FFINT_D];
|
|
if (irqbyte & (ANY_XMIT_INT)) {
|
|
st5481_sched_d_out_event(cs, DUNDERRUN_EVENT);
|
|
}
|
|
|
|
irqbyte = data[MPINT];
|
|
if (irqbyte & RXCI_INT) {
|
|
DBG(8,"CI %s",ST5481_IND_string(data[CCIST] & 0x0f));
|
|
cs->dc.st5481.ph_state = data[CCIST] & 0x0f;
|
|
if (cs->debug & L1_DEB_ISAC)
|
|
debugl1(cs, "ph_state change %x", cs->dc.st5481.ph_state);
|
|
st5481_sched_event(cs, D_L1STATECHANGE);
|
|
}
|
|
|
|
for (bcs = &cs->bcs[0]; bcs <= &cs->bcs[1]; bcs++) {
|
|
bcs->hw.st5481.b_flow_event |= data[FFINT_B1 + bcs->channel];
|
|
}
|
|
|
|
urb->actual_length = 0;
|
|
}
|
|
|
|
/*
|
|
The D IN pipe has been reset.
|
|
*/
|
|
static void
|
|
reset_fifoD_in_proc(void *context)
|
|
{
|
|
struct IsdnCardState *cs = context;
|
|
|
|
st5481_sched_d_out_event(cs, DNONE_EVENT);
|
|
}
|
|
|
|
|
|
/*
|
|
Decode frames received on the D channel.
|
|
Note that this function will be called continously
|
|
with 16Kb/s of data and hence it will be called 50 times
|
|
per second with 20 ISOC descriptors.
|
|
Called at interrupt.
|
|
*/
|
|
static void
|
|
usb_d_in_complete(struct urb *urb)
|
|
{
|
|
struct IsdnCardState *cs = (struct IsdnCardState *)urb->context;
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
unsigned char *ptr;
|
|
struct sk_buff *skb;
|
|
int len,count;
|
|
unsigned short status;
|
|
|
|
if (urb->status < 0) {
|
|
if (urb->status != USB_ST_URB_KILLED) {
|
|
WARN("urb status %d",urb->status);
|
|
usb_pipe_reset(cs, EP_D_IN, reset_fifoD_in_proc, cs);
|
|
} else {
|
|
DBG(1,"urb killed");
|
|
return; // Give up
|
|
}
|
|
}
|
|
|
|
DUMP_ISO_PACKET(0x20,urb);
|
|
|
|
len = isoc_flatten(urb);
|
|
ptr = urb->transfer_buffer;
|
|
while (len > 0) {
|
|
status = hdlc_decode(hw->hdlc_state_in, ptr, len, &count,
|
|
cs->rcvbuf, MAX_DFRAME_LEN_L1);
|
|
ptr += count;
|
|
len -= count;
|
|
|
|
if (status & HDLC_END_OF_FRAME) {
|
|
count = status & 0x0FFF;
|
|
DBG(2,"count=%d",count);
|
|
DUMP_PACKET(0x10, cs->rcvbuf, count);
|
|
// Good frame received
|
|
if (!(skb = dev_alloc_skb(count)))
|
|
WARN("D receive out of memory\n");
|
|
else {
|
|
memcpy(skb_put(skb, count), cs->rcvbuf, count);
|
|
skb_queue_tail(&cs->rq, skb);
|
|
}
|
|
st5481_sched_event(cs, D_RCVBUFREADY);
|
|
} else if (status & HDLC_CRC_ERROR) {
|
|
WARN("CRC error");
|
|
if (cs->debug & L1_DEB_WARN)
|
|
debugl1(cs, "st5481 CRC error");
|
|
#ifdef ERROR_STATISTIC
|
|
cs->err_crc++;
|
|
#endif
|
|
} else if (status & (HDLC_FRAMING_ERROR | HDLC_LENGTH_ERROR)) {
|
|
WARN("framing/length error");
|
|
if (cs->debug & L1_DEB_WARN)
|
|
debugl1(cs, "st5481 framing/length error");
|
|
#ifdef ERROR_STATISTIC
|
|
cs->err_rx++;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Prepare URB for next transfer
|
|
urb->dev = hw->dev;
|
|
urb->actual_length = 0;
|
|
|
|
submit_urb(urb);
|
|
}
|
|
|
|
|
|
/*
|
|
D channel transmit and flow control functions:
|
|
*/
|
|
|
|
/*
|
|
D OUT state machine:
|
|
====================
|
|
|
|
Transmit short frame (< 16 bytes of encoded data):
|
|
|
|
L1 FRAME D_OUT_STATE USB D CHANNEL
|
|
-------- ----------- --- ---------
|
|
|
|
INIT
|
|
|
|
-> [xx..xx] INIT_SHORT_FRAME -> [7Exx..xxC1C27EFF]
|
|
SHORT_WAIT_DEN <> OUT_D_COUNTER=16
|
|
|
|
END_OF_SHORT <- DEN_EVENT -> 7Exx
|
|
xxxx
|
|
xxxx
|
|
xxxx
|
|
xxxx
|
|
xxxx
|
|
C1C1
|
|
7EFF
|
|
WAIT_FOR_RESET_IDLE <- D_UNDERRUN <- (8ms)
|
|
IDLE <> Reset pipe
|
|
|
|
|
|
|
|
Transmit long frame (>= 16 bytes of encoded data):
|
|
|
|
L1 FRAME D_OUT_STATE USB D CHANNEL
|
|
-------- ----------- --- ---------
|
|
|
|
-> [xx...xx] IDLE
|
|
WAIT_FOR_STOP <> OUT_D_COUNTER=0
|
|
WAIT_FOR_RESET <> Reset pipe
|
|
STOP
|
|
INIT_LONG_FRAME -> [7Exx..xx]
|
|
WAIT_DEN <> OUT_D_COUNTER=16
|
|
OUT_NORMAL <- DEN_EVENT -> 7Exx
|
|
END_OF_FRAME_BUSY -> [xxxx] xxxx
|
|
END_OF_FRAME_NOT_BUSY -> [xxxx] xxxx
|
|
-> [xxxx] xxxx
|
|
-> [C1C2] xxxx
|
|
-> [7EFF] xxxx
|
|
xxxx
|
|
xxxx
|
|
....
|
|
xxxx
|
|
C1C2
|
|
7EFF
|
|
<- D_UNDERRUN <- (> 8ms)
|
|
WAIT_FOR_STOP <> OUT_D_COUNTER=0
|
|
WAIT_FOR_RESET <> Reset pipe
|
|
STOP
|
|
|
|
*/
|
|
|
|
#define d_out_set_state(hw,new_state) \
|
|
DBG(2,"state=%s,new state=%s",D_STATE_string((hw)->d_out_state),D_STATE_string(new_state)); \
|
|
(hw)->d_out_state = new_state;
|
|
|
|
/*
|
|
Start the transfer of a D channel frame.
|
|
*/
|
|
static void
|
|
usb_d_out(struct IsdnCardState *cs,int buf_nr)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
struct urb *urb;
|
|
unsigned int num_packets,packet_offset;
|
|
int len,buf_size,bytes_sent;
|
|
struct sk_buff *skb;
|
|
|
|
if (test_and_set_bit(buf_nr,&hw->d_out_busy)) {
|
|
DBG(2,"ep %d urb %d busy",EP_D_OUT,buf_nr);
|
|
return;
|
|
}
|
|
urb = hw->d_out_urb[buf_nr];
|
|
|
|
DBG(2,"state=%s",D_STATE_string(hw->d_out_state));
|
|
|
|
skb = cs->tx_skb;
|
|
switch(hw->d_out_state){
|
|
case DOUT_INIT:
|
|
if (skb) {
|
|
DUMP_SKB(0x10, skb);
|
|
len = hdlc_encode(hw->hdlc_state_out,
|
|
skb->data, skb->len, &bytes_sent,
|
|
urb->transfer_buffer, 16);
|
|
skb_pull(skb, bytes_sent);
|
|
} else {
|
|
// Send flags or idle
|
|
len = hdlc_encode(hw->hdlc_state_out,
|
|
NULL, 0, &bytes_sent,
|
|
urb->transfer_buffer, 16);
|
|
}
|
|
|
|
if(len < 16){
|
|
d_out_set_state(hw, DOUT_INIT_SHORT_FRAME);
|
|
} else {
|
|
d_out_set_state(hw, DOUT_INIT_LONG_FRAME);
|
|
}
|
|
packet_offset = len;
|
|
break;
|
|
case DOUT_NORMAL:
|
|
buf_size = NUM_ISO_PACKETS_D*2;
|
|
packet_offset = 2;
|
|
|
|
if (skb) {
|
|
len = hdlc_encode(hw->hdlc_state_out,
|
|
skb->data, skb->len, &bytes_sent,
|
|
urb->transfer_buffer, buf_size);
|
|
skb_pull(skb,bytes_sent);
|
|
} else {
|
|
// Send flags or idle
|
|
len = hdlc_encode(hw->hdlc_state_out,
|
|
NULL, 0, &bytes_sent,
|
|
urb->transfer_buffer, buf_size);
|
|
}
|
|
|
|
if(len < buf_size){
|
|
st5481_sched_d_out_event(cs, DXSHORT_EVENT);
|
|
if (len < 2) {
|
|
packet_offset = len;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (skb && !skb->len) {
|
|
dev_kfree_skb_any(skb);
|
|
if (!(cs->tx_skb = skb_dequeue(&cs->sq))) {
|
|
st5481_sched_event(cs, D_XMTBUFREADY);
|
|
}
|
|
}
|
|
|
|
// Prepare the URB
|
|
urb->transfer_buffer_length = len;
|
|
urb->actual_length = len;
|
|
|
|
urb->iso_frame_desc[0].offset = 0;
|
|
urb->iso_frame_desc[0].length = packet_offset;
|
|
urb->iso_frame_desc[0].actual_length = packet_offset;
|
|
|
|
num_packets = 1;
|
|
if(len){
|
|
while(packet_offset < len){
|
|
urb->iso_frame_desc[num_packets].offset = packet_offset;
|
|
urb->iso_frame_desc[num_packets].length = 2;
|
|
urb->iso_frame_desc[num_packets].actual_length = 2;
|
|
packet_offset += 2;
|
|
num_packets++;
|
|
}
|
|
}
|
|
urb->number_of_packets = num_packets;
|
|
|
|
// Prepare the URB
|
|
urb->dev = hw->dev;
|
|
// Need to transmit the next buffer 8ms after the DEN_EVENT
|
|
urb->transfer_flags = 0;
|
|
urb->start_frame = usb_get_current_frame_number(hw->dev)+2;
|
|
|
|
DUMP_ISO_PACKET(0x10,urb);
|
|
|
|
if (usb_submit_urb(urb) < 0) {
|
|
// There is another URB queued up
|
|
urb->transfer_flags = USB_ISO_ASAP;
|
|
submit_urb(urb);
|
|
}
|
|
}
|
|
|
|
static void
|
|
reset_fifoD_out_proc(void *context)
|
|
{
|
|
struct IsdnCardState *cs = context;
|
|
|
|
d_out_set_state(&cs->hw.st5481, DOUT_STOP);
|
|
st5481_sched_d_out_event(cs, DNONE_EVENT);
|
|
}
|
|
|
|
static void
|
|
usb_d_out_complete(struct urb *urb)
|
|
{
|
|
struct IsdnCardState *cs = urb->context;
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
int buf_nr;
|
|
|
|
buf_nr = get_buf_nr(hw->d_out_urb, urb);
|
|
test_and_clear_bit(buf_nr, &hw->d_out_busy);
|
|
|
|
if (urb->status < 0) {
|
|
if (urb->status != USB_ST_URB_KILLED) {
|
|
WARN("urb status %d",urb->status);
|
|
if(hw->d_out_busy==0) {
|
|
usb_pipe_reset(cs, EP_D_OUT | USB_DIR_OUT, reset_fifoD_out_proc, cs);
|
|
}
|
|
return;
|
|
} else {
|
|
DBG(1,"urb killed");
|
|
return; // Give up
|
|
}
|
|
}
|
|
|
|
switch(hw->d_out_state){
|
|
case DOUT_INIT_SHORT_FRAME:
|
|
case DOUT_INIT_LONG_FRAME:
|
|
st5481_sched_d_out_event(cs, DXMIT_INITED);
|
|
break;
|
|
case DOUT_NORMAL:
|
|
usb_d_out(cs, buf_nr);
|
|
break;
|
|
case DOUT_END_OF_FRAME_BUSY:
|
|
case DOUT_WAIT_FOR_NOT_BUSY:
|
|
if(hw->d_out_busy==0)
|
|
st5481_sched_d_out_event(cs, DXMIT_NOT_BUSY);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
reset_fifoD_out_proc_idle(void *context)
|
|
{
|
|
struct IsdnCardState *cs = context;
|
|
|
|
d_out_set_state(&cs->hw.st5481, DOUT_IDLE);
|
|
st5481_sched_d_out_event(cs, DNONE_EVENT);
|
|
}
|
|
|
|
static void
|
|
out_d_stop_event(void *context)
|
|
{
|
|
struct IsdnCardState *cs = context;
|
|
|
|
st5481_sched_d_out_event(cs, DXMIT_STOPPED);
|
|
}
|
|
|
|
static void
|
|
out_d_start(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
|
|
if (!cs->tx_skb) {
|
|
return;
|
|
}
|
|
|
|
DBG(2,"len=%d",cs->tx_skb->len);
|
|
|
|
if(hw->d_out_state == DOUT_IDLE && cs->tx_skb->len > 4) {
|
|
d_out_set_state(hw, DOUT_WAIT_FOR_STOP);
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 0, out_d_stop_event, cs);
|
|
return;
|
|
}
|
|
|
|
d_out_set_state(hw, DOUT_INIT);
|
|
|
|
hdlc_out_init(hw->hdlc_state_out, 1, 0);
|
|
usb_d_out(cs,0);
|
|
}
|
|
|
|
static void
|
|
out_d_short_fifoD(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DXMIT_INITED:
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 16, NULL, NULL);
|
|
d_out_set_state(&cs->hw.st5481, DOUT_SHORT_WAIT_DEN);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
enable_fifoD_proc(void *context)
|
|
{
|
|
struct IsdnCardState *cs = context;
|
|
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 16, NULL, NULL);
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_DEN);
|
|
}
|
|
|
|
static void
|
|
out_d_long_fifoD(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DXMIT_INITED:
|
|
usb_pipe_reset(cs, EP_D_OUT | USB_DIR_OUT, enable_fifoD_proc, cs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_wait_den(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DEN_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_NORMAL);
|
|
usb_d_out(cs,0);
|
|
usb_d_out(cs,1);
|
|
break;
|
|
case DCOLL_EVENT:
|
|
case DUNDERRUN_EVENT:
|
|
case DXRESET_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_NOT_BUSY);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_short_wait_den(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DEN_EVENT:
|
|
case DCOLL_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_END_OF_SHORT_FRAME);
|
|
break;
|
|
case DUNDERRUN_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_RESET_IDLE);
|
|
usb_pipe_reset(cs, EP_D_OUT | USB_DIR_OUT, reset_fifoD_out_proc_idle, cs);
|
|
break;
|
|
case DXRESET_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_STOP);
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 0, out_d_stop_event, cs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_end_of_short_frame(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DUNDERRUN_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_RESET_IDLE);
|
|
usb_pipe_reset(cs, EP_D_OUT | USB_DIR_OUT, reset_fifoD_out_proc_idle, cs);
|
|
break;
|
|
case DCOLL_EVENT:
|
|
case DXRESET_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_STOP);
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 0, out_d_stop_event, cs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_end_of_frame_not_busy(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DCOLL_EVENT:
|
|
case DUNDERRUN_EVENT:
|
|
case DXRESET_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_STOP);
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 0, out_d_stop_event, cs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_end_of_frame_busy(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DCOLL_EVENT:
|
|
case DUNDERRUN_EVENT:
|
|
case DXRESET_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_NOT_BUSY);
|
|
break;
|
|
case DXMIT_NOT_BUSY:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_END_OF_FRAME_NOT_BUSY);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_normal(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DCOLL_EVENT:
|
|
case DUNDERRUN_EVENT:
|
|
case DXRESET_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_NOT_BUSY);
|
|
break;
|
|
case DXSHORT_EVENT:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_END_OF_FRAME_BUSY);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_wait_for_not_busy(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DXMIT_NOT_BUSY:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_STOP);
|
|
usb_device_ctrl_msg(cs, OUT_D_COUNTER, 0, out_d_stop_event, cs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
out_d_wait_for_stop(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
switch(event){
|
|
case DXMIT_STOPPED:
|
|
d_out_set_state(&cs->hw.st5481, DOUT_WAIT_FOR_RESET);
|
|
usb_pipe_reset(cs, EP_D_OUT | USB_DIR_OUT, reset_fifoD_out_proc, cs);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
OUT D state machine
|
|
*/
|
|
static void
|
|
out_d(struct IsdnCardState *cs, unsigned int event)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
|
|
DBG(2,"state=%s,event=%s",D_STATE_string(hw->d_out_state),D_EVENT_string(event));
|
|
|
|
switch(hw->d_out_state){
|
|
case DOUT_NONE:
|
|
case DOUT_STOP:
|
|
case DOUT_IDLE:
|
|
if (event == DNONE_EVENT) {
|
|
out_d_start(cs);
|
|
}
|
|
break;
|
|
case DOUT_INIT_SHORT_FRAME:
|
|
out_d_short_fifoD(cs, event);
|
|
break;
|
|
case DOUT_INIT_LONG_FRAME:
|
|
out_d_long_fifoD(cs, event);
|
|
break;
|
|
case DOUT_WAIT_DEN:
|
|
out_d_wait_den(cs, event);
|
|
break;
|
|
case DOUT_SHORT_WAIT_DEN:
|
|
out_d_short_wait_den(cs, event);
|
|
break;
|
|
case DOUT_END_OF_SHORT_FRAME:
|
|
out_d_end_of_short_frame(cs, event);
|
|
break;
|
|
case DOUT_END_OF_FRAME_BUSY:
|
|
out_d_end_of_frame_busy(cs, event);
|
|
break;
|
|
case DOUT_END_OF_FRAME_NOT_BUSY:
|
|
out_d_end_of_frame_not_busy(cs, event);
|
|
break;
|
|
case DOUT_NORMAL:
|
|
out_d_normal(cs, event);
|
|
break;
|
|
case DOUT_WAIT_FOR_NOT_BUSY:
|
|
out_d_wait_for_not_busy(cs, event);
|
|
break;
|
|
case DOUT_WAIT_FOR_STOP:
|
|
out_d_wait_for_stop(cs, event);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Start transmitting D channel frame.
|
|
*/
|
|
static void
|
|
st5481_fill_fifo(struct IsdnCardState *cs)
|
|
{
|
|
out_d(cs, DNONE_EVENT);
|
|
}
|
|
|
|
/*
|
|
Remove the event from the FIFO and call the OUT D
|
|
state machine.
|
|
*/
|
|
static void
|
|
st5481_d_out_event(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
int r_index;
|
|
unsigned int event;
|
|
|
|
if (hw->xmt_evt_fifo == NULL) {
|
|
return;
|
|
}
|
|
|
|
while ((r_index = fifo_remove(&hw->xmt_evt_fifo->f)) >= 0) {
|
|
event = hw->xmt_evt_fifo->data[r_index];
|
|
out_d(cs,event);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
Physical level functions
|
|
*/
|
|
|
|
static void
|
|
ph_command(struct IsdnCardState *cs, unsigned int command)
|
|
{
|
|
DBG(8,"command=%s",ST5481_CMD_string(command));
|
|
|
|
if (cs->debug & L1_DEB_ISAC)
|
|
debugl1(cs, "ph_command %x", command);
|
|
|
|
usb_device_ctrl_msg(cs, TXCI, command, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
Start receiving on the D channel since entered state F7.
|
|
*/
|
|
static void
|
|
ph_connect(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
|
|
DBG(8,"");
|
|
|
|
d_out_set_state(hw, DOUT_NONE);
|
|
|
|
usb_device_ctrl_msg(cs, FFMSK_D, OUT_UNDERRUN, NULL, NULL);
|
|
usb_device_ctrl_msg(cs, IN_D_COUNTER, 16, NULL, NULL);
|
|
|
|
#if LOOPBACK
|
|
// Turn loopback on (data sent on B and D looped back)
|
|
usb_device_ctrl_msg(cs, LBB, 0x04, NULL, NULL);
|
|
#endif
|
|
|
|
hdlc_rcv_init(hw->hdlc_state_in, 0);
|
|
|
|
usb_pipe_reset(cs, EP_D_OUT | USB_DIR_OUT, NULL, NULL);
|
|
usb_pipe_reset(cs, EP_D_IN | USB_DIR_IN, NULL, NULL);
|
|
|
|
// Turn on the green LED to tell that we are in state F7
|
|
hw->leds |= GREEN_LED;
|
|
usb_device_ctrl_msg(cs, GPIO_OUT, hw->leds, NULL, NULL);
|
|
|
|
// Start receiving on the isoc endpoints
|
|
hw->d_in_urb[0]->dev = hw->dev;
|
|
submit_urb(hw->d_in_urb[0]);
|
|
hw->d_in_urb[1]->dev = hw->dev;
|
|
submit_urb(hw->d_in_urb[1]);
|
|
}
|
|
|
|
/*
|
|
Stop receiving on the D channel since not in state F7.
|
|
*/
|
|
static void
|
|
ph_disconnect(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
|
|
DBG(8,"");
|
|
|
|
// Stop receiving on the isoc endpoints
|
|
usb_unlink_urb(hw->d_in_urb[0]);
|
|
usb_unlink_urb(hw->d_in_urb[1]);
|
|
|
|
// Turn off the green LED to tell that we left state F7
|
|
hw->leds &= ~GREEN_LED;
|
|
usb_device_ctrl_msg(cs, GPIO_OUT, hw->leds, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
Reset the adapter to default values.
|
|
*/
|
|
static void
|
|
ph_exit(struct IsdnCardState *cs)
|
|
{
|
|
DBG(8,"");
|
|
|
|
usb_device_ctrl_msg(cs, SET_DEFAULT, 0, NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
Initialize the adapter.
|
|
*/
|
|
static void
|
|
ph_init(struct IsdnCardState *cs)
|
|
{
|
|
static const __u8 init_cmd_table[]={
|
|
SET_DEFAULT,0,
|
|
STT,0,
|
|
SDA_MIN,0x0d,
|
|
SDA_MAX,0x29,
|
|
SDELAY_VALUE,0x14,
|
|
GPIO_DIR,0x01,
|
|
GPIO_OUT,RED_LED,
|
|
FFCTRL_OUT_B1,6,
|
|
FFCTRH_OUT_B1,20,
|
|
FFCTRL_OUT_B2,6,
|
|
FFCTRH_OUT_B2,20,
|
|
MPMSK,RXCI_INT+DEN_INT+DCOLL_INT,
|
|
0
|
|
};
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
int i = 0;
|
|
__u8 request,value;
|
|
|
|
DBG(8,"");
|
|
|
|
hw->leds = RED_LED;
|
|
|
|
cs->dc.st5481.ph_state = -1;
|
|
|
|
// Start receiving on the interrupt endpoint
|
|
submit_urb(hw->int_urb);
|
|
|
|
while ((request = init_cmd_table[i++])) {
|
|
value = init_cmd_table[i++];
|
|
usb_device_ctrl_msg(cs, request, value, NULL, NULL);
|
|
}
|
|
ph_command(cs, ST5481_CMD_PUP);
|
|
}
|
|
|
|
static void
|
|
st5481_new_ph(struct IsdnCardState *cs)
|
|
{
|
|
DBG(8,"state=%s",ST5481_IND_string(cs->dc.st5481.ph_state));
|
|
|
|
switch (cs->dc.st5481.ph_state) {
|
|
case ST5481_IND_DI:
|
|
ph_disconnect(cs);
|
|
l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
|
|
break;
|
|
case ST5481_IND_DP:
|
|
ph_disconnect(cs);
|
|
l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
|
|
break;
|
|
case ST5481_IND_RSY:
|
|
ph_disconnect(cs);
|
|
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
|
|
break;
|
|
case ST5481_IND_AP:
|
|
l1_msg(cs, HW_INFO2 | INDICATION, NULL);
|
|
break;
|
|
case ST5481_IND_AI8:
|
|
l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
|
|
break;
|
|
case ST5481_IND_AI10:
|
|
l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
|
|
break;
|
|
default:
|
|
WARN("unknown st5481.ph_state %x",cs->dc.st5481.ph_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
st5481_bh(struct IsdnCardState *cs)
|
|
{
|
|
struct PStack *stptr;
|
|
|
|
if (!cs)
|
|
return;
|
|
|
|
if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
|
|
stptr = cs->stlist;
|
|
while (stptr != NULL) {
|
|
stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
|
|
stptr = stptr->next;
|
|
}
|
|
}
|
|
if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
|
|
st5481_new_ph(cs);
|
|
}
|
|
if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
|
|
DChannel_proc_rcv(cs);
|
|
}
|
|
if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
|
|
DChannel_proc_xmt(cs);
|
|
}
|
|
if (test_and_clear_bit(D_OUT_EVENT, &cs->event)) {
|
|
st5481_d_out_event(cs);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
S/T state machine:
|
|
|
|
Activation by TE:
|
|
-----------------
|
|
F1
|
|
|
|
PUP -> F2 INFO0 ->
|
|
|
|
<- DP F3 <- INFO0
|
|
|
|
AR8 -> F4 INFO1 ->
|
|
|
|
<- AP F6 <- INFO2
|
|
INFO3 ->
|
|
<- AI8 F7 <- INFO4
|
|
|
|
Activation by NT:
|
|
-----------------
|
|
F1 INFO0 ->
|
|
<- INFO0
|
|
|
|
<- AP F6 <- INFO2
|
|
INFO3 ->
|
|
<- AI8 F7 <- INFO4
|
|
|
|
Deactivation by NT:
|
|
-------------------
|
|
F7 <- INFO4
|
|
|
|
<- DP F3 <- INFO0
|
|
( DR ->)
|
|
|
|
Loss of frame synchronisation by NT:
|
|
-----------------------------
|
|
F7 <- INFO4
|
|
Loss of frame
|
|
<- AP F6 <- INFO2
|
|
INFO3 ->
|
|
<- AI8 F7 <- INFO4
|
|
|
|
Loss of frame synchronisation by TE:
|
|
-----------------------------
|
|
F7 <- INFO4
|
|
Loss of frame
|
|
<- RSY F8 INFO0 ->
|
|
<- AP F6 <- INFO2
|
|
INFO3 ->
|
|
<- AI8 F7 <- INFO4
|
|
|
|
Loss of power:
|
|
--------------
|
|
F7 <- INFO4
|
|
|
|
<- DP F1 Loss of power
|
|
INFO0 ->
|
|
<- INFO2
|
|
<- DP F2 Power recovered
|
|
INFO0 ->
|
|
<- AP F6 <- INFO2
|
|
INFO3 ->
|
|
*/
|
|
|
|
static void
|
|
st5481_l1hw(struct PStack *st, int pr, void *arg)
|
|
{
|
|
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
|
|
struct sk_buff *skb = arg;
|
|
|
|
switch (pr) {
|
|
case (PH_DATA |REQUEST):
|
|
if (cs->debug & DEB_DLOG_HEX)
|
|
LogFrame(cs, skb->data, skb->len);
|
|
if (cs->debug & DEB_DLOG_VERBOSE)
|
|
dlogframe(cs, skb, 0);
|
|
if (cs->tx_skb) {
|
|
skb_queue_tail(&cs->sq, skb);
|
|
#ifdef L2FRAME_DEBUG /* psa */
|
|
if (cs->debug & L1_DEB_LAPD)
|
|
Logl2Frame(cs, skb, "PH_DATA Queued", 0);
|
|
#endif
|
|
} else {
|
|
cs->tx_skb = skb;
|
|
cs->tx_cnt = 0;
|
|
#ifdef L2FRAME_DEBUG /* psa */
|
|
if (cs->debug & L1_DEB_LAPD)
|
|
Logl2Frame(cs, skb, "PH_DATA", 0);
|
|
#endif
|
|
st5481_fill_fifo(cs);
|
|
}
|
|
break;
|
|
case (PH_PULL |INDICATION):
|
|
if (cs->tx_skb) {
|
|
if (cs->debug & L1_DEB_WARN)
|
|
debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
|
|
skb_queue_tail(&cs->sq, skb);
|
|
break;
|
|
}
|
|
if (cs->debug & DEB_DLOG_HEX)
|
|
LogFrame(cs, skb->data, skb->len);
|
|
if (cs->debug & DEB_DLOG_VERBOSE)
|
|
dlogframe(cs, skb, 0);
|
|
cs->tx_skb = skb;
|
|
cs->tx_cnt = 0;
|
|
#ifdef L2FRAME_DEBUG /* psa */
|
|
if (cs->debug & L1_DEB_LAPD)
|
|
Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
|
|
#endif
|
|
st5481_fill_fifo(cs);
|
|
break;
|
|
case (PH_PULL | REQUEST):
|
|
#ifdef L2FRAME_DEBUG /* psa */
|
|
if (cs->debug & L1_DEB_LAPD)
|
|
debugl1(cs, "-> PH_REQUEST_PULL");
|
|
#endif
|
|
if (!cs->tx_skb) {
|
|
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
|
st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
|
|
} else
|
|
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
|
break;
|
|
case (HW_RESET | REQUEST):
|
|
DBG(8,"HW_RESET_REQUEST,state=%s",ST5481_IND_string(cs->dc.st5481.ph_state));
|
|
ph_command(cs, ST5481_CMD_PUP);
|
|
l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
|
|
break;
|
|
case (HW_ENABLE | REQUEST):
|
|
DBG(8,"HW_ENABLE_REQUEST,state=%s",ST5481_IND_string(cs->dc.st5481.ph_state));
|
|
ph_command(cs, ST5481_CMD_DR);
|
|
ph_command(cs, ST5481_CMD_PUP);
|
|
break;
|
|
case (HW_INFO3 | REQUEST):
|
|
DBG(8,"HW_INFO_3_REQUEST,state=%s",ST5481_IND_string(cs->dc.st5481.ph_state));
|
|
if (cs->dc.st5481.ph_state == ST5481_IND_AI8) {
|
|
ph_command(cs, ST5481_CMD_AR8);
|
|
ph_connect(cs);
|
|
} else if (cs->dc.st5481.ph_state == ST5481_IND_AI10) {
|
|
ph_command(cs, ST5481_CMD_AR10);
|
|
ph_connect(cs);
|
|
} else {
|
|
ph_command(cs, ST5481_CMD_AR8);
|
|
}
|
|
break;
|
|
case (HW_TESTLOOP | REQUEST):
|
|
DBG(8,"HW_TESTLOOP_REQUEST,state=%s",ST5481_IND_string(cs->dc.st5481.ph_state));
|
|
switch ((int)arg) {
|
|
case 0:
|
|
// Off
|
|
usb_device_ctrl_msg(cs, LBA, 0x2, NULL, NULL);
|
|
break;
|
|
case 1:
|
|
// B1 channel
|
|
usb_device_ctrl_msg(cs, LBA, 0x42 , NULL, NULL);
|
|
break;
|
|
case 2:
|
|
// B2 channel
|
|
usb_device_ctrl_msg(cs, LBA, 0x22 , NULL, NULL);
|
|
break;
|
|
case 4:
|
|
// LOOP4 on
|
|
usb_device_ctrl_msg(cs, LBA, 0x0e, NULL, NULL);
|
|
break;
|
|
}
|
|
break;
|
|
case (HW_DEACTIVATE | RESPONSE):
|
|
DBG(8,"HW_DEACTIVATE_RESPONSE,state=%s",ST5481_IND_string(cs->dc.st5481.ph_state));
|
|
//ph_command(cs, ST5481_CMD_DR);
|
|
skb_queue_purge(&cs->rq);
|
|
skb_queue_purge(&cs->sq);
|
|
if (cs->tx_skb) {
|
|
dev_kfree_skb_any(cs->tx_skb);
|
|
cs->tx_skb = NULL;
|
|
}
|
|
if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
|
|
del_timer(&cs->dbusytimer);
|
|
if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
|
|
st5481_sched_event(cs, D_CLEARBUSY);
|
|
break;
|
|
default:
|
|
if (cs->debug & L1_DEB_WARN)
|
|
debugl1(cs, __FUNCTION__ "unknown %04x", pr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Initialisation of USB, D and B channels
|
|
*/
|
|
|
|
static void
|
|
setstack_d_st5481(struct PStack *st, struct IsdnCardState *cs)
|
|
{
|
|
st->l1.l1hw = st5481_l1hw;
|
|
}
|
|
|
|
|
|
static void
|
|
DC_Close_st5481(struct IsdnCardState *cs)
|
|
{
|
|
}
|
|
|
|
static int
|
|
init_st5481(struct IsdnCardState *cs)
|
|
{
|
|
int i;
|
|
|
|
DBG(1,"");
|
|
|
|
// B channels
|
|
for (i = 0; i < 2; i++) {
|
|
cs->bcs[i].BC_SetStack = BC_SetStack_st5481;
|
|
cs->bcs[i].BC_Close = BC_Close_st5481;
|
|
}
|
|
|
|
// D channel
|
|
cs->tqueue.routine = (void *) (void *) st5481_bh;
|
|
cs->setstack_d = setstack_d_st5481;
|
|
cs->DC_Close = DC_Close_st5481;
|
|
|
|
// Physical layer
|
|
ph_init(cs);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
Release buffers and URBs for the B channels
|
|
*/
|
|
static void
|
|
release_b(struct BCState *bcs)
|
|
{
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
struct urb *urb;
|
|
int j;
|
|
|
|
DBG(1,"");
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
if ((urb = hw->b_out_urb[j])) {
|
|
usb_unlink_urb(urb);
|
|
if (urb->transfer_buffer)
|
|
kfree(urb->transfer_buffer);
|
|
usb_free_urb(urb);
|
|
hw->b_out_urb[j] = NULL;
|
|
}
|
|
}
|
|
for (j = 0; j < 2; j++) {
|
|
if ((urb = hw->b_in_urb[j])) {
|
|
usb_unlink_urb(urb);
|
|
if (urb->transfer_buffer)
|
|
kfree(urb->transfer_buffer);
|
|
usb_free_urb(urb);
|
|
hw->b_in_urb[j] = NULL;
|
|
}
|
|
}
|
|
if (hw->hdlc_state_out) {
|
|
kfree(hw->hdlc_state_out);
|
|
hw->hdlc_state_out = NULL;
|
|
}
|
|
if (hw->hdlc_state_in) {
|
|
kfree(hw->hdlc_state_in);
|
|
hw->hdlc_state_in = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Release buffers and URBs for the D channel
|
|
*/
|
|
static void
|
|
release_d(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
struct urb *urb;
|
|
int j;
|
|
|
|
DBG(1,"");
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
if ((urb = hw->d_out_urb[j])) {
|
|
usb_unlink_urb(urb);
|
|
if (urb->transfer_buffer)
|
|
kfree(urb->transfer_buffer);
|
|
usb_free_urb(urb);
|
|
hw->d_out_urb[j] = NULL;
|
|
}
|
|
}
|
|
for (j = 0; j < 2; j++) {
|
|
if ((urb = hw->d_in_urb[j])) {
|
|
usb_unlink_urb(urb);
|
|
if (urb->transfer_buffer)
|
|
kfree(urb->transfer_buffer);
|
|
usb_free_urb(urb);
|
|
hw->d_in_urb[j] = NULL;
|
|
}
|
|
}
|
|
if (hw->hdlc_state_out) {
|
|
kfree(hw->hdlc_state_out);
|
|
hw->hdlc_state_out = NULL;
|
|
}
|
|
if (hw->hdlc_state_in) {
|
|
kfree(hw->hdlc_state_in);
|
|
hw->hdlc_state_in = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Release buffers and URBs for the interrupt and control
|
|
endpoint.
|
|
*/
|
|
static void
|
|
release_usb(struct IsdnCardState *cs)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
struct urb *urb;
|
|
|
|
DBG(1,"");
|
|
|
|
// Stop and free Control and Interrupt URBs
|
|
if ((urb = hw->int_urb)) {
|
|
usb_unlink_urb(urb);
|
|
if (urb->transfer_buffer)
|
|
kfree(urb->transfer_buffer);
|
|
usb_free_urb(urb);
|
|
hw->int_urb = NULL;
|
|
}
|
|
if ((urb = hw->ctrl_urb)) {
|
|
usb_unlink_urb(urb);
|
|
if (urb->transfer_buffer)
|
|
kfree(urb->transfer_buffer);
|
|
usb_free_urb(urb);
|
|
hw->ctrl_urb = NULL;
|
|
}
|
|
|
|
// Release memory for the FIFOs
|
|
if (hw->ctrl_msg_fifo) {
|
|
kfree(hw->ctrl_msg_fifo);
|
|
hw->ctrl_msg_fifo = NULL;
|
|
}
|
|
if (hw->xmt_evt_fifo) {
|
|
kfree(hw->xmt_evt_fifo);
|
|
hw->xmt_evt_fifo = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
release_st5481(struct IsdnCardState *cs)
|
|
{
|
|
int i;
|
|
|
|
INFO("cardnr=%d",cs->cardnr);
|
|
|
|
ph_exit(cs);
|
|
release_d(cs);
|
|
for (i=0; i < 2; i++) {
|
|
release_b(&cs->bcs[i]);
|
|
}
|
|
release_usb(cs);
|
|
}
|
|
|
|
static int
|
|
st5481_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
|
{
|
|
DBG(1,"mt=%x",mt);
|
|
|
|
switch (mt) {
|
|
case CARD_RESET:
|
|
return(0);
|
|
case CARD_RELEASE:
|
|
release_st5481(cs);
|
|
return(0);
|
|
case CARD_INIT:
|
|
return(init_st5481(cs));
|
|
case CARD_TEST:
|
|
return(0);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
Set the USB configuration and allocate memory
|
|
and URBs for the interrupt and control endpoints.
|
|
*/
|
|
static int __devinit
|
|
setup_usb(struct IsdnCardState *cs, struct usb_device *dev)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
struct usb_interface_descriptor *altsetting;
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
int status;
|
|
urb_t *urb;
|
|
u_char *buf;
|
|
|
|
DBG(1,"");
|
|
|
|
memset(hw,0,sizeof(*hw));
|
|
|
|
if ((status = usb_set_configuration (dev,dev->config[0].bConfigurationValue)) < 0) {
|
|
WARN("set_configuration failed,status=%d",status);
|
|
return (status);
|
|
}
|
|
|
|
|
|
altsetting = &(dev->config->interface[0].altsetting[3]);
|
|
|
|
// Check if the config is sane
|
|
if ( altsetting->bNumEndpoints != (7) ) {
|
|
WARN("expecting %d got %d endpoints!", 7, altsetting->bNumEndpoints);
|
|
return (-EINVAL);
|
|
}
|
|
|
|
// The descriptor is wrong for some early samples of the ST5481 chip
|
|
altsetting->endpoint[3].wMaxPacketSize = SIZE_ISO_PACKETS_B;
|
|
altsetting->endpoint[4].wMaxPacketSize = SIZE_ISO_PACKETS_B;
|
|
|
|
// Use alternative setting 3 on interface 0 to have 2B+D
|
|
if ((status = usb_set_interface (dev, 0, 3)) < 0) {
|
|
WARN("usb_set_interface failed,status=%d",status);
|
|
return (status);
|
|
}
|
|
|
|
// Good device
|
|
hw->dev = dev;
|
|
|
|
// Allocate URB for control endpoint
|
|
urb = usb_alloc_urb(0);
|
|
if (!urb) {
|
|
return (-ENOMEM);
|
|
}
|
|
hw->ctrl_urb = urb;
|
|
|
|
// Fill the control URB
|
|
FILL_CONTROL_URB (urb, dev,
|
|
usb_sndctrlpipe(dev, 0),
|
|
NULL, NULL, 0, usb_ctrl_complete, cs);
|
|
|
|
|
|
// Allocate URBs and buffers for interrupt endpoint
|
|
urb = usb_alloc_urb(0);
|
|
if (!urb) {
|
|
return (-ENOMEM);
|
|
}
|
|
hw->int_urb = urb;
|
|
|
|
buf = kmalloc(INT_PKT_SIZE, GFP_KERNEL);
|
|
if (!buf) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
endpoint = &altsetting->endpoint[EP_INT-1];
|
|
|
|
// Fill the interrupt URB
|
|
FILL_INT_URB(urb, dev,
|
|
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
|
buf, INT_PKT_SIZE,
|
|
usb_int_complete, cs,
|
|
endpoint->bInterval);
|
|
|
|
// Allocate memory for the FIFOs
|
|
if (!(hw->ctrl_msg_fifo = kmalloc(sizeof(*hw->ctrl_msg_fifo), GFP_KERNEL))) {
|
|
return (-ENOMEM);
|
|
}
|
|
hw->ctrl_msg_fifo->f.r = hw->ctrl_msg_fifo->f.w = hw->ctrl_msg_fifo->f.count = 0;
|
|
hw->ctrl_msg_fifo->f.size = sizeof(hw->ctrl_msg_fifo->data)/sizeof(hw->ctrl_msg_fifo->data[0]);
|
|
spin_lock_init(hw->ctrl_msg_fifo->f.lock);
|
|
|
|
if (!(hw->xmt_evt_fifo = kmalloc(sizeof(*hw->xmt_evt_fifo), GFP_KERNEL))) {
|
|
return (-ENOMEM);
|
|
}
|
|
hw->xmt_evt_fifo->f.r = hw->xmt_evt_fifo->f.w = hw->xmt_evt_fifo->f.count = 0;
|
|
hw->xmt_evt_fifo->f.size = sizeof(hw->xmt_evt_fifo->data)/sizeof(hw->xmt_evt_fifo->data[0]);
|
|
spin_lock_init(hw->xmt_evt_fifo->f.lock);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
Allocate buffers and URBs for the D channel endpoints.
|
|
*/
|
|
static int __devinit
|
|
setup_d(struct IsdnCardState *cs,struct usb_device *dev)
|
|
{
|
|
struct st5481_hw *hw = &cs->hw.st5481;
|
|
struct usb_interface_descriptor *altsetting;
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
struct urb *urb;
|
|
unsigned char *buf;
|
|
int j;
|
|
|
|
DBG(2,"");
|
|
|
|
if (!(hw->hdlc_state_in = kmalloc(sizeof(struct hdlc_vars), GFP_KERNEL))) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
if (!(hw->hdlc_state_out = kmalloc(sizeof(struct hdlc_vars), GFP_KERNEL))) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
altsetting = &(dev->config->interface[0].altsetting[3]);
|
|
|
|
// Allocate URBs and buffers for the D channel out
|
|
endpoint = &altsetting->endpoint[EP_D_OUT-1];
|
|
|
|
DBG(1,"endpoint address=%02x,packet size=%d",
|
|
endpoint->bEndpointAddress,endpoint->wMaxPacketSize);
|
|
|
|
for (j=0; j < 2; j++) {
|
|
urb = usb_alloc_urb(NUM_ISO_PACKETS_D);
|
|
if (!urb) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
hw->d_out_urb[j] = urb;
|
|
|
|
// Allocate memory for 2000bytes/sec (16Kb/s)
|
|
buf = kmalloc(NUM_ISO_PACKETS_D * 2, GFP_KERNEL);
|
|
if (!buf) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
// Fill the isochronous URB
|
|
fill_isoc_urb(urb, dev,
|
|
usb_sndisocpipe(dev, endpoint->bEndpointAddress),
|
|
buf, NUM_ISO_PACKETS_D, 2,
|
|
usb_d_out_complete, cs);
|
|
}
|
|
|
|
// Allocate URBs and buffers for the D channel in
|
|
endpoint = &altsetting->endpoint[EP_D_IN-1];
|
|
|
|
DBG(1,"endpoint address=%02x,packet size=%d",
|
|
endpoint->bEndpointAddress,endpoint->wMaxPacketSize);
|
|
|
|
for (j=0; j < 2; j++) {
|
|
urb = usb_alloc_urb(NUM_ISO_PACKETS_D);
|
|
if (!urb) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
hw->d_in_urb[j] = urb;
|
|
|
|
buf = kmalloc(NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D, GFP_KERNEL);
|
|
if (!buf) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
// Fill the isochronous URB
|
|
fill_isoc_urb(urb, dev,
|
|
usb_rcvisocpipe(dev, endpoint->bEndpointAddress),
|
|
buf, NUM_ISO_PACKETS_D, SIZE_ISO_PACKETS_D,
|
|
usb_d_in_complete, cs);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
Allocate buffers and URBs for the B channel endpoints.
|
|
*/
|
|
static int __devinit
|
|
setup_b(struct BCState *bcs,struct usb_device *dev)
|
|
|
|
{
|
|
struct st5481B_hw *hw = &bcs->hw.st5481;
|
|
struct urb *urb;
|
|
unsigned char *buf;
|
|
int j;
|
|
|
|
DBG(4,"");
|
|
|
|
memset(hw,0,sizeof(*hw));
|
|
|
|
if (!(hw->hdlc_state_in = kmalloc(sizeof(struct hdlc_vars), GFP_KERNEL))) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
if (!(hw->hdlc_state_out = kmalloc(sizeof(struct hdlc_vars), GFP_KERNEL))) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
// Allocate URBs and buffers for the B channel out
|
|
for (j=0; j < 2; j++) {
|
|
urb = usb_alloc_urb(NUM_ISO_PACKETS_B);
|
|
if (!urb)
|
|
return (-ENOMEM);
|
|
|
|
hw->b_out_urb[j] = urb;
|
|
|
|
// Allocate memory for 8000bytes/sec + extra bytes if underrun
|
|
buf = kmalloc((NUM_ISO_PACKETS_B*8)+8, GFP_KERNEL);
|
|
if (!buf) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
// Fill the isochronous URB
|
|
fill_isoc_urb(urb, dev,
|
|
0, // Unknown B channel
|
|
buf, NUM_ISO_PACKETS_B, 8,
|
|
usb_b_out_complete, bcs);
|
|
}
|
|
|
|
// Allocate URBs and buffers for the B channel in
|
|
for (j=0; j < 2; j++) {
|
|
urb = usb_alloc_urb(NUM_ISO_PACKETS_B);
|
|
if (!urb)
|
|
return (-ENOMEM);
|
|
|
|
hw->b_in_urb[j] = urb;
|
|
|
|
buf = kmalloc(NUM_ISO_PACKETS_B * SIZE_ISO_PACKETS_B, GFP_KERNEL);
|
|
if (!buf) {
|
|
return (-ENOMEM);
|
|
}
|
|
|
|
// Fill the isochronous URB
|
|
fill_isoc_urb(urb, dev,
|
|
0, // Unknown B channel
|
|
buf, NUM_ISO_PACKETS_B, SIZE_ISO_PACKETS_B,
|
|
usb_b_in_complete, bcs);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
This function will be called when the adapter is plugged
|
|
into the USB bus (via init_usb_st5481).
|
|
*/
|
|
int __devinit
|
|
setup_st5481(struct IsdnCard *card)
|
|
{
|
|
struct IsdnCardState *cs = card->cs;
|
|
char tmp[64];
|
|
struct usb_device *dev;
|
|
int i;
|
|
|
|
if (cs->typ != ISDN_CTYPE_ST5481) {
|
|
return (0);
|
|
}
|
|
|
|
dev = (struct usb_device *)card->para[1]; // FIXME: broken for sizeof(void *) != sizeof(int)
|
|
if (!dev) {
|
|
ERR("no usb_device");
|
|
return (0);
|
|
}
|
|
|
|
strcpy(tmp, st5481_revision);
|
|
INFO("Rev. %s",HiSax_getrev(tmp));
|
|
INFO("adapter with VendorId %04x,ProductId %04x,LEDs %d",
|
|
dev->descriptor.idVendor,dev->descriptor.idProduct,NUMBER_OF_LEDS);
|
|
|
|
if (setup_usb(cs, dev) < 0) {
|
|
WARN("setup_usb failed");
|
|
return (0);
|
|
}
|
|
|
|
if (setup_d(cs, dev) < 0) {
|
|
WARN("setup_d failed");
|
|
return (0);
|
|
}
|
|
|
|
for (i=0; i < 2; i++) {
|
|
if (setup_b(&cs->bcs[i], dev) < 0) {
|
|
WARN("setup_b failed");
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
cs->cardmsg = &st5481_card_msg;
|
|
|
|
return (1);
|
|
}
|