Pic CDC USB routines. More...
Data Structures | |
struct | line_coding |
union | long_union |
Defines | |
#define | not_SERIAL_STATE 0xa1 |
#define | req_CLEAR_COMM_FEATURE 0x04 |
#define | req_GET_COMM_FEATURE 0x03 |
#define | req_GET_ENCAPSULATED_RESPONSE 0x01 |
#define | req_GET_LINE_CODING 0x21 |
#define | req_SEND_BREAK 0x23 |
#define | req_SEND_ENCAPSULATED_COMMAND 0x00 |
#define | req_SET_COMM_FEATURE 0x02 |
#define | req_SET_CONTROL_LINE_STATE 0x22 |
#define | req_SET_LINE_CODING 0x20 |
Functions | |
uns8 | usb_cdc_getc (void) |
Retrieve a received character from the USB serial port. | |
void | usb_cdc_handle_tx () |
Transmit any buffered characters over ther USB virtual serial port. | |
void | usb_cdc_print_int (uns16 i) |
Print a 16 bit number to the USB virtual serial port. | |
void | usb_cdc_print_str (char *str) |
Print a string out to the USB virtual serial port. | |
void | usb_cdc_putc (uns8 c) |
Sends a single character to the USB serial port. | |
uns8 | usb_cdc_rx_avail () |
Check to see if a character is available in the receive buffer. | |
void | usb_cdc_set_dcd () |
void | usb_cdc_set_dsr () |
void | usb_cdc_setup () |
Set up data structures ready for USB CDC use. | |
uns8 | usb_cdc_tx_empty () |
Check to see if the transmit buffer is empty. | |
void | usb_ep_data_in_callback (uns8 end_point, uns16 byte_count) |
Callback routine triggered when data has been sent to the host. | |
void | usb_ep_data_out_callback (uns8 end_point, uns8 *buffer, uns16 byte_count) |
Callback routine triggered when data has been sent to the device. | |
void | usb_handle_class_ctrl_read_callback () |
Callback routine for a class control read. | |
void | usb_handle_class_ctrl_write_callback (uns8 *data, uns16 count) |
Callback routine for a class control write. | |
void | usb_handle_class_request_callback (setup_data_packet sdp) |
Callback routine for a control transfer request that is placed on the class. | |
void | usb_SOF_callback (uns16 frame) |
Callback routine triggered each time a start of frame (SOF) has been received. | |
Variables | |
uns8 | cdc_rx_buffer [USB_CDC_RX_BUFFER_SIZE] |
uns8 | cdc_rx_end = 0 |
uns8 | cdc_rx_start = 0 |
uns8 | cdc_tx_buffer [USB_CDC_TX_BUFFER_SIZE] |
uns8 | cdc_tx_end = 0 |
uns8 | cdc_tx_start = 0 |
uns8 | class_data [8] |
uns16 | current_bit_rate |
uns8 | data_bits |
long_union | dte_rate |
uns8 | parity |
Communication Device Class (Serial Port) USB routines
#define not_SERIAL_STATE 0xa1 |
#define req_CLEAR_COMM_FEATURE 0x04 |
#define req_GET_COMM_FEATURE 0x03 |
#define req_GET_ENCAPSULATED_RESPONSE 0x01 |
#define req_GET_LINE_CODING 0x21 |
#define req_SEND_BREAK 0x23 |
#define req_SEND_ENCAPSULATED_COMMAND 0x00 |
#define req_SET_COMM_FEATURE 0x02 |
#define req_SET_CONTROL_LINE_STATE 0x22 |
#define req_SET_LINE_CODING 0x20 |
uns8 usb_cdc_getc | ( | ) |
Receive a character from the USB serial port. If a character has not been received, this routine will wait indefinately.
00377 { 00378 uns8 cdc_rx_char, cdc_rx_next; 00379 00380 while(cdc_rx_end == cdc_rx_start); // wait until there is something received 00381 00382 start_crit_sec(); // make sure nobody else can muck with the buffer 00383 00384 cdc_rx_char = cdc_rx_buffer[cdc_rx_start]; // get character from the front of the buffer 00385 cdc_rx_start++; // increment fifo start 00386 if (cdc_rx_start == USB_CDC_RX_BUFFER_SIZE) { // if we're at the end 00387 cdc_rx_start = 0; // then wrap to the beginning 00388 } 00389 00390 end_crit_sec(); // now they can muck with the buffer 00391 00392 return (cdc_rx_char); // return the result we first thought of 00393 00394 } // -- getc
void usb_cdc_handle_tx | ( | ) |
Generally only used internally, this routine will attempt to place any characters in the transmit queue into the USB buffers. It will fail gracefully if the USB buffer is already owned by the SIE.
00398 { 00399 uns8 cdc_tx_next; 00400 uns8 count; 00401 uns16 buffer_size; 00402 uns8 *buffer; 00403 buffer_descriptor *bd; 00404 00405 bd = ep_in_bd_location[USB_CDC_DATA_ENDPOINT]; 00406 if (test_bit(bd->stat, UOWN)) { // if there's already something in play 00407 return; // give up 00408 } 00409 00410 buffer_size = ep_in_buffer_size[USB_CDC_DATA_ENDPOINT]; 00411 buffer = ep_in_buffer_location[USB_CDC_DATA_ENDPOINT]; 00412 00413 if (cdc_tx_end == cdc_tx_start) { // anything in the fifo? 00414 return; // nope 00415 } 00416 #ifdef CDC_DEBUG 00417 serial_putc('<'); 00418 #endif 00419 start_crit_sec(); 00420 00421 count = 0; 00422 while ((cdc_tx_end != cdc_tx_start) && (count < buffer_size)) { 00423 00424 cdc_tx_next = cdc_tx_start + 1; // get next position 00425 if (cdc_tx_next == USB_CDC_TX_BUFFER_SIZE) { // if we're at the end of the buffer 00426 cdc_tx_next = 0; // wrap to the beginning 00427 } 00428 buffer[count] = cdc_tx_buffer[cdc_tx_start]; // transmit the character 00429 #ifdef CDC_DEBUG 00430 serial_putc(buffer[count]); 00431 #endif 00432 count++; 00433 cdc_tx_start = cdc_tx_next; // move start position of fifo 00434 } 00435 if (count > 0) { 00436 bd->count = count; 00437 bd->addr = (uns16)buffer; 00438 00439 toggle_bit(bd->stat, DTS); 00440 clear_bit(bd->stat, KEN); // clear the keep bit 00441 clear_bit(bd->stat, INCDIS); // clear the increment disable 00442 set_bit (bd->stat, DTSEN); 00443 clear_bit(bd->stat, BSTALL); // clear stall bit 00444 clear_bit(bd->stat, BC9); 00445 clear_bit(bd->stat, BC8); 00446 00447 set_bit (bd->stat, UOWN); // SIE owns the buffer 00448 } 00449 end_crit_sec(); 00450 #ifdef CDC_DEBUG 00451 serial_putc('>'); 00452 serial_print_str("send="); 00453 serial_print_int(count); 00454 serial_putc(' '); 00455 #endif 00456 #ifdef USB_CDC_USE_LEDS 00457 platform_leds_flash(2); 00458 #endif 00459 }
void usb_cdc_print_int | ( | uns16 | i | ) |
Print a 16 bit unsigned number in decimal to the USB virtual serial port
i | the 16 bit number to be printed |
00497 { 00498 00499 char buffer[6]; // up to 5 characters plus \0 00500 uns8 count = 5; 00501 buffer[5] = '\0'; 00502 do { 00503 count--; 00504 buffer[count] = '0' + i % 10; 00505 i = i / 10; 00506 } while (i > 0); 00507 while (buffer[count]) { 00508 usb_cdc_putc(buffer[count]); 00509 count++; 00510 } 00511 //serial_print_str(&buffer[count]); // print it out 00512 // for(count = 0 ; str[count] != 0; count++) 00513 // { 00514 // } 00515 00516 }
void usb_cdc_print_str | ( | char * | str | ) |
Send a null terminated string out the virtual serial port
str | the string to be sent |
00464 { 00465 00466 uns8 count; 00467 buffer_descriptor *bd; 00468 00469 for(count = 0 ; str[count] != 0; count++) 00470 { 00471 usb_cdc_putc(str[count]); 00472 } 00473 00474 // This will give possibly quicker send: 00475 00476 //bd = ep_in_bd_location[CDC_DATA_ENDPOINT]; 00477 //if (!test_bit(bd->stat, UOWN)) { 00478 // usb_cdc_handle_tx(); 00479 //} 00480 // otherwise we wait for the SOF interrupt to send 00481 }
void usb_cdc_putc | ( | uns8 | c | ) |
Deliver a single character out the virtual serial port. This routine will add the character to the transmit buffer. The actual buffer will be physically sent on the Start Of Frame (SOF) interrupt (each 1ms) or on the end point interrupt - ie, when the last character or chunk of characters was sent.
c | The 8 bit byte to be transmitted. |
00348 { 00349 uns8 cdc_tx_next; 00350 bit my_store_gie; 00351 #ifdef CDC_IDE_DEBUG 00352 return; 00353 #endif 00354 00355 cdc_tx_next = cdc_tx_end + 1; // get next buffer position 00356 if (cdc_tx_next == USB_CDC_TX_BUFFER_SIZE) { // if we're at the end 00357 cdc_tx_next = 0; // wrap to the beginning 00358 } 00359 00360 if ((!intcon.GIE) && (cdc_tx_next == cdc_tx_start)) { 00361 return; 00362 } 00363 while (cdc_tx_next == cdc_tx_start) { 00364 00365 } 00366 00367 start_crit_sec(); 00368 00369 cdc_tx_buffer[cdc_tx_end] = c; // put it in 00370 cdc_tx_end = cdc_tx_next; // move pointer along 00371 00372 end_crit_sec(); 00373 00374 }
uns8 usb_cdc_rx_avail | ( | ) |
If one or more bytes are available in the USB serial port receive buffer, this routine will return true. If there are no bytes available, it will return false.
00461 { return cdc_rx_start != cdc_rx_end; }
void usb_cdc_setup | ( | ) |
Configures the default DTE rate, stop bits etc.
00488 { 00489 00490 current_bit_rate = SPBRG_9600; 00491 parity = 0; 00492 // 9600 00493 dte_rate.as_long = 9600; 00494 00495 }
uns8 usb_cdc_tx_empty | ( | ) |
Sometimes it is useful to see if the transmit buffer is empty, since then you can be sure your data is well on its way. In the case of USB, this means that the data has been at least placed into the outbound USB buffer; it's not possible to tell until after the fact if the data has actually been squirted out the USB port.
00462 { return cdc_tx_start == cdc_tx_end; }
void usb_ep_data_in_callback | ( | uns8 | end_point, | |
uns16 | byte_count | |||
) |
If you have called usb_send_data to transfer data to the host, this routine will be fired once this data has been transferred. You may send more data by using usb_send_data(). Since the current PicPack USB library supports only single buffering, transfer speed is limited by how quickly you can refill the buffer again. In the future, we may support double buffering (ping poing buffering) which will most likely improve transfer speeds (to be fair, transfer performance has not been a limiting factor in tests so far).
In order for this callback to be triggered, you must define USB_EP_DATA_CALLBACK in your config.h
end_point | The endpoint on which the data was transferred | |
byte_count | The number of bytes that were actually transferred |
00328 { 00329 #ifdef CDC_DEBUG 00330 serial_print_str(" EP data in: "); 00331 serial_print_int(byte_count); 00332 serial_print_str(" bytes "); 00333 #endif 00334 // data has been sent, so do we need to send more? 00335 if (end_point == USB_CDC_DATA_ENDPOINT) { // it's the data end point 00336 usb_cdc_handle_tx(); 00337 } 00338 }
void usb_ep_data_out_callback | ( | uns8 | end_point, | |
uns8 * | buffer_location, | |||
uns16 | byte_count | |||
) |
If data is sent to the device and the endpoint is not endpoint 0 (the control transfer endpoint) then this routine is called. Since the routine is passed the actual hardware buffer location, it is important to pull data out of the buffer as soon as possible in order to free up the buffer to receive more data. The buffer is re-primed only once this routine completes since PicPack only supports single-buffered mode. In the future, we may look at supporting double buffering (ping-pong buffering) in order to be able to receive more data even while this routine is being called.
In order for this callback to be triggered, you must define USB_EP_DATA_CALLBACK in your config.h
end_point | The endpoint the data was sent do | |
buffer_lcoation | The memory location of the USB buffer where the data was received into | |
byte_count | The number of bytes received |
00289 { 00290 uns8 cdc_rx_next; 00291 #ifdef CDC_DEBUG 00292 serial_print_str(" EP data out: "); 00293 serial_print_int(byte_count); 00294 serial_print_str(" bytes "); 00295 #endif 00296 00297 // We have some data! 00298 00299 if (end_point == USB_CDC_DATA_ENDPOINT) { // it's the data end point 00300 uns8 count; 00301 for (count = 0; count < byte_count; count++) { 00302 cdc_rx_next = cdc_rx_end + 1; // get next buffer position 00303 if (cdc_rx_next == USB_CDC_RX_BUFFER_SIZE) { // if we're at the end 00304 cdc_rx_next = 0; // then wrap to the beginning 00305 } 00306 if (cdc_rx_next != cdc_rx_start) { // if space in the fifo 00307 cdc_rx_buffer[cdc_rx_end] = buffer[count]; // put it in 00308 cdc_rx_end = cdc_rx_next; // and move pointer along 00309 } else { 00310 // else... just ignore it, we've lost a byte, no room in the inn 00311 break; 00312 } 00313 } 00314 } else { 00315 #ifdef CDC_DEBUG 00316 serial_print_str("data for ep "); 00317 serial_print_int(end_point); 00318 #endif 00319 } 00320 00321 #ifdef USB_CDC_USE_LEDS 00322 platform_leds_flash(3); 00323 #endif 00324 }
void usb_handle_class_ctrl_read_callback | ( | ) |
When a control transfer is taking place, this routine is called to indicate that a control read for the class has taken place. Since everything in USB land is all about what has just happened, this callback will occur after data has been transferred to the host. If you wish to send more data to the host, use usb_send_data(), or if your control read has sent all the data required, you will need to indicate that the state has changed by setting the control_mode variable to cm_CTRL_READ_AWAITING_STATUS. This will indicate to the stack that it should now wait for the status packet before completing the control transfer.
To allow this callback to trigger, ensure you define USB_CALLBACK_ON_CLASS_CTRL in your config.h
00273 { 00274 switch (usb_sdp.bRequest) { 00275 case req_GET_LINE_CODING: 00276 // we know we've already sent everything, so now wait for status 00277 control_mode = cm_CTRL_READ_AWAITING_STATUS; 00278 break; 00279 default: 00280 #ifdef CDC_DEBUG 00281 serial_print_str(" cl read ?? "); 00282 serial_print_int(usb_sdp.bRequest); 00283 #endif 00284 } 00285 00286 }
void usb_handle_class_ctrl_write_callback | ( | uns8 * | data, | |
uns16 | count | |||
) |
When a control transfer is taking place, this routine is called to indicate that a control write for the class has taken place. Since everything in USB land is all about what has just happened, this callback will occur after data has been received by the device. If you expect more data from the host, it will arrive in due course since endpoint 0 will be primed for more data automatically. If you have received all the data from the host, you will need to set the control_mode state variable to cm_CTRL_WRITE_SENDING_STATUS and then actually send the status by calling usb_send_status_ack(). Once the status has actually been sent, the control_mode state will automatically change to cm_IDLE to indicate the transfer has completed.
To allow this callback to trigger, ensure you define USB_CALLBACK_ON_CLASS_CTRL in your config.h
00171 { 00172 00173 switch (usb_sdp.bRequest) { 00174 case req_SET_LINE_CODING: 00175 // dump it into class_data 00176 memcpy(/* dst */ (void *)&class_data,/* src */ (void *)data, count); 00177 00178 // Now we need to send an ACK status back 00179 00180 usb_send_status_ack(); 00181 control_mode = cm_CTRL_WRITE_SENDING_STATUS; 00182 00183 line_coding *my_lc; 00184 my_lc = (line_coding*) &class_data; 00185 #ifdef CDC_DEBUG 00186 serial_print_int_hex(my_lc->dte_rate.as_byte_array[0]); 00187 serial_print_int_hex(my_lc->dte_rate.as_byte_array[1]); 00188 serial_print_int_hex(my_lc->dte_rate.as_byte_array[2]); 00189 serial_print_int_hex(my_lc->dte_rate.as_byte_array[3]); 00190 serial_print_str(" st="); 00191 serial_print_int(my_lc->stop_bits); 00192 serial_print_str(" p="); 00193 serial_print_int(my_lc->parity); 00194 serial_print_str(" db="); 00195 serial_print_int(my_lc->data_bits); 00196 serial_print_str(" bit rate: "); 00197 #endif 00198 00199 dte_rate.as_byte_array[0] = my_lc->dte_rate.as_byte_array[3]; 00200 dte_rate.as_byte_array[1] = my_lc->dte_rate.as_byte_array[2]; 00201 dte_rate.as_byte_array[2] = my_lc->dte_rate.as_byte_array[1]; 00202 dte_rate.as_byte_array[3] = my_lc->dte_rate.as_byte_array[0]; 00203 parity = my_lc->parity; 00204 data_bits = my_lc->data_bits; 00205 00206 switch (my_lc->dte_rate.as_long) { 00207 case 2400: 00208 current_bit_rate = SPBRG_2400; 00209 #ifdef CDC_DEBUG 00210 serial_print_str("2400 "); 00211 #endif 00212 break; 00213 case 4800: 00214 current_bit_rate = SPBRG_4800; 00215 #ifdef CDC_DEBUG 00216 serial_print_str("4800 "); 00217 #endif 00218 break; 00219 case 9600: 00220 current_bit_rate = SPBRG_9600; 00221 #ifdef CDC_DEBUG 00222 serial_print_str("9600 "); 00223 #endif 00224 break; 00225 case 19200: 00226 current_bit_rate = SPBRG_19200; 00227 #ifdef CDC_DEBUG 00228 serial_print_str("19200 "); 00229 #endif 00230 break; 00231 case 38400: 00232 current_bit_rate = SPBRG_38400; 00233 #ifdef CDC_DEBUG 00234 serial_print_str("38400 "); 00235 #endif 00236 break; 00237 case 115200: 00238 current_bit_rate = SPBRG_115200; 00239 #ifdef CDC_DEBUG 00240 serial_print_str("115200 "); 00241 #endif 00242 break; 00243 default: 00244 #ifdef CDC_DEBUG 00245 serial_print_str("Don't handle this bit rate"); 00246 #endif 00247 } 00248 00249 clear_bit(rcsta, SPEN); 00250 clear_bit(txsta, TXEN); 00251 clear_bit(rcsta, CREN); 00252 00253 00254 serial_setup(current_bit_rate); 00255 if (!serial_tx_empty()) { 00256 #ifdef USB_CDC_USE_LEDS 00257 platform_leds_flash(1); 00258 #endif 00259 set_bit(pie1, TXIE); 00260 serial_tx_isr(); 00261 } 00262 break; 00263 default: 00264 #ifdef CDC_DEBUG 00265 serial_print_str(" ??cw req="); 00266 serial_print_int_hex(usb_sdp.bRequest); 00267 serial_putc(' '); 00268 #endif 00269 break; 00270 } 00271 }
void usb_handle_class_request_callback | ( | setup_data_packet | sdp | ) |
After receiving a setup packet, where the request is placed on the class, this routine is called. In usb_handle_class_request_callback, you can set up ready for the data stage of the control transfer. The direction of the data stage can be determined by examining test_bit(sdp.bRequest, DATA_STAGE_DIR) although generally it appears to be obvious from the request. The request is stored in sdp.bRequest.
Typically, if it is a control read transfer (that is, it is a request by the host for data), then you will need to move the control_mode state variable to cm_CTRL_READ_DATA_STAGE_CLASS and send data using usb_send_data(). If you only intend to send one packet, you can immediately move the control_mode state variable to cm_CTRL_READ_AWAITING_STATUS to indicate you are waiting for the status to arrive. You could wait for the usb_handle_class_ctrl_read callback and do it (move to cm_CTROL_READ_AWAITING_STATUS) but the PicPack USB stack can handle the control read event for you if you've already switched states.
If it is a control write transfer (that is, it is a request by the host to send data to the device), then you will need to move the control_mode state variable to cm_CTRL_WRITE_DATA_STAGE_CLASS. Then, the usb_handle_class_ctrl_write will be fired when data is received by the device in the data stage.
To allow this callback to trigger, ensure you define USB_CALLBACK_ON_CLASS_CTRL in your config.h
00115 { 00116 00117 switch (sdp.bRequest) { 00118 case req_SET_LINE_CODING: 00119 // we now expect the line coding to arrive in the data stage 00120 00121 #ifdef CDC_DEBUG 00122 serial_print_str("SET_LINE "); 00123 #endif 00124 control_mode = cm_CTRL_WRITE_DATA_STAGE_CLASS; 00125 break; 00126 case req_GET_LINE_CODING: 00127 #ifdef CDC_DEBUG 00128 serial_print_str("GET_LINE "); 00129 serial_print_str(" len="); 00130 serial_print_int(sdp.wLength); 00131 serial_putc(' '); 00132 #endif 00133 //control_mode = cm_CTRL_READ_DATA_STAGE_CLASS; 00134 control_mode = cm_CTRL_READ_DATA_STAGE_CLASS; 00135 // need to prime ep0 IN with some funky data here 00136 line_coding my_line_coding; 00137 00138 // We stored dte rate from ealier 00139 my_line_coding.dte_rate.as_byte_array[0] = dte_rate.as_byte_array[3]; 00140 my_line_coding.dte_rate.as_byte_array[1] = dte_rate.as_byte_array[2]; 00141 my_line_coding.dte_rate.as_byte_array[2] = dte_rate.as_byte_array[1]; 00142 my_line_coding.dte_rate.as_byte_array[3] = dte_rate.as_byte_array[0]; 00143 my_line_coding.stop_bits = 0; // 1 stop bit 00144 my_line_coding.data_bits = data_bits; 00145 my_line_coding.parity = parity; 00146 00147 usb_send_data(/*ep*/ 0, /*data*/ (uns8 *)&my_line_coding, /*count*/ sizeof(my_line_coding), /*first*/ 1); 00148 // actually we know this will be the last packet, so go straight to waiting for the status ack 00149 control_mode = cm_CTRL_READ_AWAITING_STATUS; 00150 00151 break; 00152 case req_SET_CONTROL_LINE_STATE: 00153 #ifdef CDC_DEBUG 00154 serial_print_str("scls=");//dtr = bit 0, rts = bit 1 00155 serial_print_int_hex(sdp.wValue); 00156 #endif 00157 // no data, so just ack the status 00158 // !!! set dtr, rts 00159 usb_send_status_ack(); 00160 control_mode = cm_CTRL_WRITE_SENDING_STATUS; 00161 // Could put a callback here for your own code when DTR or RTS change 00162 break; 00163 default: 00164 #ifdef CDC_DEBUG 00165 serial_print_str("??r="); 00166 serial_print_int(sdp.bRequest); 00167 #endif 00168 } 00169 }
void usb_SOF_callback | ( | uns16 | frame | ) |
Frames in USB occur each 1ms. A SOF packet is sent to each device at the start of each frame. This is a really neat way of getting a 1ms timer without any further work.
frame | The frame number. Frames will wrap at 65535. |
00483 { 00484 // we don't care about the frame number, we only care if there's something to send... 00485 usb_cdc_handle_tx(); // start transmission 00486 }
uns8 cdc_rx_buffer[USB_CDC_RX_BUFFER_SIZE] |
Receive fifo
uns8 cdc_rx_end = 0 |
Receive fifo end point
uns8 cdc_rx_start = 0 |
Receive fifo start point
uns8 cdc_tx_buffer[USB_CDC_TX_BUFFER_SIZE] |
Transmit fifo
uns8 cdc_tx_end = 0 |
Transmit fifo end point
uns8 cdc_tx_start = 0 |
Transmit fifo start point
uns8 class_data[8] |
uns16 current_bit_rate |
uns8 data_bits |
uns8 parity |