?? dm9601.c
字號:
}
/* Auto Sense Media Policy:
Fast EtherNet NIC: don't need to do.
Force media mode: don't need to do.
HomeRun/LongRun NIC and AUTO_Mode:
INT_MII not link, select EXT_MII
EXT_MII not link, select INT_MII
*/
if ( !(d[0] & 0x40) &&
(dbi->nic_type != FASTETHER_NIC) &&
(dbi->op_mode == DM9601_AUTO) ) {
dbi->net_ctrl_reg ^= 0x80;
netif_stop_queue(net);
dbi->flags |= NET_CTRL_CHANGE;
ctrl_callback(&dbi->ctrl_urb, NULL);
netif_wake_queue(net);
}
if ( (d[1] | d[2]) & 0xf4 ) {
dbi->stats.tx_errors++;
if ( (d[0] | d[1]) & 0x84) /* EXEC & JABBER */
dbi->stats.tx_aborted_errors++;
if ( (d[0] | d[1]) & 0x10 ) /* LATE COL */
dbi->stats.tx_window_errors++;
if ( (d[0] | d[1]) & 0x60 ) /* NO or LOST CARRIER */
dbi->stats.tx_carrier_errors++;
}
}
#endif
static void dm9601_tx_timeout( struct net_device *net )
{
dm9601_board_info_t *dbi = net->priv;
if ( !dbi )
return;
warn("%s: Tx timed out.", net->name);
dbi->tx_urb.transfer_flags |= URB_NO_FSBR;//USB_ASYNC_UNLINK;
usb_unlink_urb( &dbi->tx_urb );
dbi->stats.tx_errors++;
}
//#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,48)
//#endif
static int dm9601_start_xmit( struct sk_buff *skb, struct net_device *net )
{
dm9601_board_info_t *dbi = net->priv;
int count = skb->len + 2;
int res;
__u16 l16 = skb->len;
netif_stop_queue( net );
if (!(count & 0x3f)) { count++; l16++; }
((__u16 *)dbi->tx_buff)[0] = cpu_to_le16(l16);
memcpy(dbi->tx_buff + 2, skb->data, skb->len);
#if 0
FILL_BULK_URB_TO( &dbi->tx_urb, dbi->usb,
usb_sndbulkpipe(dbi->usb, 2),
dbi->tx_buff, count,
write_bulk_callback, dbi, jiffies + HZ );
#else
usb_fill_bulk_urb( &dbi->tx_urb, dbi->usb,
usb_sndbulkpipe(dbi->usb, 2),
dbi->tx_buff, count,
write_bulk_callback, dbi);
#endif
if ((res = usb_submit_urb(&dbi->tx_urb,GFP_KERNEL))) {
warn("failed tx_urb %d", res);
dbi->stats.tx_errors++;
netif_start_queue( net );
} else {
dbi->stats.tx_packets++;
dbi->stats.tx_bytes += skb->len;
net->trans_start = jiffies;
}
dev_kfree_skb(skb);
return 0;
}
static struct net_device_stats *dm9601_netdev_stats( struct net_device *dev )
{
return &((dm9601_board_info_t *)dev->priv)->stats;
}
static inline void disable_net_traffic( dm9601_board_info_t *dbi )
{
__u8 reg5;
write_mii_word(dbi, 1, 0, 0x8000); /* RESET PHY */
get_registers(dbi, 0x5, 1, ®5);
reg5 &= 0xfe;
set_register(dbi, 0x5, reg5); /* RX disable */
set_register(dbi, 0x1f, 0x01); /* PHY power down */
}
static void set_phy_mode(dm9601_board_info_t *dbi)
{
__u16 phy_reg0 = 0x1000, phy_reg4 = 0x01e1;
/* PHY media mode setting */
if ( !(dbi->op_mode & DM9601_AUTO) ) {
switch(dbi->op_mode) {
case DM9601_10MHF:
phy_reg4 = 0x0021; break;
case DM9601_10MFD:
phy_reg4 = 0x0041; break;
case DM9601_100MHF:
phy_reg4 = 0x0081; break;
case DM9601_100MFD:
phy_reg4 = 0x0101; break;
default:
phy_reg0 = 0x8000; break;
}
write_mii_word(dbi, 1, 4, phy_reg4); /* Set PHY capability */
write_mii_word(dbi, 1, 0, phy_reg0);
}
/* Active PHY */
set_register( dbi, 0x1e, 0x01 ); /* Let GPIO0 output */
set_register( dbi, 0x1f, 0x00 ); /* Power_on PHY */
}
/*
Init HomeRun DM9801
*/
static void program_dm9801(dm9601_board_info_t *dbi, u16 HPNA_rev)
{
__u16 reg16, reg17, reg24, reg25;
if ( !nfloor ) nfloor = DM9801_NOISE_FLOOR;
read_mii_word(dbi, 1, 16, ®16);
read_mii_word(dbi, 1, 17, ®17);
read_mii_word(dbi, 1, 24, ®24);
read_mii_word(dbi, 1, 25, ®25);
switch(HPNA_rev) {
case 0xb900: /* DM9801 E3 */
reg16 |= 0x1000;
reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xf000;
break;
case 0xb901: /* DM9801 E4 */
reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xc200;
reg17 = (reg17 & 0xfff0) + nfloor + 3;
break;
case 0xb902: /* DM9801 E5 */
case 0xb903: /* DM9801 E6 */
default:
reg16 |= 0x1000;
reg25 = ( (reg24 + nfloor - 3) & 0x00ff) | 0xc200;
reg17 = (reg17 & 0xfff0) + nfloor;
break;
}
write_mii_word(dbi, 1, 16, reg16);
write_mii_word(dbi, 1, 17, reg17);
write_mii_word(dbi, 1, 25, reg25);
}
/*
Init LongRun DM9802
*/
static void program_dm9802(dm9601_board_info_t *dbi)
{
__u16 reg25;
if ( !nfloor ) nfloor = DM9802_NOISE_FLOOR;
read_mii_word(dbi, 1, 25, ®25);
reg25 = (reg25 & 0xff00) + nfloor;
write_mii_word(dbi, 1, 25, reg25);
}
/*
Identify NIC type
*/
static void identify_nic(dm9601_board_info_t* dbi)
{
__u16 phy_tmp;
/* Select EXT_MII */
dbi->net_ctrl_reg |= 0x80;
set_register(dbi, 0x00, dbi->net_ctrl_reg); /* EXT-MII */
read_mii_word(dbi, 1, 3, &phy_tmp);
switch(phy_tmp & 0xfff0) {
case 0xb900:
read_mii_word(dbi, 1, 31, &phy_tmp);
if (phy_tmp == 0x4404) {
dbi->nic_type = HOMERUN_NIC;
program_dm9801(dbi, phy_tmp);
} else {
dbi->nic_type = LONGRUN_NIC;
program_dm9802(dbi);
}
break;
default:
dbi->nic_type = FASTETHER_NIC;
}
/* Select INT_MII */
dbi->net_ctrl_reg &= ~0x80;
set_register(dbi, 0x00, dbi->net_ctrl_reg);
}
static void init_dm9601(struct net_device *net)
{
dm9601_board_info_t *dbi = (dm9601_board_info_t *)net->priv;
/* User passed argument */
dbi->rx_ctrl_reg = reg5 | 0x01;
dbi->net_ctrl_reg = 0x00;
dbi->reg08 = reg8;
dbi->reg09 = reg9;
dbi->reg0a = rega;
/* RESET device */
set_register(dbi, 0x00, 0x01); /* Reset */
udelay(100);
/* NIC type: FASTETHER, HOMERUN, LONGRUN */
identify_nic(dbi);
/* Set PHY */
dbi->op_mode = dm9601_mode;
set_phy_mode(dbi);
/* MII selection */
if ( (dbi->nic_type != FASTETHER_NIC) &&
(dbi->op_mode == DM9601_1M_HPNA) )
dbi->net_ctrl_reg |= 0x80;
/* Program operating register */
set_register(dbi, 0x00, dbi->net_ctrl_reg);
set_register(dbi, 0x08, dbi->reg08);
set_register(dbi, 0x09, dbi->reg09);
set_register(dbi, 0x0a, dbi->reg0a);
set_register(dbi, 0xf4, 0x26); /* Reset EP1/EP2, INT always return */
set_registers(dbi, 0x10, 0x06, net->dev_addr); /* MAC addr */
dbi->hash_table[3] = 0x8000; /* Broadcast Address */
set_registers(dbi, 0x16, 0x08, dbi->hash_table); /* Hash Table */
set_register(dbi, 0x05, dbi->rx_ctrl_reg); /* Active RX */
}
static int dm9601_open(struct net_device *net)
{
dm9601_board_info_t *dbi = (dm9601_board_info_t *)net->priv;
int res;
int owner;
down(&dbi->ctrl_sem);
// MOD_INC_USE_COUNT;
owner = try_module_get(THIS_MODULE);
usb_fill_bulk_urb( &dbi->rx_urb, dbi->usb,
usb_rcvbulkpipe(dbi->usb, 1),
dbi->rx_buff, DM9601_MAX_MTU,
read_bulk_callback, dbi );
if ( (res = usb_submit_urb(&dbi->rx_urb,GFP_ATOMIC)) )
warn("%s: failed rx_urb %d",__FUNCTION__,res);
dbi->rx_buf_flag = 1;
#ifdef DM9601_USE_INTR
usb_fill_int_urb( &dbi->intr_urb, dbi->usb,
usb_rcvintpipe(dbi->usb, 3),
dbi->intr_buff, sizeof(dbi->intr_buff),
intr_callback, dbi, dbi->intr_interval );
if ( (res = usb_submit_urb(&dbi->intr_urb,GFP_ATOMIC)) )
warn("%s: failed intr_urb %d",__FUNCTION__,res);
#endif
init_dm9601(net);
netif_start_queue( net );
dbi->flags |= DM9601_RUNNING;
up(&dbi->ctrl_sem);
return 0;
}
static int dm9601_close( struct net_device *net )
{
dm9601_board_info_t *dbi = net->priv;
dbi->flags &= ~DM9601_RUNNING;
netif_stop_queue(net);
if ( !(dbi->flags & DM9601_UNPLUG) )
disable_net_traffic(dbi);
usb_unlink_urb(&dbi->rx_urb);
usb_unlink_urb(&dbi->tx_urb);
usb_unlink_urb(&dbi->ctrl_urb);
#ifdef DM9601_USE_INTR
usb_unlink_urb(&dbi->intr_urb);
#endif
//MOD_DEC_USE_COUNT;
module_put(THIS_MODULE);#ifdef STS_DBUG
printk("<DM9601> rx errors: %lx \n", dbi->stats.rx_errors);
printk("<DM9601> fifo over errors: %lx \n", dbi->stats.rx_fifo_errors);
printk("<DM9601> crc errors: %lx \n", dbi->stats.rx_crc_errors);
printk("<DM9601> alignment errors: %lx \n", dbi->stats.rx_frame_errors);
printk("<DM9601> physical layer errors: %lx \n", dbi->rx_ple_errors);
printk("<DM9601> watchdog errors: %lx \n", dbi->rx_wdt_errors);
printk("<DM9601> late collision errors: %lx \n", dbi->rx_lc_errors);
printk("<DM9601> runt frame errors: %lx \n", dbi->rx_runtf_errors);
printk("<DM9601> long frame errors: %lx \n", dbi->rx_longf_errors);
#endif
return 0;
}
static int dm9601_ioctl( struct net_device *net, struct ifreq *rq, int cmd )
{
__u16 *data = (__u16 *)&rq->ifr_data;
dm9601_board_info_t *dbi = net->priv;
switch(cmd) {
case SIOCDEVPRIVATE:
data[0] = dbi->phy;
case SIOCDEVPRIVATE+1:
read_mii_word(dbi, data[0], data[1]&0x1f, &data[3]);
return 0;
case SIOCDEVPRIVATE+2:
if ( !capable(CAP_NET_ADMIN) )
return -EPERM;
write_mii_word(dbi, dbi->phy, data[1] & 0x1f, data[2]);
return 0;
default:
return -EOPNOTSUPP;
}
}
/*
Calculate the CRC valude of the Rx packet
flag = 1 : return the reverse CRC (for the received packet CRC)
0 : return the normal CRC (for Hash Table index)
*/
static unsigned long cal_CRC(unsigned char * Data, unsigned int Len, u8 flag)
{
u32 crc = ether_crc_le(Len, Data);
if (flag)
return ~crc;
return crc;
}
static void dm9601_set_multicast( struct net_device *net )
{
dm9601_board_info_t *dbi = net->priv;
struct dev_mc_list *mcptr = net->mc_list;
int count = net->mc_count, i, hash_val;
netif_stop_queue(net);
if (net->flags & IFF_PROMISC) {
dbi->rx_ctrl_reg |= RX_PROMISCUOUS;
info("%s: Promiscuous mode enabled", net->name);
} else if (net->flags & IFF_ALLMULTI) {
dbi->rx_ctrl_reg |= RX_PASS_MULTICAST;
dbi->rx_ctrl_reg &= ~RX_PROMISCUOUS;
info("%s set allmulti", net->name);
} else {
dbi->rx_ctrl_reg &= ~RX_PASS_MULTICAST;
dbi->rx_ctrl_reg &= ~RX_PROMISCUOUS;
/* Clear Hash Table */
for (i = 0; i < 4; i++) dbi->hash_table[i] = 0;
/* Set Broadcast Address */
dbi->hash_table[3] = 0x8000;
/* the multicast address in Hash Table : 64 bits */
for (i = 0; i < count; i++, mcptr = mcptr->next) {
hash_val = cal_CRC((char *)mcptr->dmi_addr, 6, 0) & 0x3f;
dbi->hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16);
}
info("%s: set Rx mode", net->name);
}
dbi->flags |= HASH_REGS_CHANGE | RX_CTRL_CHANGE;
ctrl_callback(&dbi->ctrl_urb, NULL);
netif_wake_queue(net);
}
static int dm9601_probe( struct usb_interface *udev, const struct usb_device_id *id)
{
struct net_device *net;
dm9601_board_info_t *dbi;
int dev_index = id - dm9601_ids;
struct usb_device *dev = interface_to_usbdev (udev);
int status;
#if 0
if (usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue)) {
err("usb_set_configuration() failed");
return -ENODEV;
}
#endif
if(!(dbi = kmalloc(sizeof(dm9601_board_info_t), GFP_KERNEL))) {
err("out of memory allocating device structure");
return -ENOMEM;
}
// usb_inc_dev_use( dev );
// printk("dev_index %d dbi %x\n",dev_index,dbi);
usb_get_dev(dev);
memset(dbi, 0, sizeof(dm9601_board_info_t));
// initialize dbi struct
{
usb_init_urb(&dbi->ctrl_urb);
usb_init_urb(&dbi->rx_urb);
usb_init_urb(&dbi->tx_urb);
usb_init_urb(&dbi->intr_urb);
usb_init_urb(&dbi->dump_urb);
}
dbi->dev_index = dev_index;
init_waitqueue_head( &dbi->ctrl_wait );
// net = init_etherdev( NULL, 0 );
net = alloc_etherdev(0);
if ( !net ) {
kfree( dbi );
return -ENOMEM;
}
init_MUTEX(&dbi->ctrl_sem);
down(&dbi->ctrl_sem);
dbi->usb = dev;
dbi->net = net;
net->priv = dbi;
net->open = dm9601_open;
net->stop = dm9601_close;
net->watchdog_timeo = DM9601_TX_TIMEOUT;
net->tx_timeout = dm9601_tx_timeout;
net->do_ioctl = dm9601_ioctl;
net->hard_start_xmit = dm9601_start_xmit;
net->set_multicast_list = dm9601_set_multicast;
net->get_stats = dm9601_netdev_stats;
net->mtu = DM9601_MTU;
dbi->intr_interval = 0xff; /* Default is 0x80 */
/* Get Node Address */
read_eprom_word(dbi, 0, (__u16 *)net->dev_addr);
read_eprom_word(dbi, 1, (__u16 *)(net->dev_addr + 2));
read_eprom_word(dbi, 2, (__u16 *)(net->dev_addr + 4));
dbi->features = usb_dev_id[dev_index].private;
info( "%s: %s", net->name, usb_dev_id[dev_index].name );
usb_set_intfdata (udev, dbi);
SET_NETDEV_DEV(net, &udev->dev);
status = register_netdev (net);
up(&dbi->ctrl_sem);
if (status)
return status;
// start as if the link is up
netif_device_attach (net);
return 0;
}
static void dm9601_disconnect( struct usb_interface *intf/*struct usb_device *dev, void *ptr*/ )
{
struct usb_device *dev = interface_to_usbdev (intf);;
dm9601_board_info_t *dbi = usb_get_intfdata(intf);
if ( !dbi ) {
warn("unregistering non-existant device");
return;
}
dbi->flags |= DM9601_UNPLUG;
unregister_netdev( dbi->net );
// usb_dec_dev_use( dev );
usb_put_dev(dev);
kfree( dbi );
dbi = NULL;
}
static struct usb_driver dm9601_driver = {
name: "dm9601",
probe: dm9601_probe,
disconnect: dm9601_disconnect,
id_table: dm9601_ids,
};
int __init dm9601_init(void)
{
info( "%s", version );
switch(mode) {
case DM9601_10MHF:
case DM9601_100MHF:
case DM9601_10MFD:
case DM9601_100MFD:
case DM9601_1M_HPNA:
dm9601_mode = mode;
break;
default:
dm9601_mode = DM9601_AUTO;
}
nfloor = (nfloor > 15) ? 0:nfloor;
return usb_register( &dm9601_driver );
}
void __exit dm9601_exit(void)
{
usb_deregister( &dm9601_driver );
}
module_init( dm9601_init );
module_exit( dm9601_exit );
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -